sequelizer 0.2.0 → 0.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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +43 -0
  3. data/LICENSE +21 -0
  4. data/README.md +28 -14
  5. data/lib/sequel/extensions/cold_col.rb +39 -15
  6. data/lib/sequel/extensions/funky.rb +1 -1
  7. data/lib/sequel/extensions/make_readyable.rb +18 -8
  8. data/lib/sequel/extensions/platform.rb +8 -9
  9. data/lib/sequel/extensions/smart_select_remove.rb +101 -0
  10. data/lib/sequelizer/options.rb +10 -5
  11. data/lib/sequelizer/version.rb +1 -1
  12. metadata +7 -52
  13. data/.beads/.gitignore +0 -54
  14. data/.beads/.jsonl.lock +0 -0
  15. data/.beads/.migration-hint-ts +0 -1
  16. data/.beads/README.md +0 -81
  17. data/.beads/config.yaml +0 -42
  18. data/.beads/issues.jsonl +0 -20
  19. data/.beads/metadata.json +0 -7
  20. data/.coderabbit.yaml +0 -94
  21. data/.devcontainer/.p10k.zsh +0 -1713
  22. data/.devcontainer/.zshrc +0 -29
  23. data/.devcontainer/Dockerfile +0 -137
  24. data/.devcontainer/copy-claude-credentials.sh +0 -32
  25. data/.devcontainer/devcontainer.json +0 -102
  26. data/.devcontainer/init-firewall.sh +0 -123
  27. data/.devcontainer/setup-credentials.sh +0 -95
  28. data/.github/dependabot.yml +0 -18
  29. data/.github/workflows/dependabot-auto-merge.yml +0 -36
  30. data/.github/workflows/test.yml +0 -49
  31. data/.gitignore +0 -40
  32. data/.overcommit.yml +0 -73
  33. data/.rubocop.yml +0 -167
  34. data/AGENTS.md +0 -126
  35. data/CLAUDE.md +0 -230
  36. data/Gemfile +0 -12
  37. data/Gemfile.lock +0 -189
  38. data/Guardfile +0 -28
  39. data/LICENSE.txt +0 -22
  40. data/Rakefile +0 -35
  41. data/examples/database.with_one_set_of_options.yml +0 -15
  42. data/examples/database.yml +0 -30
  43. data/examples/dot_env.txt +0 -10
  44. data/sequelizer.gemspec +0 -38
  45. data/test/lib/sequel/extensions/test_cold_col.rb +0 -251
  46. data/test/lib/sequel/extensions/test_db_opts.rb +0 -42
  47. data/test/lib/sequel/extensions/test_make_readyable.rb +0 -258
  48. data/test/lib/sequel/extensions/test_more_sql.rb +0 -132
  49. data/test/lib/sequel/extensions/test_platform.rb +0 -222
  50. data/test/lib/sequel/extensions/test_settable.rb +0 -109
  51. data/test/lib/sequel/extensions/test_sql_recorder.rb +0 -231
  52. data/test/lib/sequel/extensions/test_unionize.rb +0 -76
  53. data/test/lib/sequel/extensions/test_usable.rb +0 -15
  54. data/test/lib/sequelizer/test_connection_maker.rb +0 -123
  55. data/test/lib/sequelizer/test_env_config.rb +0 -37
  56. data/test/lib/sequelizer/test_gemfile_modifier.rb +0 -88
  57. data/test/lib/sequelizer/test_options.rb +0 -111
  58. data/test/lib/sequelizer/test_yaml_config.rb +0 -83
  59. data/test/test_helper.rb +0 -45
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f067d6d5a8661d14249493213e942d02a9a9d8b2aca1148915ee02acf5062c1f
4
- data.tar.gz: eb913ecf490d79fe57fa6d0d8dfb3321703b4e852bd8cac6e2745610cbd48370
3
+ metadata.gz: 348bea43e8571c35056619a31d45731f55b48a24cb4b93e94f23ecf7eba3a4af
4
+ data.tar.gz: 170ae6de8d06dc7f8030fd97151b1ff7b5666a6d94849a4fe3e056a863938a65
5
5
  SHA512:
