duckdb 1.5.3.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.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +47 -0
  3. data/CLAUDE.md +4 -0
  4. data/README.md +52 -0
  5. data/duckdb.gemspec +1 -1
  6. data/ext/duckdb/aggregate_function.c +0 -1
  7. data/ext/duckdb/appender.c +17 -0
  8. data/ext/duckdb/arrow_array_stream.c +226 -0
  9. data/ext/duckdb/arrow_array_stream.h +61 -0
  10. data/ext/duckdb/arrow_import.c +165 -0
  11. data/ext/duckdb/arrow_import.h +6 -0
  12. data/ext/duckdb/blob.c +1 -1
  13. data/ext/duckdb/blob.h +1 -2
  14. data/ext/duckdb/config.c +1 -1
  15. data/ext/duckdb/config.h +1 -1
  16. data/ext/duckdb/connection.c +26 -4
  17. data/ext/duckdb/converter.h +1 -0
  18. data/ext/duckdb/conveter.c +39 -9
  19. data/ext/duckdb/data_chunk.c +10 -0
  20. data/ext/duckdb/data_chunk.h +1 -0
  21. data/ext/duckdb/duckdb.c +13 -11
  22. data/ext/duckdb/error.c +13 -1
  23. data/ext/duckdb/error.h +2 -3
  24. data/ext/duckdb/function_executor.c +308 -2
  25. data/ext/duckdb/function_executor.h +44 -0
  26. data/ext/duckdb/function_vector.c +14 -0
  27. data/ext/duckdb/prepared_statement.c +141 -5
  28. data/ext/duckdb/result.c +49 -3
  29. data/ext/duckdb/result.h +11 -0
  30. data/ext/duckdb/ruby-duckdb.h +3 -0
  31. data/ext/duckdb/scalar_function.c +97 -29
  32. data/ext/duckdb/scalar_function.h +2 -4
  33. data/ext/duckdb/scalar_function_bind_info.c +13 -13
  34. data/ext/duckdb/scalar_function_bind_info.h +1 -1
  35. data/ext/duckdb/scalar_function_set.c +9 -9
  36. data/ext/duckdb/scalar_function_set.h +2 -2
  37. data/ext/duckdb/table_description.c +19 -19
  38. data/ext/duckdb/table_description.h +1 -1
  39. data/ext/duckdb/table_function.c +94 -28
  40. data/ext/duckdb/table_function.h +2 -2
  41. data/ext/duckdb/table_function_bind_info.c +20 -20
  42. data/ext/duckdb/table_function_bind_info.h +2 -2
  43. data/ext/duckdb/table_function_function_info.c +5 -5
  44. data/ext/duckdb/table_function_function_info.h +2 -2
  45. data/ext/duckdb/table_function_init_info.c +70 -5
  46. data/ext/duckdb/table_function_init_info.h +2 -2
  47. data/ext/duckdb/util.c +8 -0
  48. data/ext/duckdb/util.h +1 -0
  49. data/ext/duckdb/value.c +526 -1
  50. data/lib/duckdb/appender.rb +23 -0
  51. data/lib/duckdb/arrow_array_stream.rb +33 -0
  52. data/lib/duckdb/connection.rb +73 -0
  53. data/lib/duckdb/converter/int_to_sym.rb +54 -1
  54. data/lib/duckdb/converter.rb +7 -7
  55. data/lib/duckdb/error.rb +26 -0
  56. data/lib/duckdb/function_type_validation.rb +1 -0
  57. data/lib/duckdb/logical_type.rb +6 -3
  58. data/lib/duckdb/prepared_statement.rb +32 -0
  59. data/lib/duckdb/scalar_function.rb +6 -6
  60. data/lib/duckdb/value.rb +400 -0
  61. data/lib/duckdb/version.rb +1 -1
  62. data/lib/duckdb.rb +2 -0
  63. metadata +10 -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
+ }
@@ -558,6 +558,29 @@ module DuckDB
558
558
  raise_appender_error('failed to append_uhugeint')
559
559
  end
560
560
 
561
+ # :call-seq:
562
+ # appender.append_uuid(val) -> self
563
+ #
564
+ # Appends a UUID value to the current row in the appender.
565
+ # +val+ must be a String in canonical UUID format
566
+ # (<tt>xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx</tt>).
567
+ # Raises ArgumentError if +val+ is not a valid UUID string.
568
+ #
569
+ # require 'duckdb'
570
+ # db = DuckDB::Database.open
571
+ # con = db.connect
572
+ # con.query('CREATE TABLE uuids (id UUID)')
573
+ # appender = con.appender('uuids')
574
+ # appender
575
+ # .append_uuid('550e8400-e29b-41d4-a716-446655440000')
576
+ # .end_row
577
+ # .flush
578
+ def append_uuid(value)
579
+ return self if _append_uuid(value)
580
+
581
+ raise_appender_error('failed to append_uuid')
582
+ end
583
+
561
584
  # call-seq:
562
585
  # appender.append_date(val) -> self
563
586
  #
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DuckDB
4
+ # The ArrowArrayStream class represents an exported Arrow C stream of a
5
+ # query result (Arrow C Data Interface). It is created by
6
+ # DuckDB::Result#arrow_c_stream and cannot be instantiated directly.
7
+ #
8
+ # The object satisfies the Ruby Arrow C stream protocol: #arrow_c_stream
9
+ # returns self and #to_i returns the address of the underlying
10
+ # <tt>struct ArrowArrayStream</tt>, so it can be consumed by ruby-polars,
11
+ # red-arrow and other Arrow consumers:
12
+ #
13
+ # result = con.query('SELECT * FROM users')
14
+ #
15
+ # # ruby-polars
16
+ # df = Polars::DataFrame.new(result)
17
+ #
18
+ # # red-arrow
19
+ # reader = Arrow::RecordBatchReader.import(result.arrow_c_stream.to_i)
20
+ #
21
+ # The consumer takes ownership of the stream's contents; a result can be
22
+ # exported only once.
23
+ #
24
+ # [EXPERIMENTAL] This API is built on DuckDB's unstable Arrow C API and
25
+ # may change in any minor release.
26
+ class ArrowArrayStream
27
+ class << self
28
+ def new
29
+ raise DuckDB::Error, 'DuckDB::ArrowArrayStream cannot be instantiated directly. Use DuckDB::Result#arrow_c_stream.'
30
+ end
31
+ end
32
+ end
33
+ end