sequel-duckdb 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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +31 -20
  3. data/lib/sequel/duckdb/version.rb +1 -4
  4. metadata +3 -59
  5. data/.beads/.beads-credential-key +0 -1
  6. data/.beads/.gitignore +0 -66
  7. data/.beads/README.md +0 -85
  8. data/.beads/config.yaml +0 -56
  9. data/.beads/hooks/post-checkout +0 -24
  10. data/.beads/hooks/post-merge +0 -24
  11. data/.beads/hooks/pre-commit +0 -24
  12. data/.beads/hooks/pre-push +0 -24
  13. data/.beads/hooks/prepare-commit-msg +0 -24
  14. data/.beads/metadata.json +0 -7
  15. data/.kiro/specs/advanced-sql-features-implementation/design.md +0 -26
  16. data/.kiro/specs/advanced-sql-features-implementation/requirements.md +0 -43
  17. data/.kiro/specs/advanced-sql-features-implementation/tasks.md +0 -28
  18. data/.kiro/specs/duckdb-sql-syntax-compatibility/design.md +0 -272
  19. data/.kiro/specs/duckdb-sql-syntax-compatibility/requirements.md +0 -84
  20. data/.kiro/specs/duckdb-sql-syntax-compatibility/tasks.md +0 -107
  21. data/.kiro/specs/edge-cases-and-validation-fixes/requirements.md +0 -32
  22. data/.kiro/specs/integration-test-database-setup/design.md +0 -0
  23. data/.kiro/specs/integration-test-database-setup/requirements.md +0 -117
  24. data/.kiro/specs/sequel-duckdb-adapter/design.md +0 -549
  25. data/.kiro/specs/sequel-duckdb-adapter/requirements.md +0 -202
  26. data/.kiro/specs/sequel-duckdb-adapter/tasks.md +0 -292
  27. data/.kiro/specs/sql-expression-handling-fix/design.md +0 -331
  28. data/.kiro/specs/sql-expression-handling-fix/requirements.md +0 -86
  29. data/.kiro/specs/sql-expression-handling-fix/tasks.md +0 -25
  30. data/.kiro/specs/test-infrastructure-improvements/requirements.md +0 -106
  31. data/.kiro/steering/product.md +0 -26
  32. data/.kiro/steering/structure.md +0 -88
  33. data/.kiro/steering/tech.md +0 -137
  34. data/.kiro/steering/testing.md +0 -213
  35. data/.mdformat.toml +0 -2
  36. data/.release-please-manifest.json +0 -3
  37. data/.rubocop.yml +0 -161
  38. data/.rubocop_todo.yml +0 -323
  39. data/.yardopts +0 -8
  40. data/AGENTS.md +0 -154
  41. data/API_DOCUMENTATION.md +0 -943
  42. data/FINAL_STATUS.md +0 -99
  43. data/MIGRATION_EXAMPLES.md +0 -740
  44. data/PERFORMANCE_OPTIMIZATIONS.md +0 -726
  45. data/REFACTORING_SUMMARY.md +0 -264
  46. data/Rakefile +0 -43
  47. data/TASK_10.2_IMPLEMENTATION_SUMMARY.md +0 -182
  48. data/docs/DUCKDB_SQL_PATTERNS.md +0 -448
  49. data/docs/TASK_12_VERIFICATION_SUMMARY.md +0 -135
  50. data/justfile +0 -52
  51. data/plans/date_arithmetic.md +0 -420
  52. data/plans/engineering/Sequel.md +0 -471
  53. data/plans/engineering/duckdb.md +0 -712
  54. data/plans/engineering/sqlite.md +0 -453
  55. data/plans/mock_connection_bug.md +0 -333
  56. data/plans/mock_without_driver_gem.md +0 -371
  57. data/plans/over_engineering_analysis.md +0 -122
  58. data/plans/schema_management.md +0 -383
  59. data/release-please-config.json +0 -14
  60. data/sig/sequel/duckdb.rbs +0 -6