6
- metadata.gz: 34060489fcade8850421f28c0ee062ab6f134fab779b0925ffa55e7078468f7bc4a70bcdc38e9e29fecc4c7a01acdbd11aedf56d315d8e8fa04da0d82692b09e
7
- data.tar.gz: 635178a3e8c60a531885b2df7e4ad468c8301ea67d55a4d64c7ded78268f66a38e13cc901c5550ca1d00b239539400e3212496a248e388b4d9d632eed6914220
6
+ metadata.gz: 497923374b34e892918aa86dea037c0b25323dc8d829398bb0e97d77386c13e248b68d47f4c7c31650d099fdf85d9cf62203703c2576f31310054f9937df9119
7
+ data.tar.gz: f6a8dd133701a962fab9b16a0c0df30a4897d9c08526db07ec7ddef9029eb7140adcfbbab44974be7da2d970f2203308aa419b7112baad046947a50ad4ad93f5
data/CHANGELOG.md CHANGED
@@ -2,14 +2,42 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [0.3.0] - 2026-10-01
6
+
7
+ ### Breaking changes
8
+
9
+ - Drop support for Ruby 3.2 ([feef6de](https://github.com/outcomesinsights/sequelizer/commit/feef6de5c155dc0dd39642ad1686ff9bab0211de)): sequelizer now requires Ruby >= 3.3.0.
10
+
11
+ ### Added
12
+
13
+ - Add smart_select_remove Sequel extension ([a1366ec](https://github.com/outcomesinsights/sequelizer/commit/a1366ec8f68e64728b3c794de15c2fd78cfa9e2e))
14
+ - DuckDB support in make_readyable and cold_col extensions ([9433d98](https://github.com/outcomesinsights/sequelizer/commit/9433d982bc0855df11ac6f48a915e861fd78a572))
15
+
16
+ ### Changed
17
+
18
+ - Extract available_tables to fix AbcSize in ReadyMaker#run ([60c6a68](https://github.com/outcomesinsights/sequelizer/commit/60c6a6803afbfde2b0747fd19e3bcd346bf2d1ef))
19
+ - Simplify cold_col find_columns and remove rescue nil guards ([752eab1](https://github.com/outcomesinsights/sequelizer/commit/752eab14595af23974ea56a976dd99e29d9d5552))
20
+ - Remove OptionalAdapterSupport, add sequel-duckdb to bundle ([74d6d16](https://github.com/outcomesinsights/sequelizer/commit/74d6d16f564742138a32e111d7b6107289abec75))
21
+ - Let Sequel handle adapter loading, remove pre-require ([c2b592d](https://github.com/outcomesinsights/sequelizer/commit/c2b592d1965e24370dae2f5cfb515097358cd3da))
22
+ - Ship only runtime files in the gem: lib/, the sequelizer executable, the platform CSVs, and the docs and licence ([1cc49b6](https://github.com/outcomesinsights/sequelizer/commit/1cc49b69dec0c6c879a469f0774c1196c467ed17))
23
+
24
+ ### Fixed
25
+
26
+ - Fall back to database query in cold_col for unregistered tables ([d07f7c5](https://github.com/outcomesinsights/sequelizer/commit/d07f7c5e482074e631828afbf558a767d6695b66))
27
+ - Replace rescue modifier with explicit begin/rescue in cold_col ([6fced2b](https://github.com/outcomesinsights/sequelizer/commit/6fced2b4957277b5c6d45fabf11e89d0b476a8e5))
28
+ - Handle string SQL in cold_col create_table_as and create_view_sql ([8f30602](https://github.com/outcomesinsights/sequelizer/commit/8f306028857e3838370fc623cd619a9a79c8c06f))
29
+ - Handle Array search_path in after_connect ([c376825](https://github.com/outcomesinsights/sequelizer/commit/c3768251695286668ca49dcbb84aa12c3bbb7f69))
30
+
5
31
  ## 0.2.0
6
32
 
7
33
  ### Added
34
+
8
35
  - funky Sequel extension for database-specific function abstraction
9
36
  - kvcsv as a runtime dependency
10
37
  - Platform configuration fixtures in config/platforms/
11
38
 
12
39
  ### Changed
40
+
13
41
  - Broadened activesupport dependency from ~> 7.0 to >= 7, < 9
14
42
  - Broadened hashie dependency from ~> 3.2 to >= 3.2, < 6.0
15
43
  - Broadened dotenv dependency from ~> 2.1 to >= 2.1, < 4.0
@@ -17,11 +45,13 @@ All notable changes to this project will be documented in this file.
17
45
  - Added multi-Ruby CI matrix (3.2, 3.3, 3.4, 4.0) with separate lint job
18
46
 
19
47
  ### Fixed
48
+
20
49
  - Adapter extraction from URL scheme for search_path processing
21
50
 
22
51
  ## 0.1.6
23
52
 
24
53
  ### Removed
54
+
25
55
  - JDBC adapter support (jdbc_hive2, jdbc_impala, jdbc_postgres) - not needed for foreseeable future
26
56
  - Native Impala adapter support - not needed for foreseeable future
27
57
  - Kerberos authentication functionality for enterprise databases
@@ -29,9 +59,11 @@ All notable changes to this project will be documented in this file.
29
59
  - 16 test methods related to JDBC and Impala adapters
30
60
 
31
61
  ### Added
62
+
32
63
  - pg gem as development dependency to support PostgreSQL testing
33
64
 
34
65
  ### Changed
66
+
35
67
  - Simplified ConnectionMaker class by removing adapter-specific configuration methods
36
68
  - Improved test coverage from 84.95% to 93.54% by removing unused code paths
37
69
  - Updated documentation to reflect focus on standard Sequel adapters
@@ -45,11 +77,13 @@ All notable changes to this project will be documented in this file.
45
77
  ## [0.1.0] - 2016-11-08
46
78
 
47
79
  ### Added
80
+
48
81
  - Connections are cached by options to avoid over-allocation
49
82
  - URL or URI option represent connection string
50
83
  - sequelizer.yml is passed through ERB
51
84
 
52
85
  ### Changed
86
+
53
87
  - Format of this [CHANGELOG](http://keepachangelog.com/en/0.3.0/)
54
88
  - Prefer environment variables over other options
55
89
  - Use config/sequelizer.yml instead of config/database.yml
@@ -57,34 +91,41 @@ All notable changes to this project will be documented in this file.
57
91
  ## [0.0.6] - 2015-08-21
58
92
 
59
93
  ### Added
94
+
60
95
  - Support for ruby-oci8 (Oracle) in update_gemfile command
61
96
  - Read user-level configuration from ~/.config/sequelizer/database.yml
62
97
 
63
98
  ### Fixed
99
+
64
100
  - Bug where options passed as symbols where sometimes ignored.
65
101
 
66
102
  ## [0.0.5] - 2014-08-29
67
103
 
68
104
  ### Added
105
+
69
106
  - Support for TinyTDS (SQL Server) in update_gemfile command
70
107
 
71
108
  ### Fixed
109
+
72
110
  - timeout is always converted to integer before passing to Sequel.connect
73
111
 
74
112
  ## [0.0.4] - 2014-08-18
75
113
 
76
114
  ### Added
115
+
77
116
  - `init_env` command to bin/sequelizer
78
117
  - Prefer `search_path` over `schema_search_path` option
79
118
 
80
119
  ## [0.0.3] - 2014-07-10
81
120
 
82
121
  ### Added
122
+
83
123
  - Ability to view configuration by running `sequelizer config`
84
124
 
85
125
  ## [0.0.2] - 2014-07-10
86
126
 
87
127
  ### Added
128
+
88
129
  - This [CHANGELOG](http://keepachangelog.com/)
89
130
  - Behavior to merge options from all sources (#3)
90
131
  - Dependency on hashie gem
@@ -93,9 +134,11 @@ All notable changes to this project will be documented in this file.
93
134
  - Reference to issues #1, #3 in README
94
135
 
95
136
  ### Fixed
137
+
96
138
  - Prevented TestEnvConfig from polluting environment variables
97
139
 
98
140
  ## [0.0.1] - 2014-07-10
99
141
 
100
142
  ### Added
143
+
101
144
  - The project itself
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2014-2026 Outcomes Insights, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md CHANGED
@@ -12,35 +12,46 @@ I normally use this gem when I'm writing a quick script or a Thor-based command
12
12
 
13
13
  Add this line to your application's Gemfile:
14
14
 
15
- gem 'sequelizer'
15
+ ```
16
+ gem 'sequelizer'
17
+ ```
16
18
 
17
19
  And then execute:
18
20
 
19
- $ bundle
21
+ ```
22
+ $ bundle
23
+ ```
20
24
 
21
25
  Or install it yourself as:
22
26
 
23
- $ gem install sequelizer
27
+ ```
28
+ $ gem install sequelizer
29
+ ```
24
30
 
25
31
  ## Usage
26
32
 
27
33
  To get the most out of Sequelizer, you'll need to create a config/database.yml file or a .env file and specify your database configuration options in that file.
28
34
 
29
- Sequelizer comes with a handy command that will print out your connection parameters. Just run:
35
+ Sequelizer comes with a handy command that will print out your connection parameters. Just run:
30
36
 
31
- bundle exec sequelizer config
37
+ ```
38
+ bundle exec sequelizer config
39
+ ```
32
40
 
33
- You'll also need to make sure the gem for your database is installed. You can do this by adding the gem to your application's Gemfile.
41
+ You'll also need to make sure the gem for your database is installed. You can do this by adding the gem to your application's Gemfile.
34
42
 
35
- Sequelizer comes with a handy command that will update your Gemfile for you. Once you've specified your database configuration, run
43
+ Sequelizer comes with a handy command that will update your Gemfile for you. Once you've specified your database configuration, run
36
44
 
37
- bundle exec sequelizer update_gemfile
45
+ ```
46
+ bundle exec sequelizer update_gemfile
47
+ ```
38
48
 
39
49
  The command will look up the right gem to use with the adapter you've specified, add a line to your Gemfile specifying that gem, and run `bundle install` to install the gem for you.
40
50
 
41
51
  Once you've specified your options and made sure your database's gem is installed, simply include the Sequelizer module in any class that needs a database connection and you'll get two handy-dandy methods: `db` and `new_db`
42
52
 
43
53
  Observe:
54
+
44
55
  ```ruby
45
56
  require 'sequelizer'
46
57
 
@@ -59,17 +70,19 @@ end
59
70
 
60
71
  `new_db` will create a new connection to the database on each call.
61
72
 
62
- Both take a hash of database options if you don't want to create a config/database.yml or .env file, or simply wish to override those options. Options are merged together from all sources with the following precedence:
73
+ Both take a hash of database options if you don't want to create a config/database.yml or .env file, or simply wish to override those options. Options are merged together from all sources with the following precedence:
63
74
 
64
- passed_options > .env > manually defined environment variables > config/database.yml > ~/.config/sequelizer/database.yml
75
+ ```
76
+ passed_options > .env > manually defined environment variables > config/database.yml > ~/.config/sequelizer/database.yml
77
+ ```
65
78
 
66
- So if config/database.yml specifies a connection, you can set an environment variable (either manually, or through .env) to override one of those options. Similarly, if you pass an option to the method directly, that option will override the YAML and ENV-based options. See #3 for further discussion.
79
+ So if config/database.yml specifies a connection, you can set an environment variable (either manually, or through .env) to override one of those options. Similarly, if you pass an option to the method directly, that option will override the YAML and ENV-based options. See #3 for further discussion.
67
80
 
68
81
  Take a look at the examples directory for a few ways you can specify your database configuration options.
69
82
 
70
83
  ## Frustrations
71
84
 
72
- I can't seem to figure out a way to avoid having to specify the database gem in the a user's bundler file. If anyone has ideas on how to automagically load the correct database gem based on the database options fed to Sequelizer, please let me know (#1)!
85
+ I can't seem to figure out a way to avoid having to specify the database gem in the a user's bundler file. If anyone has ideas on how to automagically load the correct database gem based on the database options fed to Sequelizer, please let me know (#1)!
73
86
 
74
87
  ## Contributing
75
88
 
@@ -82,9 +95,10 @@ I can't seem to figure out a way to avoid having to specify the database gem in
82
95
  ## Thanks
83
96
 
84
97
  - [Outcomes Insights, Inc.](http://outins.com)
85
- - Many thanks for allowing me to release a portion of my work as Open Source Software!
98
+ - Many thanks for allowing me to release a portion of my work as Open Source Software!
86
99
  - Jeremy Evans
87
- - For writing Sequel!
100
+ - For writing Sequel!
88
101
 
89
102
  ## License
103
+
90
104
  Released under the MIT license, Copyright (c) 2014 Outcomes Insights, Inc.
@@ -88,37 +88,27 @@ module Sequel
88
88
  end
89
89
 
90
90
  # Find column information for a given table/view name.
91
- # This method searches through all available registries in priority order:
91
+ # Searches all registries in priority order:
92
92
  # 1. Created views (highest priority - most recent)
93
93
  # 2. Created tables (medium priority - session specific)
94
94
  # 3. Loaded schemas (lowest priority - external definitions)
95
95
  #
96
- # @param name [String, Symbol] the table/view name to look up
96
+ # @param name [String, Symbol, Sequel::SQL::Identifier, Sequel::SQL::QualifiedIdentifier]
97
97
  # @return [Array<Symbol>, nil] array of column names as symbols, or nil if not found
98
98
  def find_columns(name)
99
- table_name = name.to_s
100
99
  literal_name = @db.literal(name)
100
+ table_name = name_to_string(name)
101
101
 
102
- # Search through registries in priority order
103
102
  [@created_views, @created_tables, @schemas].each do |registry|
104
103
  next unless registry
105
104
 
106
- # Try literal representation first (most common for created tables/views)
107
105
  if (columns = Sequel.synchronize { registry[literal_name] })
108
106
  return columns.map { |c, _| c }
109
107
  end
110
108
 
111
- # Try string representation (for manually added schemas)
112
109
  if (columns = Sequel.synchronize { registry[table_name] })
113
110
  return columns.map { |c, _| c }
114
111
  end
115
-
116
- # Try finding by Sequel::LiteralString key (for test setup compatibility)
117
- registry.each_key do |key|
118
- if key.respond_to?(:to_s) && key.to_s == literal_name && (columns = Sequel.synchronize { registry[key] })
119
- return columns.map { |c, _| c }
120
- end
121
- end
122
112
  end
123
113
 
124
114
  nil
@@ -140,6 +130,22 @@ module Sequel
140
130
  Sequel.synchronize { @schemas = schemas_hash }
141
131
  end
142
132
 
133
+ private
134
+
135
+ # Extract a plain string name from any Sequel name type.
136
+ # Sequel::SQL::Identifier#to_s returns an inspect-like string, not the value,
137
+ # so we must unwrap expression types to get a usable lookup key.
138
+ def name_to_string(name)
139
+ case name
140
+ when Sequel::SQL::QualifiedIdentifier
141
+ "#{name_to_string(name.table)}.#{name_to_string(name.column)}"
142
+ when Sequel::SQL::Identifier
143
+ name.value.to_s
144
+ else
145
+ name.to_s
146
+ end
147
+ end
148
+
143
149
  end
144
150
 
145
151
  # Sets up the cold column tracking when the extension is loaded
@@ -172,7 +178,12 @@ module Sequel
172
178
 
173
179
  def create_table_as(name, sql, options = {})
174
180
  super.tap do |_|
175
- record_table(name, columns_from_sql(sql))
181
+ columns = begin
182
+ columns_from_sql(sql)
183
+ rescue StandardError
184
+ nil
185
+ end
186
+ record_table(name, columns) if columns
176
187
  end
177
188
  end
178
189
 
@@ -190,7 +201,12 @@ module Sequel
190
201
 
191
202
  def create_view_sql(name, source, options)
192
203
  super.tap do |_|
193
- record_view(name, columns_from_sql(source)) unless options[:dont_record]
204
+ columns = begin
205
+ columns_from_sql(source)
206
+ rescue StandardError
207
+ nil
208
+ end
209
+ record_view(name, columns) if columns && !options[:dont_record]
194
210
  end
195
211
  end
196
212
 
@@ -348,6 +364,14 @@ module Sequel
348
364
  return columns
349
365
  end
350
366
 
367
+ # Fall back to database query for real tables cold_col doesn't know about
368
+ begin
369
+ columns = db[name].columns!
370
+ return columns if columns && !columns.empty?
371
+ rescue StandardError
372
+ nil
373
+ end
374
+
351
375
  raise("Failed to find columns for #{literal(name)}")
352
376
  end
353
377
 
@@ -89,7 +89,7 @@ module Sequel
89
89
  def make_json_column(ds, key_column, value_column)
90
90
  json_object_col = Sequel.function(
91
91
  :json_object,
92
- key_column,
92
+ Sequel.cast_string(key_column),
93
93
  value_column,
94
94
  ).then do |json_object_col|
95
95
  Sequel.function(
@@ -87,13 +87,9 @@ module Sequel
87
87
  only_tables = Array(opts[:only])
88
88
  created_views = Array(opts[:except]) || []
89
89
  (opts[:search_path] || []).flatten.each do |schema|
90
- schema = schema.to_sym unless schema.is_a?(Pathname)
90
+ schema = schema.to_sym unless schema.is_a?(Pathname) || schema.is_a?(Sequel::SQL::QualifiedIdentifier)
91
91
  source = get_source(db, schema)
92
- tables = if schema.is_a?(Pathname)
93
- source.tables - created_views
94
- else
95
- source.tables(schema: schema) - created_views
96
- end
92
+ tables = available_tables(source, schema, created_views)
97
93
  tables &= only_tables unless only_tables.empty?
98
94
  tables.each do |table|
99
95
  create_view(source, table, schema)
@@ -102,6 +98,20 @@ module Sequel
102
98
  end
103
99
  end
104
100
 
101
+ # Returns tables (and views) from the source that haven't already been created.
102
+ #
103
+ # @param source [Sequel::Database, FileSourcerer] the source handler
104
+ # @param schema [Symbol, Pathname] the schema or file path
105
+ # @param created_views [Array<Symbol>] tables/views already created
106
+ # @return [Array<Symbol>] available table names
107
+ def available_tables(source, schema, created_views)
108
+ if schema.is_a?(Pathname)
109
+ source.tables - created_views
110
+ else
111
+ (source.tables(schema: schema) + source.views(schema: schema)) - created_views
112
+ end
113
+ end
114
+
105
115
  # Creates a temporary view for the given table.
106
116
  #
107
117
  # @param source [Object] the source (database or FileSourcerer)
@@ -110,9 +120,9 @@ module Sequel
110
120
  def create_view(source, table, schema)
111
121
  if schema.to_s =~ %r{/}
112
122
  source.create_view(table, temp: true)
123
+ elsif db.database_type == :duckdb
124
+ source.create_or_replace_view(table, db[Sequel.qualify(schema, table)], temp: true)
113
125
  else
114
- # For schema-based tables, just create temporary views
115
- # This extension is primarily for Spark SQL-based databases
116
126
  source.create_view(table, db[Sequel.qualify(schema, table)], temp: true)
117
127
  end
118
128
  end
@@ -265,20 +265,19 @@ module Sequel
265
265
  platform_class.new(db, *config_paths)
266
266
  end
267
267
 
268
- # Detect the effective adapter for platform selection.
269
- # For mock databases, use database_type or host option; otherwise use adapter_scheme.
268
+ # Returns the effective database type for platform selection.
269
+ #
270
+ # For real connections, database_type is authoritative. For mock
271
+ # connections whose shared adapter isn't installed (e.g. snowflake,
272
+ # athena), database_type returns :mock; fall back to opts[:host].
270
273
  #
271
274
  # @param db [Sequel::Database] Database connection
272
- # @return [Symbol] Effective adapter type
275
+ # @return [Symbol] Effective database type
273
276
  def self.effective_adapter(db)
274
- return db.adapter_scheme unless db.adapter_scheme == :mock
275
-
276
- # Mock databases: try database_type first (set for known types like postgres, spark)
277
277
  db_type = db.database_type
278
- return db_type if db_type && db_type != :mock
278
+ return db_type unless db_type == :mock
279
279
 
280
- # Fall back to host option (for unknown types like snowflake, athena)
281
- db.opts[:host]
280
+ db.opts[:host]&.to_sym
282
281
  end
283
282
 
284
283
  # Extension hook - called when extension is loaded
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # The smart_select_remove extension overrides select_remove to resolve
5
+ # column information from the dataset's expression tree before falling
6
+ # back to the original select_remove behavior (which executes SQL via
7
+ # columns!).
8
+ #
9
+ # This is essential for lazy query building patterns where datasets
10
+ # reference tables that may not exist yet at query-build time. By
11
+ # walking through from_self chains and inspecting inner SELECT lists,
12
+ # this extension can determine which columns to keep without touching
13
+ # the database.
14
+ #
15
+ # You should load this extension into all of a database's datasets:
16
+ #
17
+ # DB.extension(:smart_select_remove)
18
+ #
19
+ # This automatically loads the select_remove extension as well.
20
+ #
21
+ # Related module: Sequel::SmartSelectRemove
22
+
23
+ module Sequel
24
+
25
+ module SmartSelectRemove
26
+
27
+ # Override select_remove to attempt static column resolution before
28
+ # falling back to the original implementation.
29
+ #
30
+ # When the dataset has no explicit SELECT clause, walks through
31
+ # from_self chains to find inner column expressions and resolves
32
+ # them to outer-scope symbol names. This avoids the columns! call
33
+ # that the original select_remove uses, which would execute SQL.
34
+ #
35
+ # @param cols [Array<Symbol>] columns to remove
36
+ # @return [Sequel::Dataset] dataset with columns removed
37
+ def select_remove(*cols)
38
+ # If there's an explicit select, original behavior handles it
39
+ return super if @opts[:select] && !@opts[:select].empty?
40
+
41
+ # Try to resolve columns by walking the from_self chain
42
+ if (inner_cols = resolve_selected_columns)
43
+ outer_cols = inner_cols.map { |c| resolve_outer_column_name(c) }
44
+ # All columns must be resolvable; bail to super if any are opaque
45
+ return super if outer_cols.any?(&:nil?)
46
+
47
+ return select(*(outer_cols - cols))
48
+ end
49
+
50
+ super
51
+ end
52
+
53
+ private
54
+
55
+ # Walk through from_self chains to find the innermost explicit
56
+ # SELECT list. Returns nil if no explicit selection is found.
57
+ #
58
+ # @param ds [Sequel::Dataset] the dataset to inspect
59
+ # @return [Array, nil] the inner SELECT expressions, or nil
60
+ def resolve_selected_columns(ds = self)
61
+ opts = ds.opts
62
+ if (sel = opts[:select]) && !sel.empty?
63
+ sel
64
+ elsif (from = opts[:from]&.first)
65
+ case from
66
+ when Sequel::Dataset
67
+ resolve_selected_columns(from)
68
+ when Sequel::SQL::AliasedExpression
69
+ resolve_selected_columns(from.expression) if from.expression.is_a?(Sequel::Dataset)
70
+ end
71
+ end
72
+ end
73
+
74
+ # Map an inner SELECT expression to its outer-scope column name.
75
+ # After from_self, inner expressions become subquery output columns
76
+ # referenced by their alias or base name in the outer scope.
77
+ #
78
+ # @param col [Object] a column expression from the inner SELECT
79
+ # @return [Symbol, nil] the outer column name, or nil if unresolvable
80
+ def resolve_outer_column_name(col)
81
+ case col
82
+ when Symbol
83
+ col
84
+ when Sequel::SQL::AliasedExpression
85
+ a = col.alias
86
+ a.is_a?(Symbol) ? a : a.to_sym
87
+ when Sequel::SQL::QualifiedIdentifier
88
+ resolve_outer_column_name(col.column)
89
+ when Sequel::SQL::Identifier
90
+ col.value.to_sym
91
+ end
92
+ end
93
+
94
+ end
95
+
96
+ Database.register_extension(:smart_select_remove) do |db|
97
+ db.extension(:select_remove)
98
+ db.extend_datasets(SmartSelectRemove)
99
+ end
100
+
101
+ end
@@ -147,17 +147,22 @@ module Sequelizer
147
147
  # 1. Create each schema in the search path if it doesn't exist
148
148
  # 2. Set the search_path for the connection
149
149
  #
150
- # @param search_path [String] comma-separated list of PostgreSQL schemas
150
+ # @param search_path [String, Array<String>] PostgreSQL schema names as a
151
+ # comma-separated string, an Array of schema names, or a mixed Array
152
+ # containing comma-separated strings
151
153
  # @return [Proc] callback to execute after connection
152
- # @example
154
+ # @example String input
153
155
  # callback = after_connect('public,app_schema')
154
- # # When called, creates schemas and sets search_path
156
+ # @example Array input
157
+ # callback = after_connect(['public', 'app_schema'])
155
158
  def after_connect(search_path)
159
+ schemas = Array(search_path).flat_map { |p| p.to_s.split(',') }.map(&:strip)
160
+ path_string = schemas.join(', ')
156
161
  proc do |conn|
157
- search_path.split(',').map(&:strip).each do |schema|
162
+ schemas.each do |schema|
158
163
  conn.execute("CREATE SCHEMA IF NOT EXISTS #{schema}")
159
164
  end
160
- conn.execute("SET search_path TO #{search_path}")
165
+ conn.execute("SET search_path TO #{path_string}")
161
166
  end
162
167
  end
163
168
 
@@ -1,6 +1,6 @@
1
1
  module Sequelizer
2
2
 
3
3
  # Version for the gem
4
- VERSION = '0.2.0'.freeze
4
+ VERSION = '0.3.0'.freeze
5
5
 
6
6
  end