duckdb 1.5.2.0 → 1.5.2.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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/duckdb.gemspec +37 -0
  4. data/ext/duckdb/aggregate_function.c +61 -100
  5. data/ext/duckdb/aggregate_function.h +2 -2
  6. data/ext/duckdb/appender.c +76 -35
  7. data/ext/duckdb/appender.h +1 -1
  8. data/ext/duckdb/client_context.c +5 -5
  9. data/ext/duckdb/client_context.h +2 -2
  10. data/ext/duckdb/column.c +13 -13
  11. data/ext/duckdb/column.h +1 -1
  12. data/ext/duckdb/connection.c +40 -41
  13. data/ext/duckdb/connection.h +2 -2
  14. data/ext/duckdb/converter.h +1 -7
  15. data/ext/duckdb/conveter.c +6 -6
  16. data/ext/duckdb/data_chunk.c +22 -22
  17. data/ext/duckdb/data_chunk.h +2 -2
  18. data/ext/duckdb/database.c +10 -10
  19. data/ext/duckdb/database.h +1 -1
  20. data/ext/duckdb/duckdb.c +17 -17
  21. data/ext/duckdb/expression.c +8 -8
  22. data/ext/duckdb/expression.h +1 -1
  23. data/ext/duckdb/extconf.rb +4 -3
  24. data/ext/duckdb/extracted_statements.c +15 -15
  25. data/ext/duckdb/extracted_statements.h +1 -1
  26. data/ext/duckdb/instance_cache.c +10 -10
  27. data/ext/duckdb/instance_cache.h +1 -1
  28. data/ext/duckdb/logical_type.c +94 -133
  29. data/ext/duckdb/logical_type.h +2 -2
  30. data/ext/duckdb/memory_helper.c +28 -28
  31. data/ext/duckdb/pending_result.c +27 -27
  32. data/ext/duckdb/pending_result.h +2 -2
  33. data/ext/duckdb/prepared_statement.c +103 -103
  34. data/ext/duckdb/prepared_statement.h +2 -2
  35. data/ext/duckdb/result.c +33 -33
  36. data/ext/duckdb/result.h +2 -3
  37. data/ext/duckdb/ruby-duckdb.h +4 -0
  38. data/ext/duckdb/scalar_function.c +3 -3
  39. data/ext/duckdb/table_description.c +1 -1
  40. data/ext/duckdb/table_function.c +3 -3
  41. data/ext/duckdb/table_function_bind_info.c +1 -1
  42. data/ext/duckdb/value.c +62 -50
  43. data/ext/duckdb/value.h +2 -2
  44. data/ext/duckdb/vector.c +20 -20
  45. data/ext/duckdb/vector.h +2 -2
  46. data/lib/duckdb/aggregate_function.rb +202 -3
  47. data/lib/duckdb/appender.rb +74 -0
  48. data/lib/duckdb/connection.rb +1 -16
  49. data/lib/duckdb/converter.rb +5 -0
  50. data/lib/duckdb/logical_type.rb +1 -3
  51. data/lib/duckdb/prepared_statement.rb +1 -1
  52. data/lib/duckdb/table_function.rb +0 -1
  53. data/lib/duckdb/value.rb +19 -0
  54. data/lib/duckdb/version.rb +1 -1
  55. metadata +2 -2
  56. data/lib/duckdb/duckdb_native.so +0 -0
@@ -12,6 +12,10 @@
12
12
  #define HAVE_DUCKDB_H_GE_V1_5_0 1
13
13
  #endif
14
14
 
15
+ #ifdef HAVE_DUCKDB_GEOMETRY_TYPE_GET_CRS
16
+ #define HAVE_DUCKDB_H_GE_V1_5_2 1
17
+ #endif
18
+
15
19
  #include "./error.h"
16
20
  #include "./database.h"
17
21
  #include "./connection.h"
