duckdb 1.5.6.1 → 2.0.0.1

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.
data/ext/duckdb/result.c CHANGED
@@ -3,6 +3,30 @@
3
3
  struct chunk_arg {
4
4
  duckdb_data_chunk chunk;
5
5
  idx_t col_count;
6
+ duckdb_logical_type *types;
7
+ };
8
+
9
+ struct owned_type_arg {
10
+ duckdb_vector vector;
11
+ duckdb_logical_type type;
12
+ idx_t bgn;
13
+ idx_t end;
14
+ };
15
+
16
+ struct struct_fields_arg {
17
+ duckdb_logical_type ty;
18
+ duckdb_vector vector;
19
+ idx_t row_idx;
20
+ duckdb_logical_type child_type;
21
+ };
22
+
23
+ struct map_entries_arg {
24
+ duckdb_vector key_vector;
25
+ duckdb_vector value_vector;
26
+ duckdb_logical_type key_type;
27
+ duckdb_logical_type value_type;
28
+ idx_t bgn;
29
+ idx_t end;
6
30
  };
7
31
 
8
32
  static VALUE cDuckDBResult;
@@ -14,6 +38,7 @@ static VALUE result_column_count(VALUE oDuckDBResult);
14
38
  static VALUE result_rows_changed(VALUE oDuckDBResult);
15
39
  static VALUE result_columns(VALUE oDuckDBResult);
16
40
  static VALUE destroy_data_chunk(VALUE arg);
41
+ static duckdb_logical_type *result_column_types(rubyDuckDBResult *ctx);
17
42
 
18
43
  static VALUE result__chunk_stream(VALUE oDuckDBResult);
19
44
  static VALUE result_arrow_c_stream(VALUE oDuckDBResult);
@@ -22,20 +47,10 @@ static VALUE result__return_type(VALUE oDuckDBResult);
22
47
  static VALUE result__statement_type(VALUE oDuckDBResult);
23
48
  static VALUE result__enum_internal_type(VALUE oDuckDBResult, VALUE col_idx);
24
49
 
25
- static VALUE vector_date(void *vector_data, idx_t row_idx);
26
- static VALUE vector_timestamp(void* vector_data, idx_t row_idx);
27
- static VALUE vector_time(void* vector_data, idx_t row_idx);
28
- static VALUE vector_interval(void* vector_data, idx_t row_idx);
29
50
  static VALUE vector_blob(void* vector_data, idx_t row_idx);
30
51
  static VALUE vector_varchar(void* vector_data, idx_t row_idx);
31
- static VALUE vector_hugeint(void* vector_data, idx_t row_idx);
32
- static VALUE vector_uhugeint(void* vector_data, idx_t row_idx);
33
52
  static VALUE vector_decimal(duckdb_logical_type ty, void* vector_data, idx_t row_idx);
34
53
 
35
- static VALUE vector_timestamp_s(void* vector_data, idx_t row_idx);
36
- static VALUE vector_timestamp_ms(void* vector_data, idx_t row_idx);
37
- static VALUE vector_timestamp_ns(void* vector_data, idx_t row_idx);
38
- static VALUE vector_time_ns(void* vector_data, idx_t row_idx);
39
54
  static VALUE vector_enum(duckdb_logical_type ty, void* vector_data, idx_t row_idx);
40
55
  static VALUE vector_array(duckdb_logical_type ty, duckdb_vector vector, idx_t row_idx);
41
56
  static VALUE vector_list(duckdb_logical_type ty, duckdb_vector vector, void* vector_data, idx_t row_idx);
@@ -46,7 +61,13 @@ static VALUE vector_bit(void* vector_data, idx_t row_idx);
46
61
  static VALUE vector_time_tz(void* vector_data, idx_t row_idx);
47
62
  static VALUE vector_timestamp_tz(void* vector_data, idx_t row_idx);
48
63
  static VALUE vector_uuid(void* vector_data, idx_t row_idx);
49
- static VALUE vector_value(duckdb_vector vector, idx_t row_idx);
64
+ static VALUE convert_values_in_range(VALUE varg);
65
+ static VALUE destroy_owned_type(VALUE varg);
66
+ static VALUE vector_values_with_owned_type(duckdb_vector vector, duckdb_logical_type type, idx_t bgn, idx_t end);
67
+ static VALUE convert_map_entries(VALUE varg);
68
+ static VALUE destroy_map_types(VALUE varg);
69
+ static VALUE convert_struct_fields(VALUE varg);
70
+ static VALUE destroy_struct_child_type(VALUE varg);
50
71
 
