duckdb 1.5.2.1 → 1.5.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +40 -0
  3. data/README.md +52 -0
  4. data/ext/duckdb/aggregate_function.c +1 -1
  5. data/ext/duckdb/aggregate_function_set.c +86 -0
  6. data/ext/duckdb/aggregate_function_set.h +14 -0
  7. data/ext/duckdb/appender.c +62 -4
  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 -3
  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 +14 -11
  22. data/ext/duckdb/error.c +1 -1
  23. data/ext/duckdb/error.h +1 -3
  24. data/ext/duckdb/extconf.rb +28 -13
  25. data/ext/duckdb/function_executor.c +308 -2
  26. data/ext/duckdb/function_executor.h +44 -0
  27. data/ext/duckdb/prepared_statement.c +38 -0
  28. data/ext/duckdb/result.c +49 -53
  29. data/ext/duckdb/result.h +11 -0
  30. data/ext/duckdb/ruby-duckdb.h +4 -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/lib/duckdb/aggregate_function.rb +7 -1
  48. data/lib/duckdb/aggregate_function_set.rb +29 -0
  49. data/lib/duckdb/appender.rb +97 -0
  50. data/lib/duckdb/arrow_array_stream.rb +33 -0
  51. data/lib/duckdb/connection.rb +139 -9
  52. data/lib/duckdb/prepared_statement.rb +35 -0
  53. data/lib/duckdb/result.rb +39 -2
  54. data/lib/duckdb/scalar_function.rb +9 -4
  55. data/lib/duckdb/scalar_function_set.rb +0 -1
  56. data/lib/duckdb/table_description.rb +7 -0
  57. data/lib/duckdb/table_name_parser.rb +58 -0
  58. data/lib/duckdb/version.rb +1 -1
  59. data/lib/duckdb.rb +3 -0
  60. metadata +11 -2
@@ -11,16 +11,21 @@ static void deallocate(void *ctx);
11
11
  static VALUE allocate(VALUE klass);
12
12
  static size_t memsize(const void *p);
13
13
  static void compact(void *ctx);
14
- static VALUE duckdb_table_function_initialize(VALUE self);
15
- static VALUE rbduckdb_table_function_set_name(VALUE self, VALUE name);
16
- static VALUE rbduckdb_table_function_add_parameter(VALUE self, VALUE logical_type);
17
- static VALUE rbduckdb_table_function_add_named_parameter(VALUE self, VALUE name, VALUE logical_type);
18
- static VALUE rbduckdb_table_function_set_bind(VALUE self);
14
+ static VALUE table_function_initialize(VALUE self);
15
+ static VALUE table_function_set_name(VALUE self, VALUE name);
16
+ static VALUE table_function_add_parameter(VALUE self, VALUE logical_type);
17
+ static VALUE table_function_add_named_parameter(VALUE self, VALUE name, VALUE logical_type);
18
+ static VALUE table_function_bind(VALUE self);
19
19
  static void table_function_bind_callback(duckdb_bind_info info);
20
- static VALUE rbduckdb_table_function_set_init(VALUE self);
20
+ static VALUE table_function_init(VALUE self);
21
21
  static void table_function_init_callback(duckdb_init_info info);
22
- static VALUE rbduckdb_table_function_set_execute(VALUE self);
22
+ static VALUE table_function_execute(VALUE self);
23
23
  static void table_function_execute_callback(duckdb_function_info info, duckdb_data_chunk output);
24
+ #ifdef HAVE_DUCKDB_H_GE_V1_5_0
25
+ /* Thread detection (declared in function_executor.c); used to skip the proxy on Ruby threads. */
26
+ extern int ruby_native_thread_p(void);
27
+ static void table_function_local_init_callback(duckdb_init_info info);
28
+ #endif
24
29
 
