sequel-snowflake 2.2.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/gem-push.yml +2 -2
- data/.github/workflows/ruby.yml +2 -2
- data/CHANGELOG.md +5 -0
- data/README.md +6 -1
- data/lib/sequel/adapters/snowflake.rb +16 -3
- data/lib/sequel-snowflake/version.rb +1 -1
- data/mise.toml +2 -0
- data/spec/sequel/adapters/snowflake_spec.rb +152 -19
- metadata +4 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d6b1159aee571c7ffc1ebf32e71fbdc7e572cbd98eafae9c88443b2363dd5754
|
|
4
|
+
data.tar.gz: 8f17176c4dc7405215dac368192083f9ccb6ea8fe15c42128ff696521e8f928e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5721cfb399697e66a49c15f1e508a7ef2387822f6101150285e0c0cb8642321a0f74caaaaf9ceb22f7bcc7ecac24d214d9201997524bdbcfd72532defffe6d68
|
|
7
|
+
data.tar.gz: d16d45d262ceedf60d3b95909322c9020bcacebfbc4de0f4a8fbe6bae33233179844f6738c7ac493218379cc8305d68319e608f280bbe93543e90d98974df51f
|
data/.github/workflows/ruby.yml
CHANGED
|
@@ -19,7 +19,7 @@ jobs:
|
|
|
19
19
|
runs-on: ubuntu-latest
|
|
20
20
|
strategy:
|
|
21
21
|
matrix:
|
|
22
|
-
ruby-version: ['3.
|
|
22
|
+
ruby-version: ['3.3', '3.4']
|
|
23
23
|
|
|
24
24
|
steps:
|
|
25
25
|
- uses: actions/checkout@v2
|
|
@@ -30,7 +30,7 @@ jobs:
|
|
|
30
30
|
- name: Install Snowflake ODBC driver
|
|
31
31
|
run: curl ${SNOWFLAKE_DRIVER_URL} -o snowflake_driver.deb && sudo dpkg -i snowflake_driver.deb
|
|
32
32
|
env:
|
|
33
|
-
SNOWFLAKE_DRIVER_URL: https://sfc-repo.snowflakecomputing.com/odbc/linux/3.
|
|
33
|
+
SNOWFLAKE_DRIVER_URL: https://sfc-repo.snowflakecomputing.com/odbc/linux/3.12.0/snowflake-odbc-3.12.0.x86_64.deb
|
|
34
34
|
- name: Set up Ruby
|
|
35
35
|
uses: ruby/setup-ruby@v1
|
|
36
36
|
with:
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
|
5
5
|
and this project adheres to [Semantic Versioning](http://semver.org/).
|
|
6
6
|
|
|
7
|
+
## 2.3.0 / 2025-11-17
|
|
8
|
+
* Add support for `GROUP CUBE`
|
|
9
|
+
* Add support for `GROUP ROLLUP`
|
|
10
|
+
* Add support for `GROUPING SETS`
|
|
11
|
+
|
|
7
12
|
## 2.2.0 / 2023-10-17
|
|
8
13
|
* Add support for `MERGE` (credit: @benalavi)
|
|
9
14
|
* Add requirement for `sequel` v5.58.0 or newer (to support the new MERGE methods).
|
data/README.md
CHANGED
|
@@ -24,6 +24,11 @@ You'll also need [unixODBC](http://www.unixodbc.org/) (if on Linux/macOS) and th
|
|
|
24
24
|
this adapter. Follow the Snowflake documentation on their ODBC Driver
|
|
25
25
|
[here](https://docs.snowflake.com/en/user-guide/odbc.html) before proceeding.
|
|
26
26
|
|
|
27
|
+
After installing, you may need to configure `simba.snowflake.ini` and set
|
|
28
|
+
`DriverManagerEncoding` to "UTF-16", the default encoding for unixODBC. If
|
|
29
|
+
unixODBC was built specifically with the `DSQL_WCHART_CONVERT` flag, then
|
|
30
|
+
Snowflake's default of "UTF-32" is fine as-is.
|
|
31
|
+
|
|
27
32
|
## Usage
|
|
28
33
|
|
|
29
34
|
When establishing the connection, specify `:snowflake` as the adapter to use.
|
|
@@ -57,7 +62,7 @@ be taken down either via the `after(:each)` blocks or when the connection is clo
|
|
|
57
62
|
|
|
58
63
|
We have two workflows included in this project:
|
|
59
64
|
|
|
60
|
-
* Ruby (`ruby.yml`): This runs the specs for this gem against Ruby 3.
|
|
65
|
+
* Ruby (`ruby.yml`): This runs the specs for this gem against Ruby 3.3 and 3.4. Note
|
|
61
66
|
that this requires the secret `SNOWFLAKE_CONN_STR` to be set (see above for example connection string),
|
|
62
67
|
as we need to connect to Snowflake to run tests. These specs will be run for every pull request,
|
|
63
68
|
and is run after every commit to those branches.
|
|
@@ -38,9 +38,22 @@ module Sequel
|
|
|
38
38
|
self
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
-
#
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
# https://docs.snowflake.com/en/sql-reference/constructs/group-by-cube
|
|
42
|
+
def supports_group_cube?
|
|
43
|
+
true
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# https://docs.snowflake.com/en/sql-reference/constructs/group-by-rollup
|
|
47
|
+
def supports_group_rollup?
|
|
48
|
+
true
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# https://docs.snowflake.com/en/sql-reference/constructs/group-by-grouping-sets
|
|
52
|
+
def supports_grouping_sets?
|
|
53
|
+
true
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# https://docs.snowflake.com/en/sql-reference/sql/merge
|
|
44
57
|
def supports_merge?
|
|
45
58
|
true
|
|
46
59
|
end
|
data/mise.toml
ADDED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
require 'securerandom'
|
|
2
2
|
|
|
3
3
|
describe Sequel::Snowflake::Dataset do
|
|
4
|
-
let(:db) { Sequel.connect(adapter: :snowflake, drvconnect: ENV['SNOWFLAKE_CONN_STR']) }
|
|
4
|
+
let(:db) { @db ||= Sequel.connect(adapter: :snowflake, drvconnect: ENV['SNOWFLAKE_CONN_STR']) }
|
|
5
|
+
|
|
6
|
+
before(:all) do
|
|
7
|
+
@db = Sequel.connect(adapter: :snowflake, drvconnect: ENV['SNOWFLAKE_CONN_STR'])
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
after(:all) do
|
|
11
|
+
@db.disconnect if @db
|
|
12
|
+
end
|
|
5
13
|
|
|
6
14
|
describe 'Converting Snowflake data types' do
|
|
7
15
|
# Create a test table with a reasonably-random suffix
|
|
@@ -70,33 +78,157 @@ describe Sequel::Snowflake::Dataset do
|
|
|
70
78
|
end
|
|
71
79
|
end
|
|
72
80
|
|
|
81
|
+
describe 'GROUP BY features' do
|
|
82
|
+
before(:all) do
|
|
83
|
+
@products = "SEQUEL_SNOWFLAKE_SPECS_#{SecureRandom.hex(10)}".to_sym
|
|
84
|
+
@sales = "SEQUEL_SNOWFLAKE_SPECS_#{SecureRandom.hex(10)}".to_sym
|
|
85
|
+
|
|
86
|
+
@db.create_table(@products, :temp => true) do
|
|
87
|
+
Integer :product_id
|
|
88
|
+
Float :wholesale_price
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
@db.create_table(@sales, :temp => true) do
|
|
92
|
+
Integer :product_id
|
|
93
|
+
Float :retail_price
|
|
94
|
+
Integer :quantity
|
|
95
|
+
String :city
|
|
96
|
+
String :state
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
@db[@products].insert({ product_id: 1, wholesale_price: 1.00 })
|
|
100
|
+
@db[@products].insert({ product_id: 2, wholesale_price: 2.00 })
|
|
101
|
+
@db[@sales].insert({ product_id: 1, retail_price: 2.00, quantity: 1, city: 'SF', state: 'CA' })
|
|
102
|
+
@db[@sales].insert({ product_id: 1, retail_price: 2.00, quantity: 2, city: 'SJ', state: 'CA' })
|
|
103
|
+
@db[@sales].insert({ product_id: 2, retail_price: 5.00, quantity: 4, city: 'SF', state: 'CA' })
|
|
104
|
+
@db[@sales].insert({ product_id: 2, retail_price: 5.00, quantity: 8, city: 'SJ', state: 'CA' })
|
|
105
|
+
@db[@sales].insert({ product_id: 2, retail_price: 5.00, quantity: 16, city: 'Miami', state: 'FL' })
|
|
106
|
+
@db[@sales].insert({ product_id: 2, retail_price: 5.00, quantity: 32, city: 'Orlando', state: 'FL' })
|
|
107
|
+
@db[@sales].insert({ product_id: 2, retail_price: 5.00, quantity: 64, city: 'SJ', state: 'CA' })
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
after(:all) do
|
|
111
|
+
@db.drop_table(@products) if @products
|
|
112
|
+
@db.drop_table(@sales) if @sales
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
let(:products) { @products }
|
|
116
|
+
let(:sales) { @sales }
|
|
117
|
+
|
|
118
|
+
it 'can use GROUP CUBE' do
|
|
119
|
+
res = db.from(Sequel[products].as(:p)).
|
|
120
|
+
join(Sequel[sales].as(:s), Sequel[:p][:product_id] => Sequel[:s][:product_id]).
|
|
121
|
+
select(
|
|
122
|
+
Sequel[:s][:state],
|
|
123
|
+
Sequel[:s][:city],
|
|
124
|
+
Sequel.function(:sum, Sequel.*(Sequel.-(Sequel[:s][:retail_price], Sequel[:p][:wholesale_price]), Sequel[:s][:quantity])).as(:profit)
|
|
125
|
+
).
|
|
126
|
+
group(Sequel[:s][:state], Sequel[:s][:city]).
|
|
127
|
+
group_cube.
|
|
128
|
+
order(Sequel.asc(Sequel[:s][:state], nulls: :last)).
|
|
129
|
+
order_append(Sequel[:s][:city]).
|
|
130
|
+
all
|
|
131
|
+
|
|
132
|
+
expect(res).to match_array([
|
|
133
|
+
{ state: 'CA', city: 'SF', profit: 13 },
|
|
134
|
+
{ state: 'CA', city: 'SJ', profit: 218 },
|
|
135
|
+
{ state: 'CA', city: nil, profit: 231 },
|
|
136
|
+
{ state: 'FL', city: 'Miami', profit: 48 },
|
|
137
|
+
{ state: 'FL', city: 'Orlando', profit: 96 },
|
|
138
|
+
{ state: 'FL', city: nil, profit: 144 },
|
|
139
|
+
{ state: nil, city: 'Miami', profit: 48 },
|
|
140
|
+
{ state: nil, city: 'Orlando', profit: 96 },
|
|
141
|
+
{ state: nil, city: 'SF', profit: 13 },
|
|
142
|
+
{ state: nil, city: 'SJ', profit: 218 },
|
|
143
|
+
{ state: nil, city: nil, profit: 375 },
|
|
144
|
+
])
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
it 'can use GROUP ROLLUP' do
|
|
148
|
+
res = db.from(Sequel[products].as(:p)).
|
|
149
|
+
join(Sequel[sales].as(:s), Sequel[:p][:product_id] => Sequel[:s][:product_id]).
|
|
150
|
+
select(
|
|
151
|
+
Sequel[:s][:state],
|
|
152
|
+
Sequel[:s][:city],
|
|
153
|
+
Sequel.function(:sum, Sequel.*(Sequel.-(Sequel[:s][:retail_price], Sequel[:p][:wholesale_price]), Sequel[:s][:quantity])).as(:profit)
|
|
154
|
+
).
|
|
155
|
+
group(Sequel[:s][:state], Sequel[:s][:city]).
|
|
156
|
+
group_rollup.
|
|
157
|
+
order(Sequel.asc(Sequel[:s][:state], nulls: :last)).
|
|
158
|
+
order_append(Sequel[:s][:city]).
|
|
159
|
+
all
|
|
160
|
+
|
|
161
|
+
expect(res).to match_array([
|
|
162
|
+
{ state: 'CA', city: 'SF', profit: 13 },
|
|
163
|
+
{ state: 'CA', city: 'SJ', profit: 218 },
|
|
164
|
+
{ state: 'CA', city: nil, profit: 231 },
|
|
165
|
+
{ state: 'FL', city: 'Miami', profit: 48 },
|
|
166
|
+
{ state: 'FL', city: 'Orlando', profit: 96 },
|
|
167
|
+
{ state: 'FL', city: nil, profit: 144 },
|
|
168
|
+
{ state: nil, city: nil, profit: 375 },
|
|
169
|
+
])
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
it 'can use GROUPING SETS' do
|
|
173
|
+
res = db.from(Sequel[products].as(:p)).
|
|
174
|
+
join(Sequel[sales].as(:s), Sequel[:p][:product_id] => Sequel[:s][:product_id]).
|
|
175
|
+
select(
|
|
176
|
+
Sequel[:s][:state],
|
|
177
|
+
Sequel[:s][:city],
|
|
178
|
+
Sequel.function(:sum, Sequel.*(Sequel.-(Sequel[:s][:retail_price], Sequel[:p][:wholesale_price]), Sequel[:s][:quantity])).as(:profit)
|
|
179
|
+
).
|
|
180
|
+
group([Sequel[:s][:state]], [Sequel[:s][:city]]).
|
|
181
|
+
grouping_sets.
|
|
182
|
+
order(Sequel.asc(Sequel[:s][:state], nulls: :last)).
|
|
183
|
+
order_append(Sequel[:s][:city]).
|
|
184
|
+
all
|
|
185
|
+
|
|
186
|
+
expect(res).to match_array([
|
|
187
|
+
{ state: 'CA', city: nil, profit: 231 },
|
|
188
|
+
{ state: 'FL', city: nil, profit: 144 },
|
|
189
|
+
{ state: nil, city: 'Miami', profit: 48 },
|
|
190
|
+
{ state: nil, city: 'Orlando', profit: 96 },
|
|
191
|
+
{ state: nil, city: 'SF', profit: 13 },
|
|
192
|
+
{ state: nil, city: 'SJ', profit: 218 },
|
|
193
|
+
])
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
73
197
|
describe 'MERGE feature' do
|
|
74
|
-
|
|
75
|
-
|
|
198
|
+
before(:all) do
|
|
199
|
+
@target_table = "SEQUEL_SNOWFLAKE_SPECS_#{SecureRandom.hex(10)}".to_sym
|
|
200
|
+
@source_table = "SEQUEL_SNOWFLAKE_SPECS_#{SecureRandom.hex(10)}".to_sym
|
|
76
201
|
|
|
77
|
-
|
|
78
|
-
db.create_table(target_table, :temp => true) do
|
|
202
|
+
@db.create_table(@target_table, :temp => true) do
|
|
79
203
|
String :str
|
|
80
204
|
String :str2
|
|
81
205
|
String :str3
|
|
82
206
|
end
|
|
83
207
|
|
|
84
|
-
db.create_table(source_table, :temp => true) do
|
|
208
|
+
@db.create_table(@source_table, :temp => true) do
|
|
85
209
|
String :from
|
|
86
210
|
String :to
|
|
87
211
|
String :whomst
|
|
88
212
|
end
|
|
213
|
+
end
|
|
89
214
|
|
|
90
|
-
|
|
91
|
-
db
|
|
92
|
-
db
|
|
215
|
+
after(:all) do
|
|
216
|
+
@db.drop_table(@target_table) if @target_table
|
|
217
|
+
@db.drop_table(@source_table) if @source_table
|
|
93
218
|
end
|
|
94
219
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
db.
|
|
220
|
+
before(:each) do
|
|
221
|
+
# Clear and repopulate data for each test since MERGE modifies data
|
|
222
|
+
db[@target_table].delete
|
|
223
|
+
db[@source_table].delete
|
|
224
|
+
db[@target_table].insert({ str: 'foo', str2: 'foo', str3: 'phoo' })
|
|
225
|
+
db[@target_table].insert({ str: 'baz', str2: 'foo', str3: 'buzz' })
|
|
226
|
+
db[@source_table].insert({ from: 'foo', to: 'bar', whomst: 'me' })
|
|
98
227
|
end
|
|
99
228
|
|
|
229
|
+
let(:target_table) { @target_table }
|
|
230
|
+
let(:source_table) { @source_table }
|
|
231
|
+
|
|
100
232
|
it 'can use MERGE' do
|
|
101
233
|
db[target_table].merge_using(source_table, str: :from).merge_update(str2: :to).merge
|
|
102
234
|
|
|
@@ -108,26 +240,27 @@ describe Sequel::Snowflake::Dataset do
|
|
|
108
240
|
end
|
|
109
241
|
|
|
110
242
|
describe '#explain' do
|
|
111
|
-
|
|
112
|
-
|
|
243
|
+
before(:all) do
|
|
244
|
+
@test_table = "SEQUEL_SNOWFLAKE_SPECS_#{SecureRandom.hex(10)}".to_sym
|
|
113
245
|
|
|
114
|
-
|
|
115
|
-
db.create_table(test_table, :temp => true) do
|
|
246
|
+
@db.create_table(@test_table, :temp => true) do
|
|
116
247
|
Numeric :id
|
|
117
248
|
String :name
|
|
118
249
|
String :email
|
|
119
250
|
String :title
|
|
120
251
|
end
|
|
121
252
|
|
|
122
|
-
db[test_table].insert(
|
|
253
|
+
@db[@test_table].insert(
|
|
123
254
|
{ id: 1, name: 'John Null', email: 'j.null@example.com', title: 'Software Tester' }
|
|
124
255
|
)
|
|
125
256
|
end
|
|
126
257
|
|
|
127
|
-
after(:
|
|
128
|
-
db.drop_table(test_table)
|
|
258
|
+
after(:all) do
|
|
259
|
+
@db.drop_table(@test_table) if @test_table
|
|
129
260
|
end
|
|
130
261
|
|
|
262
|
+
let(:test_table) { @test_table }
|
|
263
|
+
|
|
131
264
|
it "should have explain output" do
|
|
132
265
|
query = db.fetch("SELECT * FROM #{test_table} WHERE ID=1;")
|
|
133
266
|
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sequel-snowflake
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yesware, Inc
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: sequel
|
|
@@ -100,6 +99,7 @@ files:
|
|
|
100
99
|
- lib/sequel-snowflake/version.rb
|
|
101
100
|
- lib/sequel/adapters/shared/snowflake.rb
|
|
102
101
|
- lib/sequel/adapters/snowflake.rb
|
|
102
|
+
- mise.toml
|
|
103
103
|
- sequel-snowflake.gemspec
|
|
104
104
|
- spec/sequel/adapters/snowflake_spec.rb
|
|
105
105
|
- spec/snowflake_spec.rb
|
|
@@ -108,7 +108,6 @@ homepage: https://github.com/vendasta/sequel-snowflake
|
|
|
108
108
|
licenses:
|
|
109
109
|
- MIT
|
|
110
110
|
metadata: {}
|
|
111
|
-
post_install_message:
|
|
112
111
|
rdoc_options: []
|
|
113
112
|
require_paths:
|
|
114
113
|
- lib
|
|
@@ -123,8 +122,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
123
122
|
- !ruby/object:Gem::Version
|
|
124
123
|
version: '0'
|
|
125
124
|
requirements: []
|
|
126
|
-
rubygems_version: 3.
|
|
127
|
-
signing_key:
|
|
125
|
+
rubygems_version: 3.6.9
|
|
128
126
|
specification_version: 4
|
|
129
127
|
summary: Sequel adapter for Snowflake
|
|
130
128
|
test_files:
|