@@ -139,7 +139,7 @@ static VALUE rbduckdb_scalar_function__set_return_type(VALUE self, VALUE logical
139
139
  rubyDuckDBLogicalType *lt;
140
140
 
141
141
  TypedData_Get_Struct(self, rubyDuckDBScalarFunction, &scalar_function_data_type, p);
142
- lt = get_struct_logical_type(logical_type);
142
+ lt = rbduckdb_get_struct_logical_type(logical_type);
143
143
 
144
144
  duckdb_scalar_function_set_return_type(p->scalar_function, lt->logical_type);
145
145
 
@@ -151,7 +151,7 @@ static VALUE rbduckdb_scalar_function__set_varargs(VALUE self, VALUE logical_typ
151
151
  rubyDuckDBLogicalType *lt;
152
152
 
153
153
  TypedData_Get_Struct(self, rubyDuckDBScalarFunction, &scalar_function_data_type, p);
154
- lt = get_struct_logical_type(logical_type);
154
+ lt = rbduckdb_get_struct_logical_type(logical_type);
155
155
 
156
156
  duckdb_scalar_function_set_varargs(p->scalar_function, lt->logical_type);
157
157
 
@@ -172,7 +172,7 @@ static VALUE rbduckdb_scalar_function_add_parameter(VALUE self, VALUE logical_ty
172
172
  rubyDuckDBLogicalType *lt;
173
173
 
174
174
  TypedData_Get_Struct(self, rubyDuckDBScalarFunction, &scalar_function_data_type, p);
175
- lt = get_struct_logical_type(logical_type);
175
+ lt = rbduckdb_get_struct_logical_type(logical_type);
176
176
 
177
177
  duckdb_scalar_function_add_parameter(p->scalar_function, lt->logical_type);
178
178
 
@@ -70,7 +70,7 @@ static VALUE duckdb_table_description__initialize(VALUE self, VALUE con, VALUE c
70
70
  if (!NIL_P(table)) {
71
71
  ptable = StringValuePtr(table);
72
72
  }
73
- ctxcon = get_struct_connection(con);
73
+ ctxcon = rbduckdb_get_struct_connection(con);
74
74
  ctx = get_struct_table_description(self);
75
75
 
76
76
  if (ctx->table_description) {
@@ -148,7 +148,7 @@ static VALUE rbduckdb_table_function_add_parameter(VALUE self, VALUE logical_typ
148
148
  rb_raise(eDuckDBError, "Table function is destroyed");
149
149
  }
150
150
 
151
- ctx_logical_type = get_struct_logical_type(logical_type);
151
+ ctx_logical_type = rbduckdb_get_struct_logical_type(logical_type);
152
152
  duckdb_table_function_add_parameter(ctx->table_function, ctx_logical_type->logical_type);
153
153
 
154
154
  return self;
@@ -174,7 +174,7 @@ static VALUE rbduckdb_table_function_add_named_parameter(VALUE self, VALUE name,
174
174
  }
175
175
 
176
176
  param_name = StringValueCStr(name);
177
- ctx_logical_type = get_struct_logical_type(logical_type);
177
+ ctx_logical_type = rbduckdb_get_struct_logical_type(logical_type);
178
178
  duckdb_table_function_add_named_parameter(ctx->table_function, param_name, ctx_logical_type->logical_type);
179
179
 
180
180
  return self;
@@ -388,7 +388,7 @@ static void execute_execute_callback_protected(void *user_data) {
388
388
  func_info_ctx->info = darg->info;
389
389
 
390
390
  data_chunk_obj = rb_class_new_instance(0, NULL, cDuckDBDataChunk);
391
- data_chunk_ctx = get_struct_data_chunk(data_chunk_obj);
391
+ data_chunk_ctx = rbduckdb_get_struct_data_chunk(data_chunk_obj);
392
392
  data_chunk_ctx->data_chunk = darg->output;
393
393
 
394
394
  VALUE call_args[3] = { darg->ctx->execute_proc, func_info_obj, data_chunk_obj };
@@ -135,7 +135,7 @@ static VALUE rbduckdb_bind_info__add_result_column(VALUE self, VALUE column_name
135
135
  const char *col_name;
136
136
 
137
137
  TypedData_Get_Struct(self, rubyDuckDBBindInfo, &bind_info_data_type, ctx);
138
- ctx_logical_type = get_struct_logical_type(logical_type);
138
+ ctx_logical_type = rbduckdb_get_struct_logical_type(logical_type);
139
139
 
140
140
  col_name = StringValueCStr(column_name);
141
141
  duckdb_bind_add_result_column(ctx->bind_info, col_name, ctx_logical_type->logical_type);
data/ext/duckdb/value.c CHANGED
@@ -5,22 +5,23 @@ VALUE cDuckDBValue;
5
5
  static void deallocate(void *);
6
6
  static VALUE allocate(VALUE klass);
7
7
  static size_t memsize(const void *p);
8
- static VALUE duckdb_value_s__create_bool(VALUE klass, VALUE flag);
9
- static VALUE duckdb_value_s__create_int8(VALUE klass, VALUE val);
10
- static VALUE duckdb_value_s__create_int16(VALUE klass, VALUE val);
11
- static VALUE duckdb_value_s__create_int32(VALUE klass, VALUE val);
12
- static VALUE duckdb_value_s__create_int64(VALUE klass, VALUE val);
13
- static VALUE duckdb_value_s__create_uint8(VALUE klass, VALUE val);
14
- static VALUE duckdb_value_s__create_uint16(VALUE klass, VALUE val);
15
- static VALUE duckdb_value_s__create_uint32(VALUE klass, VALUE val);
16
- static VALUE duckdb_value_s__create_uint64(VALUE klass, VALUE val);
17
- static VALUE duckdb_value_s__create_float(VALUE klass, VALUE val);
18
- static VALUE duckdb_value_s__create_double(VALUE klass, VALUE val);
19
- static VALUE duckdb_value_s__create_varchar(VALUE klass, VALUE str);
20
- static VALUE duckdb_value_s__create_blob(VALUE klass, VALUE str);
21
- static VALUE duckdb_value_s__create_hugeint(VALUE klass, VALUE lower, VALUE upper);
22
- static VALUE duckdb_value_s__create_uhugeint(VALUE klass, VALUE lower, VALUE upper);
23
- static VALUE duckdb_value_s_create_null(VALUE klass);
8
+ static VALUE value_s__create_bool(VALUE klass, VALUE flag);
9
+ static VALUE value_s__create_int8(VALUE klass, VALUE val);
10
+ static VALUE value_s__create_int16(VALUE klass, VALUE val);
11
+ static VALUE value_s__create_int32(VALUE klass, VALUE val);
12
+ static VALUE value_s__create_int64(VALUE klass, VALUE val);
13
+ static VALUE value_s__create_uint8(VALUE klass, VALUE val);
14
+ static VALUE value_s__create_uint16(VALUE klass, VALUE val);
15
+ static VALUE value_s__create_uint32(VALUE klass, VALUE val);
16
+ static VALUE value_s__create_uint64(VALUE klass, VALUE val);
17
+ static VALUE value_s__create_float(VALUE klass, VALUE val);
18
+ static VALUE value_s__create_double(VALUE klass, VALUE val);
19
+ static VALUE value_s__create_varchar(VALUE klass, VALUE str);
20
+ static VALUE value_s__create_blob(VALUE klass, VALUE str);
21
+ static VALUE value_s__create_hugeint(VALUE klass, VALUE lower, VALUE upper);
22
+ static VALUE value_s__create_uhugeint(VALUE klass, VALUE lower, VALUE upper);
23
+ static VALUE value_s__create_decimal(VALUE klass, VALUE lower, VALUE upper, VALUE width, VALUE scale);
24
+ static VALUE value_s_create_null(VALUE klass);
24
25
 
25
26
  static const rb_data_type_t value_data_type = {
26
27
  "DuckDB/Value",
@@ -44,76 +45,76 @@ static size_t memsize(const void *p) {
44
45
  return sizeof(rubyDuckDBValue);
45
46
  }
46
47
 
47
- static VALUE duckdb_value_s__create_bool(VALUE klass, VALUE flag) {
48
+ static VALUE value_s__create_bool(VALUE klass, VALUE flag) {
48
49
  duckdb_value value = duckdb_create_bool(RTEST(flag) ? true : false);
49
50
  return rbduckdb_value_new(value);
50
51
  }
51
52
 
52
- static VALUE duckdb_value_s__create_int8(VALUE klass, VALUE val) {
53
+ static VALUE value_s__create_int8(VALUE klass, VALUE val) {
53
54
  duckdb_value value = duckdb_create_int8((int8_t)NUM2INT(val));
54
55
  return rbduckdb_value_new(value);
55
56
  }
56
57
 
57
- static VALUE duckdb_value_s__create_int16(VALUE klass, VALUE val) {
58
+ static VALUE value_s__create_int16(VALUE klass, VALUE val) {
58
59
  duckdb_value value = duckdb_create_int16((int16_t)NUM2INT(val));
59
60
  return rbduckdb_value_new(value);
60
61
  }
61
62
 
62
- static VALUE duckdb_value_s__create_int32(VALUE klass, VALUE val) {
63
+ static VALUE value_s__create_int32(VALUE klass, VALUE val) {
63
64
  duckdb_value value = duckdb_create_int32(NUM2INT(val));
64
65
  return rbduckdb_value_new(value);
65
66
  }
66
67
 
67
- static VALUE duckdb_value_s__create_int64(VALUE klass, VALUE val) {
68
+ static VALUE value_s__create_int64(VALUE klass, VALUE val) {
68
69
  duckdb_value value = duckdb_create_int64((int64_t)NUM2LL(val));
69
70
  return rbduckdb_value_new(value);
70
71
  }
71
72
 
72
- static VALUE duckdb_value_s__create_uint8(VALUE klass, VALUE val) {
73
+ static VALUE value_s__create_uint8(VALUE klass, VALUE val) {
73
74
  duckdb_value value = duckdb_create_uint8((uint8_t)NUM2UINT(val));
74
75
  return rbduckdb_value_new(value);
75
76
  }
76
77
 
77
- static VALUE duckdb_value_s__create_uint16(VALUE klass, VALUE val) {
78
+ static VALUE value_s__create_uint16(VALUE klass, VALUE val) {
78
79
  duckdb_value value = duckdb_create_uint16((uint16_t)NUM2UINT(val));
79
80
  return rbduckdb_value_new(value);
80
81
  }
81
82
 
82
- static VALUE duckdb_value_s__create_uint32(VALUE klass, VALUE val) {
83
+ static VALUE value_s__create_uint32(VALUE klass, VALUE val) {
83
84
  duckdb_value value = duckdb_create_uint32((uint32_t)NUM2UINT(val));
84
85
  return rbduckdb_value_new(value);
85
86
  }
86
87
 
87
- static VALUE duckdb_value_s__create_uint64(VALUE klass, VALUE val) {
88
+ static VALUE value_s__create_uint64(VALUE klass, VALUE val) {
88
89
  duckdb_value value = duckdb_create_uint64((uint64_t)RB_NUM2ULL(val));
89
90
  return rbduckdb_value_new(value);
90
91
  }
91
92
 
92
- static VALUE duckdb_value_s__create_float(VALUE klass, VALUE val) {
93
+ static VALUE value_s__create_float(VALUE klass, VALUE val) {
93
94
  duckdb_value value = duckdb_create_float((float)NUM2DBL(val));
94
95
  return rbduckdb_value_new(value);
95
96
  }
96
97
 
97
- static VALUE duckdb_value_s__create_double(VALUE klass, VALUE val) {
98
+ static VALUE value_s__create_double(VALUE klass, VALUE val) {
98
99
  duckdb_value value = duckdb_create_double(NUM2DBL(val));
99
100
  return rbduckdb_value_new(value);
100
101
  }
101
102
 
102
- static VALUE duckdb_value_s__create_varchar(VALUE klass, VALUE str) {
103
+ static VALUE value_s__create_varchar(VALUE klass, VALUE str) {
103
104
  const char *str_ptr = StringValuePtr(str);
104
105
  idx_t str_len = RSTRING_LEN(str);
105
106
  duckdb_value value = duckdb_create_varchar_length(str_ptr, str_len);
106
107
  return rbduckdb_value_new(value);
107
108
  }
108
109
 
109
- static VALUE duckdb_value_s__create_blob(VALUE klass, VALUE str) {
110
+ static VALUE value_s__create_blob(VALUE klass, VALUE str) {
110
111
  const uint8_t *data_ptr = (const uint8_t *)StringValuePtr(str);
111
112
  idx_t data_len = RSTRING_LEN(str);
112
113
  duckdb_value value = duckdb_create_blob(data_ptr, data_len);
113
114
  return rbduckdb_value_new(value);
114
115
  }
115
116
 
116
- static VALUE duckdb_value_s__create_hugeint(VALUE klass, VALUE lower, VALUE upper) {
117
+ static VALUE value_s__create_hugeint(VALUE klass, VALUE lower, VALUE upper) {
117
118
  duckdb_hugeint hugeint;
118
119
  hugeint.lower = NUM2ULL(lower);
119
120
  hugeint.upper = NUM2LL(upper);
@@ -121,7 +122,7 @@ static VALUE duckdb_value_s__create_hugeint(VALUE klass, VALUE lower, VALUE uppe
121
122
  return rbduckdb_value_new(value);
122
123
  }
123
124
 
124
- static VALUE duckdb_value_s__create_uhugeint(VALUE klass, VALUE lower, VALUE upper) {
125
+ static VALUE value_s__create_uhugeint(VALUE klass, VALUE lower, VALUE upper) {
125
126
  duckdb_uhugeint uhugeint;
126
127
  uhugeint.lower = NUM2ULL(lower);
127
128
  uhugeint.upper = NUM2ULL(upper);
@@ -129,6 +130,16 @@ static VALUE duckdb_value_s__create_uhugeint(VALUE klass, VALUE lower, VALUE upp
129
130
  return rbduckdb_value_new(value);
130
131
  }
131
132
 
133
+ static VALUE value_s__create_decimal(VALUE klass, VALUE lower, VALUE upper, VALUE width, VALUE scale) {
134
+ duckdb_decimal decimal;
135
+ decimal.value.lower = NUM2ULL(lower);
136
+ decimal.value.upper = NUM2LL(upper);
137
+ decimal.width = (uint8_t)NUM2UINT(width);
138
+ decimal.scale = (uint8_t)NUM2UINT(scale);
139
+ duckdb_value value = duckdb_create_decimal(decimal);
140
+ return rbduckdb_value_new(value);
141
+ }
142
+
132
143
  /*
133
144
  * call-seq:
134
145
  * DuckDB::Value.create_null -> DuckDB::Value
@@ -138,7 +149,7 @@ static VALUE duckdb_value_s__create_uhugeint(VALUE klass, VALUE lower, VALUE upp
138
149
  * require 'duckdb'
139
150
  * value = DuckDB::Value.create_null
140
151
  */
141
- static VALUE duckdb_value_s_create_null(VALUE klass) {
152
+ static VALUE value_s_create_null(VALUE klass) {
142
153
  duckdb_value value = duckdb_create_null_value();
143
154
  return rbduckdb_value_new(value);
144
155
  }
@@ -151,7 +162,7 @@ VALUE rbduckdb_value_new(duckdb_value value) {
151
162
  return obj;
152
163
  }
153
164
 
154
- rubyDuckDBValue *get_struct_value(VALUE obj) {
165
+ rubyDuckDBValue *rbduckdb_get_struct_value(VALUE obj) {
155
166
  rubyDuckDBValue *ctx;
156
167
  TypedData_Get_Struct(obj, rubyDuckDBValue, &value_data_type, ctx);
157
168
  return ctx;
@@ -252,28 +263,29 @@ VALUE rbduckdb_duckdb_value_to_ruby(duckdb_value val) {
252
263
  return result;
253
264
  }
254
265
 
255
- void rbduckdb_init_duckdb_value(void) {
266
+ void rbduckdb_init_value(void) {
256
267
  #if 0
257
268
  VALUE mDuckDB = rb_define_module("DuckDB");
258
269
  #endif
259
270
  cDuckDBValue = rb_define_class_under(mDuckDB, "Value", rb_cObject);
260
271
  rb_define_alloc_func(cDuckDBValue, allocate);
261
272
 
262
- rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_bool", duckdb_value_s__create_bool, 1);
263
- rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_int8", duckdb_value_s__create_int8, 1);
264
- rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_int16", duckdb_value_s__create_int16, 1);
265
- rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_int32", duckdb_value_s__create_int32, 1);
266
- rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_int64", duckdb_value_s__create_int64, 1);
267
- rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_uint8", duckdb_value_s__create_uint8, 1);
268
- rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_uint16", duckdb_value_s__create_uint16, 1);
269
- rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_uint32", duckdb_value_s__create_uint32, 1);
270
- rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_uint64", duckdb_value_s__create_uint64, 1);
271
- rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_float", duckdb_value_s__create_float, 1);
272
- rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_double", duckdb_value_s__create_double, 1);
273
- rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_varchar", duckdb_value_s__create_varchar, 1);
274
- rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_blob", duckdb_value_s__create_blob, 1);
275
- rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_hugeint", duckdb_value_s__create_hugeint, 2);
276
- rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_uhugeint", duckdb_value_s__create_uhugeint, 2);
277
- rb_define_singleton_method(cDuckDBValue, "create_null", duckdb_value_s_create_null, 0);
273
+ rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_bool", value_s__create_bool, 1);
274
+ rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_int8", value_s__create_int8, 1);
275
+ rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_int16", value_s__create_int16, 1);
276
+ rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_int32", value_s__create_int32, 1);
277
+ rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_int64", value_s__create_int64, 1);
278
+ rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_uint8", value_s__create_uint8, 1);
279
+ rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_uint16", value_s__create_uint16, 1);
280
+ rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_uint32", value_s__create_uint32, 1);
281
+ rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_uint64", value_s__create_uint64, 1);
282
+ rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_float", value_s__create_float, 1);
283
+ rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_double", value_s__create_double, 1);
284
+ rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_varchar", value_s__create_varchar, 1);
285
+ rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_blob", value_s__create_blob, 1);
286
+ rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_hugeint", value_s__create_hugeint, 2);
287
+ rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_uhugeint", value_s__create_uhugeint, 2);
288
+ rb_define_private_method(rb_singleton_class(cDuckDBValue), "_create_decimal", value_s__create_decimal, 4);
289
+ rb_define_singleton_method(cDuckDBValue, "create_null", value_s_create_null, 0);
278
290
  }
279
291
 
data/ext/duckdb/value.h CHANGED
@@ -7,9 +7,9 @@ struct _rubyDuckDBValue {
7
7
 
8
8
  typedef struct _rubyDuckDBValue rubyDuckDBValue;
9
9
 
10
- void rbduckdb_init_duckdb_value(void);
10
+ void rbduckdb_init_value(void);
11
11
  VALUE rbduckdb_value_new(duckdb_value value);
12
12
  VALUE rbduckdb_duckdb_value_to_ruby(duckdb_value val);
13
- rubyDuckDBValue *get_struct_value(VALUE obj);
13
+ rubyDuckDBValue *rbduckdb_get_struct_value(VALUE obj);
14
14
 
15
15
  #endif
data/ext/duckdb/vector.c CHANGED
@@ -5,12 +5,12 @@ VALUE cDuckDBVector;
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_vector_get_data(VALUE self);
9
- static VALUE rbduckdb_vector_get_validity(VALUE self);
10
- static VALUE rbduckdb_vector_assign_string_element(VALUE self, VALUE index, VALUE str);
11
- static VALUE rbduckdb_vector_assign_string_element_len(VALUE self, VALUE index, VALUE str);
12
- static VALUE rbduckdb_vector_logical_type(VALUE self);
13
- static VALUE rbduckdb_vector_set_validity(VALUE self, VALUE index, VALUE valid);
8
+ static VALUE vector_get_data(VALUE self);
9
+ static VALUE vector_get_validity(VALUE self);
10
+ static VALUE vector_assign_string_element(VALUE self, VALUE index, VALUE str);
11
+ static VALUE vector_assign_string_element_len(VALUE self, VALUE index, VALUE str);
12
+ static VALUE vector_logical_type(VALUE self);
13
+ static VALUE vector_set_validity(VALUE self, VALUE index, VALUE valid);
14
14
 
15
15
  static const rb_data_type_t vector_data_type = {
16
16
  "DuckDB/Vector",
@@ -32,7 +32,7 @@ static size_t memsize(const void *p) {
32
32
  return sizeof(rubyDuckDBVector);
33
33
  }
34
34
 
35
- rubyDuckDBVector *get_struct_vector(VALUE obj) {
35
+ rubyDuckDBVector *rbduckdb_get_struct_vector(VALUE obj) {
36
36
  rubyDuckDBVector *ctx;
37
37
  TypedData_Get_Struct(obj, rubyDuckDBVector, &vector_data_type, ctx);
38
38
  return ctx;
@@ -47,7 +47,7 @@ rubyDuckDBVector *get_struct_vector(VALUE obj) {
47
47
  *
48
48
  * ptr = vector.get_data
49
49
  */
50
- static VALUE rbduckdb_vector_get_data(VALUE self) {
50
+ static VALUE vector_get_data(VALUE self) {
51
51
  rubyDuckDBVector *ctx;
52
52
  void *data;
53
53
 
@@ -67,7 +67,7 @@ static VALUE rbduckdb_vector_get_data(VALUE self) {
67
67
  *
68
68
  * validity = vector.get_validity
69
69
  */
70
- static VALUE rbduckdb_vector_get_validity(VALUE self) {
70
+ static VALUE vector_get_validity(VALUE self) {
71
71
  rubyDuckDBVector *ctx;
72
72
  uint64_t *validity;
73
73
 
@@ -90,7 +90,7 @@ static VALUE rbduckdb_vector_get_validity(VALUE self) {
90
90
  *
91
91
  * vector.assign_string_element(0, 'hello')
92
92
  */
93
- static VALUE rbduckdb_vector_assign_string_element(VALUE self, VALUE index, VALUE str) {
93
+ static VALUE vector_assign_string_element(VALUE self, VALUE index, VALUE str) {
94
94
  rubyDuckDBVector *ctx;
95
95
  idx_t idx;
96
96
  const char *string_val;
@@ -114,7 +114,7 @@ static VALUE rbduckdb_vector_assign_string_element(VALUE self, VALUE index, VALU
114
114
  *
115
115
  * vector.assign_string_element_len(0, "\x00\x01\x02\x03")
116
116
  */
117
- static VALUE rbduckdb_vector_assign_string_element_len(VALUE self, VALUE index, VALUE str) {
117
+ static VALUE vector_assign_string_element_len(VALUE self, VALUE index, VALUE str) {
118
118
  rubyDuckDBVector *ctx;
119
119
  idx_t idx;
120
120
  const char *string_val;
@@ -141,7 +141,7 @@ static VALUE rbduckdb_vector_assign_string_element_len(VALUE self, VALUE index,
141
141
  * type = vector.logical_type
142
142
  * type.id #=> DuckDB::Type::BIGINT
143
143
  */
144
- static VALUE rbduckdb_vector_logical_type(VALUE self) {
144
+ static VALUE vector_logical_type(VALUE self) {
145
145
  rubyDuckDBVector *ctx;
146
146
  duckdb_logical_type logical_type;
147
147
 
@@ -161,7 +161,7 @@ static VALUE rbduckdb_vector_logical_type(VALUE self) {
161
161
  * vector.set_validity(0, false) # Mark row 0 as NULL
162
162
  * vector.set_validity(1, true) # Mark row 1 as valid
163
163
  */
164
- static VALUE rbduckdb_vector_set_validity(VALUE self, VALUE index, VALUE valid) {
164
+ static VALUE vector_set_validity(VALUE self, VALUE index, VALUE valid) {
165
165
  rubyDuckDBVector *ctx;
166
166
  idx_t idx;
167
167
  uint64_t *validity;
@@ -185,17 +185,17 @@ static VALUE rbduckdb_vector_set_validity(VALUE self, VALUE index, VALUE valid)
185
185
  return self;
186
186
  }
187
187
 
188
- void rbduckdb_init_duckdb_vector(void) {
188
+ void rbduckdb_init_vector(void) {
189
189
  #if 0
190
190
  VALUE mDuckDB = rb_define_module("DuckDB");
191
191
  #endif
192
192
  cDuckDBVector = rb_define_class_under(mDuckDB, "Vector", rb_cObject);
193
193
  rb_define_alloc_func(cDuckDBVector, allocate);
194
194
 
195
- rb_define_method(cDuckDBVector, "get_data", rbduckdb_vector_get_data, 0);
196
- rb_define_method(cDuckDBVector, "get_validity", rbduckdb_vector_get_validity, 0);
197
- rb_define_method(cDuckDBVector, "assign_string_element", rbduckdb_vector_assign_string_element, 2);
198
- rb_define_method(cDuckDBVector, "assign_string_element_len", rbduckdb_vector_assign_string_element_len, 2);
199
- rb_define_method(cDuckDBVector, "logical_type", rbduckdb_vector_logical_type, 0);
200
- rb_define_method(cDuckDBVector, "set_validity", rbduckdb_vector_set_validity, 2);
195
+ rb_define_method(cDuckDBVector, "get_data", vector_get_data, 0);
196
+ rb_define_method(cDuckDBVector, "get_validity", vector_get_validity, 0);
197
+ rb_define_method(cDuckDBVector, "assign_string_element", vector_assign_string_element, 2);
198
+ rb_define_method(cDuckDBVector, "assign_string_element_len", vector_assign_string_element_len, 2);
199
+ rb_define_method(cDuckDBVector, "logical_type", vector_logical_type, 0);
200
+ rb_define_method(cDuckDBVector, "set_validity", vector_set_validity, 2);
201
201
  }
data/ext/duckdb/vector.h CHANGED
@@ -7,7 +7,7 @@ struct _rubyDuckDBVector {
7
7
 
8
8
  typedef struct _rubyDuckDBVector rubyDuckDBVector;
9
9
 
10
- rubyDuckDBVector *get_struct_vector(VALUE obj);
11
- void rbduckdb_init_duckdb_vector(void);
10
+ rubyDuckDBVector *rbduckdb_get_struct_vector(VALUE obj);
11
+ void rbduckdb_init_vector(void);
12
12
 
13
13
  #endif
@@ -1,13 +1,153 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DuckDB
4
- # DuckDB::AggregateFunction encapsulates DuckDB's aggregate function.
4
+ # DuckDB::AggregateFunction lets you register a custom aggregate function
5
+ # written in Ruby and call it from SQL.
5
6
  #
6
- # @note DuckDB::AggregateFunction is experimental. Phase 1.0 only supports
7
- # +set_init+ and +set_finalize+; +update+ and +combine+ are internal no-ops.
7
+ # An aggregate function folds many rows into a single value. You define its
8
+ # behaviour with four callbacks:
9
+ #
10
+ # * +set_init+ — called once per group; returns the initial state.
11
+ # * +set_update+ — called once per row; receives the current state and the
12
+ # input value(s), returns the new state.
13
+ # * +set_combine+ — merges two partial states (required for parallel
14
+ # execution); receives source and target states, returns the
15
+ # merged state.
16
+ # * +set_finalize+ — converts the final state into the SQL result value.
17
+ #
18
+ # Only +set_init+ is required. The other three have sensible defaults:
19
+ # * +set_update+ defaults to +{ |state, *| state }+ (ignore inputs)
20
+ # * +set_combine+ defaults to +{ |s1, _s2| s1 }+ (keep source state)
21
+ # * +set_finalize+ defaults to +{ |x| x }+ (return state as-is)
22
+ #
23
+ # @note The default +set_combine+ keeps the source state and discards the
24
+ # target, which is only correct for single-threaded (single-partition)
25
+ # execution. If DuckDB runs the aggregate in parallel it will produce
26
+ # wrong results. Always supply an explicit +set_combine+ when the
27
+ # aggregate must be parallel-safe.
28
+ #
29
+ # == Basic example: custom SUM
30
+ #
31
+ # af = DuckDB::AggregateFunction.new
32
+ # af.name = 'my_sum'
33
+ # af.return_type = DuckDB::LogicalType::BIGINT
34
+ # af.add_parameter(DuckDB::LogicalType::BIGINT)
35
+ #
36
+ # af.set_init { 0 }
37
+ # af.set_update { |state, value| state + value }
38
+ # af.set_combine { |s1, s2| s1 + s2 }
39
+ #
40
+ # con.register_aggregate_function(af)
41
+ # con.query('SELECT my_sum(i) FROM range(100) t(i)').first.first # => 4950
42
+ #
43
+ # == Example: weighted average with Hash state
44
+ #
45
+ # af = DuckDB::AggregateFunction.new
46
+ # af.name = 'weighted_avg'
47
+ # af.return_type = DuckDB::LogicalType::DOUBLE
48
+ # af.add_parameter(DuckDB::LogicalType::DOUBLE) # value
49
+ # af.add_parameter(DuckDB::LogicalType::DOUBLE) # weight
50
+ #
51
+ # af.set_init { { sum: 0.0, weight: 0.0 } }
52
+ # af.set_update { |state, value, weight| { sum: state[:sum] + value * weight, weight: state[:weight] + weight } }
53
+ # af.set_combine { |s1, s2| { sum: s1[:sum] + s2[:sum], weight: s1[:weight] + s2[:weight] } }
54
+ # af.set_finalize { |state| state[:weight].zero? ? nil : state[:sum] / state[:weight] }
55
+ #
56
+ # con.register_aggregate_function(af)
8
57
  class AggregateFunction
9
58
  include FunctionTypeValidation
10
59
 
60
+ class << self
61
+ # Creates a new AggregateFunction in a single call.
62
+ #
63
+ # This is a convenience factory that builds and configures an
64
+ # AggregateFunction without requiring you to set each attribute
65
+ # separately.
66
+ #
67
+ # @param name [String] the SQL function name
68
+ # @param return_type [DuckDB::LogicalType | Symbol] the SQL return type
69
+ # @param params [Array<DuckDB::LogicalType | Symbol>] input parameter types
70
+ # (empty array for a zero-argument aggregate)
71
+ # @param init [#call] callable that returns the initial per-group state
72
+ # @param update [#call] callable that folds one row into the state;
73
+ # receives +state, *inputs+ and must return the updated state.
74
+ # Default: +->( state, *) { state }+ (ignore inputs)
75
+ # @param combine [#call] callable that merges two partial states;
76
+ # receives +source_state, target_state+ and must return the merged
77
+ # state. Default: +->(state, _other) { state }+ (keep source only —
78
+ # only correct for single-threaded execution)
79
+ # @param finalize [#call] callable that converts the final state into the
80
+ # SQL result value; receives +state+ and must return a value compatible
81
+ # with +return_type+.
82
+ # Default: +->(state) { state }+ (return state as-is)
83
+ # @param null_handling [Boolean] when +true+, enables special NULL
84
+ # handling so that rows with NULL inputs are passed to +update+ as
85
+ # +nil+ instead of being skipped (default: +false+)
86
+ # @return [DuckDB::AggregateFunction] the configured aggregate function,
87
+ # ready to be passed to +Connection#register_aggregate_function+
88
+ # @raise [ArgumentError] if any of +init+, +update+, +combine+, or
89
+ # +finalize+ does not respond to +call+
90
+ #
91
+ # == Example: custom SUM
92
+ #
93
+ # af = DuckDB::AggregateFunction.create(
94
+ # name: 'my_sum',
95
+ # return_type: :bigint,
96
+ # params: [:bigint],
97
+ # init: -> { 0 },
98
+ # update: ->(state, value) { state + value },
99
+ # combine: ->(state, other) { state + other }
100
+ # )
101
+ # con.register_aggregate_function(af)
102
+ # con.query('SELECT my_sum(i) FROM range(100) t(i)').first.first # => 4950
103
+ #
104
+ # == Example: count including NULL values
105
+ #
106
+ # af = DuckDB::AggregateFunction.create(
107
+ # name: 'count_with_nulls',
108
+ # return_type: :bigint,
109
+ # params: [:bigint],
110
+ # init: -> { 0 },
111
+ # update: ->(state, _value) { state + 1 },
112
+ # combine: ->(state, other) { state + other },
113
+ # null_handling: true
114
+ # )
115
+ def create( # rubocop:disable Metrics/MethodLength, Metrics/ParameterLists, Metrics/AbcSize
116
+ name:,
117
+ return_type:,
118
+ params: [], # rubocop:disable Style/KeywordParametersOrder
119
+ init:,
120
+ update: ->(state, *_inputs) { state },
121
+ combine: ->(state, _other_state) { state },
122
+ finalize: ->(state) { state },
123
+ null_handling: false
124
+ )
125
+ callable!(:init, init)
126
+ callable!(:update, update)
127
+ callable!(:combine, combine)
128
+ callable!(:finalize, finalize)
129
+
130
+ af = AggregateFunction.new
131
+ af.name = name
132
+ af.return_type = return_type
133
+ params.each do |param|
134
+ af.add_parameter(param)
135
+ end
136
+ af.set_init { init.call }
137
+ af.set_update { |state, *inputs| update.call(state, *inputs) }
138
+ af.set_combine { |state, other_state| combine.call(state, other_state) }
139
+ af.set_finalize { |state| finalize.call(state) }
140
+ af.set_special_handling if null_handling
141
+ af
142
+ end
143
+
144
+ private
145
+
146
+ def callable!(name, arg)
147
+ raise ArgumentError, "#{name} must respond to `call`" unless arg.respond_to?(:call)
148
+ end
149
+ end
150
+
11
151
  # Sets the return type for the aggregate function.
12
152
  #
13
153
  # @param logical_type [DuckDB::LogicalType | :logical_type_symbol] the return type
@@ -30,6 +170,65 @@ module DuckDB
30
170
  _add_parameter(logical_type)
31
171
  end
32
172
 
173
+ # Sets the block that initialises the per-group state.
174
+ # The block takes no arguments and returns the initial state value.
175
+ # This is the only required callback; defaults for +set_update+,
176
+ # +set_combine+, and +set_finalize+ are injected automatically on the
177
+ # first call if those methods have not been called explicitly.
178
+ #
179
+ # @note The injected default for +set_combine+ is +{ |s1, _s2| s1 }+, which
180
+ # is only correct for single-threaded execution. Always call +set_combine+
181
+ # explicitly when the aggregate must be parallel-safe.
182
+ #
183
+ # @return [DuckDB::AggregateFunction] self
184
+ def set_init(&)
185
+ unless @init_set
186
+ _set_update { |state, *| state } unless @update_set
187
+ _set_combine { |s1, _s2| s1 } unless @combine_set
188
+ _set_finalize { |x| x } unless @finalize_set
189
+ end
190
+ _set_init(&)
191
+ @init_set = true
192
+ end
193
+
194
+ # Sets the block that accumulates one row into the state.
195
+ # The block receives the current state followed by the input column
196
+ # value(s) for that row, and must return the updated state.
197
+ # Default: +{ |state, *| state }+ (ignore inputs, keep state unchanged).
198
+ # May be called after +set_init+ to override the injected default.
199
+ #
200
+ # @return [DuckDB::AggregateFunction] self
201
+ def set_update(&)
202
+ @update_set = true
203
+ _set_update(&)
204
+ end
205
+
206
+ # Sets the block that merges two partial states during parallel execution.
207
+ # The block receives the source and target states and must return the
208
+ # merged state.
209
+ # May be called after +set_init+ to override the injected default.
210
+ #
211
+ # @note The default +{ |s1, _s2| s1 }+ is only correct for single-threaded
212
+ # execution. Supply an explicit combine block for parallel-safe aggregates.
213
+ #
214
+ # @return [DuckDB::AggregateFunction] self
215
+ def set_combine(&)
216
+ @combine_set = true
217
+ _set_combine(&)
218
+ end
219
+
220
+ # Sets the block that converts the final state into the SQL result value.
221
+ # The block receives the accumulated state and must return a value
222
+ # compatible with the declared +return_type+.
223
+ # Default: +{ |x| x }+ (return the state as-is).
224
+ # May be called after +set_init+ to override the injected default.
225
+ #
226
+ # @return [DuckDB::AggregateFunction] self
227
+ def set_finalize(&)
228
+ @finalize_set = true
229
+ _set_finalize(&)
230
+ end
231
+
33
232
  # Sets special NULL handling for the aggregate function.
34
233
  # By default DuckDB skips rows with NULL input values. Calling this
35
234
  # method disables that behaviour so the update callback is invoked even