sequelizer 0.1.4 → 0.1.6

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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/settings.local.json +64 -0
  3. data/.devcontainer/.p10k.zsh +1713 -0
  4. data/.devcontainer/.zshrc +29 -0
  5. data/.devcontainer/Dockerfile +137 -0
  6. data/.devcontainer/copy-claude-credentials.sh +32 -0
  7. data/.devcontainer/devcontainer.json +102 -0
  8. data/.devcontainer/init-firewall.sh +123 -0
  9. data/.devcontainer/setup-credentials.sh +95 -0
  10. data/.github/workflows/test.yml +1 -1
  11. data/.gitignore +6 -1
  12. data/.overcommit.yml +73 -0
  13. data/.rubocop.yml +167 -0
  14. data/CHANGELOG.md +24 -0
  15. data/CLAUDE.md +219 -0
  16. data/Gemfile +6 -2
  17. data/Gemfile.lock +158 -0
  18. data/Guardfile +1 -1
  19. data/Rakefile +28 -3
  20. data/lib/sequel/extensions/cold_col.rb +436 -0
  21. data/lib/sequel/extensions/db_opts.rb +65 -4
  22. data/lib/sequel/extensions/make_readyable.rb +148 -30
  23. data/lib/sequel/extensions/more_sql.rb +76 -0
  24. data/lib/sequel/extensions/settable.rb +64 -0
  25. data/lib/sequel/extensions/sql_recorder.rb +85 -0
  26. data/lib/sequel/extensions/unionize.rb +169 -0
  27. data/lib/sequel/extensions/usable.rb +30 -1
  28. data/lib/sequelizer/cli.rb +61 -18
  29. data/lib/sequelizer/connection_maker.rb +54 -72
  30. data/lib/sequelizer/env_config.rb +6 -6
  31. data/lib/sequelizer/gemfile_modifier.rb +23 -21
  32. data/lib/sequelizer/monkey_patches/database_in_after_connect.rb +7 -5
  33. data/lib/sequelizer/options.rb +97 -18
  34. data/lib/sequelizer/options_hash.rb +2 -0
  35. data/lib/sequelizer/version.rb +3 -1
  36. data/lib/sequelizer/yaml_config.rb +9 -3
  37. data/lib/sequelizer.rb +65 -9
  38. data/sequelizer.gemspec +12 -7
  39. data/test/lib/sequel/extensions/test_cold_col.rb +251 -0
  40. data/test/lib/sequel/extensions/test_db_opts.rb +10 -8
  41. data/test/lib/sequel/extensions/test_make_readyable.rb +199 -28
  42. data/test/lib/sequel/extensions/test_more_sql.rb +132 -0
  43. data/test/lib/sequel/extensions/test_settable.rb +109 -0
  44. data/test/lib/sequel/extensions/test_sql_recorder.rb +231 -0
  45. data/test/lib/sequel/extensions/test_unionize.rb +76 -0
  46. data/test/lib/sequel/extensions/test_usable.rb +5 -2
  47. data/test/lib/sequelizer/test_connection_maker.rb +21 -17
  48. data/test/lib/sequelizer/test_env_config.rb +5 -2
  49. data/test/lib/sequelizer/test_gemfile_modifier.rb +7 -6
  50. data/test/lib/sequelizer/test_options.rb +14 -9
  51. data/test/lib/sequelizer/test_yaml_config.rb +13 -12
  52. data/test/test_helper.rb +36 -8
  53. metadata +107 -28
  54. data/lib/sequel/extensions/sqls.rb +0 -31
@@ -1,10 +1,12 @@
1
- require_relative "../../../test_helper"
2
- require "fileutils"
3
- require "pathname"
4
- require "sequel"
5
- require "sequel/extensions/make_readyable"
1
+ require_relative '../../../test_helper'
2
+ require 'fileutils'
3
+ require 'tmpdir'
4
+ require 'pathname'
5
+ require 'sequel'
6
+ require 'sequel/extensions/make_readyable'
6
7
 