51
72
  /*
52
73
  * No RUBY_TYPED_FREE_IMMEDIATELY: deallocate can block, and must not run
@@ -66,6 +87,11 @@ static void deallocate(void *ctx) {
66
87
  }
67
88
 
68
89
  static VALUE allocate(VALUE klass) {
90
+ /*
91
+ * Use calloc (not xcalloc): rbduckdb_result_unref frees the struct, and it
92
+ * can run from the Arrow stream release callback on a non-Ruby thread
93
+ * where xfree is unsafe.
94
+ */
69
95
  rubyDuckDBResult *ctx = calloc((size_t)1, sizeof(rubyDuckDBResult));
70
96
 
71
97
  if (ctx == NULL) {
@@ -80,7 +106,13 @@ void rbduckdb_result_ref(rubyDuckDBResult *ctx) {
80
106
  }
81
107
 
82
108
  void rbduckdb_result_unref(rubyDuckDBResult *ctx) {
109
+ idx_t col_idx;
110
+
83
111
  if (RUBY_ATOMIC_FETCH_SUB(ctx->refcount, 1) == 1) {
112
+ for (col_idx = 0; col_idx < ctx->column_types_count; col_idx++) {
113
+ duckdb_destroy_logical_type(&(ctx->column_types[col_idx]));
114
+ }
115
+ free(ctx->column_types);
84
116
  duckdb_destroy_result(&(ctx->result));
85
117
  free(ctx);
86
118
  }
@@ -176,6 +208,25 @@ static VALUE destroy_data_chunk(VALUE arg) {
176
208
  return Qnil;
177
209
  }
178
210
 
211
+ static duckdb_logical_type *result_column_types(rubyDuckDBResult *ctx) {
212
+ idx_t col_idx;
213
+ idx_t column_count;
214
+
215
+ if (ctx->column_types == NULL) {
216
+ column_count = duckdb_column_count(&(ctx->result));
217
+ /* Use calloc (not xcalloc): freed by rbduckdb_result_unref (see allocate). */
218
+ ctx->column_types = calloc(column_count + 1, sizeof(duckdb_logical_type));
219
+ if (ctx->column_types == NULL) {
220
+ rb_raise(rb_eNoMemError, "failed to allocate DuckDB::Result column types");
221
+ }
222
+ for (col_idx = 0; col_idx < column_count; col_idx++) {
223
+ ctx->column_types[col_idx] = duckdb_column_logical_type(&(ctx->result), col_idx);
224
+ }
225
+ ctx->column_types_count = column_count;
226
+ }
227
+ return ctx->column_types;
228
+ }
229
+
179
230
  /* :nodoc: */
180
231
  static VALUE result__chunk_stream(VALUE oDuckDBResult) {
181
232
  rubyDuckDBResult *ctx;
@@ -186,6 +237,7 @@ static VALUE result__chunk_stream(VALUE oDuckDBResult) {
186
237
  RETURN_ENUMERATOR(oDuckDBResult, 0, 0);
187
238
 
188
239
  arg.col_count = duckdb_column_count(&(ctx->result));
240
+ arg.types = result_column_types(ctx);
189
241
 
190
242
  while((arg.chunk = duckdb_fetch_chunk(ctx->result)) != NULL) {
191
243
  rb_ensure(yield_rows, (VALUE)&arg, destroy_data_chunk, (VALUE)&arg);
@@ -208,7 +260,7 @@ static VALUE yield_rows(VALUE arg) {
208
260
  row = rb_ary_new2(p->col_count);
209
261
  for (col_idx = 0; col_idx < p->col_count; col_idx++) {
210
262
  vector = duckdb_data_chunk_get_vector(p->chunk, col_idx);
211
- val = vector_value(vector, row_idx);
263
+ val = rbduckdb_vector_value_at(vector, p->types[col_idx], row_idx);
212
264
  rb_ary_store(row, col_idx, val);
213
265
  }
214
266
  rb_yield(row);
@@ -249,46 +301,14 @@ VALUE rbduckdb_create_result(void) {
249
301
  return allocate(cDuckDBResult);
250
302
  }
251
303
 
252
- static VALUE vector_date(void *vector_data, idx_t row_idx) {
253
- return rbduckdb_date_to_ruby(((duckdb_date *)vector_data)[row_idx]);
254
- }
255
-
256
- static VALUE vector_timestamp(void* vector_data, idx_t row_idx) {
257
- return rbduckdb_timestamp_to_ruby(((duckdb_timestamp *)vector_data)[row_idx]);
258
- }
259
-
260
- static VALUE vector_time(void* vector_data, idx_t row_idx) {
261
- return rbduckdb_time_to_ruby(((duckdb_time *)vector_data)[row_idx]);
262
- }
263
-
264
- static VALUE vector_interval(void* vector_data, idx_t row_idx) {
265
- return rbduckdb_interval_to_ruby(((duckdb_interval *)vector_data)[row_idx]);
266
- }
267
-
268
304
  static VALUE vector_blob(void* vector_data, idx_t row_idx) {
269
305
  duckdb_string_t s = (((duckdb_string_t *)vector_data)[row_idx]);
270
- if(duckdb_string_is_inlined(s)) {
271
- return rb_str_new(s.value.inlined.inlined, s.value.inlined.length);
272
- } else {
273
- return rb_str_new(s.value.pointer.ptr, s.value.pointer.length);
274
- }
306
+ return rb_str_new(duckdb_string_t_data(&s), duckdb_string_t_length(s));
275
307
  }
276
308
 
277
309
  static VALUE vector_varchar(void* vector_data, idx_t row_idx) {
278
310
  duckdb_string_t s = (((duckdb_string_t *)vector_data)[row_idx]);
279
- if(duckdb_string_is_inlined(s)) {
280
- return rb_utf8_str_new(s.value.inlined.inlined, s.value.inlined.length);
281
- } else {
282
- return rb_utf8_str_new(s.value.pointer.ptr, s.value.pointer.length);
283
- }
284
- }
285
-
286
- static VALUE vector_hugeint(void* vector_data, idx_t row_idx) {
287
- return rbduckdb_hugeint_to_ruby(((duckdb_hugeint *)vector_data)[row_idx]);
288
- }
289
-
290
- static VALUE vector_uhugeint(void* vector_data, idx_t row_idx) {
291
- return rbduckdb_uhugeint_to_ruby(((duckdb_uhugeint *)vector_data)[row_idx]);
311
+ return rb_utf8_str_new(duckdb_string_t_data(&s), duckdb_string_t_length(s));
292
312
  }
293
313
 
294
314
  static VALUE vector_decimal(duckdb_logical_type ty, void* vector_data, idx_t row_idx) {
@@ -329,26 +349,6 @@ static VALUE vector_decimal(duckdb_logical_type ty, void* vector_data, idx_t row
329
349
  );
330
350
  }
331
351
 
332
-
333
-
334
- static VALUE vector_timestamp_s(void* vector_data, idx_t row_idx) {
335
- return rbduckdb_timestamp_s_to_ruby(((duckdb_timestamp_s *)vector_data)[row_idx]);
336
- }
337
-
338
-
339
- static VALUE vector_timestamp_ms(void* vector_data, idx_t row_idx) {
340
- return rbduckdb_timestamp_ms_to_ruby(((duckdb_timestamp_ms *)vector_data)[row_idx]);
341
- }
342
-
343
-
344
- static VALUE vector_timestamp_ns(void* vector_data, idx_t row_idx) {
345
- return rbduckdb_timestamp_ns_to_ruby(((duckdb_timestamp_ns *)vector_data)[row_idx]);
346
- }
347
-
348
- static VALUE vector_time_ns(void* vector_data, idx_t row_idx) {
349
- return rbduckdb_time_ns_to_ruby(((duckdb_time_ns *)vector_data)[row_idx]);
350
- }
351
-
352
352
  static VALUE vector_enum(duckdb_logical_type ty, void* vector_data, idx_t row_idx) {
353
353
  duckdb_type type = duckdb_enum_internal_type(ty);
354
354
  uint8_t index;
@@ -371,23 +371,11 @@ static VALUE vector_enum(duckdb_logical_type ty, void* vector_data, idx_t row_id
371
371
  }
372
372
 
373
373
  static VALUE vector_array(duckdb_logical_type ty, duckdb_vector vector, idx_t row_idx) {
374
- VALUE ary = Qnil;
375
- VALUE value = Qnil;
376
-
377
- duckdb_logical_type child_logical_type = duckdb_array_type_child_type(ty);
378
374
  idx_t size = duckdb_array_type_array_size(ty);
379
375
  idx_t bgn = row_idx * size;
380
- idx_t end = bgn + size;
381
- duckdb_vector child = duckdb_array_vector_get_child(vector);
382
-
383
- ary = rb_ary_new2(size);
384
- for (idx_t i = bgn; i < end; ++i) {
385
- value = rbduckdb_vector_value_at(child, child_logical_type, i);
386
- rb_ary_store(ary, i - bgn, value);
387
- }
376
+ duckdb_vector child = duckdb_array_vector_get_child(vector);
388
377
 
389
- duckdb_destroy_logical_type(&child_logical_type);
390
- return ary;
378
+ return vector_values_with_owned_type(child, duckdb_array_type_child_type(ty), bgn, bgn + size);
391
379
  }
392
380
 
393
381
  VALUE rbduckdb_vector_value_at(duckdb_vector vector, duckdb_logical_type element_type, idx_t index) {
@@ -442,22 +430,22 @@ VALUE rbduckdb_vector_value_at(duckdb_vector vector, duckdb_logical_type element
442
430
  obj = DBL2NUM((((double *) vector_data)[index]));
443
431
  break;
444
432
  case DUCKDB_TYPE_TIMESTAMP:
445
- obj = vector_timestamp(vector_data, index);
433
+ obj = rbduckdb_timestamp_to_ruby(((duckdb_timestamp *)vector_data)[index]);
446
434
  break;
447
435
  case DUCKDB_TYPE_DATE:
448
- obj = vector_date(vector_data, index);
436
+ obj = rbduckdb_date_to_ruby(((duckdb_date *)vector_data)[index]);
449
437
  break;
450
438
  case DUCKDB_TYPE_TIME:
451
- obj = vector_time(vector_data, index);
439
+ obj = rbduckdb_time_to_ruby(((duckdb_time *)vector_data)[index]);
452
440
  break;
453
441
  case DUCKDB_TYPE_INTERVAL:
454
- obj = vector_interval(vector_data, index);
442
+ obj = rbduckdb_interval_to_ruby(((duckdb_interval *)vector_data)[index]);
455
443
  break;
456
444
  case DUCKDB_TYPE_HUGEINT:
457
- obj = vector_hugeint(vector_data, index);
445
+ obj = rbduckdb_hugeint_to_ruby(((duckdb_hugeint *)vector_data)[index]);
458
446
  break;
459
447
  case DUCKDB_TYPE_UHUGEINT:
460
- obj = vector_uhugeint(vector_data, index);
448
+ obj = rbduckdb_uhugeint_to_ruby(((duckdb_uhugeint *)vector_data)[index]);
461
449
  break;
462
450
  case DUCKDB_TYPE_VARCHAR:
463
451
  obj = vector_varchar(vector_data, index);
@@ -469,16 +457,16 @@ VALUE rbduckdb_vector_value_at(duckdb_vector vector, duckdb_logical_type element
469
457
  obj = vector_decimal(element_type, vector_data, index);
470
458
  break;
471
459
  case DUCKDB_TYPE_TIMESTAMP_S:
472
- obj = vector_timestamp_s(vector_data, index);
460
+ obj = rbduckdb_timestamp_s_to_ruby(((duckdb_timestamp_s *)vector_data)[index]);
473
461
  break;
474
462
  case DUCKDB_TYPE_TIMESTAMP_MS:
475
- obj = vector_timestamp_ms(vector_data, index);
463
+ obj = rbduckdb_timestamp_ms_to_ruby(((duckdb_timestamp_ms *)vector_data)[index]);
476
464
  break;
477
465
  case DUCKDB_TYPE_TIMESTAMP_NS:
478
- obj = vector_timestamp_ns(vector_data, index);
466
+ obj = rbduckdb_timestamp_ns_to_ruby(((duckdb_timestamp_ns *)vector_data)[index]);
479
467
  break;
480
468
  case DUCKDB_TYPE_TIME_NS:
481
- obj = vector_time_ns(vector_data, index);
469
+ obj = rbduckdb_time_ns_to_ruby(((duckdb_time_ns *)vector_data)[index]);
482
470
  break;
483
471
  case DUCKDB_TYPE_ENUM:
484
472
  obj = vector_enum(element_type, vector_data, index);
@@ -519,93 +507,121 @@ VALUE rbduckdb_vector_value_at(duckdb_vector vector, duckdb_logical_type element
519
507
  }
520
508
 
521
509
  static VALUE vector_list(duckdb_logical_type ty, duckdb_vector vector, void * vector_data, idx_t row_idx) {
522
- VALUE ary = Qnil;
523
- VALUE value = Qnil;
524
- idx_t i;
525
-
526
- duckdb_logical_type child_logical_type = duckdb_list_type_child_type(ty);
527
-
528
510
  duckdb_list_entry list_entry = ((duckdb_list_entry *)vector_data)[row_idx];
529
511
  idx_t bgn = list_entry.offset;
530
- idx_t end = bgn + list_entry.length;
531
- ary = rb_ary_new2(list_entry.length);
532
-
533
512
  duckdb_vector child = duckdb_list_vector_get_child(vector);
534
513
 
535
- for (i = bgn; i < end; ++i) {
536
- value = rbduckdb_vector_value_at(child, child_logical_type, i);
537
- rb_ary_store(ary, i - bgn, value);
538
- }
539
- duckdb_destroy_logical_type(&child_logical_type);
540
- return ary;
514
+ return vector_values_with_owned_type(child, duckdb_list_type_child_type(ty), bgn, bgn + list_entry.length);
541
515
  }
542
516
 
543
517
  static VALUE vector_map(duckdb_logical_type ty, duckdb_vector vector, void* vector_data, idx_t row_idx) {
544
- VALUE hash = rb_hash_new();
545
- VALUE key = Qnil;
546
- VALUE value = Qnil;
547
-
548
- duckdb_logical_type key_logical_type = duckdb_map_type_key_type(ty);
549
- duckdb_logical_type value_logical_type = duckdb_map_type_value_type(ty);
550
-
518
+ struct map_entries_arg arg;
551
519
  duckdb_list_entry list_entry = ((duckdb_list_entry *)vector_data)[row_idx];
520
+ duckdb_vector child = duckdb_list_vector_get_child(vector);
552
521
 
553
- idx_t bgn = list_entry.offset;
554
- idx_t end = bgn + list_entry.length;
522
+ arg.key_vector = duckdb_struct_vector_get_child(child, 0);
523
+ arg.value_vector = duckdb_struct_vector_get_child(child, 1);
524
+ arg.key_type = duckdb_map_type_key_type(ty);
525
+ arg.value_type = duckdb_map_type_value_type(ty);
526
+ arg.bgn = list_entry.offset;
527
+ arg.end = list_entry.offset + list_entry.length;
555
528
 
556
- duckdb_vector child = duckdb_list_vector_get_child(vector);
557
- duckdb_vector key_vector = duckdb_struct_vector_get_child(child, 0);
558
- duckdb_vector value_vector = duckdb_struct_vector_get_child(child, 1);
529
+ return rb_ensure(convert_map_entries, (VALUE)&arg, destroy_map_types, (VALUE)&arg);
530
+ }
559
531
 
560
- for (idx_t i = bgn; i < end; ++i) {
561
- key = rbduckdb_vector_value_at(key_vector, key_logical_type, i);
562
- value = rbduckdb_vector_value_at(value_vector, value_logical_type, i);
532
+ static VALUE convert_map_entries(VALUE varg) {
533
+ struct map_entries_arg *arg = (struct map_entries_arg *)varg;
534
+ VALUE hash = rb_hash_new();
535
+ VALUE key;
536
+ VALUE value;
537
+
538
+ for (idx_t i = arg->bgn; i < arg->end; ++i) {
539
+ key = rbduckdb_vector_value_at(arg->key_vector, arg->key_type, i);
540
+ value = rbduckdb_vector_value_at(arg->value_vector, arg->value_type, i);
563
541
  rb_hash_aset(hash, key, value);
564
542
  }
565
-
566
- duckdb_destroy_logical_type(&key_logical_type);
567
- duckdb_destroy_logical_type(&value_logical_type);
568
543
  return hash;
569
544
  }
570
545
 
546
+ static VALUE destroy_map_types(VALUE varg) {
547
+ struct map_entries_arg *arg = (struct map_entries_arg *)varg;
548
+
549
+ duckdb_destroy_logical_type(&(arg->key_type));
550
+ duckdb_destroy_logical_type(&(arg->value_type));
551
+ return Qnil;
552
+ }
553
+
571
554
  static VALUE vector_struct(duckdb_logical_type ty, duckdb_vector vector, idx_t row_idx) {
555
+ struct struct_fields_arg arg = {ty, vector, row_idx, NULL};
556
+
557
+ return rb_ensure(convert_struct_fields, (VALUE)&arg, destroy_struct_child_type, (VALUE)&arg);
558
+ }
559
+
560
+ static VALUE convert_struct_fields(VALUE varg) {
561
+ struct struct_fields_arg *arg = (struct struct_fields_arg *)varg;
572
562
  VALUE hash = rb_hash_new();
573
563
  VALUE value = Qnil;
574
564
  VALUE key = Qnil;
575
565
  duckdb_vector child;
576
- duckdb_logical_type child_type;
577
566
  char *p;
578
567
 
579
- idx_t child_count = duckdb_struct_type_child_count(ty);
568
+ idx_t child_count = duckdb_struct_type_child_count(arg->ty);
580
569
 
581
570
  for (idx_t i = 0; i < child_count; ++i) {
582
- p = duckdb_struct_type_child_name(ty, i);
571
+ p = duckdb_struct_type_child_name(arg->ty, i);
583
572
  if (p) {
584
573
  key = ID2SYM(rb_intern_const(p));
585
- child = duckdb_struct_vector_get_child(vector, i);
586
- child_type = duckdb_struct_type_child_type(ty, i);
587
- value = rbduckdb_vector_value_at(child, child_type, row_idx);
588
- rb_hash_aset(hash, key, value);
589
- duckdb_destroy_logical_type(&child_type);
590
574
  duckdb_free(p);
575
+ child = duckdb_struct_vector_get_child(arg->vector, i);
576
+ arg->child_type = duckdb_struct_type_child_type(arg->ty, i);
577
+ value = rbduckdb_vector_value_at(child, arg->child_type, arg->row_idx);
578
+ duckdb_destroy_logical_type(&(arg->child_type));
579
+ rb_hash_aset(hash, key, value);
591
580
  }
592
581
  }
593
582
 
594
583
  return hash;
595
584
  }
596
585
 
586
+ static VALUE destroy_struct_child_type(VALUE varg) {
587
+ struct struct_fields_arg *arg = (struct struct_fields_arg *)varg;
588
+
589
+ duckdb_destroy_logical_type(&(arg->child_type));
590
+ return Qnil;
591
+ }
592
+
597
593
  static VALUE vector_union(duckdb_logical_type ty, duckdb_vector vector, void* vector_data, idx_t row_idx){
598
- VALUE value = Qnil;
599
594
  duckdb_vector type_vector = duckdb_struct_vector_get_child(vector, 0);
600
595
  void *data = duckdb_vector_get_data(type_vector);
601
596
  uint8_t index = ((int8_t *)data)[row_idx];
602
597
 
603
- duckdb_logical_type child_type = duckdb_union_type_member_type(ty, index);
598
+ duckdb_vector member_vector = duckdb_struct_vector_get_child(vector, index + 1);
599
+ VALUE values = vector_values_with_owned_type(member_vector, duckdb_union_type_member_type(ty, index), row_idx, row_idx + 1);
604
600
 
605
- duckdb_vector vector_value = duckdb_struct_vector_get_child(vector, index + 1);
606
- value = rbduckdb_vector_value_at(vector_value, child_type, row_idx);
607
- duckdb_destroy_logical_type(&child_type);
608
- return value;
601
+ return rb_ary_entry(values, 0);
602
+ }
603
+
604
+ static VALUE convert_values_in_range(VALUE varg) {
605
+ struct owned_type_arg *arg = (struct owned_type_arg *)varg;
606
+ VALUE ary = rb_ary_new2(arg->end - arg->bgn);
607
+
608
+ for (idx_t i = arg->bgn; i < arg->end; ++i) {
609
+ rb_ary_store(ary, i - arg->bgn, rbduckdb_vector_value_at(arg->vector, arg->type, i));
610
+ }
611
+ return ary;
612
+ }
613
+
614
+ static VALUE destroy_owned_type(VALUE varg) {
615
+ struct owned_type_arg *arg = (struct owned_type_arg *)varg;
616
+
617
+ duckdb_destroy_logical_type(&(arg->type));
618
+ return Qnil;
619
+ }
620
+
621
+ static VALUE vector_values_with_owned_type(duckdb_vector vector, duckdb_logical_type type, idx_t bgn, idx_t end) {
622
+ struct owned_type_arg arg = {vector, type, bgn, end};
623
+
624
+ return rb_ensure(convert_values_in_range, (VALUE)&arg, destroy_owned_type, (VALUE)&arg);
609
625
  }
610
626
 
611
627
  static VALUE str_concat_byte(VALUE str, unsigned char byte, int offset) {
@@ -625,7 +641,7 @@ static VALUE str_concat_byte(VALUE str, unsigned char byte, int offset) {
625
641
  return rb_str_cat(str, p, 8 - offset);
626
642
  }
627
643
 
628
- static VALUE bytes_to_string(char *bytes, uint32_t length, int offset) {
644
+ static VALUE bytes_to_string(const char *bytes, uint32_t length, int offset) {
629
645
  VALUE str = rb_str_new_literal("");
630
646
  str = str_concat_byte(str, bytes[0], offset);
631
647
  for (uint32_t i = 1; i < length; i++) {
@@ -636,22 +652,16 @@ static VALUE bytes_to_string(char *bytes, uint32_t length, int offset) {
636
652
 
637
653
  static VALUE vector_bit(void* vector_data, idx_t row_idx) {
638
654
  duckdb_string_t s = (((duckdb_string_t *)vector_data)[row_idx]);
639
- char *p;
655
+ const char *p = duckdb_string_t_data(&s);
656
+ uint32_t length = duckdb_string_t_length(s);
640
657
  int offset;
641
- uint32_t length;
642
658
  VALUE str = Qnil;
643
659
 
644
- if(duckdb_string_is_inlined(s)) {
645
- length = s.value.inlined.length - 1;
646
- p = &s.value.inlined.inlined[1];
647
- offset = s.value.inlined.inlined[0];
648
- str = bytes_to_string(p, length, offset);
649
- } else {
650
- length = s.value.pointer.length - 1;
651
- p = &s.value.pointer.ptr[1];
652
- offset = s.value.pointer.ptr[0];
653
- str = bytes_to_string(p, length, offset);
660
+ if (length < 2 || (unsigned char)p[0] > 7) {
661
+ rb_raise(eDuckDBError, "invalid BIT value");
654
662
  }
663
+ offset = (unsigned char)p[0];
664
+ str = bytes_to_string(p + 1, length - 1, offset);
655
665
  return str;
656
666
  }
657
667
 
@@ -697,18 +707,6 @@ static VALUE result_arrow_c_stream(VALUE oDuckDBResult) {
697
707
  return stream;
698
708
  }
699
709
 
700
- static VALUE vector_value(duckdb_vector vector, idx_t row_idx) {
701
- duckdb_logical_type ty;
702
- VALUE obj = Qnil;
703
-
704
- ty = duckdb_vector_get_column_type(vector);
705
-
706
- obj = rbduckdb_vector_value_at(vector, ty, row_idx);
707
-
708
- duckdb_destroy_logical_type(&ty);
709
- return obj;
710
- }
711
-
712
710
  void rbduckdb_init_result(void) {
713
711
  #if 0
714
712
  VALUE mDuckDB = rb_define_module("DuckDB");
data/ext/duckdb/result.h CHANGED
@@ -12,6 +12,8 @@ struct _rubyDuckDBResult {
12
12
  duckdb_result result;
13
13
  bool arrow_exported;
14
14
  rb_atomic_t refcount;
15
+ duckdb_logical_type *column_types;
16
+ idx_t column_types_count;
15
17
  };
16
18
 
17
19
  typedef struct _rubyDuckDBResult rubyDuckDBResult;
@@ -18,11 +18,6 @@ static VALUE scalar_function__set_bind(VALUE self);
18
18
  static void scalar_function_callback(duckdb_function_info info, duckdb_data_chunk input, duckdb_vector output);
19
19
  static void scalar_function_bind_callback(duckdb_bind_info info);
20
20
  #ifdef HAVE_DUCKDB_H_GE_V1_5_0
21
- /*
22
- * Thread detection functions (available since Ruby 2.3).
23
- * Used to skip the proxy on Ruby threads.
24
- */
25
- extern int ruby_native_thread_p(void);
26
21
  static void scalar_function_init_callback(duckdb_init_info info);
27
22
  #endif
28
23
 
@@ -251,15 +246,8 @@ static VALUE process_rows(VALUE varg) {
251
246
  * a GC, and the earlier columns' objects must stay reachable. */
252
247
  VALUE args = rb_ary_new_capa((long)arg->col_count);
253
248
 
254
- /* Allocate arrays to hold input vectors and their types */
255
- arg->input_vectors = ALLOC_N(duckdb_vector, arg->col_count);
256
- arg->input_types = ALLOC_N(duckdb_logical_type, arg->col_count);
257
-
258
249
  /* Get all input vectors and their types */
259
- for (j = 0; j < arg->col_count; j++) {
260
- arg->input_vectors[j] = duckdb_data_chunk_get_vector(arg->input, j);
261
- arg->input_types[j] = duckdb_vector_get_column_type(arg->input_vectors[j]);
262
- }
250
+ rbduckdb_input_columns_load(arg->input, arg->col_count, &arg->input_vectors, &arg->input_types);
263
251
 
264
252
  /* Process each row */
265
253
  for (i = 0; i < arg->row_count; i++) {
@@ -282,24 +270,10 @@ static VALUE process_rows(VALUE varg) {
282
270
 
283
271
  static VALUE cleanup_callback(VALUE varg) {
284
272
  struct callback_arg *arg = (struct callback_arg *)varg;
285
- idx_t j;
286
273
 
287
- /* Destroy all logical types */
288
- if (arg->input_types != NULL) {
289
- for (j = 0; j < arg->col_count; j++) {
290
- duckdb_destroy_logical_type(&arg->input_types[j]);
291
- }
292
- }
274
+ rbduckdb_input_columns_free(arg->input_vectors, arg->input_types, arg->col_count);
293
275
  duckdb_destroy_logical_type(&arg->output_type);
294
276
 
295
- /* Free allocated memory */
296
- if (arg->input_types != NULL) {
297
- xfree(arg->input_types);
298
- }
299
- if (arg->input_vectors != NULL) {
300
- xfree(arg->input_vectors);
301
- }
302
-
303
277
  return Qnil;
304
278
  }
305
279
 
@@ -307,50 +281,16 @@ static VALUE cleanup_callback(VALUE varg) {
307
281
  /*
308
282
  * Per-worker init for the execute path (DuckDB >= 1.5.0).
309
283
  *
310
- * DuckDB calls this once on each worker thread that will run the execute
311
- * callback. We create a per-worker proxy (allocating its Ruby thread under the
312
- * GVL via the global executor, since this runs on a non-Ruby thread) and store
313
- * it as per-worker state. The execute callback then dispatches through it
314
- * instead of the shared global executor, so workers run callbacks concurrently.
315
- * DuckDB invokes rbduckdb_worker_proxy_destroy when the state is freed.
284
+ * Runs once on each worker thread that will execute the callback. Creates the
285
+ * worker's proxy and stores it as per-worker state, so the execute callback
286
+ * dispatches through it rather than the shared global executor. DuckDB calls
287
+ * rbduckdb_worker_proxy_destroy when the state is freed.
316
288
  */
317
- struct create_proxy_callback_arg {
318
- struct worker_proxy *proxy;
319
- };
320
-
321
- static VALUE create_proxy_callback(VALUE varg) {
322
- struct create_proxy_callback_arg *arg = (struct create_proxy_callback_arg *)varg;
323
- arg->proxy = rbduckdb_worker_proxy_create();
324
- return Qnil;
325
- }
326
-
327
- /*
328
- * rbduckdb_worker_proxy_create may raise (NoMemError, Thread.new failure),
329
- * and the executor runs callbacks unprotected — a raise would longjmp past
330
- * its done-signaling and block the waiting DuckDB worker forever. Swallow
331
- * the exception instead: the proxy stays NULL, init sets no state, and the
332
- * execute callback falls back to the global executor.
333
- */
334
- static void create_proxy_callback_protected(void *user_data) {
335
- int exception_state;
336
-
337
- rb_protect(create_proxy_callback, (VALUE)user_data, &exception_state);
338
- if (exception_state) {
339
- rb_set_errinfo(Qnil);
340
- }
341
- }
342
-
343
289
  static void scalar_function_init_callback(duckdb_init_info info) {
344
- struct create_proxy_callback_arg arg;
345
-
346
- /* A Ruby calling thread runs the callback inline (Case 1/2); no proxy needed. */
347
- if (ruby_native_thread_p()) return;
348
-
349
- arg.proxy = NULL;
350
- rbduckdb_function_executor_dispatch(create_proxy_callback_protected, &arg);
290
+ struct worker_proxy *proxy = rbduckdb_worker_proxy_create_for_current_thread();
351
291
 
352
- if (arg.proxy != NULL) {
353
- duckdb_scalar_function_init_set_state(info, arg.proxy, rbduckdb_worker_proxy_destroy);
292
+ if (proxy != NULL) {
293
+ duckdb_scalar_function_init_set_state(info, proxy, rbduckdb_worker_proxy_destroy);
354
294
  }
355
295
  }
356
296
  #endif