@@ -1,371 +0,0 @@
1
- # DuckDB Mock-Without-Driver Refactor Plan
2
-
3
- ## Goal
4
-
5
- Make DuckDB's Sequel adapter behave like Sequel's PostgreSQL adapter:
6
-
7
- - `Sequel.mock(host: :duckdb)` must work without the `duckdb` gem installed.
8
- - DuckDB-specific SQL generation and dataset behavior must remain available in mock mode.
9
- - Real DuckDB connections (`Sequel.connect("duckdb:...")`) must still require the `duckdb` gem and fail clearly if it is missing.
10
-
11
- This plan is intentionally detailed enough for a less capable agent to implement directly.
12
-
13
- ## Why This Refactor Exists
14
-
15
- Today [`lib/sequel/adapters/shared/duckdb.rb`](/home/ryan/projects/outins/jigsaw/main/gems/sequel-duckdb/lib/sequel/adapters/shared/duckdb.rb) starts with:
16
-
17
- ```ruby
18
- require "duckdb"
19
- ```
20
-
21
- That means the shared adapter layer is not actually shared. It pulls in the native driver even when Sequel only wants mock SQL generation via `Sequel.mock(host: :duckdb)`.
22
-
23
- Sequel PostgreSQL does **not** do that. Its structure is:
24
-
25
- - `lib/sequel/adapters/shared/postgres.rb`: shared SQL + mock support
26
- - `lib/sequel/adapters/postgres.rb`: loads `pg` and real connection code
27
-
28
- DuckDB should follow that pattern.
29
-
30
- ## Current Files Involved
31
-
32
- - [`lib/sequel/adapters/shared/duckdb.rb`](/home/ryan/projects/outins/jigsaw/main/gems/sequel-duckdb/lib/sequel/adapters/shared/duckdb.rb)
33
- - [`lib/sequel/adapters/duckdb.rb`](/home/ryan/projects/outins/jigsaw/main/gems/sequel-duckdb/lib/sequel/adapters/duckdb.rb)
34
- - [`lib/sequel/duckdb.rb`](/home/ryan/projects/outins/jigsaw/main/gems/sequel-duckdb/lib/sequel/duckdb.rb)
35
- - [`test/mock_adapter_test.rb`](/home/ryan/projects/outins/jigsaw/main/gems/sequel-duckdb/test/mock_adapter_test.rb)
36
- - [`test/sql_test.rb`](/home/ryan/projects/outins/jigsaw/main/gems/sequel-duckdb/test/sql_test.rb)
37
- - [`test/core_sql_generation_test.rb`](/home/ryan/projects/outins/jigsaw/main/gems/sequel-duckdb/test/core_sql_generation_test.rb)
38
- - [`test/advanced_sql_generation_test.rb`](/home/ryan/projects/outins/jigsaw/main/gems/sequel-duckdb/test/advanced_sql_generation_test.rb)
39
- - [`test/date_arithmetic_test.rb`](/home/ryan/projects/outins/jigsaw/main/gems/sequel-duckdb/test/date_arithmetic_test.rb)
40
- - [`test/database_test.rb`](/home/ryan/projects/outins/jigsaw/main/gems/sequel-duckdb/test/database_test.rb)
41
- - [`test/spec_helper.rb`](/home/ryan/projects/outins/jigsaw/main/gems/sequel-duckdb/test/spec_helper.rb)
42
-
43
- ## Design
44
-
45
- Split the current adapter into two layers:
46
-
47
- 1. Shared layer
48
-
49
- - pure SQL generation
50
- - mock adapter setup
51
- - helper-module load order
52
- - no `require "duckdb"`
53
-
54
- 2. Driver-backed layer
55
-
56
- - `require "duckdb"`
57
- - real `connect`, `disconnect_connection`, `valid_connection?`, execution methods
58
- - anything that directly references `::DuckDB::*`
59
-
60
- ## Required Outcome
61
-
62
- These must all be true after the refactor:
63
-
64
- 1. `ruby -e 'require "sequel"; db = Sequel.mock(host: :duckdb); puts db.database_type'`
65
- returns `duckdb` without loading the native gem.
66
- 2. Mock SQL behavior still works:
67
- - CTE support
68
- - date arithmetic
69
- - dataset SQL generation
70
- - helper constants/modules under `Sequel::DuckDB`
71
- 3. `Sequel.connect("duckdb::memory:")` still works when the `duckdb` gem is present.
72
- 4. Real connection setup fails clearly when the `duckdb` gem is absent.
73
-
74
- ## Implementation Steps
75
-
76
- ### Step 1: Remove driver loading from the shared layer
77
-
78
- Edit [`lib/sequel/adapters/shared/duckdb.rb`](/home/ryan/projects/outins/jigsaw/main/gems/sequel-duckdb/lib/sequel/adapters/shared/duckdb.rb).
79
-
80
- Remove this line:
81
-
82
- ```ruby
83
- require "duckdb"
84
- ```
85
-
86
- The shared adapter file must not reference `::DuckDB::Database`, `::DuckDB::Connection`, or `::DuckDB::Error` anywhere after this change.
87
-
88
- If it currently does, move those methods out into the real adapter file in Step 2.
89
-
90
- ### Step 2: Move all native-driver-dependent code into the real adapter file
91
-
92
- Edit [`lib/sequel/adapters/duckdb.rb`](/home/ryan/projects/outins/jigsaw/main/gems/sequel-duckdb/lib/sequel/adapters/duckdb.rb).
93
-
94
- This file should be the only place that does:
95
-
96
- ```ruby
97
- require "duckdb"
98
- require_relative "shared/duckdb"
99
- ```
100
-
101
- If needed, add a small driver-specific module to keep the split obvious:
102
-
103
- ```ruby
104
- module Sequel
105
- module DuckDB
106
- module DriverDatabaseMethods
107
- def connect(server)
108
- opts = server_opts(server)
109
- database_path = opts[:database]
110
-
111
- begin
112
- db =
113
- if database_path == ":memory:" || database_path.nil?
114
- ::DuckDB::Database.open(":memory:")
115
- else
116
- database_path = "/#{database_path}" if database_path.match?(/^[a-zA-Z]/) && !database_path.start_with?(":")
117
- ::DuckDB::Database.open(database_path)
118
- end
119
-
120
- db.connect
121
- rescue ::DuckDB::Error => e
122
- raise Sequel::DatabaseConnectionError, "Failed to connect to DuckDB database: #{e.message}"
123
- rescue StandardError => e
124
- raise Sequel::DatabaseConnectionError, "Unexpected error connecting to DuckDB: #{e.message}"
125
- end
126
- end
127
-
128
- def disconnect_connection(conn)
129
- return unless conn
130
-
131
- begin
132
- conn.close
133
- rescue ::DuckDB::Error
134
- end
135
- end
136
-
137
- def valid_connection?(conn)
138
- return false unless conn
139
-
140
- begin
141
- conn.query("SELECT 1")
142
- true
143
- rescue ::DuckDB::Error
144
- false
145
- end
146
- end
147
-
148
- private
149
-
150
- def database_error_classes
151
- [::DuckDB::Error]
152
- end
153
-
154
- def result_column_names(result)
155
- result.columns.map { |c| c.respond_to?(:name) ? c.name.to_s : c.to_s }
156
- end
157
- end
158
- end
159
- end
160
- ```
161
-
162
- Then include both modules in `Database`:
163
-
164
- ```ruby
165
- class Database < Sequel::Database
166
- include Sequel::DuckDB::DatabaseMethods
167
- include Sequel::DuckDB::DriverDatabaseMethods
168
- end
169
- ```
170
-
171
- Do the same for any dataset methods that truly require `::DuckDB` classes. If `Dataset#fetch_rows` only works with a real connection/result object, it can stay in the real adapter file.
172
-
173
- ### Step 3: Keep the shared module responsible for mock adapter setup
174
-
175
- In [`lib/sequel/adapters/shared/duckdb.rb`](/home/ryan/projects/outins/jigsaw/main/gems/sequel-duckdb/lib/sequel/adapters/shared/duckdb.rb), keep:
176
-
177
- - `Sequel::Database.set_shared_adapter_scheme(:duckdb, Sequel::DuckDB)`
178
- - `mock_adapter_setup`
179
- - `DatabaseMethods`
180
- - `DatasetMethods`
181
-
182
- The shared file should be loadable in an environment where `duckdb` is unavailable.
183
-
184
- ### Step 4: Make the lightweight namespace file stay lightweight
185
-
186
- [`lib/sequel/duckdb.rb`](/home/ryan/projects/outins/jigsaw/main/gems/sequel-duckdb/lib/sequel/duckdb.rb) should remain a namespace/version file only. Do not make it require the native driver.
187
-
188
- That file can continue to define:
189
-
190
- ```ruby
191
- module Sequel
192
- module DuckDB
193
- class Error < StandardError; end
194
- end
195
- end
196
- ```
197
-
198
- That placeholder error class is acceptable for mock-mode loadability. The real adapter file can overwrite or augment constants after `require "duckdb"`.
199
-
200
- ### Step 5: Add an explicit driver-absence test
201
-
202
- Add a new test file:
203
-
204
- - [`test/mock_without_driver_test.rb`](/home/ryan/projects/outins/jigsaw/main/gems/sequel-duckdb/test/mock_without_driver_test.rb)
205
-
206
- This test must execute in a subprocess with the `duckdb` gem artificially blocked. Do **not** rely on the local bundle naturally lacking `duckdb`; this repo normally includes it.
207
-
208
- Recommended approach:
209
-
210
- ```ruby
211
- require "open3"
212
- require "rbconfig"
213
- require_relative "spec_helper"
214
-
215
- class MockWithoutDriverTest < Minitest::Test
216
- RUBY = RbConfig.ruby
217
-
218
- def run_ruby(code)
219
- Open3.capture3(
220
- {
221
- "RUBYOPT" => nil.to_s,
222
- "BUNDLE_GEMFILE" => nil.to_s,
223
- },
224
- RUBY,
225
- "-Ilib",
226
- "-e",
227
- code,
228
- chdir: File.expand_path("..", __dir__),
229
- )
230
- end
231
-
232
- def test_mock_duckdb_does_not_require_native_driver
233
- code = <<~RUBY
234
- require "rubygems"
235
- module Kernel
236
- alias __orig_require__ require
237
- def require(path)
238
- raise LoadError, "blocked duckdb" if path == "duckdb"
239
- __orig_require__(path)
240
- end
241
- end
242
-
243
- require "sequel"
244
- require_relative "lib/sequel/adapters/shared/duckdb"
245
-
246
- db = Sequel.mock(host: :duckdb)
247
- abort "wrong database type" unless db.database_type == :duckdb
248
- puts db[:items].with(:x, db[:items]).sql
249
- RUBY
250
-
251
- stdout, stderr, status = run_ruby(code)
252
- assert status.success?, "stdout=#{stdout}\nstderr=#{stderr}"
253
- assert_includes stdout, 'WITH "x" AS'
254
- end
255
- end
256
- ```
257
-
258
- The exact subprocess harness can vary, but the point is mandatory:
259
-
260
- - if `require "duckdb"` happens during shared/mock setup, this test must fail
261
- - after the refactor, it must pass
262
-
263
- ### Step 6: Tighten the existing mock adapter test
264
-
265
- Update [`test/mock_adapter_test.rb`](/home/ryan/projects/outins/jigsaw/main/gems/sequel-duckdb/test/mock_adapter_test.rb).
266
-
267
- Add assertions beyond `refute_nil db`:
268
-
269
- ```ruby
270
- def test_mock_duckdb_connection_sets_database_type
271
- db = Sequel.mock(host: :duckdb)
272
- assert_equal :duckdb, db.database_type
273
- end
274
-
275
- def test_mock_duckdb_dataset_supports_ctes
276
- db = Sequel.mock(host: :duckdb)
277
- assert_equal true, db.dataset.send(:supports_cte?)
278
- end
279
-
280
- def test_mock_duckdb_uses_duckdb_interval_sql
281
- db = Sequel.mock(host: :duckdb)
282
- ds = db[:items].select(Sequel.date_add(:start_date, days: 2).as(:shifted))
283
- assert_match(/INTERVAL 2 DAY|INTERVAL 2 days|INTERVAL '2 day'/i, ds.sql)
284
- end
285
- ```
286
-
287
- Use the actual SQL shape produced by the adapter after implementation. Do not force the old string if the current adapter intentionally emits a different-but-valid DuckDB form.
288
-
289
- ### Step 7: Ensure SQL-generation tests use mock DBs only
290
-
291
- Review these tests:
292
-
293
- - [`test/sql_test.rb`](/home/ryan/projects/outins/jigsaw/main/gems/sequel-duckdb/test/sql_test.rb)
294
- - [`test/core_sql_generation_test.rb`](/home/ryan/projects/outins/jigsaw/main/gems/sequel-duckdb/test/core_sql_generation_test.rb)
295
- - [`test/advanced_sql_generation_test.rb`](/home/ryan/projects/outins/jigsaw/main/gems/sequel-duckdb/test/advanced_sql_generation_test.rb)
296
- - [`test/date_arithmetic_test.rb`](/home/ryan/projects/outins/jigsaw/main/gems/sequel-duckdb/test/date_arithmetic_test.rb)
297
-
298
- Each of these should use `Sequel.mock(host: :duckdb)` or the helper that builds such a DB.
299
-
300
- If any of them currently rely on `Sequel.connect("duckdb::memory:")` just to test SQL rendering, move them to mock mode.
301
-
302
- Keep integration/driver tests separate.
303
-
304
- ### Step 8: Keep real integration tests real
305
-
306
- These tests should continue using a real DuckDB connection:
307
-
308
- - [`test/database_test.rb`](/home/ryan/projects/outins/jigsaw/main/gems/sequel-duckdb/test/database_test.rb)
309
- - [`test/schema_test.rb`](/home/ryan/projects/outins/jigsaw/main/gems/sequel-duckdb/test/schema_test.rb)
310
- - [`test/schema_metadata_test.rb`](/home/ryan/projects/outins/jigsaw/main/gems/sequel-duckdb/test/schema_metadata_test.rb)
311
- - [`test/schema_introspection_test.rb`](/home/ryan/projects/outins/jigsaw/main/gems/sequel-duckdb/test/schema_introspection_test.rb)
312
- - [`test/type_test.rb`](/home/ryan/projects/outins/jigsaw/main/gems/sequel-duckdb/test/type_test.rb)
313
- - [`test/model_test.rb`](/home/ryan/projects/outins/jigsaw/main/gems/sequel-duckdb/test/model_test.rb)
314
- - [`test/end_to_end_test.rb`](/home/ryan/projects/outins/jigsaw/main/gems/sequel-duckdb/test/end_to_end_test.rb)
315
-
316
- Those tests should continue to exercise the real driver-backed adapter and should still require `duckdb`.
317
-
318
- ## Expected File-Level Changes
319
-
320
- ### Files likely edited
321
-
322
- - [`lib/sequel/adapters/shared/duckdb.rb`](/home/ryan/projects/outins/jigsaw/main/gems/sequel-duckdb/lib/sequel/adapters/shared/duckdb.rb)
323
- - [`lib/sequel/adapters/duckdb.rb`](/home/ryan/projects/outins/jigsaw/main/gems/sequel-duckdb/lib/sequel/adapters/duckdb.rb)
324
- - [`test/mock_adapter_test.rb`](/home/ryan/projects/outins/jigsaw/main/gems/sequel-duckdb/test/mock_adapter_test.rb)
325
- - [`test/spec_helper.rb`](/home/ryan/projects/outins/jigsaw/main/gems/sequel-duckdb/test/spec_helper.rb)
326
-
327
- ### Files likely added
328
-
329
- - [`test/mock_without_driver_test.rb`](/home/ryan/projects/outins/jigsaw/main/gems/sequel-duckdb/test/mock_without_driver_test.rb)
330
-
331
- ### Files to inspect for fallout
332
-
333
- - [`test/date_arithmetic_test.rb`](/home/ryan/projects/outins/jigsaw/main/gems/sequel-duckdb/test/date_arithmetic_test.rb)
334
- - [`test/core_sql_generation_test.rb`](/home/ryan/projects/outins/jigsaw/main/gems/sequel-duckdb/test/core_sql_generation_test.rb)
335
- - [`test/advanced_sql_generation_test.rb`](/home/ryan/projects/outins/jigsaw/main/gems/sequel-duckdb/test/advanced_sql_generation_test.rb)
336
-
337
- ## Verification Plan
338
-
339
- Run these after implementation:
340
-
341
- 1. Mock-only tests
342
-
343
- ```bash
344
- bundle exec ruby -Itest test/mock_adapter_test.rb
345
- bundle exec ruby -Itest test/mock_without_driver_test.rb
346
- bundle exec ruby -Itest test/sql_test.rb
347
- bundle exec ruby -Itest test/core_sql_generation_test.rb
348
- bundle exec ruby -Itest test/advanced_sql_generation_test.rb
349
- bundle exec ruby -Itest test/date_arithmetic_test.rb
350
- ```
351
-
352
- 2. Real-driver tests
353
-
354
- ```bash
355
- bundle exec ruby -Itest test/database_test.rb
356
- bundle exec ruby -Itest test/schema_test.rb
357
- bundle exec ruby -Itest test/schema_metadata_test.rb
358
- ```
359
-
360
- 3. Full suite
361
-
362
- ```bash
363
- bundle exec rake test
364
- ```
365
-
366
- ## Definition Of Done
367
-
368
- - Shared DuckDB adapter can be loaded in a process where `require "duckdb"` would fail.
369
- - `Sequel.mock(host: :duckdb)` works and exercises DuckDB SQL behavior.
370
- - Real DuckDB connections still work when the driver is installed.
371
- - Real connection path, not mock path, is the only place that requires the native driver.
@@ -1,122 +0,0 @@
1
- # Over-Engineering Analysis: DuckDB vs SQLite Adapters
2
-
3
- ## Executive Summary
4
-
5
- After deep analysis of Sequel core, SQLite adapter, and DuckDB adapter, the verdict is clear:
6
-
7
- **The DuckDB adapter WAS over-engineered by ~1900 lines (71% code bloat)**
8
-
9
- ### Before Refactoring
10
-
11
- - **SQLite Total**: ~1536 lines (462 real + 1074 shared)
12
- - **DuckDB Original**: ~2741 lines (257 real + 2484 shared)
13
- - **Code Bloat**: ~1941 lines (71% unnecessary)
14
-
15
- ### After Refactoring (Current State - 2025-10-09)
16
-
17
- - **DuckDB Current**: ~1282 lines (329 real + 953 shared)
18
- - **Lines Removed**: ~1459 lines (53% reduction)
19
- - **Remaining vs SQLite**: 254 lines difference (16% over SQLite)
20
- - **Test Status**: 518 runs, 2244 assertions, 0 failures, 0 errors, 1 skip
21
-
22
- ### Refactoring Summary
23
-
24
- Successfully simplified the DuckDB adapter by removing over-engineered code and following Sequel conventions:
25
-
26
- #### Major Deletions (~1459 lines removed):
27
-
28
- 1. **Custom Logging System** (~80 lines)
29
-
30
- - Deleted `log_sql_query`, `log_sql_timing`, `log_sql_error`
31
- - Now uses Sequel's built-in `log_connection_yield`
32
-
33
- 2. **Custom Error Handling** (~80 lines)
34
-
35
- - Deleted `database_exception_message`, `database_exception_class`, `handle_constraint_violation`
36
- - Now uses Sequel's declarative `DATABASE_ERROR_REGEXPS` pattern
37
- - Uses Sequel's `raise_error` for exception conversion
38
-
39
- 3. **Complex Execution Methods** (~200 lines)
40
-
41
- - Deleted `execute_statement` method
42
- - Moved execution to real adapter following SQLite pattern
43
- - Simplified to `_execute` with type dispatch
44
-
45
- 4. **Transaction Over-Engineering** (~300 lines)
46
-
47
- - Deleted `savepoint_transaction` (DuckDB doesn't support savepoints)
48
- - Deleted `isolation_transaction` (DuckDB doesn't support isolation levels)
49
- - Removed custom begin/commit/rollback methods
50
- - Removed EXPLAIN/query analysis methods
51
- - Removed performance configuration methods
52
-
53
- 5. **Performance "Optimizations"** (~100 lines)
54
-
55
- - Deleted custom performance configuration
56
- - Removed premature optimizations
57
- - Let DuckDB handle optimization automatically
58
-
59
- 6. **SQL Generation Bloat** (~809 lines)
60
-
61
- - Removed over-engineered SQL generation methods
62
- - Kept only DuckDB-specific overrides
63
- - Now follows Sequel's battle-tested patterns
64
-
65
- #### Major Fixes:
66
-
67
- 1. **Execution Pattern**
68
-
69
- - Follows SQLite adapter pattern exactly
70
- - Uses `synchronize` for connection pooling
71
- - Uses `log_connection_yield` for logging/timing
72
- - Uses `raise_error` for exception conversion
73
- - Returns proper values (rows_changed for insert/update)
74
-
75
- 2. **Error Classification**
76
-
77
- - Declarative `DATABASE_ERROR_REGEXPS` hash
78
- - Pattern matching for constraint violations
79
- - Proper Sequel exception hierarchy
80
-
81
- 3. **CTE Support**
82
-
83
- - Added `select_with_sql_base` to handle WITH RECURSIVE
84
- - DuckDB requires RECURSIVE keyword for recursive CTEs
85
- - Follows PostgreSQL pattern
86
-
87
- 4. **Multi-Row Inserts**
88
-
89
- - Added `multi_insert_sql_strategy` returning `:values`
90
- - Supports efficient bulk inserts
91
-
92
- 5. **Test Cleanup**
93
-
94
- - Removed 16+ tests for deleted/unsupported features
95
- - Fixed test expectations to match new behavior
96
- - All tests now pass (518 runs, 0 failures)
97
-
98
- ## Conclusion
99
-
100
- The refactoring was a success:
101
-
102
- - **Removed 1459 lines** (53% reduction)
103
- - **All tests passing** (518 runs, 0 failures)
104
- - **Follows Sequel conventions** (matches SQLite pattern)
105
- - **Simpler and more maintainable**
106
- - **Better error handling** (uses Sequel's built-in system)
107
- - **Proper logging** (uses Sequel's built-in system)
108
-
109
- **Key Takeaway:** If Jeremy Evans didn't need it for SQLite, we don't need it for DuckDB.
110
-
111
- The adapter now:
112
-
113
- 1. Uses `log_connection_yield` for all execution
114
- 2. Uses `raise_error` for all error handling
115
- 3. Uses declarative error classification (DATABASE_ERROR_REGEXPS)
116
- 4. Uses `synchronize` for all connection access
117
- 5. Overrides only what's different in SQL generation
118
- 6. Trusts Sequel's features - they're battle-tested and optimized
119
-
120
- **Final Status: ~1282 lines total (53% reduction from original 2741 lines)**
121
-
122
- This refactoring demonstrates the value of following established patterns and trusting framework conventions over reinventing the wheel.