7
8
  class TestUsable < Minitest::Test
9
+
8
10
  def setup
9
11
  # These features are mostly intended for Spark, but sqlite is a close enough
10
12
  # mock that we'll just roll with it
@@ -15,74 +17,243 @@ class TestUsable < Minitest::Test
15
17
  when :schema1
16
18
  [:a]
17
19
  when :schema2
18
- [:a, :b]
20
+ %i[a b]
19
21
  when :schema3
20
- [:a, :b]
22
+ %i[a b]
21
23
  end
22
24
  end
23
25
  end
24
26
 
25
27
  def test_should_call_use_schema
26
28
  @db.make_ready(use_schema: :some_schema)
27
- assert_equal(["USE `some_schema`"], @db.sqls)
29
+
30
+ assert_equal(['USE `some_schema`'], @db.sqls)
28
31
  end
29
32
 
30
33
  def test_should_create_views_based_on_tables_in_search_paths
31
- @db.make_ready(search_path: [:schema1, :schema2, :schema3])
34
+ @db.make_ready(search_path: %i[schema1 schema2 schema3])
35
+
32
36
  assert_equal([
33
- "CREATE TEMPORARY VIEW `a` AS SELECT * FROM `schema1`.`a`",
34
- "CREATE TEMPORARY VIEW `b` AS SELECT * FROM `schema2`.`b`"
35
- ], @db.sqls)
37
+ 'CREATE TEMPORARY VIEW `a` AS SELECT * FROM `schema1`.`a`',
38
+ 'CREATE TEMPORARY VIEW `b` AS SELECT * FROM `schema2`.`b`',
39
+ ], @db.sqls)
36
40
  end
37
41
 
38
42
  def test_should_create_views_based_on_tables_in_search_paths_passed_as_strings
39
- @db.make_ready(search_path: ["schema1", "schema2", "schema3"])
43
+ @db.make_ready(search_path: %w[schema1 schema2 schema3])
44
+
40
45
  assert_equal([
41
- "CREATE TEMPORARY VIEW `a` AS SELECT * FROM `schema1`.`a`",
42
- "CREATE TEMPORARY VIEW `b` AS SELECT * FROM `schema2`.`b`"
43
- ], @db.sqls)
46
+ 'CREATE TEMPORARY VIEW `a` AS SELECT * FROM `schema1`.`a`',
47
+ 'CREATE TEMPORARY VIEW `b` AS SELECT * FROM `schema2`.`b`',
48
+ ], @db.sqls)
44
49
  end
45
50
 
46
51
  def test_should_create_views_based_on_tables_in_search_paths_accepts_except
47
- @db.make_ready(search_path: [:schema1, :schema2, :schema3], except: :a)
52
+ @db.make_ready(search_path: %i[schema1 schema2 schema3], except: :a)
53
+
48
54
  assert_equal([
49
- "CREATE TEMPORARY VIEW `b` AS SELECT * FROM `schema2`.`b`"
50
- ], @db.sqls)
55
+ 'CREATE TEMPORARY VIEW `b` AS SELECT * FROM `schema2`.`b`',
56
+ ], @db.sqls)
51
57
  end
52
58
 
53
59
  def test_should_create_views_based_on_tables_in_search_paths_accepts_only
54
- @db.make_ready(search_path: [:schema1, :schema2, :schema3], only: :b)
60
+ @db.make_ready(search_path: %i[schema1 schema2 schema3], only: :b)
61
+
55
62
  assert_equal([
56
- "CREATE TEMPORARY VIEW `b` AS SELECT * FROM `schema2`.`b`"
57
- ], @db.sqls)
63
+ 'CREATE TEMPORARY VIEW `b` AS SELECT * FROM `schema2`.`b`',
64
+ ], @db.sqls)
58
65
  end
59
66
 
60
67
  def test_should_create_views_based_on_path
61
68
  dir = Pathname.new(Dir.mktmpdir)
