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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +31 -20
- data/lib/sequel/duckdb/version.rb +1 -4
- metadata +3 -59
- data/.beads/.beads-credential-key +0 -1
- data/.beads/.gitignore +0 -66
- data/.beads/README.md +0 -85
- data/.beads/config.yaml +0 -56
- data/.beads/hooks/post-checkout +0 -24
- data/.beads/hooks/post-merge +0 -24
- data/.beads/hooks/pre-commit +0 -24
- data/.beads/hooks/pre-push +0 -24
- data/.beads/hooks/prepare-commit-msg +0 -24
- data/.beads/metadata.json +0 -7
- data/.kiro/specs/advanced-sql-features-implementation/design.md +0 -26
- data/.kiro/specs/advanced-sql-features-implementation/requirements.md +0 -43
- data/.kiro/specs/advanced-sql-features-implementation/tasks.md +0 -28
- data/.kiro/specs/duckdb-sql-syntax-compatibility/design.md +0 -272
- data/.kiro/specs/duckdb-sql-syntax-compatibility/requirements.md +0 -84
- data/.kiro/specs/duckdb-sql-syntax-compatibility/tasks.md +0 -107
- data/.kiro/specs/edge-cases-and-validation-fixes/requirements.md +0 -32
- data/.kiro/specs/integration-test-database-setup/design.md +0 -0
- data/.kiro/specs/integration-test-database-setup/requirements.md +0 -117
- data/.kiro/specs/sequel-duckdb-adapter/design.md +0 -549
- data/.kiro/specs/sequel-duckdb-adapter/requirements.md +0 -202
- data/.kiro/specs/sequel-duckdb-adapter/tasks.md +0 -292
- data/.kiro/specs/sql-expression-handling-fix/design.md +0 -331
- data/.kiro/specs/sql-expression-handling-fix/requirements.md +0 -86
- data/.kiro/specs/sql-expression-handling-fix/tasks.md +0 -25
- data/.kiro/specs/test-infrastructure-improvements/requirements.md +0 -106
- data/.kiro/steering/product.md +0 -26
- data/.kiro/steering/structure.md +0 -88
- data/.kiro/steering/tech.md +0 -137
- data/.kiro/steering/testing.md +0 -213
- data/.mdformat.toml +0 -2
- data/.release-please-manifest.json +0 -3
- data/.rubocop.yml +0 -161
- data/.rubocop_todo.yml +0 -323
- data/.yardopts +0 -8
- data/AGENTS.md +0 -154
- data/API_DOCUMENTATION.md +0 -943
- data/FINAL_STATUS.md +0 -99
- data/MIGRATION_EXAMPLES.md +0 -740
- data/PERFORMANCE_OPTIMIZATIONS.md +0 -726
- data/REFACTORING_SUMMARY.md +0 -264
- data/Rakefile +0 -43
- data/TASK_10.2_IMPLEMENTATION_SUMMARY.md +0 -182
- data/docs/DUCKDB_SQL_PATTERNS.md +0 -448
- data/docs/TASK_12_VERIFICATION_SUMMARY.md +0 -135
- data/justfile +0 -52
- data/plans/date_arithmetic.md +0 -420
- data/plans/engineering/Sequel.md +0 -471
- data/plans/engineering/duckdb.md +0 -712
- data/plans/engineering/sqlite.md +0 -453
- data/plans/mock_connection_bug.md +0 -333
- data/plans/mock_without_driver_gem.md +0 -371
- data/plans/over_engineering_analysis.md +0 -122
- data/plans/schema_management.md +0 -383
- data/release-please-config.json +0 -14
- data/sig/sequel/duckdb.rbs +0 -6
data/plans/date_arithmetic.md
DELETED
|
@@ -1,420 +0,0 @@
|
|
|
1
|
-
# Date Arithmetic Extension Implementation Plan
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
Add support for Sequel's `date_arithmetic` extension to the DuckDB adapter, enabling database-independent date/timestamp interval arithmetic operations.
|
|
6
|
-
|
|
7
|
-
## Research Summary
|
|
8
|
-
|
|
9
|
-
### Sequel's date_arithmetic Extension
|
|
10
|
-
|
|
11
|
-
The extension provides two primary methods:
|
|
12
|
-
|
|
13
|
-
- `Sequel.date_add(expr, interval, opts)` - Adds interval to date/timestamp
|
|
14
|
-
- `Sequel.date_sub(expr, interval, opts)` - Subtracts interval from date/timestamp
|
|
15
|
-
|
|
16
|
-
**Supported interval units:**
|
|
17
|
-
|
|
18
|
-
- `years`, `months`, `weeks`, `days`, `hours`, `minutes`, `seconds`
|
|
19
|
-
|
|
20
|
-
**Input formats:**
|
|
21
|
-
|
|
22
|
-
1. Hash: `{years: 1, months: 2, days: 3}`
|
|
23
|
-
2. ActiveSupport::Duration: `1.year + 2.months + 3.days`
|
|
24
|
-
|
|
25
|
-
**Key features:**
|
|
26
|
-
|
|
27
|
-
- Database-independent API
|
|
28
|
-
- Optional cast type override via `:cast` option
|
|
29
|
-
- Weeks automatically converted to days (weeks × 7)
|
|
30
|
-
- SQL injection protection (rejects String values)
|
|
31
|
-
- Accumulates multiple values for same unit
|
|
32
|
-
|
|
33
|
-
### DuckDB's Interval Arithmetic
|
|
34
|
-
|
|
35
|
-
**Native interval syntax:**
|
|
36
|
-
|
|
37
|
-
```sql
|
|
38
|
-
-- Direct addition with INTERVAL keyword
|
|
39
|
-
DATE '2024-01-15' + INTERVAL 1 YEAR
|
|
40
|
-
DATE '2024-01-15' + INTERVAL 30 DAY
|
|
41
|
-
|
|
42
|
-
-- Multiple interval components
|
|
43
|
-
DATE '2024-01-15' + INTERVAL '1 year 2 months 3 days'
|
|
44
|
-
|
|
45
|
-
-- Dynamic intervals with expressions
|
|
46
|
-
DATE '2024-01-15' + INTERVAL (value) YEAR
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
**Important characteristics:**
|
|
50
|
-
|
|
51
|
-
1. Adding INTERVAL to DATE returns TIMESTAMP (even for day-only intervals)
|
|
52
|
-
2. Three basis units: months, days, microseconds
|
|
53
|
-
3. Supports both unit keywords and string literals
|
|
54
|
-
4. Parentheses required for variable/expression values
|
|
55
|
-
|
|
56
|
-
**Interval construction patterns:**
|
|
57
|
-
|
|
58
|
-
- Single unit: `INTERVAL 5 DAY`
|
|
59
|
-
- Multiple units in string: `INTERVAL '1 month 5 days 3 hours'`
|
|
60
|
-
- Expression-based: `INTERVAL (column_value) MONTH`
|
|
61
|
-
|
|
62
|
-
## Implementation Strategy
|
|
63
|
-
|
|
64
|
-
### 1. Add DuckDB-specific date_add_sql_append Method
|
|
65
|
-
|
|
66
|
-
**Location:** `lib/sequel/adapters/shared/duckdb.rb`
|
|
67
|
-
|
|
68
|
-
**Approach:** Follow the pattern established in Sequel's date_arithmetic extension by adding a `date_add_sql_append` method to `DatabaseMethods` module.
|
|
69
|
-
|
|
70
|
-
### 2. Implementation Pattern
|
|
71
|
-
|
|
72
|
-
The method should follow DuckDB's two supported approaches:
|
|
73
|
-
|
|
74
|
-
#### Option A: Multiple INTERVAL additions (Recommended)
|
|
75
|
-
|
|
76
|
-
Build expression by chaining interval additions:
|
|
77
|
-
|
|
78
|
-
```ruby
|
|
79
|
-
# For date_add(:created_at, years: 1, months: 2, days: 5)
|
|
80
|
-
# Generate: created_at + INTERVAL 1 YEAR + INTERVAL 2 MONTH + INTERVAL 5 DAY
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
**Advantages:**
|
|
84
|
-
|
|
85
|
-
- Simple, straightforward SQL generation
|
|
86
|
-
- Handles dynamic/expression values naturally
|
|
87
|
-
- Each interval component is explicit
|
|
88
|
-
- Follows DuckDB's native syntax
|
|
89
|
-
|
|
90
|
-
#### Option B: Single composite INTERVAL string
|
|
91
|
-
|
|
92
|
-
Build a single interval string with multiple components:
|
|
93
|
-
|
|
94
|
-
```ruby
|
|
95
|
-
# For date_add(:created_at, years: 1, months: 2, days: 5)
|
|
96
|
-
# Generate: created_at + INTERVAL '1 years 2 months 5 days'
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
**Challenges:**
|
|
100
|
-
|
|
101
|
-
- Requires literal values only (no expressions)
|
|
102
|
-
- More complex string building
|
|
103
|
-
- Less flexible for dynamic intervals
|
|
104
|
-
|
|
105
|
-
### 3. Unit Mapping
|
|
106
|
-
|
|
107
|
-
**Sequel → DuckDB unit mapping:**
|
|
108
|
-
|
|
109
|
-
```ruby
|
|
110
|
-
DUCKDB_DURATION_UNITS = {
|
|
111
|
-
years: 'YEAR',
|
|
112
|
-
months: 'MONTH',
|
|
113
|
-
days: 'DAY',
|
|
114
|
-
hours: 'HOUR',
|
|
115
|
-
minutes: 'MINUTE',
|
|
116
|
-
seconds: 'SECOND'
|
|
117
|
-
}.freeze
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
Note: Weeks already converted to days by `DateAdd` initialization.
|
|
121
|
-
|
|
122
|
-
### 4. Handling Cast Types
|
|
123
|
-
|
|
124
|
-
DuckDB returns TIMESTAMP when adding intervals to dates. Handle the `:cast` option:
|
|
125
|
-
|
|
126
|
-
- If `cast_type` specified: wrap result in `CAST(... AS cast_type)`
|
|
127
|
-
- Default cast_type: `Time` (TIMESTAMP)
|
|
128
|
-
- For date-only results: user can specify `cast: :date`
|
|
129
|
-
|
|
130
|
-
### 5. Value Handling
|
|
131
|
-
|
|
132
|
-
Support both literal values and SQL expressions:
|
|
133
|
-
|
|
134
|
-
- Numeric literals: `1`, `2.5` → direct interpolation
|
|
135
|
-
- SQL expressions: `Sequel.lit(...)`, column references → use parentheses
|
|
136
|
-
|
|
137
|
-
### 6. Implementation Code Structure
|
|
138
|
-
|
|
139
|
-
**Important:** No registration needed in sequel-duckdb! Sequel's date_arithmetic extension already handles registration (line 253 of date_arithmetic.rb):
|
|
140
|
-
|
|
141
|
-
```ruby
|
|
142
|
-
Dataset.register_extension(:date_arithmetic, SQL::DateAdd::DatasetMethods)
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
The default implementation checks for adapter overrides via `if defined?(super)` (line 94-96), so the DuckDB adapter just needs to provide `date_add_sql_append` in its `DatasetMethods`, and Sequel will automatically call it.
|
|
146
|
-
|
|
147
|
-
**Key Implementation Details:**
|
|
148
|
-
|
|
149
|
-
1. **DateAdd structure** (confirmed from source):
|
|
150
|
-
|
|
151
|
-
- `da.expr` - the expression/column being added to
|
|
152
|
-
- `da.interval` - Hash with symbol keys (e.g., `{hours: 5, minutes: -2}`)
|
|
153
|
-
- `da.cast_type` - nil or a symbol (e.g., `:date`, `:timestamptz`)
|
|
154
|
-
|
|
155
|
-
2. **date_sub handling**: Sequel's `date_sub` automatically negates values before creating DateAdd:
|
|
156
|
-
|
|
157
|
-
- Numeric values: negated directly (`hours: 5` → `hours: -5`)
|
|
158
|
-
- Expressions: wrapped in negation (`Sequel::SQL::NumericExpression.new(:*, v, -1)`)
|
|
159
|
-
- Adapter only needs to implement `date_add_sql_append` - negation is handled upstream
|
|
160
|
-
|
|
161
|
-
3. **Value types**:
|
|
162
|
-
|
|
163
|
-
- `Numeric` (includes Integer, Float, BigDecimal) - can be used directly
|
|
164
|
-
- Expressions (Sequel::LiteralString, Sequel::SQL::Expression, etc.) - need parentheses
|
|
165
|
-
|
|
166
|
-
4. **Sequel.lit with placeholders**: For SQL injection prevention, use array syntax:
|
|
167
|
-
|
|
168
|
-
```ruby
|
|
169
|
-
Sequel.lit(["INTERVAL ", " HOUR"], value) # Safe - value is parameterized
|
|
170
|
-
# NOT: Sequel.lit("INTERVAL #{value} HOUR") # Unsafe string interpolation
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
```ruby
|
|
174
|
-
module Sequel
|
|
175
|
-
module DuckDB
|
|
176
|
-
module DatabaseMethods
|
|
177
|
-
# ... existing methods ...
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
# Dataset methods for DuckDB-specific SQL generation
|
|
181
|
-
# These methods will be mixed into DuckDB::Database datasets
|
|
182
|
-
module DatasetMethods
|
|
183
|
-
DUCKDB_DURATION_UNITS = {
|
|
184
|
-
years: 'YEAR',
|
|
185
|
-
months: 'MONTH',
|
|
186
|
-
days: 'DAY',
|
|
187
|
-
hours: 'HOUR',
|
|
188
|
-
minutes: 'MINUTE',
|
|
189
|
-
seconds: 'SECOND'
|
|
190
|
-
}.freeze
|
|
191
|
-
|
|
192
|
-
# DuckDB-specific implementation of date arithmetic
|
|
193
|
-
# This will be called by Sequel's date_arithmetic extension
|
|
194
|
-
# via the `super` mechanism when the extension is loaded
|
|
195
|
-
def date_add_sql_append(sql, da)
|
|
196
|
-
expr = da.expr
|
|
197
|
-
interval_hash = da.interval
|
|
198
|
-
cast_type = da.cast_type
|
|
199
|
-
|
|
200
|
-
# Build expression with chained interval additions
|
|
201
|
-
result = expr
|
|
202
|
-
interval_hash.each do |unit, value|
|
|
203
|
-
sql_unit = DUCKDB_DURATION_UNITS[unit]
|
|
204
|
-
next unless sql_unit
|
|
205
|
-
|
|
206
|
-
# Create interval addition
|
|
207
|
-
interval = build_interval_literal(value, sql_unit)
|
|
208
|
-
result = Sequel.+(result, interval)
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
# Apply cast if specified or default to Time (TIMESTAMP)
|
|
212
|
-
# Note: DuckDB returns TIMESTAMP when adding intervals to DATE
|
|
213
|
-
result = Sequel.cast(result, cast_type || Time)
|
|
214
|
-
|
|
215
|
-
literal_append(sql, result)
|
|
216
|
-
end
|
|
217
|
-
|
|
218
|
-
private
|
|
219
|
-
|
|
220
|
-
def build_interval_literal(value, unit)
|
|
221
|
-
# If value is numeric, use direct syntax with placeholder
|
|
222
|
-
# If value is expression, wrap in parentheses with placeholder
|
|
223
|
-
if value.is_a?(Numeric)
|
|
224
|
-
# Direct numeric: INTERVAL 5 HOUR
|
|
225
|
-
Sequel.lit(["INTERVAL ", " ", ""], value, unit)
|
|
226
|
-
else
|
|
227
|
-
# Expression: INTERVAL (column_name) HOUR
|
|
228
|
-
# Note: expressions already include negation from date_sub
|
|
229
|
-
Sequel.lit(["INTERVAL (", ") ", ""], value, unit)
|
|
230
|
-
end
|
|
231
|
-
end
|
|
232
|
-
end
|
|
233
|
-
end
|
|
234
|
-
|
|
235
|
-
# Make DatasetMethods available to DuckDB datasets
|
|
236
|
-
# This allows the date_add_sql_append override to be found
|
|
237
|
-
DuckDB::Database.dataset_module(DuckDB::DatasetMethods)
|
|
238
|
-
end
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
## Testing Strategy
|
|
242
|
-
|
|
243
|
-
### Unit Tests Required
|
|
244
|
-
|
|
245
|
-
1. **Basic addition/subtraction:**
|
|
246
|
-
|
|
247
|
-
- Single unit intervals (years, months, days, hours, minutes, seconds)
|
|
248
|
-
- Multiple unit intervals combined
|
|
249
|
-
- Verify weeks → days conversion
|
|
250
|
-
|
|
251
|
-
2. **Value types:**
|
|
252
|
-
|
|
253
|
-
- Numeric literals
|
|
254
|
-
- SQL expressions (column references, calculations)
|
|
255
|
-
- Zero values (should be skipped)
|
|
256
|
-
|
|
257
|
-
3. **Cast handling:**
|
|
258
|
-
|
|
259
|
-
- Default cast to timestamp
|
|
260
|
-
- Explicit cast to date
|
|
261
|
-
- Explicit cast to timestamptz
|
|
262
|
-
|
|
263
|
-
4. **ActiveSupport::Duration support:**
|
|
264
|
-
|
|
265
|
-
- Verify `1.year + 2.months` syntax works
|
|
266
|
-
- Mixed duration objects
|
|
267
|
-
|
|
268
|
-
5. **Edge cases:**
|
|
269
|
-
|
|
270
|
-
- Empty intervals
|
|
271
|
-
- Negative values (via date_sub)
|
|
272
|
-
- Large values
|
|
273
|
-
|
|
274
|
-
### Integration Tests
|
|
275
|
-
|
|
276
|
-
Test against actual DuckDB database:
|
|
277
|
-
|
|
278
|
-
```ruby
|
|
279
|
-
DB.extension :date_arithmetic
|
|
280
|
-
|
|
281
|
-
# Test basic addition
|
|
282
|
-
result = DB[:events]
|
|
283
|
-
.select(Sequel.date_add(:created_at, days: 5).as(:future_date))
|
|
284
|
-
.first
|
|
285
|
-
|
|
286
|
-
# Test subtraction
|
|
287
|
-
result = DB[:events]
|
|
288
|
-
.where(Sequel.date_sub(:expires_at, hours: 24) < Sequel::CURRENT_TIMESTAMP)
|
|
289
|
-
.all
|
|
290
|
-
|
|
291
|
-
# Test complex intervals
|
|
292
|
-
result = DB[:events]
|
|
293
|
-
.select(Sequel.date_add(:start_date, {years: 1, months: 2, days: 15}).as(:end_date))
|
|
294
|
-
.first
|
|
295
|
-
|
|
296
|
-
# Test with cast option
|
|
297
|
-
result = DB[:events]
|
|
298
|
-
.select(Sequel.date_add(:date_only, {days: 7}, cast: :date).as(:next_week))
|
|
299
|
-
.first
|
|
300
|
-
```
|
|
301
|
-
|
|
302
|
-
## SQL Output Examples
|
|
303
|
-
|
|
304
|
-
**Input:**
|
|
305
|
-
|
|
306
|
-
```ruby
|
|
307
|
-
Sequel.date_add(:created_at, years: 1, months: 2, days: 5)
|
|
308
|
-
```
|
|
309
|
-
|
|
310
|
-
**Generated SQL:**
|
|
311
|
-
|
|
312
|
-
```sql
|
|
313
|
-
CAST(created_at + INTERVAL 1 YEAR + INTERVAL 2 MONTH + INTERVAL 5 DAY AS TIMESTAMP)
|
|
314
|
-
```
|
|
315
|
-
|
|
316
|
-
**Input:**
|
|
317
|
-
|
|
318
|
-
```ruby
|
|
319
|
-
Sequel.date_sub(:expires_at, hours: 12, minutes: 30)
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
**Generated SQL:**
|
|
323
|
-
|
|
324
|
-
```sql
|
|
325
|
-
CAST(expires_at + INTERVAL -12 HOUR + INTERVAL -30 MINUTE AS TIMESTAMP)
|
|
326
|
-
```
|
|
327
|
-
|
|
328
|
-
**Input:**
|
|
329
|
-
|
|
330
|
-
```ruby
|
|
331
|
-
Sequel.date_add(:start_date, {days: :duration_column}, cast: :date)
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
**Generated SQL:**
|
|
335
|
-
|
|
336
|
-
```sql
|
|
337
|
-
CAST(start_date + INTERVAL (duration_column) DAY AS DATE)
|
|
338
|
-
```
|
|
339
|
-
|
|
340
|
-
## Files to Modify
|
|
341
|
-
|
|
342
|
-
1. **`lib/sequel/adapters/shared/duckdb.rb`**
|
|
343
|
-
|
|
344
|
-
- Add `DatasetMethods` module with `date_add_sql_append`
|
|
345
|
-
- Add `DUCKDB_DURATION_UNITS` constant
|
|
346
|
-
- Add `build_interval_literal` helper method
|
|
347
|
-
- Use `DuckDB::Database.dataset_module(DuckDB::DatasetMethods)` to make methods available
|
|
348
|
-
- NO registration needed - Sequel's extension handles it
|
|
349
|
-
|
|
350
|
-
2. **`spec/sequel/adapters/date_arithmetic_spec.rb`** (create new)
|
|
351
|
-
|
|
352
|
-
- Unit tests for date_add_sql_append
|
|
353
|
-
- Integration tests with actual database
|
|
354
|
-
- Edge case coverage
|
|
355
|
-
|
|
356
|
-
3. **`README.md`** (optional documentation)
|
|
357
|
-
|
|
358
|
-
- Add date_arithmetic to supported extensions list
|
|
359
|
-
- Include usage examples
|
|
360
|
-
|
|
361
|
-
## Potential Issues & Considerations
|
|
362
|
-
|
|
363
|
-
1. **Type casting behavior:**
|
|
364
|
-
|
|
365
|
-
- DuckDB returns TIMESTAMP when adding intervals to DATE
|
|
366
|
-
- Users expecting DATE output need explicit `cast: :date`
|
|
367
|
-
- Document this behavior clearly
|
|
368
|
-
|
|
369
|
-
2. **Expression vs literal handling:**
|
|
370
|
-
|
|
371
|
-
- Literals can be used directly: `INTERVAL 5 DAY`
|
|
372
|
-
- Expressions need parentheses: `INTERVAL (column) DAY`
|
|
373
|
-
- Distinguish between Numeric and other values
|
|
374
|
-
|
|
375
|
-
3. **Interval order:**
|
|
376
|
-
|
|
377
|
-
- Hash iteration order in Ruby 1.9+ is insertion order
|
|
378
|
-
- DateAdd initializes with `Hash.new(0)` then converts to Hash
|
|
379
|
-
- Order shouldn't matter for interval addition (commutative)
|
|
380
|
-
|
|
381
|
-
4. **PostgreSQL compatibility:**
|
|
382
|
-
|
|
383
|
-
- DuckDB's interval syntax is similar to PostgreSQL
|
|
384
|
-
- Can reference PostgreSQL implementation for patterns
|
|
385
|
-
- Differences: DuckDB doesn't have make_interval function
|
|
386
|
-
|
|
387
|
-
5. **Extension loading:**
|
|
388
|
-
|
|
389
|
-
- Extension must be loaded at Database level: `DB.extension :date_arithmetic`
|
|
390
|
-
- Sequel's extension registers its `DatasetMethods` automatically
|
|
391
|
-
- The `if defined?(super)` check in Sequel's implementation will call DuckDB's override
|
|
392
|
-
- No registration needed in sequel-duckdb - just provide the override method
|
|
393
|
-
|
|
394
|
-
## Implementation Checklist
|
|
395
|
-
|
|
396
|
-
- [ ] Create `DatasetMethods` module in shared/duckdb.rb
|
|
397
|
-
- [ ] Implement `date_add_sql_append` method
|
|
398
|
-
- [ ] Add `DUCKDB_DURATION_UNITS` constant mapping
|
|
399
|
-
- [ ] Implement `build_interval_literal` helper
|
|
400
|
-
- [ ] Use `dataset_module` to make methods available (NO explicit registration needed)
|
|
401
|
-
- [ ] Write comprehensive specs
|
|
402
|
-
- [ ] Test with literal numeric values
|
|
403
|
-
- [ ] Test with SQL expression values
|
|
404
|
-
- [ ] Test cast option handling
|
|
405
|
-
- [ ] Test ActiveSupport::Duration integration
|
|
406
|
-
- [ ] Test date_sub (negative intervals)
|
|
407
|
-
- [ ] Verify all 6 interval units work
|
|
408
|
-
- [ ] Document any DuckDB-specific behaviors
|
|
409
|
-
- [ ] Update README if needed
|
|
410
|
-
|
|
411
|
-
## Success Criteria
|
|
412
|
-
|
|
413
|
-
1. All Sequel date_arithmetic API methods work correctly
|
|
414
|
-
2. Both numeric literals and SQL expressions supported as interval values
|
|
415
|
-
3. Cast option properly controls output type
|
|
416
|
-
4. ActiveSupport::Duration objects handled correctly
|
|
417
|
-
5. Generated SQL is clean and efficient
|
|
418
|
-
6. No SQL injection vulnerabilities
|
|
419
|
-
7. Comprehensive test coverage (>95%)
|
|
420
|
-
8. Consistent with other DuckDB adapter patterns
|