duckdb 1.5.4.0 → 1.5.5.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 +36 -0
- data/CLAUDE.md +4 -0
- data/duckdb.gemspec +1 -1
- data/ext/duckdb/connection.c +23 -1
- data/ext/duckdb/error.c +12 -0
- data/ext/duckdb/error.h +1 -0
- data/ext/duckdb/function_vector.c +14 -0
- data/ext/duckdb/prepared_statement.c +120 -5
- data/ext/duckdb/util.c +8 -0
- data/ext/duckdb/util.h +1 -0
- data/ext/duckdb/value.c +526 -1
- data/lib/duckdb/connection.rb +19 -0
- data/lib/duckdb/converter/int_to_sym.rb +54 -1
- data/lib/duckdb/converter.rb +7 -7
- data/lib/duckdb/error.rb +26 -0
- data/lib/duckdb/function_type_validation.rb +1 -0
- data/lib/duckdb/logical_type.rb +6 -3
- data/lib/duckdb/prepared_statement.rb +15 -0
- data/lib/duckdb/scalar_function.rb +6 -6
- data/lib/duckdb/value.rb +400 -0
- data/lib/duckdb/version.rb +1 -1
- data/lib/duckdb.rb +1 -0
- metadata +5 -3
data/ext/duckdb/value.c
CHANGED
|
@@ -21,7 +21,34 @@ static VALUE value_s__create_blob(VALUE klass, VALUE str);
|
|
|
21
21
|
static VALUE value_s__create_hugeint(VALUE klass, VALUE lower, VALUE upper);
|
|
22
22
|
static VALUE value_s__create_uhugeint(VALUE klass, VALUE lower, VALUE upper);
|
|
23
23
|
static VALUE value_s__create_decimal(VALUE klass, VALUE lower, VALUE upper, VALUE width, VALUE scale);
|
|
24
|
+
static VALUE value_s__create_date(VALUE klass, VALUE year, VALUE month, VALUE day);
|
|
25
|
+
static VALUE value_s__create_time(VALUE klass, VALUE hour, VALUE min, VALUE sec, VALUE micros);
|
|
26
|
+
static VALUE value_s__create_time_ns(VALUE klass, VALUE hour, VALUE min, VALUE sec, VALUE nanos);
|
|
27
|
+
static VALUE value_s__create_time_tz(VALUE klass, VALUE micros, VALUE offset);
|
|
28
|
+
static VALUE value_s__create_timestamp(VALUE klass, VALUE year, VALUE month, VALUE day, VALUE hour, VALUE min, VALUE sec, VALUE micros);
|
|
29
|
+
static VALUE value_s__create_timestamp_s(VALUE klass, VALUE year, VALUE month, VALUE day, VALUE hour, VALUE min, VALUE sec);
|
|
30
|
+
static VALUE value_s__create_timestamp_ms(VALUE klass, VALUE year, VALUE month, VALUE day, VALUE hour, VALUE min, VALUE sec, VALUE micros);
|
|
31
|
+
static VALUE value_s__create_timestamp_ns(VALUE klass, VALUE year, VALUE month, VALUE day, VALUE hour, VALUE min, VALUE sec, VALUE nanos);
|
|
32
|
+
static VALUE value_s__create_timestamp_tz(VALUE klass, VALUE micros);
|
|
33
|
+
static VALUE value_s__create_interval(VALUE klass, VALUE months, VALUE days, VALUE micros);
|
|
34
|
+
static VALUE value_s__create_enum(VALUE klass, VALUE ltype, VALUE index);
|
|
35
|
+
static VALUE value_s__create_union(VALUE klass, VALUE ltype, VALUE tag_index, VALUE member);
|
|
36
|
+
static VALUE value_s__create_bit(VALUE klass, VALUE data);
|
|
37
|
+
static VALUE value_s__create_bignum(VALUE klass, VALUE data, VALUE is_negative);
|
|
24
38
|
static VALUE value_s_create_null(VALUE klass);
|
|
39
|
+
static VALUE to_ruby_via_vector(duckdb_logical_type logical_type, duckdb_value val);
|
|
40
|
+
static idx_t marshal_values(VALUE ary, duckdb_value **out, volatile VALUE *guard);
|
|
41
|
+
static VALUE value_s__create_list(VALUE klass, VALUE ltype, VALUE values);
|
|
42
|
+
static VALUE value_s__create_array(VALUE klass, VALUE ltype, VALUE values);
|
|
43
|
+
static VALUE value_s__create_struct(VALUE klass, VALUE ltype, VALUE values);
|
|
44
|
+
static VALUE value_s__create_map(VALUE klass, VALUE ltype, VALUE keys, VALUE values);
|
|
45
|
+
static VALUE value_list_size(VALUE self);
|
|
46
|
+
static VALUE value_list_child(VALUE self, VALUE vidx);
|
|
47
|
+
static VALUE value_struct_child(VALUE self, VALUE vidx);
|
|
48
|
+
static VALUE value_map_size(VALUE self);
|
|
49
|
+
static VALUE value_map_key(VALUE self, VALUE vidx);
|
|
50
|
+
static VALUE value_map_value(VALUE self, VALUE vidx);
|
|
51
|
+
static VALUE value_to_ruby(VALUE self);
|
|
25
52
|
|
|
26
53
|
static const rb_data_type_t value_data_type = {
|
|
27
54
|
"DuckDB/Value",
|
|
@@ -140,6 +167,136 @@ static VALUE value_s__create_decimal(VALUE klass, VALUE lower, VALUE upper, VALU
|
|
|
140
167
|
return rbduckdb_value_new(value);
|
|
141
168
|
}
|
|
142
169
|
|
|
170
|
+
/* :nodoc: */
|
|
171
|
+
static VALUE value_s__create_date(VALUE klass, VALUE year, VALUE month, VALUE day) {
|
|
172
|
+
duckdb_value value = duckdb_create_date(rbduckdb_to_duckdb_date_from_value(year, month, day));
|
|
173
|
+
return rbduckdb_value_new(value);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/* :nodoc: */
|
|
177
|
+
static VALUE value_s__create_time(VALUE klass, VALUE hour, VALUE min, VALUE sec, VALUE micros) {
|
|
178
|
+
duckdb_value value = duckdb_create_time(rbduckdb_to_duckdb_time_from_value(hour, min, sec, micros));
|
|
179
|
+
return rbduckdb_value_new(value);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/* :nodoc: */
|
|
183
|
+
static VALUE value_s__create_time_ns(VALUE klass, VALUE hour, VALUE min, VALUE sec, VALUE nanos) {
|
|
184
|
+
duckdb_time_ns time_ns = rbduckdb_to_duckdb_time_ns_from_value(hour, min, sec, nanos);
|
|
185
|
+
|
|
186
|
+
return rbduckdb_value_new(duckdb_create_time_ns(time_ns));
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/* :nodoc: */
|
|
190
|
+
static VALUE value_s__create_time_tz(VALUE klass, VALUE micros, VALUE offset) {
|
|
191
|
+
duckdb_time_tz time_tz = duckdb_create_time_tz(NUM2LL(micros), NUM2INT(offset));
|
|
192
|
+
return rbduckdb_value_new(duckdb_create_time_tz_value(time_tz));
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/* :nodoc: */
|
|
196
|
+
static VALUE value_s__create_timestamp(VALUE klass, VALUE year, VALUE month, VALUE day, VALUE hour, VALUE min, VALUE sec, VALUE micros) {
|
|
197
|
+
duckdb_timestamp timestamp = rbduckdb_to_duckdb_timestamp_from_value(year, month, day, hour, min, sec, micros);
|
|
198
|
+
return rbduckdb_value_new(duckdb_create_timestamp(timestamp));
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/* :nodoc: */
|
|
202
|
+
static VALUE value_s__create_timestamp_s(VALUE klass, VALUE year, VALUE month, VALUE day, VALUE hour, VALUE min, VALUE sec) {
|
|
203
|
+
duckdb_timestamp_s ts;
|
|
204
|
+
|
|
205
|
+
ts.seconds = rbduckdb_to_duckdb_timestamp_from_value(year, month, day, hour, min, sec, INT2FIX(0)).micros / 1000000;
|
|
206
|
+
return rbduckdb_value_new(duckdb_create_timestamp_s(ts));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/* :nodoc: */
|
|
210
|
+
static VALUE value_s__create_timestamp_ms(VALUE klass, VALUE year, VALUE month, VALUE day, VALUE hour, VALUE min, VALUE sec, VALUE micros) {
|
|
211
|
+
duckdb_timestamp_ms ts;
|
|
212
|
+
|
|
213
|
+
ts.millis = rbduckdb_to_duckdb_timestamp_from_value(year, month, day, hour, min, sec, micros).micros / 1000;
|
|
214
|
+
return rbduckdb_value_new(duckdb_create_timestamp_ms(ts));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/* :nodoc: */
|
|
218
|
+
static VALUE value_s__create_timestamp_ns(VALUE klass, VALUE year, VALUE month, VALUE day, VALUE hour, VALUE min, VALUE sec, VALUE nanos) {
|
|
219
|
+
duckdb_timestamp_ns ts;
|
|
220
|
+
|
|
221
|
+
ts.nanos = rbduckdb_to_duckdb_timestamp_from_value(year, month, day, hour, min, sec, INT2FIX(0)).micros * 1000 + NUM2LL(nanos);
|
|
222
|
+
return rbduckdb_value_new(duckdb_create_timestamp_ns(ts));
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/* :nodoc: */
|
|
226
|
+
static VALUE value_s__create_timestamp_tz(VALUE klass, VALUE micros) {
|
|
227
|
+
duckdb_timestamp ts;
|
|
228
|
+
|
|
229
|
+
ts.micros = NUM2LL(micros);
|
|
230
|
+
return rbduckdb_value_new(duckdb_create_timestamp_tz(ts));
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/* :nodoc: */
|
|
234
|
+
static VALUE value_s__create_interval(VALUE klass, VALUE months, VALUE days, VALUE micros) {
|
|
235
|
+
duckdb_interval interval;
|
|
236
|
+
|
|
237
|
+
rbduckdb_to_duckdb_interval_from_value(&interval, months, days, micros);
|
|
238
|
+
return rbduckdb_value_new(duckdb_create_interval(interval));
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/* :nodoc: */
|
|
242
|
+
static VALUE value_s__create_enum(VALUE klass, VALUE ltype, VALUE index) {
|
|
243
|
+
duckdb_logical_type type = rbduckdb_get_struct_logical_type(ltype)->logical_type;
|
|
244
|
+
duckdb_value value = duckdb_create_enum_value(type, NUM2ULL(index));
|
|
245
|
+
|
|
246
|
+
if (value == NULL) {
|
|
247
|
+
rb_raise(eDuckDBError, "failed to create ENUM value");
|
|
248
|
+
}
|
|
249
|
+
return rbduckdb_value_new(value);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/* :nodoc: */
|
|
253
|
+
static VALUE value_s__create_union(VALUE klass, VALUE ltype, VALUE tag_index, VALUE member) {
|
|
254
|
+
duckdb_logical_type type = rbduckdb_get_struct_logical_type(ltype)->logical_type;
|
|
255
|
+
duckdb_value value = duckdb_create_union_value(type, (idx_t)NUM2ULL(tag_index), rbduckdb_get_struct_value(member)->value);
|
|
256
|
+
|
|
257
|
+
if (value == NULL) {
|
|
258
|
+
rb_raise(eDuckDBError, "failed to create UNION value (mismatched member type?)");
|
|
259
|
+
}
|
|
260
|
+
return rbduckdb_value_new(value);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/*
|
|
264
|
+
* :nodoc:
|
|
265
|
+
* data is the raw DuckDB BIT blob built in Ruby: one byte holding the
|
|
266
|
+
* number of padding bits, then the bits MSB-first with the padding bits
|
|
267
|
+
* of the first data byte set to 1.
|
|
268
|
+
*/
|
|
269
|
+
static VALUE value_s__create_bit(VALUE klass, VALUE data) {
|
|
270
|
+
duckdb_bit bit;
|
|
271
|
+
duckdb_value value;
|
|
272
|
+
|
|
273
|
+
bit.data = (uint8_t *)StringValuePtr(data);
|
|
274
|
+
bit.size = RSTRING_LEN(data);
|
|
275
|
+
value = duckdb_create_bit(bit);
|
|
276
|
+
if (value == NULL) {
|
|
277
|
+
rb_raise(eDuckDBError, "failed to create BIT value");
|
|
278
|
+
}
|
|
279
|
+
return rbduckdb_value_new(value);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/*
|
|
283
|
+
* :nodoc:
|
|
284
|
+
* data is the magnitude in big-endian bytes, built in Ruby.
|
|
285
|
+
*/
|
|
286
|
+
static VALUE value_s__create_bignum(VALUE klass, VALUE data, VALUE is_negative) {
|
|
287
|
+
duckdb_bignum bignum;
|
|
288
|
+
duckdb_value value;
|
|
289
|
+
|
|
290
|
+
bignum.data = (uint8_t *)StringValuePtr(data);
|
|
291
|
+
bignum.size = RSTRING_LEN(data);
|
|
292
|
+
bignum.is_negative = RTEST(is_negative);
|
|
293
|
+
value = duckdb_create_bignum(bignum);
|
|
294
|
+
if (value == NULL) {
|
|
295
|
+
rb_raise(eDuckDBError, "failed to create BIGNUM value");
|
|
296
|
+
}
|
|
297
|
+
return rbduckdb_value_new(value);
|
|
298
|
+
}
|
|
299
|
+
|
|
143
300
|
/*
|
|
144
301
|
* call-seq:
|
|
145
302
|
* DuckDB::Value.create_null -> DuckDB::Value
|
|
@@ -154,6 +311,231 @@ static VALUE value_s_create_null(VALUE klass) {
|
|
|
154
311
|
return rbduckdb_value_new(value);
|
|
155
312
|
}
|
|
156
313
|
|
|
314
|
+
/*
|
|
315
|
+
* Fills *out with the unwrapped duckdb_values of a Ruby Array of
|
|
316
|
+
* DuckDB::Value objects and returns the array length. The buffer is
|
|
317
|
+
* an ALLOCV tmpbuf; the caller must call ALLOCV_END(*guard) after use.
|
|
318
|
+
* The buffer is non-NULL even for an empty array because
|
|
319
|
+
* duckdb_create_*_value functions reject a NULL values pointer.
|
|
320
|
+
*
|
|
321
|
+
* This always allocates via rb_alloc_tmp_buffer2 (heap-backed)
|
|
322
|
+
* rather than the ALLOCV_N macro, because that macro's small-size fast
|
|
323
|
+
* path uses alloca() in *this* function's stack frame: the returned
|
|
324
|
+
* pointer would dangle once marshal_values returns, and a second call
|
|
325
|
+
* (e.g. keys then values for MAP) would silently reuse and clobber the
|
|
326
|
+
* same stack slot as the first call's "buffer".
|
|
327
|
+
*/
|
|
328
|
+
static idx_t marshal_values(VALUE ary, duckdb_value **out, volatile VALUE *guard) {
|
|
329
|
+
idx_t n = (idx_t)RARRAY_LEN(ary);
|
|
330
|
+
idx_t count = n == 0 ? 1 : n;
|
|
331
|
+
duckdb_value *buf = (duckdb_value *)rb_alloc_tmp_buffer2(guard, (long)count, sizeof(duckdb_value));
|
|
332
|
+
idx_t i;
|
|
333
|
+
|
|
334
|
+
for (i = 0; i < n; i++) {
|
|
335
|
+
buf[i] = rbduckdb_get_struct_value(RARRAY_AREF(ary, i))->value;
|
|
336
|
+
}
|
|
337
|
+
*out = buf;
|
|
338
|
+
return n;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/* :nodoc: */
|
|
342
|
+
static VALUE value_s__create_list(VALUE klass, VALUE ltype, VALUE values) {
|
|
343
|
+
duckdb_logical_type type = rbduckdb_get_struct_logical_type(ltype)->logical_type;
|
|
344
|
+
duckdb_value *buf;
|
|
345
|
+
volatile VALUE guard;
|
|
346
|
+
idx_t n = marshal_values(values, &buf, &guard);
|
|
347
|
+
duckdb_value value = duckdb_create_list_value(type, buf, n);
|
|
348
|
+
|
|
349
|
+
RB_GC_GUARD(values);
|
|
350
|
+
ALLOCV_END(guard);
|
|
351
|
+
if (value == NULL) {
|
|
352
|
+
rb_raise(eDuckDBError, "failed to create LIST value");
|
|
353
|
+
}
|
|
354
|
+
return rbduckdb_value_new(value);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/* :nodoc: */
|
|
358
|
+
static VALUE value_s__create_array(VALUE klass, VALUE ltype, VALUE values) {
|
|
359
|
+
duckdb_logical_type type = rbduckdb_get_struct_logical_type(ltype)->logical_type;
|
|
360
|
+
duckdb_value *buf;
|
|
361
|
+
volatile VALUE guard;
|
|
362
|
+
idx_t n = marshal_values(values, &buf, &guard);
|
|
363
|
+
duckdb_value value = duckdb_create_array_value(type, buf, n);
|
|
364
|
+
|
|
365
|
+
RB_GC_GUARD(values);
|
|
366
|
+
ALLOCV_END(guard);
|
|
367
|
+
if (value == NULL) {
|
|
368
|
+
rb_raise(eDuckDBError, "failed to create ARRAY value");
|
|
369
|
+
}
|
|
370
|
+
return rbduckdb_value_new(value);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/* :nodoc: */
|
|
374
|
+
static VALUE value_s__create_struct(VALUE klass, VALUE ltype, VALUE values) {
|
|
375
|
+
duckdb_logical_type type = rbduckdb_get_struct_logical_type(ltype)->logical_type;
|
|
376
|
+
duckdb_value *buf;
|
|
377
|
+
volatile VALUE guard;
|
|
378
|
+
idx_t n = marshal_values(values, &buf, &guard);
|
|
379
|
+
duckdb_value value = duckdb_create_struct_value(type, buf);
|
|
380
|
+
|
|
381
|
+
(void)n;
|
|
382
|
+
RB_GC_GUARD(values);
|
|
383
|
+
ALLOCV_END(guard);
|
|
384
|
+
if (value == NULL) {
|
|
385
|
+
rb_raise(eDuckDBError, "failed to create STRUCT value");
|
|
386
|
+
}
|
|
387
|
+
return rbduckdb_value_new(value);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/* :nodoc: */
|
|
391
|
+
static VALUE value_s__create_map(VALUE klass, VALUE ltype, VALUE keys, VALUE values) {
|
|
392
|
+
duckdb_logical_type type = rbduckdb_get_struct_logical_type(ltype)->logical_type;
|
|
393
|
+
duckdb_value *kbuf;
|
|
394
|
+
duckdb_value *vbuf;
|
|
395
|
+
volatile VALUE kguard;
|
|
396
|
+
volatile VALUE vguard;
|
|
397
|
+
idx_t n = marshal_values(keys, &kbuf, &kguard);
|
|
398
|
+
duckdb_value value;
|
|
399
|
+
|
|
400
|
+
marshal_values(values, &vbuf, &vguard); /* same length, validated in Ruby */
|
|
401
|
+
value = duckdb_create_map_value(type, kbuf, vbuf, n);
|
|
402
|
+
RB_GC_GUARD(keys);
|
|
403
|
+
RB_GC_GUARD(values);
|
|
404
|
+
ALLOCV_END(kguard);
|
|
405
|
+
ALLOCV_END(vguard);
|
|
406
|
+
if (value == NULL) {
|
|
407
|
+
rb_raise(eDuckDBError, "failed to create MAP value");
|
|
408
|
+
}
|
|
409
|
+
return rbduckdb_value_new(value);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/*
|
|
413
|
+
* call-seq:
|
|
414
|
+
* value.list_size -> Integer
|
|
415
|
+
*
|
|
416
|
+
* Returns the number of elements of a LIST value.
|
|
417
|
+
*
|
|
418
|
+
* require 'duckdb'
|
|
419
|
+
* child_type = DuckDB::LogicalType.resolve(:integer)
|
|
420
|
+
* values = [1, 2, 3].map { |i| DuckDB::Value.create_int32(i) }
|
|
421
|
+
* list = DuckDB::Value.create_list(child_type, values)
|
|
422
|
+
* list.list_size # => 3
|
|
423
|
+
*/
|
|
424
|
+
static VALUE value_list_size(VALUE self) {
|
|
425
|
+
return ULL2NUM(duckdb_get_list_size(rbduckdb_get_struct_value(self)->value));
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/*
|
|
429
|
+
* call-seq:
|
|
430
|
+
* value.list_child(index) -> DuckDB::Value
|
|
431
|
+
*
|
|
432
|
+
* Returns the element at the specified index (0-based) of a LIST value
|
|
433
|
+
* as a DuckDB::Value.
|
|
434
|
+
* Raises IndexError if the index is out of range or the value is not a LIST.
|
|
435
|
+
*
|
|
436
|
+
* require 'duckdb'
|
|
437
|
+
* child_type = DuckDB::LogicalType.resolve(:integer)
|
|
438
|
+
* values = [1, 2, 3].map { |i| DuckDB::Value.create_int32(i) }
|
|
439
|
+
* list = DuckDB::Value.create_list(child_type, values)
|
|
440
|
+
* list.list_child(0) # => DuckDB::Value
|
|
441
|
+
*/
|
|
442
|
+
static VALUE value_list_child(VALUE self, VALUE vidx) {
|
|
443
|
+
duckdb_value child = duckdb_get_list_child(rbduckdb_get_struct_value(self)->value, (idx_t)NUM2ULL(vidx));
|
|
444
|
+
|
|
445
|
+
if (child == NULL) {
|
|
446
|
+
rb_raise(rb_eIndexError, "list index out of range (or the value is not a LIST)");
|
|
447
|
+
}
|
|
448
|
+
return rbduckdb_value_new(child);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/*
|
|
452
|
+
* call-seq:
|
|
453
|
+
* value.struct_child(index) -> DuckDB::Value
|
|
454
|
+
*
|
|
455
|
+
* Returns the field at the specified index (0-based) of a STRUCT value
|
|
456
|
+
* as a DuckDB::Value.
|
|
457
|
+
* Raises IndexError if the index is out of range or the value is not a STRUCT.
|
|
458
|
+
*
|
|
459
|
+
* require 'duckdb'
|
|
460
|
+
* struct_type = DuckDB::LogicalType.create_struct(a: :integer, b: :varchar)
|
|
461
|
+
* values = [DuckDB::Value.create_int32(1), DuckDB::Value.create_varchar('x')]
|
|
462
|
+
* struct = DuckDB::Value.create_struct(struct_type, values)
|
|
463
|
+
* struct.struct_child(0) # => DuckDB::Value
|
|
464
|
+
*/
|
|
465
|
+
static VALUE value_struct_child(VALUE self, VALUE vidx) {
|
|
466
|
+
duckdb_value child = duckdb_get_struct_child(rbduckdb_get_struct_value(self)->value, (idx_t)NUM2ULL(vidx));
|
|
467
|
+
|
|
468
|
+
if (child == NULL) {
|
|
469
|
+
rb_raise(rb_eIndexError, "struct index out of range (or the value is not a STRUCT)");
|
|
470
|
+
}
|
|
471
|
+
return rbduckdb_value_new(child);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/*
|
|
475
|
+
* call-seq:
|
|
476
|
+
* value.map_size -> Integer
|
|
477
|
+
*
|
|
478
|
+
* Returns the number of entries of a MAP value.
|
|
479
|
+
*
|
|
480
|
+
* require 'duckdb'
|
|
481
|
+
* map_type = DuckDB::LogicalType.create_map(:varchar, :integer)
|
|
482
|
+
* keys = [DuckDB::Value.create_varchar('a')]
|
|
483
|
+
* values = [DuckDB::Value.create_int32(1)]
|
|
484
|
+
* map = DuckDB::Value.create_map(map_type, keys, values)
|
|
485
|
+
* map.map_size # => 1
|
|
486
|
+
*/
|
|
487
|
+
static VALUE value_map_size(VALUE self) {
|
|
488
|
+
return ULL2NUM(duckdb_get_map_size(rbduckdb_get_struct_value(self)->value));
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/*
|
|
492
|
+
* call-seq:
|
|
493
|
+
* value.map_key(index) -> DuckDB::Value
|
|
494
|
+
*
|
|
495
|
+
* Returns the key at the specified index (0-based) of a MAP value
|
|
496
|
+
* as a DuckDB::Value.
|
|
497
|
+
* Raises IndexError if the index is out of range or the value is not a MAP.
|
|
498
|
+
*
|
|
499
|
+
* require 'duckdb'
|
|
500
|
+
* map_type = DuckDB::LogicalType.create_map(:varchar, :integer)
|
|
501
|
+
* keys = [DuckDB::Value.create_varchar('a')]
|
|
502
|
+
* values = [DuckDB::Value.create_int32(1)]
|
|
503
|
+
* map = DuckDB::Value.create_map(map_type, keys, values)
|
|
504
|
+
* map.map_key(0) # => DuckDB::Value
|
|
505
|
+
*/
|
|
506
|
+
static VALUE value_map_key(VALUE self, VALUE vidx) {
|
|
507
|
+
duckdb_value child = duckdb_get_map_key(rbduckdb_get_struct_value(self)->value, (idx_t)NUM2ULL(vidx));
|
|
508
|
+
|
|
509
|
+
if (child == NULL) {
|
|
510
|
+
rb_raise(rb_eIndexError, "map index out of range (or the value is not a MAP)");
|
|
511
|
+
}
|
|
512
|
+
return rbduckdb_value_new(child);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/*
|
|
516
|
+
* call-seq:
|
|
517
|
+
* value.map_value(index) -> DuckDB::Value
|
|
518
|
+
*
|
|
519
|
+
* Returns the value at the specified index (0-based) of a MAP value
|
|
520
|
+
* as a DuckDB::Value.
|
|
521
|
+
* Raises IndexError if the index is out of range or the value is not a MAP.
|
|
522
|
+
*
|
|
523
|
+
* require 'duckdb'
|
|
524
|
+
* map_type = DuckDB::LogicalType.create_map(:varchar, :integer)
|
|
525
|
+
* keys = [DuckDB::Value.create_varchar('a')]
|
|
526
|
+
* values = [DuckDB::Value.create_int32(1)]
|
|
527
|
+
* map = DuckDB::Value.create_map(map_type, keys, values)
|
|
528
|
+
* map.map_value(0) # => DuckDB::Value
|
|
529
|
+
*/
|
|
530
|
+
static VALUE value_map_value(VALUE self, VALUE vidx) {
|
|
531
|
+
duckdb_value child = duckdb_get_map_value(rbduckdb_get_struct_value(self)->value, (idx_t)NUM2ULL(vidx));
|
|
532
|
+
|
|
533
|
+
if (child == NULL) {
|
|
534
|
+
rb_raise(rb_eIndexError, "map index out of range (or the value is not a MAP)");
|
|
535
|
+
}
|
|
536
|
+
return rbduckdb_value_new(child);
|
|
537
|
+
}
|
|
538
|
+
|
|
157
539
|
VALUE rbduckdb_value_new(duckdb_value value) {
|
|
158
540
|
rubyDuckDBValue *ctx;
|
|
159
541
|
VALUE obj = allocate(cDuckDBValue);
|
|
@@ -168,12 +550,38 @@ rubyDuckDBValue *rbduckdb_get_struct_value(VALUE obj) {
|
|
|
168
550
|
return ctx;
|
|
169
551
|
}
|
|
170
552
|
|
|
553
|
+
/*
|
|
554
|
+
* Converts a duckdb_value to Ruby by referencing it into a one-element
|
|
555
|
+
* vector and reusing the vector read path, so Value#to_ruby returns the
|
|
556
|
+
* same Ruby object a query result produces for that type. Used for types
|
|
557
|
+
* the C API has no direct duckdb_get_* accessors for.
|
|
558
|
+
*/
|
|
559
|
+
static VALUE to_ruby_via_vector(duckdb_logical_type logical_type, duckdb_value val) {
|
|
560
|
+
duckdb_vector vec = duckdb_create_vector(logical_type, 1);
|
|
561
|
+
VALUE result;
|
|
562
|
+
|
|
563
|
+
duckdb_vector_reference_value(vec, val);
|
|
564
|
+
result = rbduckdb_vector_value_at(vec, logical_type, 0);
|
|
565
|
+
duckdb_destroy_vector(&vec);
|
|
566
|
+
return result;
|
|
567
|
+
}
|
|
568
|
+
|
|
171
569
|
VALUE rbduckdb_duckdb_value_to_ruby(duckdb_value val) {
|
|
172
570
|
duckdb_logical_type logical_type;
|
|
173
571
|
duckdb_type type_id;
|
|
572
|
+
duckdb_value child_value;
|
|
174
573
|
VALUE result;
|
|
574
|
+
VALUE elem;
|
|
575
|
+
VALUE key;
|
|
175
576
|
char *str;
|
|
577
|
+
idx_t i;
|
|
578
|
+
idx_t size;
|
|
579
|
+
|
|
580
|
+
if (duckdb_is_null_value(val)) {
|
|
581
|
+
return Qnil;
|
|
582
|
+
}
|
|
176
583
|
|
|
584
|
+
/* logical_type from duckdb_get_value_type is borrowed and must not be destroyed */
|
|
177
585
|
logical_type = duckdb_get_value_type(val);
|
|
178
586
|
type_id = duckdb_get_type_id(logical_type);
|
|
179
587
|
|
|
@@ -255,6 +663,73 @@ VALUE rbduckdb_duckdb_value_to_ruby(duckdb_value val) {
|
|
|
255
663
|
case DUCKDB_TYPE_UUID:
|
|
256
664
|
result = rbduckdb_uuid_uhugeint_to_ruby(duckdb_get_uuid(val));
|
|
257
665
|
break;
|
|
666
|
+
case DUCKDB_TYPE_ENUM:
|
|
667
|
+
str = duckdb_enum_dictionary_value(logical_type, duckdb_get_enum_value(val));
|
|
668
|
+
if (str == NULL) {
|
|
669
|
+
result = Qnil;
|
|
670
|
+
break;
|
|
671
|
+
}
|
|
672
|
+
result = rb_utf8_str_new_cstr(str);
|
|
673
|
+
duckdb_free(str);
|
|
674
|
+
break;
|
|
675
|
+
case DUCKDB_TYPE_LIST:
|
|
676
|
+
size = duckdb_get_list_size(val);
|
|
677
|
+
result = rb_ary_new_capa(size);
|
|
678
|
+
for (i = 0; i < size; i++) {
|
|
679
|
+
child_value = duckdb_get_list_child(val, i);
|
|
680
|
+
elem = rbduckdb_duckdb_value_to_ruby(child_value);
|
|
681
|
+
duckdb_destroy_value(&child_value);
|
|
682
|
+
rb_ary_push(result, elem);
|
|
683
|
+
}
|
|
684
|
+
break;
|
|
685
|
+
case DUCKDB_TYPE_STRUCT:
|
|
686
|
+
size = duckdb_struct_type_child_count(logical_type);
|
|
687
|
+
result = rb_hash_new();
|
|
688
|
+
for (i = 0; i < size; i++) {
|
|
689
|
+
str = duckdb_struct_type_child_name(logical_type, i);
|
|
690
|
+
child_value = duckdb_get_struct_child(val, i);
|
|
691
|
+
elem = rbduckdb_duckdb_value_to_ruby(child_value);
|
|
692
|
+
duckdb_destroy_value(&child_value);
|
|
693
|
+
rb_hash_aset(result, ID2SYM(rb_intern(str)), elem);
|
|
694
|
+
duckdb_free(str);
|
|
695
|
+
}
|
|
696
|
+
break;
|
|
697
|
+
case DUCKDB_TYPE_MAP:
|
|
698
|
+
size = duckdb_get_map_size(val);
|
|
699
|
+
result = rb_hash_new();
|
|
700
|
+
for (i = 0; i < size; i++) {
|
|
701
|
+
child_value = duckdb_get_map_key(val, i);
|
|
702
|
+
key = rbduckdb_duckdb_value_to_ruby(child_value);
|
|
703
|
+
duckdb_destroy_value(&child_value);
|
|
704
|
+
child_value = duckdb_get_map_value(val, i);
|
|
705
|
+
elem = rbduckdb_duckdb_value_to_ruby(child_value);
|
|
706
|
+
duckdb_destroy_value(&child_value);
|
|
707
|
+
rb_hash_aset(result, key, elem);
|
|
708
|
+
}
|
|
709
|
+
break;
|
|
710
|
+
case DUCKDB_TYPE_ARRAY:
|
|
711
|
+
case DUCKDB_TYPE_UNION:
|
|
712
|
+
case DUCKDB_TYPE_BIT:
|
|
713
|
+
case DUCKDB_TYPE_DECIMAL:
|
|
714
|
+
result = to_ruby_via_vector(logical_type, val);
|
|
715
|
+
break;
|
|
716
|
+
case DUCKDB_TYPE_BLOB: {
|
|
717
|
+
duckdb_blob blob = duckdb_get_blob(val);
|
|
718
|
+
|
|
719
|
+
result = rb_str_new((const char *)blob.data, blob.size);
|
|
720
|
+
duckdb_free(blob.data);
|
|
721
|
+
break;
|
|
722
|
+
}
|
|
723
|
+
case DUCKDB_TYPE_BIGNUM: {
|
|
724
|
+
duckdb_bignum bignum = duckdb_get_bignum(val);
|
|
725
|
+
|
|
726
|
+
result = rb_integer_unpack(bignum.data, bignum.size, 1, 0, INTEGER_PACK_BIG_ENDIAN);
|
|
727
|
+
duckdb_free(bignum.data);
|
|
728
|
+
if (bignum.is_negative) {
|
|
729
|
+
result = rb_funcall(result, rb_intern("-@"), 0);
|
|
730
|
+
}
|
|
731
|
+
break;
|
|
732
|
+
}
|
|
258
733
|
default:
|
|
259
734
|
result = Qnil;
|
|
260
735
|
break;
|
|
@@ -263,6 +738,31 @@ VALUE rbduckdb_duckdb_value_to_ruby(duckdb_value val) {
|
|
|
263
738
|
return result;
|
|
264
739
|
}
|
|
265
740
|
|
|
741
|
+
/*
|
|
742
|
+
* call-seq:
|
|
743
|
+
* value.to_ruby -> Object
|
|
744
|
+
*
|
|
745
|
+
* Converts the DuckDB::Value to a Ruby object. Scalar types are converted
|
|
746
|
+
* to their natural Ruby classes. LIST and ARRAY values are converted to
|
|
747
|
+
* Array recursively. STRUCT and MAP values are converted to Hash
|
|
748
|
+
* recursively (STRUCT keys are Symbols; MAP keys keep their natural Ruby
|
|
749
|
+
* type). ENUM values are converted to the member String. UNION values
|
|
750
|
+
* are converted to the member's Ruby value. BIT values are converted to
|
|
751
|
+
* a String of '0'/'1' characters. BIGNUM values are converted to
|
|
752
|
+
* Integer. BLOB values are converted to a BINARY-encoded String.
|
|
753
|
+
* DECIMAL values are converted to BigDecimal. NULL is converted to nil.
|
|
754
|
+
* Returns nil for unsupported types.
|
|
755
|
+
*
|
|
756
|
+
* require 'duckdb'
|
|
757
|
+
* child_type = DuckDB::LogicalType.resolve(:integer)
|
|
758
|
+
* values = [1, 2, 3].map { |i| DuckDB::Value.create_int32(i) }
|
|
759
|
+
* list = DuckDB::Value.create_list(child_type, values)
|
|
760
|
+
* list.to_ruby # => [1, 2, 3]
|
|
761
|
+
*/
|
|
762
|
+
static VALUE value_to_ruby(VALUE self) {
|
|
763
|
+
return rbduckdb_duckdb_value_to_ruby(rbduckdb_get_struct_value(self)->value);
|
|
764
|
+
}
|
|
765
|
+
|
|
266
766
|
void rbduckdb_init_value(void) {
|
|
267
767
|
#if 0
|
|
268
768
|
VALUE mDuckDB = rb_define_module("DuckDB");
|
|
@@ -286,6 +786,31 @@ void rbduckdb_init_value(void) {
|
|
|
286
786
|
rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_hugeint", value_s__create_hugeint, 2);
|
|
287
787
|
rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_uhugeint", value_s__create_uhugeint, 2);
|
|
288
788
|
rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_decimal", value_s__create_decimal, 4);
|
|
789
|
+
rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_date", value_s__create_date, 3);
|
|
790
|
+
rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_time", value_s__create_time, 4);
|
|
791
|
+
rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_time_ns", value_s__create_time_ns, 4);
|
|
792
|
+
rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_time_tz", value_s__create_time_tz, 2);
|
|
793
|
+
rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_timestamp", value_s__create_timestamp, 7);
|
|
794
|
+
rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_timestamp_s", value_s__create_timestamp_s, 6);
|
|
795
|
+
rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_timestamp_ms", value_s__create_timestamp_ms, 7);
|
|
796
|
+
rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_timestamp_ns", value_s__create_timestamp_ns, 7);
|
|
797
|
+
rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_timestamp_tz", value_s__create_timestamp_tz, 1);
|
|
798
|
+
rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_interval", value_s__create_interval, 3);
|
|
799
|
+
rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_enum", value_s__create_enum, 2);
|
|
800
|
+
rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_union", value_s__create_union, 3);
|
|
801
|
+
rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_bit", value_s__create_bit, 1);
|
|
802
|
+
rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_bignum", value_s__create_bignum, 2);
|
|
803
|
+
rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_list", value_s__create_list, 2);
|
|
804
|
+
rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_array", value_s__create_array, 2);
|
|
805
|
+
rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_struct", value_s__create_struct, 2);
|
|
806
|
+
rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_map", value_s__create_map, 3);
|
|
289
807
|
rb_define_singleton_method(cDuckDBValue, "create_null", value_s_create_null, 0);
|
|
290
|
-
}
|
|
291
808
|
|
|
809
|
+
rb_define_method(cDuckDBValue, "list_size", value_list_size, 0);
|
|
810
|
+
rb_define_method(cDuckDBValue, "list_child", value_list_child, 1);
|
|
811
|
+
rb_define_method(cDuckDBValue, "struct_child", value_struct_child, 1);
|
|
812
|
+
rb_define_method(cDuckDBValue, "map_size", value_map_size, 0);
|
|
813
|
+
rb_define_method(cDuckDBValue, "map_key", value_map_key, 1);
|
|
814
|
+
rb_define_method(cDuckDBValue, "map_value", value_map_value, 1);
|
|
815
|
+
rb_define_method(cDuckDBValue, "to_ruby", value_to_ruby, 0);
|
|
816
|
+
}
|
data/lib/duckdb/connection.rb
CHANGED
|
@@ -423,6 +423,25 @@ module DuckDB
|
|
|
423
423
|
end
|
|
424
424
|
end
|
|
425
425
|
|
|
426
|
+
# Returns the names of the tables referenced by the given SQL query,
|
|
427
|
+
# without executing it.
|
|
428
|
+
#
|
|
429
|
+
# @param query [String] the SQL query to inspect
|
|
430
|
+
# @param qualified [Boolean] if true, returns each table reference as
|
|
431
|
+
# written in the query, keeping any catalog/schema qualification;
|
|
432
|
+
# if false (default), bare table names only
|
|
433
|
+
# @return [Array<String>] the referenced table names, in unspecified order
|
|
434
|
+
# @raise [DuckDB::Error] if the query cannot be parsed
|
|
435
|
+
#
|
|
436
|
+
# @example
|
|
437
|
+
# con.table_names('SELECT * FROM users u JOIN orders o ON u.id = o.user_id').sort
|
|
438
|
+
# #=> ["orders", "users"]
|
|
439
|
+
# con.table_names('SELECT * FROM memory.main.users', qualified: true)
|
|
440
|
+
# #=> ["memory.main.users"]
|
|
441
|
+
def table_names(query, qualified: false)
|
|
442
|
+
_get_table_names(query, qualified).to_ruby
|
|
443
|
+
end
|
|
444
|
+
|
|
426
445
|
private
|
|
427
446
|
|
|
428
447
|
# Drives the Arrow stream at +address+ chunk by chunk into +table+,
|
|
@@ -73,11 +73,64 @@ module DuckDB
|
|
|
73
73
|
35 => :bignum,
|
|
74
74
|
36 => :sqlnull,
|
|
75
75
|
37 => :string_literal,
|
|
76
|
-
38 => :integer_literal
|
|
76
|
+
38 => :integer_literal,
|
|
77
|
+
39 => :time_ns
|
|
77
78
|
}.freeze
|
|
78
79
|
|
|
80
|
+
ERROR_TYPES = %i[
|
|
81
|
+
invalid
|
|
82
|
+
out_of_range
|
|
83
|
+
conversion
|
|
84
|
+
unknown_type
|
|
85
|
+
decimal
|
|
86
|
+
mismatch_type
|
|
87
|
+
divide_by_zero
|
|
88
|
+
object_size
|
|
89
|
+
invalid_type
|
|
90
|
+
serialization
|
|
91
|
+
transaction
|
|
92
|
+
not_implemented
|
|
93
|
+
expression
|
|
94
|
+
catalog
|
|
95
|
+
parser
|
|
96
|
+
planner
|
|
97
|
+
scheduler
|
|
98
|
+
executor
|
|
99
|
+
constraint
|
|
100
|
+
index
|
|
101
|
+
stat
|
|
102
|
+
connection
|
|
103
|
+
syntax
|
|
104
|
+
settings
|
|
105
|
+
binder
|
|
106
|
+
network
|
|
107
|
+
optimizer
|
|
108
|
+
null_pointer
|
|
109
|
+
io
|
|
110
|
+
interrupt
|
|
111
|
+
fatal
|
|
112
|
+
internal
|
|
113
|
+
invalid_input
|
|
114
|
+
out_of_memory
|
|
115
|
+
permission
|
|
116
|
+
parameter_not_resolved
|
|
117
|
+
parameter_not_allowed
|
|
118
|
+
dependency
|
|
119
|
+
http
|
|
120
|
+
missing_extension
|
|
121
|
+
autoload
|
|
122
|
+
sequence
|
|
123
|
+
invalid_configuration
|
|
124
|
+
].freeze
|
|
125
|
+
|
|
79
126
|
module_function
|
|
80
127
|
|
|
128
|
+
def error_type_to_sym(val) # :nodoc:
|
|
129
|
+
raise DuckDB::Error, "Unknown error type: #{val}" if val >= ERROR_TYPES.size
|
|
130
|
+
|
|
131
|
+
ERROR_TYPES[val]
|
|
132
|
+
end
|
|
133
|
+
|
|
81
134
|
def statement_type_to_sym(val) # :nodoc:
|
|
82
135
|
raise DuckDB::Error, "Unknown statement type: #{val}" if val >= STATEMENT_TYPES.size
|
|
83
136
|
|
data/lib/duckdb/converter.rb
CHANGED
|
@@ -83,18 +83,18 @@ module DuckDB
|
|
|
83
83
|
|
|
84
84
|
def _to_time_from_duckdb_timestamp_ns(time)
|
|
85
85
|
_to_time_from_duckdb_timestamp_s(time / 1_000_000_000).then do |tm|
|
|
86
|
-
_to_time(tm.year, tm.month, tm.day, tm.hour, tm.min, tm.sec, time % 1_000_000_000
|
|
86
|
+
_to_time(tm.year, tm.month, tm.day, tm.hour, tm.min, tm.sec, Rational(time % 1_000_000_000, 1000))
|
|
87
87
|
end
|
|
88
88
|
end
|
|
89
89
|
|
|
90
90
|
def _to_time_from_duckdb_time_ns(nanos)
|
|
91
91
|
hour = nanos / 3_600_000_000_000
|
|
92
|
-
nanos
|
|
93
|
-
|
|
94
|
-
nanos
|
|
95
|
-
sec
|
|
96
|
-
|
|
97
|
-
|
|
92
|
+
min = nanos % 3_600_000_000_000 / 60_000_000_000
|
|
93
|
+
sec = nanos % 60_000_000_000 / 1_000_000_000
|
|
94
|
+
nsec = nanos % 1_000_000_000
|
|
95
|
+
return Time.utc(1970, 1, 1, hour, min, sec, Rational(nsec, 1_000)) if default_timezone_utc?
|
|
96
|
+
|
|
97
|
+
Time.parse(format('%<hour>02d:%<min>02d:%<sec>02d.%<nsec>09d', hour: hour, min: min, sec: sec, nsec: nsec))
|
|
98
98
|
end
|
|
99
99
|
|
|
100
100
|
def _to_time_from_duckdb_time_tz(hour, min, sec, micro, timezone)
|