62
- a_file = dir + "a.parquet"
63
- b_file = dir + "b.parquet"
69
+ a_file = dir.join('a.parquet')
70
+ b_file = dir.join('b.parquet')
64
71
  FileUtils.touch(a_file.to_s)
65
72
  FileUtils.touch(b_file.to_s)
66
73
 
67
74
  @db.make_ready(search_path: [:schema1, a_file, b_file, :schema2])
68
75
  sqls = @db.sqls.dup
69
- assert_equal("CREATE TEMPORARY VIEW `a` AS SELECT * FROM `schema1`.`a`", sqls[0])
76
+
77
+ assert_equal('CREATE TEMPORARY VIEW `a` AS SELECT * FROM `schema1`.`a`', sqls[0])
70
78
  assert_match(%r{CREATE TEMPORARY VIEW `b` USING parquet OPTIONS \('path'='/tmp/[^/]+/b.parquet'\)}, sqls[1])
71
79
  end
72
80
 
73
81
  def test_should_create_views_format_based_on_path
74
82
  dir = Pathname.new(Dir.mktmpdir)
75
- a_file = dir + "a.parquet"
76
- b_file = dir + "b.delta"
77
- c_file = dir + "c.csv"
83
+ a_file = dir.join('a.parquet')
84
+ b_file = dir.join('b.delta')
85
+ c_file = dir.join('c.csv')
78
86
  FileUtils.touch(a_file.to_s)
79
87
  FileUtils.touch(b_file.to_s)
80
88
  FileUtils.touch(c_file.to_s)
81
89
 
82
90
  @db.make_ready(search_path: [a_file, b_file, c_file])
83
91
  sqls = @db.sqls.dup
92
+
84
93
  assert_match(%r{CREATE TEMPORARY VIEW `a` USING parquet OPTIONS \('path'='/tmp/[^/]+/a.parquet'\)}, sqls[0])
85
94
  assert_match(%r{CREATE TEMPORARY VIEW `b` USING delta OPTIONS \('path'='/tmp/[^/]+/b.delta'\)}, sqls[1])
86
95
  assert_match(%r{CREATE TEMPORARY VIEW `c` USING csv OPTIONS \('path'='/tmp/[^/]+/c.csv'\)}, sqls[2])
87
96
  end