25
30
  static const rb_data_type_t table_function_data_type = {
26
31
  "DuckDB/TableFunction",
@@ -84,7 +89,7 @@ static size_t memsize(const void *p) {
84
89
  * tf.name = "my_function"
85
90
  * # ... configure tf ...
86
91
  */
87
- static VALUE duckdb_table_function_initialize(VALUE self) {
92
+ static VALUE table_function_initialize(VALUE self) {
88
93
  rubyDuckDBTableFunction *ctx;
89
94
 
90
95
  TypedData_Get_Struct(self, rubyDuckDBTableFunction, &table_function_data_type, ctx);
@@ -113,7 +118,7 @@ static VALUE duckdb_table_function_initialize(VALUE self) {
113
118
  *
114
119
  * tf.name = "my_function"
115
120
  */
116
- static VALUE rbduckdb_table_function_set_name(VALUE self, VALUE name) {
121
+ static VALUE table_function_set_name(VALUE self, VALUE name) {
117
122
  rubyDuckDBTableFunction *ctx;
118
123
  const char *func_name;
119
124
 
@@ -138,7 +143,7 @@ static VALUE rbduckdb_table_function_set_name(VALUE self, VALUE name) {
138
143
  * tf.add_parameter(DuckDB::LogicalType::BIGINT)
139
144
  * tf.add_parameter(DuckDB::LogicalType::VARCHAR)
140
145
  */
141
- static VALUE rbduckdb_table_function_add_parameter(VALUE self, VALUE logical_type) {
146
+ static VALUE table_function_add_parameter(VALUE self, VALUE logical_type) {
142
147
  rubyDuckDBTableFunction *ctx;
143
148
  rubyDuckDBLogicalType *ctx_logical_type;
144
149
 
@@ -162,7 +167,7 @@ static VALUE rbduckdb_table_function_add_parameter(VALUE self, VALUE logical_typ
162
167
  *
163
168
  * tf.add_named_parameter("limit", DuckDB::LogicalType::BIGINT)
164
169
  */
165
- static VALUE rbduckdb_table_function_add_named_parameter(VALUE self, VALUE name, VALUE logical_type) {
170
+ static VALUE table_function_add_named_parameter(VALUE self, VALUE name, VALUE logical_type) {
166
171
  rubyDuckDBTableFunction *ctx;
167
172
  rubyDuckDBLogicalType *ctx_logical_type;
168
173
  const char *param_name;
@@ -192,7 +197,7 @@ static VALUE rbduckdb_table_function_add_named_parameter(VALUE self, VALUE name,
192
197
  * bind_info.add_result_column('name', DuckDB::LogicalType::VARCHAR)
193
198
  * end
194
199
  */
195
- static VALUE rbduckdb_table_function_set_bind(VALUE self) {
200
+ static VALUE table_function_bind(VALUE self) {
196
201
  rubyDuckDBTableFunction *ctx;
197
202
 
198
203
  if (!rb_block_given_p()) {
@@ -231,7 +236,7 @@ static void execute_bind_callback_protected(void *user_data) {
231
236
  int state = 0;
232
237
 
233
238
  bind_info_obj = rb_class_new_instance(0, NULL, cDuckDBTableFunctionBindInfo);
234
- bind_info_ctx = get_struct_bind_info(bind_info_obj);
239
+ bind_info_ctx = rbduckdb_get_struct_bind_info(bind_info_obj);
235
240
  bind_info_ctx->bind_info = darg->info;
236
241
 
237
242
  VALUE call_args[2] = { darg->ctx->bind_proc, bind_info_obj };
@@ -269,7 +274,7 @@ static void table_function_bind_callback(duckdb_bind_info info) {
269
274
  * # Initialize execution state
270
275
  * end
271
276
  */
272
- static VALUE rbduckdb_table_function_set_init(VALUE self) {
277
+ static VALUE table_function_init(VALUE self) {
273
278
  rubyDuckDBTableFunction *ctx;
274
279
 
275
280
  if (!rb_block_given_p()) {
@@ -307,7 +312,7 @@ static void execute_init_callback_protected(void *user_data) {
307
312
  int state = 0;
308
313
 
309
314
  init_info_obj = rb_class_new_instance(0, NULL, cDuckDBTableFunctionInitInfo);
310
- init_info_ctx = get_struct_init_info(init_info_obj);
315
+ init_info_ctx = rbduckdb_get_struct_init_info(init_info_obj);
311
316
  init_info_ctx->info = darg->info;
312
317
 
313
318
  VALUE call_args[2] = { darg->ctx->init_proc, init_info_obj };
@@ -347,7 +352,7 @@ static void table_function_init_callback(duckdb_init_info info) {
347
352
  * # Write data...
348
353
  * end
349
354
  */
350
- static VALUE rbduckdb_table_function_set_execute(VALUE self) {
355
+ static VALUE table_function_execute(VALUE self) {
351
356
  rubyDuckDBTableFunction *ctx;
352
357
 
353
358
  if (!rb_block_given_p()) {
@@ -358,6 +363,10 @@ static VALUE rbduckdb_table_function_set_execute(VALUE self) {
358
363
 
359
364
  ctx->execute_proc = rb_block_proc();
360
365
  duckdb_table_function_set_function(ctx->table_function, table_function_execute_callback);
366
+ #ifdef HAVE_DUCKDB_H_GE_V1_5_0
367
+ /* Per-worker proxy threads for the execute path (DuckDB >= 1.5.0). */
368
+ duckdb_table_function_set_local_init(ctx->table_function, table_function_local_init_callback);
369
+ #endif
361
370
 
362
371
  rbduckdb_function_executor_ensure_started();
363
372
 
@@ -384,7 +393,7 @@ static void execute_execute_callback_protected(void *user_data) {
384
393
  int state = 0;
385
394
 
386
395
  func_info_obj = rb_class_new_instance(0, NULL, cDuckDBTableFunctionFunctionInfo);
387
- func_info_ctx = get_struct_function_info(func_info_obj);
396
+ func_info_ctx = rbduckdb_get_struct_function_info(func_info_obj);
388
397
  func_info_ctx->info = darg->info;
389
398
 
390
399
  data_chunk_obj = rb_class_new_instance(0, NULL, cDuckDBDataChunk);
@@ -405,6 +414,7 @@ static void execute_execute_callback_protected(void *user_data) {
405
414
  static void table_function_execute_callback(duckdb_function_info info, duckdb_data_chunk output) {
406
415
  rubyDuckDBTableFunction *ctx;
407
416
  struct execute_dispatch_arg darg;
417
+ struct worker_proxy *proxy = NULL;
408
418
 
409
419
  ctx = (rubyDuckDBTableFunction *)duckdb_function_get_extra_info(info);
410
420
  if (!ctx || ctx->execute_proc == Qnil) return;
@@ -413,28 +423,84 @@ static void table_function_execute_callback(duckdb_function_info info, duckdb_da
413
423
  darg.info = info;
414
424
  darg.output = output;
415
425
 
416
- rbduckdb_function_executor_dispatch(execute_execute_callback_protected, &darg);
426
+ #ifdef HAVE_DUCKDB_H_GE_V1_5_0
427
+ /* On DuckDB >= 1.5.0 each worker thread carries its own proxy (see local_init). */
428
+ proxy = (struct worker_proxy *)duckdb_function_get_local_init_data(info);
429
+ #endif
430
+ rbduckdb_function_executor_dispatch_via_proxy(execute_execute_callback_protected, &darg, proxy);
431
+ }
432
+
433
+ #ifdef HAVE_DUCKDB_H_GE_V1_5_0
434
+ /*
435
+ * Per-worker init for the execute path (DuckDB >= 1.5.0).
436
+ *
437
+ * DuckDB calls this once on each worker thread that will run the execute
438
+ * callback. We create a per-worker proxy (allocating its Ruby thread under the
439
+ * GVL via the global executor, since this runs on a non-Ruby thread) and store
440
+ * it as thread-local init data. The execute callback then dispatches through it
441
+ * instead of the shared global executor, so workers run callbacks concurrently.
442
+ * DuckDB invokes rbduckdb_worker_proxy_destroy when the local state is freed.
443
+ */
444
+ struct create_proxy_callback_arg {
445
+ struct worker_proxy *proxy;
446
+ };
447
+
448
+ static VALUE create_proxy_callback(VALUE varg) {
449
+ struct create_proxy_callback_arg *arg = (struct create_proxy_callback_arg *)varg;
450
+ arg->proxy = rbduckdb_worker_proxy_create();
451
+ return Qnil;
452
+ }
453
+
454
+ /*
455
+ * rbduckdb_worker_proxy_create may raise (NoMemError, Thread.new failure),
456
+ * and the executor runs callbacks unprotected — a raise would longjmp past
457
+ * its done-signaling and block the waiting DuckDB worker forever. Swallow
458
+ * the exception instead: the proxy stays NULL, local_init sets no state, and
459
+ * the execute callback falls back to the global executor.
460
+ */
461
+ static void create_proxy_callback_protected(void *user_data) {
462
+ int exception_state;
463
+
464
+ rb_protect(create_proxy_callback, (VALUE)user_data, &exception_state);
465
+ if (exception_state) {
466
+ rb_set_errinfo(Qnil);
467
+ }
468
+ }
469
+
470
+ static void table_function_local_init_callback(duckdb_init_info info) {
471
+ struct create_proxy_callback_arg arg;
472
+
473
+ /* A Ruby calling thread runs the callback inline (Case 1/2); no proxy needed. */
474
+ if (ruby_native_thread_p()) return;
475
+
476
+ arg.proxy = NULL;
477
+ rbduckdb_function_executor_dispatch(create_proxy_callback_protected, &arg);
478
+
479
+ if (arg.proxy != NULL) {
480
+ duckdb_init_set_init_data(info, arg.proxy, rbduckdb_worker_proxy_destroy);
481
+ }
417
482
  }
483
+ #endif
418
484
 
419
- rubyDuckDBTableFunction *get_struct_table_function(VALUE self) {
485
+ rubyDuckDBTableFunction *rbduckdb_get_struct_table_function(VALUE self) {
420
486
  rubyDuckDBTableFunction *ctx;
421
487
  TypedData_Get_Struct(self, rubyDuckDBTableFunction, &table_function_data_type, ctx);
422
488
  return ctx;
423
489
  }
424
490
 
425
- void rbduckdb_init_duckdb_table_function(void) {
491
+ void rbduckdb_init_table_function(void) {
426
492
  #if 0
427
493
  VALUE mDuckDB = rb_define_module("DuckDB");
428
494
  #endif
429
495
  cDuckDBTableFunction = rb_define_class_under(mDuckDB, "TableFunction", rb_cObject);
430
496
  rb_define_alloc_func(cDuckDBTableFunction, allocate);
431
497
 
432
- rb_define_method(cDuckDBTableFunction, "initialize", duckdb_table_function_initialize, 0);
433
- rb_define_method(cDuckDBTableFunction, "set_name", rbduckdb_table_function_set_name, 1);
434
- rb_define_method(cDuckDBTableFunction, "name=", rbduckdb_table_function_set_name, 1);
435
- rb_define_method(cDuckDBTableFunction, "add_parameter", rbduckdb_table_function_add_parameter, 1);
436
- rb_define_method(cDuckDBTableFunction, "add_named_parameter", rbduckdb_table_function_add_named_parameter, 2);
437
- rb_define_method(cDuckDBTableFunction, "bind", rbduckdb_table_function_set_bind, 0);
438
- rb_define_method(cDuckDBTableFunction, "init", rbduckdb_table_function_set_init, 0);
439
- rb_define_method(cDuckDBTableFunction, "execute", rbduckdb_table_function_set_execute, 0);
498
+ rb_define_method(cDuckDBTableFunction, "initialize", table_function_initialize, 0);
499
+ rb_define_method(cDuckDBTableFunction, "set_name", table_function_set_name, 1);
500
+ rb_define_method(cDuckDBTableFunction, "name=", table_function_set_name, 1);
501
+ rb_define_method(cDuckDBTableFunction, "add_parameter", table_function_add_parameter, 1);
502
+ rb_define_method(cDuckDBTableFunction, "add_named_parameter", table_function_add_named_parameter, 2);
503
+ rb_define_method(cDuckDBTableFunction, "bind", table_function_bind, 0);
504
+ rb_define_method(cDuckDBTableFunction, "init", table_function_init, 0);
505
+ rb_define_method(cDuckDBTableFunction, "execute", table_function_execute, 0);
440
506
  }
@@ -11,7 +11,7 @@ struct _rubyDuckDBTableFunction {
11
11
  typedef struct _rubyDuckDBTableFunction rubyDuckDBTableFunction;
12
12
 
13
13
  extern VALUE cDuckDBTableFunction;
14
- rubyDuckDBTableFunction *get_struct_table_function(VALUE self);
15
- void rbduckdb_init_duckdb_table_function(void);
14
+ rubyDuckDBTableFunction *rbduckdb_get_struct_table_function(VALUE self);
15
+ void rbduckdb_init_table_function(void);
16
16
 
17
17
  #endif
@@ -5,12 +5,12 @@ VALUE cDuckDBTableFunctionBindInfo;
5
5
  static void deallocate(void *ctx);
6
6
  static VALUE allocate(VALUE klass);
7
7
  static size_t memsize(const void *p);
8
- static VALUE rbduckdb_bind_info_parameter_count(VALUE self);
9
- static VALUE rbduckdb_bind_info_get_parameter(VALUE self, VALUE index);
10
- static VALUE rbduckdb_bind_info_get_named_parameter(VALUE self, VALUE name);
11
- static VALUE rbduckdb_bind_info__add_result_column(VALUE self, VALUE column_name, VALUE logical_type);
12
- static VALUE rbduckdb_bind_info_set_cardinality(VALUE self, VALUE cardinality, VALUE is_exact);
13
- static VALUE rbduckdb_bind_info_set_error(VALUE self, VALUE error);
8
+ static VALUE table_function_bind_info_parameter_count(VALUE self);
9
+ static VALUE table_function_bind_info_get_parameter(VALUE self, VALUE index);
10
+ static VALUE table_function_bind_info_get_named_parameter(VALUE self, VALUE name);
11
+ static VALUE table_function_bind_info__add_result_column(VALUE self, VALUE column_name, VALUE logical_type);
12
+ static VALUE table_function_bind_info_set_cardinality(VALUE self, VALUE cardinality, VALUE is_exact);
13
+ static VALUE table_function_bind_info_set_error(VALUE self, VALUE error);
14
14
 
15
15
  static const rb_data_type_t bind_info_data_type = {
16
16
  "DuckDB/TableFunctionBindInfo",
@@ -32,7 +32,7 @@ static size_t memsize(const void *p) {
32
32
  return sizeof(rubyDuckDBBindInfo);
33
33
  }
34
34
 
35
- rubyDuckDBBindInfo *get_struct_bind_info(VALUE obj) {
35
+ rubyDuckDBBindInfo *rbduckdb_get_struct_bind_info(VALUE obj) {
36
36
  rubyDuckDBBindInfo *ctx;
37
37
  TypedData_Get_Struct(obj, rubyDuckDBBindInfo, &bind_info_data_type, ctx);
38
38
  return ctx;
@@ -52,7 +52,7 @@ rubyDuckDBBindInfo *get_struct_bind_info(VALUE obj) {
52
52
  *
53
53
  * bind_info.parameter_count # => 2
54
54
  */
55
- static VALUE rbduckdb_bind_info_parameter_count(VALUE self) {
55
+ static VALUE table_function_bind_info_parameter_count(VALUE self) {
56
56
  rubyDuckDBBindInfo *ctx;
57
57
  idx_t count;
58
58
 
@@ -71,7 +71,7 @@ static VALUE rbduckdb_bind_info_parameter_count(VALUE self) {
71
71
  *
72
72
  * param = bind_info.get_parameter(0)
73
73
  */
74
- static VALUE rbduckdb_bind_info_get_parameter(VALUE self, VALUE index) {
74
+ static VALUE table_function_bind_info_get_parameter(VALUE self, VALUE index) {
75
75
  rubyDuckDBBindInfo *ctx;
76
76
  idx_t idx;
77
77
  duckdb_value param_value;
@@ -97,7 +97,7 @@ static VALUE rbduckdb_bind_info_get_parameter(VALUE self, VALUE index) {
97
97
  *
98
98
  * param = bind_info.get_named_parameter('limit')
99
99
  */
100
- static VALUE rbduckdb_bind_info_get_named_parameter(VALUE self, VALUE name) {
100
+ static VALUE table_function_bind_info_get_named_parameter(VALUE self, VALUE name) {
101
101
  rubyDuckDBBindInfo *ctx;
102
102
  const char *param_name;
103
103
  duckdb_value param_value;
@@ -129,7 +129,7 @@ static VALUE rbduckdb_bind_info_get_named_parameter(VALUE self, VALUE name) {
129
129
  * bind_info.add_result_column('id', DuckDB::LogicalType::BIGINT)
130
130
  * bind_info.add_result_column('name', DuckDB::LogicalType::VARCHAR)
131
131
  */
132
- static VALUE rbduckdb_bind_info__add_result_column(VALUE self, VALUE column_name, VALUE logical_type) {
132
+ static VALUE table_function_bind_info__add_result_column(VALUE self, VALUE column_name, VALUE logical_type) {
133
133
  rubyDuckDBBindInfo *ctx;
134
134
  rubyDuckDBLogicalType *ctx_logical_type;
135
135
  const char *col_name;
@@ -152,7 +152,7 @@ static VALUE rbduckdb_bind_info__add_result_column(VALUE self, VALUE column_name
152
152
  * bind_info.set_cardinality(100, true) # Exactly 100 rows
153
153
  * bind_info.set_cardinality(1000, false) # Approximately 1000 rows
154
154
  */
155
- static VALUE rbduckdb_bind_info_set_cardinality(VALUE self, VALUE cardinality, VALUE is_exact) {
155
+ static VALUE table_function_bind_info_set_cardinality(VALUE self, VALUE cardinality, VALUE is_exact) {
156
156
  rubyDuckDBBindInfo *ctx;
157
157
  idx_t card;
158
158
  bool exact;
@@ -175,7 +175,7 @@ static VALUE rbduckdb_bind_info_set_cardinality(VALUE self, VALUE cardinality, V
175
175
  *
176
176
  * bind_info.set_error('Invalid parameter value')
177
177
  */
178
- static VALUE rbduckdb_bind_info_set_error(VALUE self, VALUE error) {
178
+ static VALUE table_function_bind_info_set_error(VALUE self, VALUE error) {
179
179
  rubyDuckDBBindInfo *ctx;
180
180
  const char *error_msg;
181
181
 
@@ -187,18 +187,18 @@ static VALUE rbduckdb_bind_info_set_error(VALUE self, VALUE error) {
187
187
  return self;
188
188
  }
189
189
 
190
- void rbduckdb_init_duckdb_table_function_bind_info(void) {
190
+ void rbduckdb_init_table_function_bind_info(void) {
191
191
  #if 0
192
192
  VALUE mDuckDB = rb_define_module("DuckDB");
193
193
  #endif
194
194
  cDuckDBTableFunctionBindInfo = rb_define_class_under(cDuckDBTableFunction, "BindInfo", rb_cObject);
195
195
  rb_define_alloc_func(cDuckDBTableFunctionBindInfo, allocate);
196
196
 
197
- rb_define_method(cDuckDBTableFunctionBindInfo, "parameter_count", rbduckdb_bind_info_parameter_count, 0);
198
- rb_define_method(cDuckDBTableFunctionBindInfo, "get_parameter", rbduckdb_bind_info_get_parameter, 1);
199
- rb_define_method(cDuckDBTableFunctionBindInfo, "get_named_parameter", rbduckdb_bind_info_get_named_parameter, 1);
200
- rb_define_method(cDuckDBTableFunctionBindInfo, "set_cardinality", rbduckdb_bind_info_set_cardinality, 2);
201
- rb_define_method(cDuckDBTableFunctionBindInfo, "set_error", rbduckdb_bind_info_set_error, 1);
197
+ rb_define_method(cDuckDBTableFunctionBindInfo, "parameter_count", table_function_bind_info_parameter_count, 0);
198
+ rb_define_method(cDuckDBTableFunctionBindInfo, "get_parameter", table_function_bind_info_get_parameter, 1);
199
+ rb_define_method(cDuckDBTableFunctionBindInfo, "get_named_parameter", table_function_bind_info_get_named_parameter, 1);
200
+ rb_define_method(cDuckDBTableFunctionBindInfo, "set_cardinality", table_function_bind_info_set_cardinality, 2);
201
+ rb_define_method(cDuckDBTableFunctionBindInfo, "set_error", table_function_bind_info_set_error, 1);
202
202
 
203
- rb_define_private_method(cDuckDBTableFunctionBindInfo, "_add_result_column", rbduckdb_bind_info__add_result_column, 2);
203
+ rb_define_private_method(cDuckDBTableFunctionBindInfo, "_add_result_column", table_function_bind_info__add_result_column, 2);
204
204
  }
@@ -8,7 +8,7 @@ struct _rubyDuckDBBindInfo {
8
8
  typedef struct _rubyDuckDBBindInfo rubyDuckDBBindInfo;
9
9
 
10
10
  extern VALUE cDuckDBTableFunctionBindInfo;
11
- rubyDuckDBBindInfo *get_struct_bind_info(VALUE obj);
12
- void rbduckdb_init_duckdb_table_function_bind_info(void);
11
+ rubyDuckDBBindInfo *rbduckdb_get_struct_bind_info(VALUE obj);
12
+ void rbduckdb_init_table_function_bind_info(void);
13
13
 
14
14
  #endif
@@ -5,7 +5,7 @@ VALUE cDuckDBTableFunctionFunctionInfo;
5
5
  static void deallocate(void *ctx);
6
6
  static VALUE allocate(VALUE klass);
7
7
  static size_t memsize(const void *p);
8
- static VALUE rbduckdb_function_info_set_error(VALUE self, VALUE error);
8
+ static VALUE table_function_function_info_set_error(VALUE self, VALUE error);
9
9
 
10
10
  static const rb_data_type_t function_info_data_type = {
11
11
  "DuckDB/TableFunctionFunctionInfo",
@@ -27,7 +27,7 @@ static size_t memsize(const void *p) {
27
27
  return sizeof(rubyDuckDBFunctionInfo);
28
28
  }
29
29
 
30
- rubyDuckDBFunctionInfo *get_struct_function_info(VALUE obj) {
30
+ rubyDuckDBFunctionInfo *rbduckdb_get_struct_function_info(VALUE obj) {
31
31
  rubyDuckDBFunctionInfo *ctx;
32
32
  TypedData_Get_Struct(obj, rubyDuckDBFunctionInfo, &function_info_data_type, ctx);
33
33
  return ctx;
@@ -42,7 +42,7 @@ rubyDuckDBFunctionInfo *get_struct_function_info(VALUE obj) {
42
42
  *
43
43
  * function_info.set_error('Invalid parameter value')
44
44
  */
45
- static VALUE rbduckdb_function_info_set_error(VALUE self, VALUE error) {
45
+ static VALUE table_function_function_info_set_error(VALUE self, VALUE error) {
46
46
  rubyDuckDBFunctionInfo *ctx;
47
47
  const char *error_msg;
48
48
 
@@ -54,12 +54,12 @@ static VALUE rbduckdb_function_info_set_error(VALUE self, VALUE error) {
54
54
  return self;
55
55
  }
56
56
 
57
- void rbduckdb_init_duckdb_table_function_function_info(void) {
57
+ void rbduckdb_init_table_function_function_info(void) {
58
58
  #if 0
59
59
  VALUE mDuckDB = rb_define_module("DuckDB");
60
60
  #endif
61
61
  cDuckDBTableFunctionFunctionInfo = rb_define_class_under(cDuckDBTableFunction, "FunctionInfo", rb_cObject);
62
62
  rb_define_alloc_func(cDuckDBTableFunctionFunctionInfo, allocate);
63
63
 
64
- rb_define_method(cDuckDBTableFunctionFunctionInfo, "set_error", rbduckdb_function_info_set_error, 1);
64
+ rb_define_method(cDuckDBTableFunctionFunctionInfo, "set_error", table_function_function_info_set_error, 1);
65
65
  }
@@ -7,7 +7,7 @@ struct _rubyDuckDBFunctionInfo {
7
7
 
8
8
  typedef struct _rubyDuckDBFunctionInfo rubyDuckDBFunctionInfo;
9
9
 
10
- rubyDuckDBFunctionInfo *get_struct_function_info(VALUE obj);
11
- void rbduckdb_init_duckdb_table_function_function_info(void);
10
+ rubyDuckDBFunctionInfo *rbduckdb_get_struct_function_info(VALUE obj);
11
+ void rbduckdb_init_table_function_function_info(void);
12
12
 
13
13
  #endif
@@ -5,7 +5,10 @@ VALUE cDuckDBTableFunctionInitInfo;
5
5
  static void deallocate(void *ctx);
6
6
  static VALUE allocate(VALUE klass);
7
7
  static size_t memsize(const void *p);
8
- static VALUE rbduckdb_init_info_set_error(VALUE self, VALUE error);
8
+ static VALUE table_function_init_info_set_error(VALUE self, VALUE error);
9
+ static VALUE table_function_init_info_set_max_threads(VALUE self, VALUE max_threads);
10
+ static VALUE table_function_init_info_column_count(VALUE self);
11
+ static VALUE table_function_init_info_column_index(VALUE self, VALUE index);
9
12
 
10
13
  static const rb_data_type_t init_info_data_type = {
11
14
  "DuckDB/TableFunctionInitInfo",
@@ -27,7 +30,7 @@ static size_t memsize(const void *p) {
27
30
  return sizeof(rubyDuckDBInitInfo);
28
31
  }
29
32
 
30
- rubyDuckDBInitInfo *get_struct_init_info(VALUE obj) {
33
+ rubyDuckDBInitInfo *rbduckdb_get_struct_init_info(VALUE obj) {
31
34
  rubyDuckDBInitInfo *ctx;
32
35
  TypedData_Get_Struct(obj, rubyDuckDBInitInfo, &init_info_data_type, ctx);
33
36
  return ctx;
@@ -42,7 +45,7 @@ rubyDuckDBInitInfo *get_struct_init_info(VALUE obj) {
42
45
  *
43
46
  * init_info.set_error('Invalid initialization')
44
47
  */
45
- static VALUE rbduckdb_init_info_set_error(VALUE self, VALUE error) {
48
+ static VALUE table_function_init_info_set_error(VALUE self, VALUE error) {
46
49
  rubyDuckDBInitInfo *ctx;
47
50
  const char *error_msg;
48
51
 
@@ -54,12 +57,74 @@ static VALUE rbduckdb_init_info_set_error(VALUE self, VALUE error) {
54
57
  return self;
55
58
  }
56
59
 
57
- void rbduckdb_init_duckdb_table_function_init_info(void) {
60
+ /*
61
+ * call-seq:
62
+ * init_info.set_max_threads(max_threads) -> self
63
+ * init_info.max_threads = max_threads
64
+ *
65
+ * Sets the maximum number of threads that can execute the table function concurrently.
66
+ * This is a hint to DuckDB's scheduler; the actual number of threads is also bounded
67
+ * by the configured worker pool size (e.g., +SET threads+).
68
+ *
69
+ * init_info.max_threads = 4
70
+ */
71
+ static VALUE table_function_init_info_set_max_threads(VALUE self, VALUE max_threads) {
72
+ rubyDuckDBInitInfo *ctx;
73
+
74
+ TypedData_Get_Struct(self, rubyDuckDBInitInfo, &init_info_data_type, ctx);
75
+
76
+ duckdb_init_set_max_threads(ctx->info, NUM2ULL(max_threads));
77
+
78
+ return self;
79
+ }
80
+
81
+ /*
82
+ * call-seq:
83
+ * init_info.column_count -> Integer
84
+ *
85
+ * Returns the number of projected result columns for this scan.
86
+ * Without projection pushdown this equals the number of result columns
87
+ * added in the bind callback.
88
+ *
89
+ * init_info.column_count # => 2
90
+ */
91
+ static VALUE table_function_init_info_column_count(VALUE self) {
92
+ rubyDuckDBInitInfo *ctx;
93
+
94
+ TypedData_Get_Struct(self, rubyDuckDBInitInfo, &init_info_data_type, ctx);
95
+
96
+ return ULL2NUM(duckdb_init_get_column_count(ctx->info));
97
+ }
98
+
99
+ /*
100
+ * call-seq:
101
+ * init_info.column_index(index) -> Integer
102
+ *
103
+ * Returns the column index of the projected result column at +index+
104
+ * (0 <= +index+ < column_count). Without projection pushdown the projected
105
+ * columns mirror the columns added in the bind callback, so this returns
106
+ * +index+ itself.
107
+ *
108
+ * init_info.column_index(0) # => 0
109
+ */
110
+ static VALUE table_function_init_info_column_index(VALUE self, VALUE index) {
111
+ rubyDuckDBInitInfo *ctx;
112
+
113
+ TypedData_Get_Struct(self, rubyDuckDBInitInfo, &init_info_data_type, ctx);
114
+
115
+ return ULL2NUM(duckdb_init_get_column_index(ctx->info, NUM2ULL(index)));
116
+ }
117
+
118
+ void rbduckdb_init_table_function_init_info(void) {
58
119
  #if 0
59
120
  VALUE mDuckDB = rb_define_module("DuckDB");
60
121
  #endif
61
122
  cDuckDBTableFunctionInitInfo = rb_define_class_under(cDuckDBTableFunction, "InitInfo", rb_cObject);
62
123
  rb_define_alloc_func(cDuckDBTableFunctionInitInfo, allocate);
63
124
 
64
- rb_define_method(cDuckDBTableFunctionInitInfo, "set_error", rbduckdb_init_info_set_error, 1);
125
+ rb_define_method(cDuckDBTableFunctionInitInfo, "set_error", table_function_init_info_set_error, 1);
126
+ rb_define_method(cDuckDBTableFunctionInitInfo, "set_max_threads", table_function_init_info_set_max_threads, 1);
127
+ rb_define_method(cDuckDBTableFunctionInitInfo, "max_threads=", table_function_init_info_set_max_threads, 1);
128
+ rb_define_method(cDuckDBTableFunctionInitInfo, "column_count", table_function_init_info_column_count, 0);
129
+ rb_define_method(cDuckDBTableFunctionInitInfo, "column_index", table_function_init_info_column_index, 1);
65
130
  }
@@ -8,7 +8,7 @@ struct _rubyDuckDBInitInfo {
8
8
  typedef struct _rubyDuckDBInitInfo rubyDuckDBInitInfo;
9
9
 
10
10
  extern VALUE cDuckDBTableFunctionInitInfo;
11
- rubyDuckDBInitInfo *get_struct_init_info(VALUE obj);
12
- void rbduckdb_init_duckdb_table_function_init_info(void);
11
+ rubyDuckDBInitInfo *rbduckdb_get_struct_init_info(VALUE obj);
12
+ void rbduckdb_init_table_function_init_info(void);
13
13
 
14
14
  #endif
@@ -57,6 +57,12 @@ module DuckDB
57
57
  class AggregateFunction
58
58
  include FunctionTypeValidation
59
59
 
60
+ def name=(value)
61
+ set_name(value.to_s)
62
+ end
63
+
64
+ private :set_name
65
+
60
66
  class << self
61
67
  # Creates a new AggregateFunction in a single call.
62
68
  #
@@ -64,7 +70,7 @@ module DuckDB
64
70
  # AggregateFunction without requiring you to set each attribute
65
71
  # separately.
66
72
  #
67
- # @param name [String] the SQL function name
73
+ # @param name [String, Symbol] the SQL function name
68
74
  # @param return_type [DuckDB::LogicalType | Symbol] the SQL return type
69
75
  # @param params [Array<DuckDB::LogicalType | Symbol>] input parameter types
70
76
  # (empty array for a zero-argument aggregate)
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DuckDB
4
+ # DuckDB::AggregateFunctionSet encapsulates DuckDB's aggregate function set,
5
+ # which allows registering multiple overloads of an aggregate function under one name.
6
+ #
7
+ # @note DuckDB::AggregateFunctionSet is experimental.
8
+ class AggregateFunctionSet
9
+ # @param name [String, Symbol] the function set name shared by all overloads
10
+ # @raise [TypeError] if name is not a String or Symbol
11
+ def initialize(name)
12
+ raise TypeError, "#{name.class} is not a String or Symbol" unless name.is_a?(String) || name.is_a?(Symbol)
13
+
14
+ _initialize(name.to_s)
15
+ end
16
+
17
+ # @param aggregate_function [DuckDB::AggregateFunction] the overload to add
18
+ # @return [self]
19
+ # @raise [TypeError] if aggregate_function is not a DuckDB::AggregateFunction
20
+ # @raise [DuckDB::Error] if the overload already exists in the set
21
+ def add(aggregate_function)
22
+ unless aggregate_function.is_a?(DuckDB::AggregateFunction)
23
+ raise TypeError, "#{aggregate_function.class} is not a DuckDB::AggregateFunction"
24
+ end
25
+
26
+ _add(aggregate_function)
27
+ end
28
+ end
29
+ end