97
+
98
+ def test_should_create_a_single_view_if_multiple_files_have_the_same_name
99
+ dir = Pathname.new(Dir.mktmpdir)
100
+ a_file = dir.join('a.parquet')
101
+ b_file = dir.join('a.delta')
102
+ c_file = dir.join('a.csv')
103
+ FileUtils.touch(a_file.to_s)
104
+ FileUtils.touch(b_file.to_s)
105
+ FileUtils.touch(c_file.to_s)
106
+
107
+ @db.make_ready(search_path: [a_file, b_file, c_file])
108
+ sqls = @db.sqls.dup
109
+
110
+ assert_equal(1, sqls.size)
111
+ assert_match(%r{CREATE TEMPORARY VIEW `a` USING parquet OPTIONS \('path'='/tmp/[^/]+/a.parquet'\)}, sqls[0])
112
+ refute_match(%r{CREATE TEMPORARY VIEW `a` USING delta OPTIONS \('path'='/tmp/[^/]+/a.delta'\)}, sqls[1])
113
+ refute_match(%r{CREATE TEMPORARY VIEW `a` USING csv OPTIONS \('path'='/tmp/[^/]+/a.csv'\)}, sqls[2])
114
+ end
115
+
116
+ def test_should_create_a_single_view_if_multiple_files_have_the_same_name_and_are_in_different_directories
117
+ dir = Pathname.new(Dir.mktmpdir)
118
+ a_file = dir / 'one' / 'a.parquet'
119
+ b_file = dir / 'two' / 'a.delta'
120
+ c_file = dir / 'three' / 'a.csv'
121
+ FileUtils.mkdir_p(a_file.dirname)
122
+ FileUtils.mkdir_p(b_file.dirname)
123
+ FileUtils.mkdir_p(c_file.dirname)
124
+ FileUtils.touch(a_file.to_s)
125
+ FileUtils.touch(b_file.to_s)
126
+ FileUtils.touch(c_file.to_s)
127
+
128
+ @db.make_ready(search_path: [a_file, b_file, c_file])
129
+ sqls = @db.sqls.dup
130
+
131
+ assert_equal(1, sqls.size)
132
+ assert_match(%r{CREATE TEMPORARY VIEW `a` USING parquet OPTIONS \('path'='/tmp/[^/]+/one/a.parquet'\)}, sqls[0])
133
+ refute_match(%r{CREATE TEMPORARY VIEW `a` USING delta OPTIONS \('path'='/tmp/[^/]+/two/a.delta'\)}, sqls[1])
134
+ refute_match(%r{CREATE TEMPORARY VIEW `a` USING csv OPTIONS \('path'='/tmp/[^/]+/three/a.csv'\)}, sqls[2])
135
+ end
136
+
137
+ def test_should_create_view_from_compact_style_path
138
+ dir = Pathname.new(Dir.mktmpdir)
139
+ a_file = dir / 'one' / 'a.parquet'
140
+ b_file = dir / 'two' / 'b.delta'
141
+ c_file = dir / 'three' / 'c.csv'
142
+ FileUtils.mkdir_p(a_file.dirname)
143
+ FileUtils.mkdir_p(b_file.dirname)
144
+ FileUtils.mkdir_p(c_file.dirname)
145
+ FileUtils.touch(a_file.to_s)
146
+ FileUtils.touch(b_file.to_s)
147
+ FileUtils.touch(c_file.to_s)
148
+
149
+ @db.make_ready(search_path: [Dir["#{dir}/{one,two,three}"].map { |path| Pathname.new(path).glob('*') }])
150
+ sqls = @db.sqls.dup
151
+
152
+ assert_equal(3, sqls.size)
153
+ assert_match(%r{CREATE TEMPORARY VIEW `a` USING parquet OPTIONS \('path'='/tmp/[^/]+/one/a.parquet'\)}, sqls[0])
154
+ assert_match(%r{CREATE TEMPORARY VIEW `b` USING delta OPTIONS \('path'='/tmp/[^/]+/two/b.delta'\)}, sqls[1])
155
+ assert_match(%r{CREATE TEMPORARY VIEW `c` USING csv OPTIONS \('path'='/tmp/[^/]+/three/c.csv'\)}, sqls[2])
156
+ end
157
+
158
+ def test_should_create_view_from_compact_style_path_with_multiple_files
159
+ dir = Pathname.new(Dir.mktmpdir)
160
+ a_file = dir / 'one' / 'a.parquet'
161
+ b_file = dir / 'two' / 'a.delta'
162
+ c_file = dir / 'three' / 'a.csv'
163
+ FileUtils.mkdir_p(a_file.dirname)
164
+ FileUtils.mkdir_p(b_file.dirname)
165
+ FileUtils.mkdir_p(c_file.dirname)
166
+ FileUtils.touch(a_file.to_s)
167
+ FileUtils.touch(b_file.to_s)
168
+ FileUtils.touch(c_file.to_s)
169
+
170
+ @db.make_ready(search_path: [Dir["#{dir}/{one,two,three}"].map { |path| Pathname.new(path).glob('*') }])
171
+ sqls = @db.sqls.dup
172
+
173
+ assert_equal(1, sqls.size)
174
+ assert_match(%r{CREATE TEMPORARY VIEW `a` USING parquet OPTIONS \('path'='/tmp/[^/]+/one/a.parquet'\)}, sqls[0])
175
+ refute_match(%r{CREATE TEMPORARY VIEW `a` USING delta OPTIONS \('path'='/tmp/[^/]+/two/a.delta'\)}, sqls[1])
176
+ refute_match(%r{CREATE TEMPORARY VIEW `a` USING csv OPTIONS \('path'='/tmp/[^/]+/three/a.csv'\)}, sqls[2])
177
+ end
178
+
179
+ def test_duckdb_external_file_support
180
+ # Test DuckDB - uses read_* functions for external files
181
+ duckdb_db = Sequel.mock
182
+ duckdb_db.extension :make_readyable
183
+ def duckdb_db.database_type
184
+ :duckdb
185
+ end
186
+
187
+ dir = Pathname.new(Dir.mktmpdir)
188
+ parquet_file = dir / 'test.parquet'
189
+ csv_file = dir / 'test.csv'
190
+ json_file = dir / 'test.json'
191
+ FileUtils.touch(parquet_file.to_s)
192
+ FileUtils.touch(csv_file.to_s)
193
+ FileUtils.touch(json_file.to_s)
194
+
195
+ # Test parquet file
196
+ duckdb_db.make_ready(search_path: [parquet_file])
197
+
198
+ assert_match(/CREATE VIEW \w*test\w* AS SELECT \* FROM read_parquet\('.*test\.parquet'\)/, duckdb_db.sqls.last)
199
+
200
+ # Test CSV file
201
+ duckdb_db.sqls.clear
202
+ duckdb_db.make_ready(search_path: [csv_file])
203
+
204
+ assert_match(/CREATE VIEW \w*test\w* AS SELECT \* FROM read_csv_auto\('.*test\.csv'\)/, duckdb_db.sqls.last)
205
+
206
+ # Test JSON file
207
+ duckdb_db.sqls.clear
208
+ duckdb_db.make_ready(search_path: [json_file])
209
+
210
+ assert_match(/CREATE VIEW \w*test\w* AS SELECT \* FROM read_json_auto\('.*test\.json'\)/, duckdb_db.sqls.last)
211
+ end
212
+
213
+ def test_unsupported_file_format_for_duckdb
214
+ # Test unsupported file format for DuckDB
215
+ duckdb_db = Sequel.mock
216
+ duckdb_db.extension :make_readyable
217
+ def duckdb_db.database_type
218
+ :duckdb
219
+ end
220
+
221
+ dir = Pathname.new(Dir.mktmpdir)
222
+ orc_file = dir / 'test.orc'
223
+ FileUtils.touch(orc_file.to_s)
224
+
225
+ error = assert_raises(Sequel::Error) do
226
+ duckdb_db.make_ready(search_path: [orc_file])
227
+ end
228
+ assert_match(/Unsupported file format 'orc' for DuckDB/, error.message)
229
+ end
230
+
231
+ def test_duckdb_directory_support
232
+ # Test DuckDB with directory paths (globbing)
233
+ duckdb_db = Sequel.mock
234
+ duckdb_db.extension :make_readyable
235
+ def duckdb_db.database_type
236
+ :duckdb
237
+ end
238
+
239
+ # Mock directory? to return true
240
+ dir = Pathname.new(Dir.mktmpdir)
241
+ def dir.directory?
242
+ true
243
+ end
244
+
245
+ file_sourcerer = Sequel::ReadyMaker::FileSourcerer.new(duckdb_db, dir)
246
+
247
+ # Override format to return parquet
248
+ def file_sourcerer.format
249
+ 'parquet'
250
+ end
251
+
252
+ # For directory support, DuckDB should use globbing pattern
253
+ file_sourcerer.create_view(:test_table)
254
+
255
+ assert_match(%r{CREATE VIEW \w*test_table\w* AS SELECT \* FROM read_parquet\('.*/\*\*/\*\.parquet'\)},
256
+ duckdb_db.sqls.last)
257
+ end
258
+
88
259
  end
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+ require 'sequel'
5
+ require 'sequel/extensions/more_sql'
6
+
7
+ class TestMoreSql < Minitest::Test
8
+
9
+ def setup
10
+ @db = Sequel.mock
11
+ @db.extension :more_sql
12
+ end
13
+
14
+ def test_create_schema_with_symbol_name
15
+ @db.create_schema(:test_schema)
16
+
17
+ sqls = @db.sqls
18
+
19
+ assert_equal 1, sqls.length
20
+ assert_equal 'CREATE SCHEMA test_schema', sqls.first
21
+ end
22
+
23
+ def test_create_schema_with_string_name
24
+ @db.create_schema('my_schema')
25
+
26
+ sqls = @db.sqls
27
+
28
+ assert_equal 1, sqls.length
29
+ assert_equal "CREATE SCHEMA 'my_schema'", sqls.first
30
+ end
31
+
32
+ def test_create_schema_with_if_not_exists_option
33
+ @db.create_schema(:analytics, if_not_exists: true)
34
+
35
+ sqls = @db.sqls
36
+
37
+ assert_equal 1, sqls.length
38
+ assert_equal 'CREATE SCHEMA IF NOT EXISTS analytics', sqls.first
39
+ end
40
+
41
+ def test_create_schema_without_if_not_exists_option
42
+ @db.create_schema(:reports, if_not_exists: false)
43
+
44
+ sqls = @db.sqls
45
+
46
+ assert_equal 1, sqls.length
47
+ assert_equal 'CREATE SCHEMA reports', sqls.first
48
+ end
49
+
50
+ def test_create_schema_with_empty_options
51
+ @db.create_schema(:staging, {})
52
+
53
+ sqls = @db.sqls
54
+
55
+ assert_equal 1, sqls.length
56
+ assert_equal 'CREATE SCHEMA staging', sqls.first
57
+ end
58
+
59
+ def test_create_schema_with_special_characters_in_name
60
+ @db.create_schema('schema-with-dashes')
61
+
62
+ sqls = @db.sqls
63
+
64
+ assert_equal 1, sqls.length
65
+ assert_equal "CREATE SCHEMA 'schema-with-dashes'", sqls.first
66
+ end
67
+
68
+ def test_create_schema_returns_nil
69
+ result = @db.create_schema(:test)
70
+
71
+ assert_nil result
72
+ end
73
+
74
+ def test_create_schema_multiple_calls
75
+ @db.create_schema(:first_schema)
76
+ @db.create_schema(:second_schema, if_not_exists: true)
77
+ @db.create_schema('third_schema')
78
+
79
+ sqls = @db.sqls
80
+
81
+ assert_equal 3, sqls.length
82
+ assert_equal 'CREATE SCHEMA first_schema', sqls[0]
83
+ assert_equal 'CREATE SCHEMA IF NOT EXISTS second_schema', sqls[1]
84
+ assert_equal "CREATE SCHEMA 'third_schema'", sqls[2]
85
+ end
86
+
87
+ def test_extension_registration
88
+ assert_respond_to Sequel::Database, :extension
89
+
90
+ db = Sequel.mock
91
+
92
+ assert_respond_to db, :extension
93
+
94
+ db.extension :more_sql
95
+
96
+ assert_respond_to db, :create_schema
97
+ end
98
+
99
+ def test_create_schema_with_qualified_name
100
+ @db.create_schema(Sequel[:public][:my_schema])
101
+
102
+ sqls = @db.sqls
103
+
104
+ assert_equal 1, sqls.length
105
+ assert_match(/CREATE SCHEMA/, sqls.first)
106
+ end
107
+
108
+ def test_create_schema_with_identifier
109
+ @db.create_schema(Sequel.identifier(:test_schema))
110
+
111
+ sqls = @db.sqls
112
+
113
+ assert_equal 1, sqls.length
114
+ assert_equal 'CREATE SCHEMA test_schema', sqls.first
115
+ end
116
+
117
+ def test_create_schema_handles_nil_options
118
+ @db.create_schema(:test_schema)
119
+
120
+ sqls = @db.sqls
121
+
122
+ assert_equal 1, sqls.length
123
+ assert_equal 'CREATE SCHEMA test_schema', sqls.first
124
+ end
125
+
126
+ def test_private_create_schema_sql_method_not_accessible
127
+ assert_raises(NoMethodError) do
128
+ @db.create_schema_sql(:test, {})
129
+ end
130
+ end
131
+
132
+ end
@@ -0,0 +1,109 @@
1
+ require_relative '../../../test_helper'
2
+ require 'sequel'
3
+ require 'sequel/extensions/settable'
4
+
5
+ class TestSettable < Minitest::Test
6
+
7
+ def test_should_register_extension
8
+ db = Sequel.mock(host: :sqlite)
9
+
10
+ assert_respond_to db, :extension
11
+ db.extension :settable
12
+
13
+ assert_respond_to db, :set
14
+ end
15
+
16
+ def test_set_with_single_option
17
+ db = Sequel.mock(host: :sqlite)
18
+ db.extension :settable
19
+ db.set(search_path: 'public')
20
+
21
+ assert_equal(['SET search_path=public'], db.sqls)
22
+ end
23
+
24
+ def test_set_with_multiple_options
25
+ db = Sequel.mock(host: :sqlite)
26
+ db.extension :settable
27
+ db.set(search_path: 'public', timezone: 'UTC')
28
+
29
+ expected_sqls = ['SET search_path=public', 'SET timezone=UTC']
30
+
31
+ assert_equal expected_sqls, db.sqls
32
+ end
33
+
34
+ def test_set_with_empty_hash
35
+ db = Sequel.mock(host: :sqlite)
36
+ db.extension :settable
37
+ db.set({})
38
+
39
+ assert_empty(db.sqls)
40
+ end
41
+
42
+ def test_set_with_no_arguments
43
+ db = Sequel.mock(host: :sqlite)
44
+ db.extension :settable
45
+ db.set
46
+
47
+ assert_empty(db.sqls)
48
+ end
49
+
50
+ def test_set_with_string_values
51
+ db = Sequel.mock(host: :sqlite)
52
+ db.extension :settable
53
+ db.set(work_mem: '256MB', statement_timeout: '30s')
54
+
55
+ expected_sqls = ['SET work_mem=256MB', 'SET statement_timeout=30s']
56
+
57
+ assert_equal expected_sqls, db.sqls
58
+ end
59
+
60
+ def test_set_with_numeric_values
61
+ db = Sequel.mock(host: :sqlite)
62
+ db.extension :settable
63
+ db.set(port: 5432, max_connections: 100)
64
+
65
+ expected_sqls = ['SET port=5432', 'SET max_connections=100']
66
+
67
+ assert_equal expected_sqls, db.sqls
68
+ end
69
+
70
+ def test_set_with_boolean_values
71
+ db = Sequel.mock(host: :sqlite)
72
+ db.extension :settable
73
+ db.set(autocommit: true, log_statement: false)
74
+
75
+ expected_sqls = ['SET autocommit=true', 'SET log_statement=false']
76
+
77
+ assert_equal expected_sqls, db.sqls
78
+ end
79
+
80
+ def test_set_with_nil_values
81
+ db = Sequel.mock(host: :sqlite)
82
+ db.extension :settable
83
+ db.set(timezone: nil, search_path: nil)
84
+
85
+ expected_sqls = ['SET timezone=', 'SET search_path=']
86
+
87
+ assert_equal expected_sqls, db.sqls
88
+ end
89
+
90
+ def test_set_sql_private_method
91
+ db = Sequel.mock(host: :sqlite)
92
+ db.extension :settable
93
+
94
+ refute_respond_to db, :set_sql
95
+ end
96
+
97
+ def test_multiple_set_calls
98
+ db = Sequel.mock(host: :sqlite)
99
+ db.extension :settable
100
+
101
+ db.set(timezone: 'UTC')
102
+ db.set(search_path: 'public')
103
+
104
+ expected_sqls = ['SET timezone=UTC', 'SET search_path=public']
105
+
106
+ assert_equal expected_sqls, db.sqls
107
+ end
108
+
109
+ end