duckdb 1.5.1.0 → 1.5.1.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.
- checksums.yaml +4 -4
- data/.github/workflows/test_on_macos.yml +1 -1
- data/.github/workflows/test_on_ubuntu.yml +1 -1
- data/CHANGELOG.md +12 -0
- data/Gemfile.lock +3 -3
- data/ext/duckdb/connection.c +23 -0
- data/ext/duckdb/converter.h +6 -0
- data/ext/duckdb/conveter.c +38 -0
- data/ext/duckdb/duckdb.c +1 -0
- data/ext/duckdb/logical_type.c +147 -0
- data/ext/duckdb/memory_helper.c +2 -10
- data/ext/duckdb/result.c +4 -21
- data/ext/duckdb/ruby-duckdb.h +1 -0
- data/ext/duckdb/scalar_function.c +60 -15
- data/ext/duckdb/scalar_function_set.c +86 -0
- data/ext/duckdb/scalar_function_set.h +14 -0
- data/ext/duckdb/util.c +16 -0
- data/ext/duckdb/util.h +1 -0
- data/ext/duckdb/value_impl.c +12 -0
- data/lib/duckdb/connection.rb +22 -0
- data/lib/duckdb/converter.rb +16 -7
- data/lib/duckdb/logical_type.rb +66 -0
- data/lib/duckdb/scalar_function.rb +17 -12
- data/lib/duckdb/scalar_function_set.rb +31 -0
- data/lib/duckdb/version.rb +1 -1
- data/lib/duckdb.rb +1 -0
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '0982b8680fcd79b493773dbbf39fd3f366fe11ccc952a8bfb4be0a140657e8fe'
|
|
4
|
+
data.tar.gz: 34cc46de733b8bc215197e5ceab3eb3022b0067ac34376b828ded5106d4b1261
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2b3043a60bbb99bd449f709befb3b84d1036d1e0643c9ed3a93eb50e88df48962095caa537f23881bfc9434a035b240230c8d81004b1d863742916baa6540902
|
|
7
|
+
data.tar.gz: 34ff46b0100769147684a4092005838ba418ab1907fc5b0690bd37f883007b0004e4838a5fdfd04ca276d157d91d39523aba74eced99133f46a8039d52b3c036
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
# Unreleased
|
|
6
6
|
|
|
7
|
+
# 1.5.1.1 - 2026-04-04
|
|
8
|
+
|
|
9
|
+
- fix `DuckDB::ScalarFunction` to allow `HUGEINT` and `UHUGEINT` as `return_type` and parameter type (the C extension's vector write path was missing those cases).
|
|
10
|
+
- add `DuckDB::ScalarFunctionSet` to register multiple overloads of a scalar function under one name (wraps `duckdb_scalar_function_set`).
|
|
11
|
+
- add `DuckDB::ScalarFunctionSet#add` to add a `DuckDB::ScalarFunction` overload to the set (wraps `duckdb_add_scalar_function_to_set`).
|
|
12
|
+
- add `DuckDB::Connection#register_scalar_function_set` to register a `DuckDB::ScalarFunctionSet` with the connection (wraps `duckdb_register_scalar_function_set`).
|
|
13
|
+
- `DuckDB::ScalarFunction.create` now accepts `name: nil` (optional) to allow creating nameless functions for use inside a `ScalarFunctionSet`.
|
|
14
|
+
- add `DuckDB::LogicalType.create_struct` to create a struct logical type.
|
|
15
|
+
- add `DuckDB::LogicalType.create_enum` to create an enum logical type.
|
|
16
|
+
- add `DuckDB::LogicalType.create_decimal` to create a decimal logical type.
|
|
17
|
+
|
|
7
18
|
# 1.5.1.0 - 2026-03-29
|
|
8
19
|
|
|
9
20
|
- add `DuckDB::ScalarFunction#varargs_type=` to register a scalar function that accepts a variable number of arguments of a given type (wraps `duckdb_scalar_function_set_varargs`).
|
|
@@ -17,6 +28,7 @@ All notable changes to this project will be documented in this file.
|
|
|
17
28
|
- add `DuckDB::ScalarFunction::BindInfo#get_argument(index)` to return the expression at the given argument index as a `DuckDB::Expression` object (wraps `duckdb_scalar_function_bind_get_argument`). Raises `ArgumentError` for out-of-range index.
|
|
18
29
|
- add `DuckDB::Expression#foldable?` to check whether an expression can be folded to a constant at query planning time (wraps `duckdb_expression_is_foldable`). Returns `true` for literals and constant arithmetic, `false` for column references and non-deterministic functions.
|
|
19
30
|
- add `DuckDB::LogicalType.create_map` to create a map logical type.
|
|
31
|
+
- add `DuckDB::LogicalType.create_union` to create a union logical type.
|
|
20
32
|
- bump duckdb to 1.5.1 on CI
|
|
21
33
|
- add `DuckDB::ScalarFunction::BindInfo#client_context` to return the client context of the bind phase as a `DuckDB::ClientContext` object (wraps `duckdb_scalar_function_get_client_context`).
|
|
22
34
|
- add `DuckDB::ClientContext#connection_id` to return the connection id of the client context (wraps `duckdb_client_context_get_connection_id`).
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
duckdb (1.5.1.
|
|
4
|
+
duckdb (1.5.1.1)
|
|
5
5
|
bigdecimal (>= 3.1.4)
|
|
6
6
|
|
|
7
7
|
GEM
|
|
@@ -14,10 +14,10 @@ GEM
|
|
|
14
14
|
json (2.19.3)
|
|
15
15
|
language_server-protocol (3.17.0.5)
|
|
16
16
|
lint_roller (1.1.0)
|
|
17
|
-
minitest (6.0.
|
|
17
|
+
minitest (6.0.3)
|
|
18
18
|
drb (~> 2.0)
|
|
19
19
|
prism (~> 1.5)
|
|
20
|
-
parallel (1.
|
|
20
|
+
parallel (1.28.0)
|
|
21
21
|
parser (3.3.11.1)
|
|
22
22
|
ast (~> 2.4.1)
|
|
23
23
|
racc
|
data/ext/duckdb/connection.c
CHANGED
|
@@ -12,6 +12,7 @@ static VALUE duckdb_connection_query_progress(VALUE self);
|
|
|
12
12
|
static VALUE duckdb_connection_connect(VALUE self, VALUE oDuckDBDatabase);
|
|
13
13
|
static VALUE duckdb_connection_query_sql(VALUE self, VALUE str);
|
|
14
14
|
static VALUE duckdb_connection_register_scalar_function(VALUE self, VALUE scalar_function);
|
|
15
|
+
static VALUE duckdb_connection_register_scalar_function_set(VALUE self, VALUE scalar_function_set);
|
|
15
16
|
static VALUE duckdb_connection_register_table_function(VALUE self, VALUE table_function);
|
|
16
17
|
|
|
17
18
|
static const rb_data_type_t connection_data_type = {
|
|
@@ -220,6 +221,27 @@ static VALUE duckdb_connection_register_scalar_function(VALUE self, VALUE scalar
|
|
|
220
221
|
return self;
|
|
221
222
|
}
|
|
222
223
|
|
|
224
|
+
/* :nodoc: */
|
|
225
|
+
static VALUE duckdb_connection_register_scalar_function_set(VALUE self, VALUE scalar_function_set) {
|
|
226
|
+
rubyDuckDBConnection *ctxcon;
|
|
227
|
+
rubyDuckDBScalarFunctionSet *ctxsfs;
|
|
228
|
+
duckdb_state state;
|
|
229
|
+
|
|
230
|
+
ctxcon = get_struct_connection(self);
|
|
231
|
+
ctxsfs = get_struct_scalar_function_set(scalar_function_set);
|
|
232
|
+
|
|
233
|
+
state = duckdb_register_scalar_function_set(ctxcon->con, ctxsfs->scalar_function_set);
|
|
234
|
+
|
|
235
|
+
if (state == DuckDBError) {
|
|
236
|
+
rb_raise(eDuckDBError, "Failed to register scalar function set");
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/* Keep reference to prevent GC while connection is alive */
|
|
240
|
+
rb_ary_push(ctxcon->registered_functions, scalar_function_set);
|
|
241
|
+
|
|
242
|
+
return self;
|
|
243
|
+
}
|
|
244
|
+
|
|
223
245
|
static VALUE duckdb_connection_register_table_function(VALUE self, VALUE table_function) {
|
|
224
246
|
rubyDuckDBConnection *ctxcon;
|
|
225
247
|
rubyDuckDBTableFunction *ctxtf;
|
|
@@ -251,6 +273,7 @@ void rbduckdb_init_duckdb_connection(void) {
|
|
|
251
273
|
rb_define_method(cDuckDBConnection, "interrupt", duckdb_connection_interrupt, 0);
|
|
252
274
|
rb_define_method(cDuckDBConnection, "query_progress", duckdb_connection_query_progress, 0);
|
|
253
275
|
rb_define_private_method(cDuckDBConnection, "_register_scalar_function", duckdb_connection_register_scalar_function, 1);
|
|
276
|
+
rb_define_private_method(cDuckDBConnection, "_register_scalar_function_set", duckdb_connection_register_scalar_function_set, 1);
|
|
254
277
|
rb_define_private_method(cDuckDBConnection, "_register_table_function", duckdb_connection_register_table_function, 1);
|
|
255
278
|
rb_define_private_method(cDuckDBConnection, "_connect", duckdb_connection_connect, 1);
|
|
256
279
|
/* TODO: query_sql => _query_sql */
|
data/ext/duckdb/converter.h
CHANGED
|
@@ -8,6 +8,7 @@ extern ID id__to_interval_from_vector;
|
|
|
8
8
|
extern ID id__to_hugeint_from_vector;
|
|
9
9
|
extern ID id__to_decimal_from_hugeint;
|
|
10
10
|
extern ID id__to_uuid_from_vector;
|
|
11
|
+
extern ID id__to_uuid_from_uhugeint;
|
|
11
12
|
extern ID id__to_time_from_duckdb_timestamp_s;
|
|
12
13
|
extern ID id__to_time_from_duckdb_timestamp_ms;
|
|
13
14
|
extern ID id__to_time_from_duckdb_timestamp_ns;
|
|
@@ -15,6 +16,11 @@ extern ID id__to_time_from_duckdb_time_tz;
|
|
|
15
16
|
extern ID id__to_time_from_duckdb_timestamp_tz;
|
|
16
17
|
extern ID id__to_infinity;
|
|
17
18
|
|
|
19
|
+
VALUE rbduckdb_uuid_to_ruby(duckdb_hugeint h);
|
|
20
|
+
VALUE rbduckdb_uuid_uhugeint_to_ruby(duckdb_uhugeint h);
|
|
21
|
+
VALUE rbduckdb_interval_to_ruby(duckdb_interval i);
|
|
22
|
+
VALUE rbduckdb_hugeint_to_ruby(duckdb_hugeint h);
|
|
23
|
+
VALUE rbduckdb_uhugeint_to_ruby(duckdb_uhugeint h);
|
|
18
24
|
VALUE rbduckdb_timestamp_s_to_ruby(duckdb_timestamp_s ts);
|
|
19
25
|
VALUE rbduckdb_timestamp_ms_to_ruby(duckdb_timestamp_ms ts);
|
|
20
26
|
VALUE rbduckdb_timestamp_ns_to_ruby(duckdb_timestamp_ns ts);
|
data/ext/duckdb/conveter.c
CHANGED
|
@@ -9,6 +9,7 @@ ID id__to_interval_from_vector;
|
|
|
9
9
|
ID id__to_hugeint_from_vector;
|
|
10
10
|
ID id__to_decimal_from_hugeint;
|
|
11
11
|
ID id__to_uuid_from_vector;
|
|
12
|
+
ID id__to_uuid_from_uhugeint;
|
|
12
13
|
ID id__to_time_from_duckdb_timestamp_s;
|
|
13
14
|
ID id__to_time_from_duckdb_timestamp_ms;
|
|
14
15
|
ID id__to_time_from_duckdb_timestamp_ns;
|
|
@@ -61,6 +62,42 @@ VALUE infinite_timestamp_ns_value(duckdb_timestamp_ns timestamp_ns) {
|
|
|
61
62
|
return Qnil;
|
|
62
63
|
}
|
|
63
64
|
|
|
65
|
+
VALUE rbduckdb_uuid_to_ruby(duckdb_hugeint h) {
|
|
66
|
+
return rb_funcall(mDuckDBConverter, id__to_uuid_from_vector, 2,
|
|
67
|
+
ULL2NUM(h.lower),
|
|
68
|
+
LL2NUM(h.upper)
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
VALUE rbduckdb_uuid_uhugeint_to_ruby(duckdb_uhugeint h) {
|
|
73
|
+
return rb_funcall(mDuckDBConverter, id__to_uuid_from_uhugeint, 2,
|
|
74
|
+
ULL2NUM(h.lower),
|
|
75
|
+
ULL2NUM(h.upper)
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
VALUE rbduckdb_interval_to_ruby(duckdb_interval i) {
|
|
80
|
+
return rb_funcall(mDuckDBConverter, id__to_interval_from_vector, 3,
|
|
81
|
+
INT2NUM(i.months),
|
|
82
|
+
INT2NUM(i.days),
|
|
83
|
+
LL2NUM(i.micros)
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
VALUE rbduckdb_hugeint_to_ruby(duckdb_hugeint h) {
|
|
88
|
+
return rb_funcall(mDuckDBConverter, id__to_hugeint_from_vector, 2,
|
|
89
|
+
ULL2NUM(h.lower),
|
|
90
|
+
LL2NUM(h.upper)
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
VALUE rbduckdb_uhugeint_to_ruby(duckdb_uhugeint h) {
|
|
95
|
+
return rb_funcall(mDuckDBConverter, id__to_hugeint_from_vector, 2,
|
|
96
|
+
ULL2NUM(h.lower),
|
|
97
|
+
ULL2NUM(h.upper)
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
64
101
|
VALUE rbduckdb_timestamp_s_to_ruby(duckdb_timestamp_s ts) {
|
|
65
102
|
VALUE obj = infinite_timestamp_s_value(ts);
|
|
66
103
|
if (obj != Qnil) {
|
|
@@ -160,6 +197,7 @@ void rbduckdb_init_duckdb_converter(void) {
|
|
|
160
197
|
id__to_hugeint_from_vector = rb_intern("_to_hugeint_from_vector");
|
|
161
198
|
id__to_decimal_from_hugeint = rb_intern("_to_decimal_from_hugeint");
|
|
162
199
|
id__to_uuid_from_vector = rb_intern("_to_uuid_from_vector");
|
|
200
|
+
id__to_uuid_from_uhugeint = rb_intern("_to_uuid_from_uhugeint");
|
|
163
201
|
id__to_time_from_duckdb_timestamp_s = rb_intern("_to_time_from_duckdb_timestamp_s");
|
|
164
202
|
id__to_time_from_duckdb_timestamp_ms = rb_intern("_to_time_from_duckdb_timestamp_ms");
|
|
165
203
|
id__to_time_from_duckdb_timestamp_ns = rb_intern("_to_time_from_duckdb_timestamp_ns");
|
data/ext/duckdb/duckdb.c
CHANGED
|
@@ -57,6 +57,7 @@ Init_duckdb_native(void) {
|
|
|
57
57
|
rbduckdb_init_duckdb_instance_cache();
|
|
58
58
|
rbduckdb_init_duckdb_value_impl();
|
|
59
59
|
rbduckdb_init_duckdb_scalar_function();
|
|
60
|
+
rbduckdb_init_duckdb_scalar_function_set();
|
|
60
61
|
rbduckdb_init_duckdb_expression();
|
|
61
62
|
rbduckdb_init_duckdb_client_context();
|
|
62
63
|
rbduckdb_init_duckdb_scalar_function_bind_info();
|
data/ext/duckdb/logical_type.c
CHANGED
|
@@ -26,6 +26,8 @@ static VALUE duckdb_logical_type__set_alias(VALUE self, VALUE aname);
|
|
|
26
26
|
static VALUE duckdb_logical_type_s_create_array_type(VALUE klass, VALUE child, VALUE array_size);
|
|
27
27
|
static VALUE duckdb_logical_type_s_create_list_type(VALUE klass, VALUE child);
|
|
28
28
|
static VALUE duckdb_logical_type_s_create_map_type(VALUE klass, VALUE key, VALUE value);
|
|
29
|
+
static VALUE duckdb_logical_type_s_create_union_type(VALUE klass, VALUE members);
|
|
30
|
+
static VALUE duckdb_logical_type_s_create_struct_type(VALUE klass, VALUE members);
|
|
29
31
|
static VALUE initialize(VALUE self, VALUE type_id_arg);
|
|
30
32
|
|
|
31
33
|
static const rb_data_type_t logical_type_data_type = {
|
|
@@ -490,6 +492,143 @@ static VALUE duckdb_logical_type_s_create_map_type(VALUE klass, VALUE key, VALUE
|
|
|
490
492
|
return rbduckdb_create_logical_type(new_type);
|
|
491
493
|
}
|
|
492
494
|
|
|
495
|
+
/*
|
|
496
|
+
* call-seq:
|
|
497
|
+
* DuckDB::LogicalType._create_union_type(members) -> DuckDB::LogicalType
|
|
498
|
+
*
|
|
499
|
+
* Return a union logical type from the given member hash.
|
|
500
|
+
*/
|
|
501
|
+
static VALUE duckdb_logical_type_s_create_union_type(VALUE klass, VALUE members) {
|
|
502
|
+
idx_t member_size = RHASH_SIZE(members);
|
|
503
|
+
duckdb_logical_type *member_types = NULL;
|
|
504
|
+
const char **member_names = NULL;
|
|
505
|
+
duckdb_logical_type new_type;
|
|
506
|
+
VALUE keys;
|
|
507
|
+
|
|
508
|
+
if (member_size == 0) {
|
|
509
|
+
rb_raise(rb_eArgError, "members hash must not be empty");
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
member_types = (duckdb_logical_type *)xcalloc(member_size, sizeof(duckdb_logical_type));
|
|
513
|
+
member_names = (const char **)xcalloc(member_size, sizeof(const char *));
|
|
514
|
+
|
|
515
|
+
keys = rb_funcall(members, rb_intern("keys"), 0);
|
|
516
|
+
|
|
517
|
+
for (idx_t i = 0; i < member_size; i++) {
|
|
518
|
+
VALUE key = rb_ary_entry(keys, (long)i);
|
|
519
|
+
VALUE val = rb_hash_aref(members, key);
|
|
520
|
+
rubyDuckDBLogicalType *type_ctx = get_struct_logical_type(val);
|
|
521
|
+
|
|
522
|
+
member_names[i] = rb_id2name(SYM2ID(key));
|
|
523
|
+
member_types[i] = type_ctx->logical_type;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
new_type = duckdb_create_union_type(member_types, member_names, member_size);
|
|
527
|
+
|
|
528
|
+
xfree(member_types);
|
|
529
|
+
xfree(member_names);
|
|
530
|
+
|
|
531
|
+
if (!new_type) {
|
|
532
|
+
rb_raise(eDuckDBError, "Failed to create union type");
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
return rbduckdb_create_logical_type(new_type);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/*
|
|
539
|
+
* call-seq:
|
|
540
|
+
* DuckDB::LogicalType._create_struct_type(members) -> DuckDB::LogicalType
|
|
541
|
+
*
|
|
542
|
+
* Return a struct logical type from the given member hash.
|
|
543
|
+
*/
|
|
544
|
+
static VALUE duckdb_logical_type_s_create_struct_type(VALUE klass, VALUE members) {
|
|
545
|
+
idx_t member_size = RHASH_SIZE(members);
|
|
546
|
+
duckdb_logical_type *member_types = NULL;
|
|
547
|
+
const char **member_names = NULL;
|
|
548
|
+
duckdb_logical_type new_type;
|
|
549
|
+
VALUE keys;
|
|
550
|
+
|
|
551
|
+
if (member_size == 0) {
|
|
552
|
+
rb_raise(rb_eArgError, "members hash must not be empty");
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
member_types = (duckdb_logical_type *)xcalloc(member_size, sizeof(duckdb_logical_type));
|
|
556
|
+
member_names = (const char **)xcalloc(member_size, sizeof(const char *));
|
|
557
|
+
|
|
558
|
+
keys = rb_funcall(members, rb_intern("keys"), 0);
|
|
559
|
+
|
|
560
|
+
for (idx_t i = 0; i < member_size; i++) {
|
|
561
|
+
VALUE key = rb_ary_entry(keys, (long)i);
|
|
562
|
+
VALUE val = rb_hash_aref(members, key);
|
|
563
|
+
rubyDuckDBLogicalType *type_ctx = get_struct_logical_type(val);
|
|
564
|
+
|
|
565
|
+
member_names[i] = rb_id2name(SYM2ID(key));
|
|
566
|
+
member_types[i] = type_ctx->logical_type;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
new_type = duckdb_create_struct_type(member_types, member_names, member_size);
|
|
570
|
+
|
|
571
|
+
xfree(member_types);
|
|
572
|
+
xfree(member_names);
|
|
573
|
+
|
|
574
|
+
if (!new_type) {
|
|
575
|
+
rb_raise(eDuckDBError, "Failed to create struct type");
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
return rbduckdb_create_logical_type(new_type);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
/*
|
|
582
|
+
* call-seq:
|
|
583
|
+
* DuckDB::LogicalType._create_enum_type(members) -> DuckDB::LogicalType
|
|
584
|
+
*
|
|
585
|
+
* Return an enum logical type from the given array of strings.
|
|
586
|
+
*/
|
|
587
|
+
static VALUE duckdb_logical_type_s_create_enum_type(VALUE klass, VALUE members) {
|
|
588
|
+
idx_t member_size = RARRAY_LEN(members);
|
|
589
|
+
const char **member_names = NULL;
|
|
590
|
+
duckdb_logical_type new_type;
|
|
591
|
+
|
|
592
|
+
if (member_size == 0) {
|
|
593
|
+
rb_raise(rb_eArgError, "members must not be empty");
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
member_names = (const char **)xcalloc(member_size, sizeof(const char *));
|
|
597
|
+
|
|
598
|
+
for (idx_t i = 0; i < member_size; i++) {
|
|
599
|
+
VALUE val = rb_ary_entry(members, (long)i);
|
|
600
|
+
member_names[i] = StringValueCStr(val);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
new_type = duckdb_create_enum_type(member_names, member_size);
|
|
604
|
+
|
|
605
|
+
xfree(member_names);
|
|
606
|
+
|
|
607
|
+
if (!new_type) {
|
|
608
|
+
rb_raise(eDuckDBError, "Failed to create enum type");
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
return rbduckdb_create_logical_type(new_type);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/*
|
|
615
|
+
* call-seq:
|
|
616
|
+
* DuckDB::LogicalType._create_decimal_type(width, scale) -> DuckDB::LogicalType
|
|
617
|
+
*
|
|
618
|
+
* Return a decimal logical type with the given width and scale.
|
|
619
|
+
*/
|
|
620
|
+
static VALUE duckdb_logical_type_s_create_decimal_type(VALUE klass, VALUE width, VALUE scale) {
|
|
621
|
+
duckdb_logical_type new_type;
|
|
622
|
+
|
|
623
|
+
new_type = duckdb_create_decimal_type((uint8_t)NUM2UINT(width), (uint8_t)NUM2UINT(scale));
|
|
624
|
+
|
|
625
|
+
if (!new_type) {
|
|
626
|
+
rb_raise(eDuckDBError, "Failed to create decimal type");
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
return rbduckdb_create_logical_type(new_type);
|
|
630
|
+
}
|
|
631
|
+
|
|
493
632
|
VALUE rbduckdb_create_logical_type(duckdb_logical_type logical_type) {
|
|
494
633
|
VALUE obj;
|
|
495
634
|
rubyDuckDBLogicalType *ctx;
|
|
@@ -534,6 +673,14 @@ void rbduckdb_init_duckdb_logical_type(void) {
|
|
|
534
673
|
duckdb_logical_type_s_create_list_type, 1);
|
|
535
674
|
rb_define_private_method(rb_singleton_class(cDuckDBLogicalType), "_create_map_type",
|
|
536
675
|
duckdb_logical_type_s_create_map_type, 2);
|
|
676
|
+
rb_define_private_method(rb_singleton_class(cDuckDBLogicalType), "_create_union_type",
|
|
677
|
+
duckdb_logical_type_s_create_union_type, 1);
|
|
678
|
+
rb_define_private_method(rb_singleton_class(cDuckDBLogicalType), "_create_struct_type",
|
|
679
|
+
duckdb_logical_type_s_create_struct_type, 1);
|
|
680
|
+
rb_define_private_method(rb_singleton_class(cDuckDBLogicalType), "_create_enum_type",
|
|
681
|
+
duckdb_logical_type_s_create_enum_type, 1);
|
|
682
|
+
rb_define_private_method(rb_singleton_class(cDuckDBLogicalType), "_create_decimal_type",
|
|
683
|
+
duckdb_logical_type_s_create_decimal_type, 2);
|
|
537
684
|
|
|
538
685
|
rb_define_method(cDuckDBLogicalType, "initialize", initialize, 1);
|
|
539
686
|
}
|
data/ext/duckdb/memory_helper.c
CHANGED
|
@@ -251,22 +251,14 @@ static VALUE rbduckdb_memory_helper_write_timestamp(VALUE self, VALUE ptr, VALUE
|
|
|
251
251
|
(void)self;
|
|
252
252
|
|
|
253
253
|
if (!rb_obj_is_kind_of(value, rb_cTime)) {
|
|
254
|
-
rb_raise(rb_eTypeError, "Expected Time object
|
|
254
|
+
rb_raise(rb_eTypeError, "Expected Time object");
|
|
255
255
|
}
|
|
256
256
|
|
|
257
257
|
data = (duckdb_timestamp *)NUM2ULL(ptr);
|
|
258
258
|
idx = (idx_t)NUM2ULL(index);
|
|
259
259
|
|
|
260
260
|
VALUE local_time = rb_funcall(value, rb_intern("getlocal"), 0);
|
|
261
|
-
data[idx] =
|
|
262
|
-
rb_funcall(local_time, rb_intern("year"), 0),
|
|
263
|
-
rb_funcall(local_time, rb_intern("month"), 0),
|
|
264
|
-
rb_funcall(local_time, rb_intern("day"), 0),
|
|
265
|
-
rb_funcall(local_time, rb_intern("hour"), 0),
|
|
266
|
-
rb_funcall(local_time, rb_intern("min"), 0),
|
|
267
|
-
rb_funcall(local_time, rb_intern("sec"), 0),
|
|
268
|
-
rb_funcall(local_time, rb_intern("usec"), 0)
|
|
269
|
-
);
|
|
261
|
+
data[idx] = rbduckdb_to_duckdb_timestamp_from_time_value(local_time);
|
|
270
262
|
|
|
271
263
|
return Qnil;
|
|
272
264
|
}
|
data/ext/duckdb/result.c
CHANGED
|
@@ -287,12 +287,7 @@ static VALUE vector_time(void* vector_data, idx_t row_idx) {
|
|
|
287
287
|
|
|
288
288
|
|
|
289
289
|
static VALUE vector_interval(void* vector_data, idx_t row_idx) {
|
|
290
|
-
|
|
291
|
-
return rb_funcall(mDuckDBConverter, id__to_interval_from_vector, 3,
|
|
292
|
-
INT2NUM(data.months),
|
|
293
|
-
INT2NUM(data.days),
|
|
294
|
-
LL2NUM(data.micros)
|
|
295
|
-
);
|
|
290
|
+
return rbduckdb_interval_to_ruby(((duckdb_interval *)vector_data)[row_idx]);
|
|
296
291
|
}
|
|
297
292
|
|
|
298
293
|
static VALUE vector_blob(void* vector_data, idx_t row_idx) {
|
|
@@ -314,19 +309,11 @@ static VALUE vector_varchar(void* vector_data, idx_t row_idx) {
|
|
|
314
309
|
}
|
|
315
310
|
|
|
316
311
|
static VALUE vector_hugeint(void* vector_data, idx_t row_idx) {
|
|
317
|
-
|
|
318
|
-
return rb_funcall(mDuckDBConverter, id__to_hugeint_from_vector, 2,
|
|
319
|
-
ULL2NUM(hugeint.lower),
|
|
320
|
-
LL2NUM(hugeint.upper)
|
|
321
|
-
);
|
|
312
|
+
return rbduckdb_hugeint_to_ruby(((duckdb_hugeint *)vector_data)[row_idx]);
|
|
322
313
|
}
|
|
323
314
|
|
|
324
315
|
static VALUE vector_uhugeint(void* vector_data, idx_t row_idx) {
|
|
325
|
-
|
|
326
|
-
return rb_funcall(mDuckDBConverter, id__to_hugeint_from_vector, 2,
|
|
327
|
-
ULL2NUM(uhugeint.lower),
|
|
328
|
-
ULL2NUM(uhugeint.upper)
|
|
329
|
-
);
|
|
316
|
+
return rbduckdb_uhugeint_to_ruby(((duckdb_uhugeint *)vector_data)[row_idx]);
|
|
330
317
|
}
|
|
331
318
|
|
|
332
319
|
static VALUE vector_decimal(duckdb_logical_type ty, void* vector_data, idx_t row_idx) {
|
|
@@ -696,11 +683,7 @@ static VALUE vector_timestamp_tz(void* vector_data, idx_t row_idx) {
|
|
|
696
683
|
}
|
|
697
684
|
|
|
698
685
|
static VALUE vector_uuid(void* vector_data, idx_t row_idx) {
|
|
699
|
-
|
|
700
|
-
return rb_funcall(mDuckDBConverter, id__to_uuid_from_vector, 2,
|
|
701
|
-
ULL2NUM(hugeint.lower),
|
|
702
|
-
LL2NUM(hugeint.upper)
|
|
703
|
-
);
|
|
686
|
+
return rbduckdb_uuid_to_ruby(((duckdb_hugeint *)vector_data)[row_idx]);
|
|
704
687
|
}
|
|
705
688
|
|
|
706
689
|
static VALUE vector_value(duckdb_vector vector, idx_t row_idx) {
|
data/ext/duckdb/ruby-duckdb.h
CHANGED
|
@@ -589,6 +589,20 @@ static void vector_set_value_at(duckdb_vector vector, duckdb_logical_type elemen
|
|
|
589
589
|
case DUCKDB_TYPE_UBIGINT:
|
|
590
590
|
((uint64_t *)vector_data)[index] = NUM2ULL(value);
|
|
591
591
|
break;
|
|
592
|
+
case DUCKDB_TYPE_HUGEINT: {
|
|
593
|
+
duckdb_hugeint hugeint;
|
|
594
|
+
hugeint.lower = NUM2ULL(rb_funcall(mDuckDBConverter, rb_intern("_hugeint_lower"), 1, value));
|
|
595
|
+
hugeint.upper = NUM2LL(rb_funcall(mDuckDBConverter, rb_intern("_hugeint_upper"), 1, value));
|
|
596
|
+
((duckdb_hugeint *)vector_data)[index] = hugeint;
|
|
597
|
+
break;
|
|
598
|
+
}
|
|
599
|
+
case DUCKDB_TYPE_UHUGEINT: {
|
|
600
|
+
duckdb_uhugeint uhugeint;
|
|
601
|
+
uhugeint.lower = NUM2ULL(rb_funcall(mDuckDBConverter, rb_intern("_hugeint_lower"), 1, value));
|
|
602
|
+
uhugeint.upper = NUM2ULL(rb_funcall(mDuckDBConverter, rb_intern("_hugeint_upper"), 1, value));
|
|
603
|
+
((duckdb_uhugeint *)vector_data)[index] = uhugeint;
|
|
604
|
+
break;
|
|
605
|
+
}
|
|
592
606
|
case DUCKDB_TYPE_FLOAT:
|
|
593
607
|
((float *)vector_data)[index] = (float)NUM2DBL(value);
|
|
594
608
|
break;
|
|
@@ -612,21 +626,33 @@ static void vector_set_value_at(duckdb_vector vector, duckdb_logical_type elemen
|
|
|
612
626
|
break;
|
|
613
627
|
}
|
|
614
628
|
case DUCKDB_TYPE_TIMESTAMP: {
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
629
|
+
duckdb_timestamp ts = rbduckdb_to_duckdb_timestamp_from_time_value(value);
|
|
630
|
+
((duckdb_timestamp *)vector_data)[index] = ts;
|
|
631
|
+
break;
|
|
632
|
+
}
|
|
633
|
+
case DUCKDB_TYPE_TIMESTAMP_S: {
|
|
634
|
+
duckdb_timestamp ts = rbduckdb_to_duckdb_timestamp_from_time_value(value);
|
|
635
|
+
duckdb_timestamp_s ts_s;
|
|
636
|
+
ts_s.seconds = ts.micros / 1000000;
|
|
637
|
+
((duckdb_timestamp_s *)vector_data)[index] = ts_s;
|
|
638
|
+
break;
|
|
639
|
+
}
|
|
640
|
+
case DUCKDB_TYPE_TIMESTAMP_MS: {
|
|
641
|
+
duckdb_timestamp ts = rbduckdb_to_duckdb_timestamp_from_time_value(value);
|
|
642
|
+
duckdb_timestamp_ms ts_ms;
|
|
643
|
+
ts_ms.millis = ts.micros / 1000;
|
|
644
|
+
((duckdb_timestamp_ms *)vector_data)[index] = ts_ms;
|
|
645
|
+
break;
|
|
646
|
+
}
|
|
647
|
+
case DUCKDB_TYPE_TIMESTAMP_NS: {
|
|
648
|
+
duckdb_timestamp ts = rbduckdb_to_duckdb_timestamp_from_time_value(value);
|
|
649
|
+
duckdb_timestamp_ns ts_ns;
|
|
650
|
+
ts_ns.nanos = ts.micros * 1000;
|
|
651
|
+
((duckdb_timestamp_ns *)vector_data)[index] = ts_ns;
|
|
652
|
+
break;
|
|
653
|
+
}
|
|
654
|
+
case DUCKDB_TYPE_TIMESTAMP_TZ: {
|
|
655
|
+
duckdb_timestamp ts = rbduckdb_to_duckdb_timestamp_from_time_value(value);
|
|
630
656
|
((duckdb_timestamp *)vector_data)[index] = ts;
|
|
631
657
|
break;
|
|
632
658
|
}
|
|
@@ -660,6 +686,25 @@ static void vector_set_value_at(duckdb_vector vector, duckdb_logical_type elemen
|
|
|
660
686
|
((duckdb_time *)vector_data)[index] = time;
|
|
661
687
|
break;
|
|
662
688
|
}
|
|
689
|
+
case DUCKDB_TYPE_TIME_TZ: {
|
|
690
|
+
if (!rb_obj_is_kind_of(value, rb_cTime)) {
|
|
691
|
+
rb_raise(rb_eTypeError, "Expected Time object for TIME_TZ");
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
VALUE hour = rb_funcall(value, rb_intern("hour"), 0);
|
|
695
|
+
VALUE min = rb_funcall(value, rb_intern("min"), 0);
|
|
696
|
+
VALUE sec = rb_funcall(value, rb_intern("sec"), 0);
|
|
697
|
+
VALUE usec = rb_funcall(value, rb_intern("usec"), 0);
|
|
698
|
+
VALUE utc_offset = rb_funcall(value, rb_intern("utc_offset"), 0);
|
|
699
|
+
|
|
700
|
+
duckdb_time t = rbduckdb_to_duckdb_time_from_value(hour, min, sec, usec);
|
|
701
|
+
int64_t micros = t.micros;
|
|
702
|
+
int32_t offset = NUM2INT(utc_offset);
|
|
703
|
+
|
|
704
|
+
duckdb_time_tz time_tz = duckdb_create_time_tz(micros, offset);
|
|
705
|
+
((duckdb_time_tz *)vector_data)[index] = time_tz;
|
|
706
|
+
break;
|
|
707
|
+
}
|
|
663
708
|
default:
|
|
664
709
|
rb_raise(rb_eArgError, "Unsupported return type for scalar function");
|
|
665
710
|
break;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#include "ruby-duckdb.h"
|
|
2
|
+
|
|
3
|
+
VALUE cDuckDBScalarFunctionSet;
|
|
4
|
+
|
|
5
|
+
static void mark(void *);
|
|
6
|
+
static void deallocate(void *);
|
|
7
|
+
static VALUE allocate(VALUE klass);
|
|
8
|
+
static size_t memsize(const void *p);
|
|
9
|
+
static void compact(void *);
|
|
10
|
+
static VALUE rbduckdb_scalar_function_set__initialize(VALUE self, VALUE name);
|
|
11
|
+
static VALUE rbduckdb_scalar_function_set__add(VALUE self, VALUE scalar_function);
|
|
12
|
+
|
|
13
|
+
static const rb_data_type_t scalar_function_set_data_type = {
|
|
14
|
+
"DuckDB/ScalarFunctionSet",
|
|
15
|
+
{mark, deallocate, memsize, compact},
|
|
16
|
+
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
static void mark(void *ctx) {
|
|
20
|
+
rubyDuckDBScalarFunctionSet *p = (rubyDuckDBScalarFunctionSet *)ctx;
|
|
21
|
+
rb_gc_mark(p->functions);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
static void deallocate(void *ctx) {
|
|
25
|
+
rubyDuckDBScalarFunctionSet *p = (rubyDuckDBScalarFunctionSet *)ctx;
|
|
26
|
+
duckdb_destroy_scalar_function_set(&(p->scalar_function_set));
|
|
27
|
+
xfree(p);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
static void compact(void *ctx) {
|
|
31
|
+
rubyDuckDBScalarFunctionSet *p = (rubyDuckDBScalarFunctionSet *)ctx;
|
|
32
|
+
p->functions = rb_gc_location(p->functions);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
static VALUE allocate(VALUE klass) {
|
|
36
|
+
rubyDuckDBScalarFunctionSet *ctx = xcalloc((size_t)1, sizeof(rubyDuckDBScalarFunctionSet));
|
|
37
|
+
VALUE obj = TypedData_Wrap_Struct(klass, &scalar_function_set_data_type, ctx);
|
|
38
|
+
ctx->functions = rb_ary_new();
|
|
39
|
+
RB_GC_GUARD(ctx->functions);
|
|
40
|
+
return obj;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
static size_t memsize(const void *p) {
|
|
44
|
+
return sizeof(rubyDuckDBScalarFunctionSet);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
rubyDuckDBScalarFunctionSet *get_struct_scalar_function_set(VALUE obj) {
|
|
48
|
+
rubyDuckDBScalarFunctionSet *ctx;
|
|
49
|
+
TypedData_Get_Struct(obj, rubyDuckDBScalarFunctionSet, &scalar_function_set_data_type, ctx);
|
|
50
|
+
return ctx;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/* :nodoc: */
|
|
54
|
+
static VALUE rbduckdb_scalar_function_set__initialize(VALUE self, VALUE name) {
|
|
55
|
+
rubyDuckDBScalarFunctionSet *p;
|
|
56
|
+
|
|
57
|
+
TypedData_Get_Struct(self, rubyDuckDBScalarFunctionSet, &scalar_function_set_data_type, p);
|
|
58
|
+
p->scalar_function_set = duckdb_create_scalar_function_set(StringValueCStr(name));
|
|
59
|
+
return self;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* :nodoc: */
|
|
63
|
+
static VALUE rbduckdb_scalar_function_set__add(VALUE self, VALUE scalar_function) {
|
|
64
|
+
rubyDuckDBScalarFunctionSet *p;
|
|
65
|
+
rubyDuckDBScalarFunction *sf;
|
|
66
|
+
|
|
67
|
+
TypedData_Get_Struct(self, rubyDuckDBScalarFunctionSet, &scalar_function_set_data_type, p);
|
|
68
|
+
sf = get_struct_scalar_function(scalar_function);
|
|
69
|
+
|
|
70
|
+
if (duckdb_add_scalar_function_to_set(p->scalar_function_set, sf->scalar_function) == DuckDBError) {
|
|
71
|
+
rb_raise(eDuckDBError, "failed to add scalar function to set (duplicate overload?)");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
rb_ary_push(p->functions, scalar_function);
|
|
75
|
+
return self;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
void rbduckdb_init_duckdb_scalar_function_set(void) {
|
|
79
|
+
#if 0
|
|
80
|
+
VALUE mDuckDB = rb_define_module("DuckDB");
|
|
81
|
+
#endif
|
|
82
|
+
cDuckDBScalarFunctionSet = rb_define_class_under(mDuckDB, "ScalarFunctionSet", rb_cObject);
|
|
83
|
+
rb_define_alloc_func(cDuckDBScalarFunctionSet, allocate);
|
|
84
|
+
rb_define_private_method(cDuckDBScalarFunctionSet, "_initialize", rbduckdb_scalar_function_set__initialize, 1);
|
|
85
|
+
rb_define_private_method(cDuckDBScalarFunctionSet, "_add", rbduckdb_scalar_function_set__add, 1);
|
|
86
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#ifndef RUBY_DUCKDB_SCALAR_FUNCTION_SET_H
|
|
2
|
+
#define RUBY_DUCKDB_SCALAR_FUNCTION_SET_H
|
|
3
|
+
|
|
4
|
+
struct _rubyDuckDBScalarFunctionSet {
|
|
5
|
+
duckdb_scalar_function_set scalar_function_set;
|
|
6
|
+
VALUE functions; /* Ruby Array of ScalarFunction objects — prevents GC collection */
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
typedef struct _rubyDuckDBScalarFunctionSet rubyDuckDBScalarFunctionSet;
|
|
10
|
+
|
|
11
|
+
void rbduckdb_init_duckdb_scalar_function_set(void);
|
|
12
|
+
rubyDuckDBScalarFunctionSet *get_struct_scalar_function_set(VALUE obj);
|
|
13
|
+
|
|
14
|
+
#endif
|
data/ext/duckdb/util.c
CHANGED
|
@@ -21,6 +21,22 @@ duckdb_time rbduckdb_to_duckdb_time_from_value(VALUE hour, VALUE min, VALUE sec,
|
|
|
21
21
|
return duckdb_to_time(time_st);
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
duckdb_timestamp rbduckdb_to_duckdb_timestamp_from_time_value(VALUE time_obj) {
|
|
25
|
+
if (!rb_obj_is_kind_of(time_obj, rb_cTime)) {
|
|
26
|
+
rb_raise(rb_eTypeError, "Expected Time object");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return rbduckdb_to_duckdb_timestamp_from_value(
|
|
30
|
+
rb_funcall(time_obj, rb_intern("year"), 0),
|
|
31
|
+
rb_funcall(time_obj, rb_intern("month"), 0),
|
|
32
|
+
rb_funcall(time_obj, rb_intern("day"), 0),
|
|
33
|
+
rb_funcall(time_obj, rb_intern("hour"), 0),
|
|
34
|
+
rb_funcall(time_obj, rb_intern("min"), 0),
|
|
35
|
+
rb_funcall(time_obj, rb_intern("sec"), 0),
|
|
36
|
+
rb_funcall(time_obj, rb_intern("usec"), 0)
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
24
40
|
duckdb_timestamp rbduckdb_to_duckdb_timestamp_from_value(VALUE year, VALUE month, VALUE day, VALUE hour, VALUE min, VALUE sec, VALUE micros) {
|
|
25
41
|
duckdb_timestamp_struct timestamp_st;
|
|
26
42
|
|
data/ext/duckdb/util.h
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
duckdb_date rbduckdb_to_duckdb_date_from_value(VALUE year, VALUE month, VALUE day);
|
|
5
5
|
duckdb_time rbduckdb_to_duckdb_time_from_value(VALUE hour, VALUE min, VALUE sec, VALUE micros);
|
|
6
|
+
duckdb_timestamp rbduckdb_to_duckdb_timestamp_from_time_value(VALUE time_obj);
|
|
6
7
|
duckdb_timestamp rbduckdb_to_duckdb_timestamp_from_value(VALUE year, VALUE month, VALUE day, VALUE hour, VALUE min, VALUE sec, VALUE micros);
|
|
7
8
|
void rbduckdb_to_duckdb_interval_from_value(duckdb_interval* interval, VALUE months, VALUE days, VALUE micros);
|
|
8
9
|
|
data/ext/duckdb/value_impl.c
CHANGED
|
@@ -61,6 +61,12 @@ VALUE rbduckdb_duckdb_value_to_ruby(duckdb_value val) {
|
|
|
61
61
|
case DUCKDB_TYPE_BIGINT:
|
|
62
62
|
result = LL2NUM(duckdb_get_int64(val));
|
|
63
63
|
break;
|
|
64
|
+
case DUCKDB_TYPE_HUGEINT:
|
|
65
|
+
result = rbduckdb_hugeint_to_ruby(duckdb_get_hugeint(val));
|
|
66
|
+
break;
|
|
67
|
+
case DUCKDB_TYPE_UHUGEINT:
|
|
68
|
+
result = rbduckdb_uhugeint_to_ruby(duckdb_get_uhugeint(val));
|
|
69
|
+
break;
|
|
64
70
|
case DUCKDB_TYPE_UTINYINT:
|
|
65
71
|
result = INT2FIX(duckdb_get_uint8(val));
|
|
66
72
|
break;
|
|
@@ -88,6 +94,9 @@ VALUE rbduckdb_duckdb_value_to_ruby(duckdb_value val) {
|
|
|
88
94
|
case DUCKDB_TYPE_TIME:
|
|
89
95
|
result = rbduckdb_time_to_ruby(duckdb_get_time(val));
|
|
90
96
|
break;
|
|
97
|
+
case DUCKDB_TYPE_INTERVAL:
|
|
98
|
+
result = rbduckdb_interval_to_ruby(duckdb_get_interval(val));
|
|
99
|
+
break;
|
|
91
100
|
case DUCKDB_TYPE_TIMESTAMP_S:
|
|
92
101
|
result = rbduckdb_timestamp_s_to_ruby(duckdb_get_timestamp_s(val));
|
|
93
102
|
break;
|
|
@@ -108,6 +117,9 @@ VALUE rbduckdb_duckdb_value_to_ruby(duckdb_value val) {
|
|
|
108
117
|
result = rb_str_new_cstr(str);
|
|
109
118
|
duckdb_free(str);
|
|
110
119
|
break;
|
|
120
|
+
case DUCKDB_TYPE_UUID:
|
|
121
|
+
result = rbduckdb_uuid_uhugeint_to_ruby(duckdb_get_uuid(val));
|
|
122
|
+
break;
|
|
111
123
|
default:
|
|
112
124
|
result = Qnil;
|
|
113
125
|
break;
|
data/lib/duckdb/connection.rb
CHANGED
|
@@ -187,6 +187,28 @@ module DuckDB
|
|
|
187
187
|
_register_scalar_function(sf)
|
|
188
188
|
end
|
|
189
189
|
|
|
190
|
+
# Registers a scalar function set with the connection.
|
|
191
|
+
# A scalar function set groups multiple overloads of a function under one name,
|
|
192
|
+
# allowing DuckDB to dispatch to the correct implementation based on argument types.
|
|
193
|
+
#
|
|
194
|
+
# @param scalar_function_set [DuckDB::ScalarFunctionSet] the function set to register
|
|
195
|
+
# @return [void]
|
|
196
|
+
# @raise [TypeError] if argument is not a DuckDB::ScalarFunctionSet
|
|
197
|
+
#
|
|
198
|
+
# @example Register multiple overloads under one name
|
|
199
|
+
# add_int = DuckDB::ScalarFunction.create(return_type: :integer, parameter_types: %i[integer integer]) { |a, b| a + b }
|
|
200
|
+
# add_dbl = DuckDB::ScalarFunction.create(return_type: :double, parameter_types: %i[double double]) { |a, b| a + b }
|
|
201
|
+
# set = DuckDB::ScalarFunctionSet.new(:add)
|
|
202
|
+
# set.add(add_int).add(add_dbl)
|
|
203
|
+
# con.register_scalar_function_set(set)
|
|
204
|
+
def register_scalar_function_set(scalar_function_set)
|
|
205
|
+
unless scalar_function_set.is_a?(ScalarFunctionSet)
|
|
206
|
+
raise TypeError, "#{scalar_function_set.class} is not a DuckDB::ScalarFunctionSet"
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
_register_scalar_function_set(scalar_function_set)
|
|
210
|
+
end
|
|
211
|
+
|
|
190
212
|
#
|
|
191
213
|
# Registers a table function with the database connection.
|
|
192
214
|
#
|
data/lib/duckdb/converter.rb
CHANGED
|
@@ -10,6 +10,7 @@ module DuckDB
|
|
|
10
10
|
module Converter # :nodoc: all
|
|
11
11
|
HALF_HUGEINT_BIT = 64
|
|
12
12
|
HALF_HUGEINT = 1 << HALF_HUGEINT_BIT
|
|
13
|
+
LOWER_HUGEINT_MASK = HALF_HUGEINT - 1
|
|
13
14
|
FLIP_HUGEINT = 1 << 63
|
|
14
15
|
EPOCH = Time.local(1970, 1, 1)
|
|
15
16
|
EPOCH_UTC = Time.utc(1970, 1, 1)
|
|
@@ -93,16 +94,21 @@ module DuckDB
|
|
|
93
94
|
(upper << HALF_HUGEINT_BIT) + lower
|
|
94
95
|
end
|
|
95
96
|
|
|
97
|
+
def _hugeint_lower(value)
|
|
98
|
+
value & LOWER_HUGEINT_MASK
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def _hugeint_upper(value)
|
|
102
|
+
value >> HALF_HUGEINT_BIT
|
|
103
|
+
end
|
|
104
|
+
|
|
96
105
|
def _to_decimal_from_hugeint(width, scale, upper, lower = nil)
|
|
97
106
|
v = lower.nil? ? upper : _to_hugeint_from_vector(lower, upper)
|
|
98
107
|
_to_decimal_from_value(width, scale, v)
|
|
99
108
|
end
|
|
100
109
|
|
|
101
110
|
def _to_decimal_from_value(_width, scale, value)
|
|
102
|
-
|
|
103
|
-
v = v.rjust(scale + 1, '0') if v.length < scale
|
|
104
|
-
v[-scale, 0] = '.' if scale.positive?
|
|
105
|
-
BigDecimal(v)
|
|
111
|
+
BigDecimal("#{value}e-#{scale}")
|
|
106
112
|
end
|
|
107
113
|
|
|
108
114
|
def _to_interval_from_vector(months, days, micros)
|
|
@@ -117,6 +123,11 @@ module DuckDB
|
|
|
117
123
|
"#{str[0, 8]}-#{str[8, 4]}-#{str[12, 4]}-#{str[16, 4]}-#{str[20, 12]}"
|
|
118
124
|
end
|
|
119
125
|
|
|
126
|
+
def _to_uuid_from_uhugeint(lower, upper)
|
|
127
|
+
str = _to_hugeint_from_vector(lower, upper).to_s(16).rjust(32, '0')
|
|
128
|
+
"#{str[0, 8]}-#{str[8, 4]}-#{str[12, 4]}-#{str[16, 4]}-#{str[20, 12]}"
|
|
129
|
+
end
|
|
130
|
+
|
|
120
131
|
def _parse_date(value)
|
|
121
132
|
case value
|
|
122
133
|
when Date, Time
|
|
@@ -177,9 +188,7 @@ module DuckDB
|
|
|
177
188
|
def integer_to_hugeint(value)
|
|
178
189
|
case value
|
|
179
190
|
when Integer
|
|
180
|
-
|
|
181
|
-
lower = value - (upper << HALF_HUGEINT_BIT)
|
|
182
|
-
[lower, upper]
|
|
191
|
+
[_hugeint_lower(value), _hugeint_upper(value)]
|
|
183
192
|
else
|
|
184
193
|
raise(ArgumentError, "The argument `#{value.inspect}` must be Integer.")
|
|
185
194
|
end
|
data/lib/duckdb/logical_type.rb
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module DuckDB
|
|
4
4
|
class LogicalType # rubocop:disable Metrics/ClassLength
|
|
5
|
+
RANGE_DECIMAL_WIDTH = 1..38
|
|
6
|
+
|
|
5
7
|
alias :alias get_alias
|
|
6
8
|
alias :alias= set_alias
|
|
7
9
|
|
|
@@ -38,6 +40,7 @@ module DuckDB
|
|
|
38
40
|
# array: 33,
|
|
39
41
|
# uuid: 27,
|
|
40
42
|
# union: 28,
|
|
43
|
+
uuid: 27,
|
|
41
44
|
bit: 29,
|
|
42
45
|
time_tz: 30,
|
|
43
46
|
timestamp_tz: 31,
|
|
@@ -111,6 +114,69 @@ module DuckDB
|
|
|
111
114
|
_create_map_type(LogicalType.resolve(key_type), LogicalType.resolve(value_type))
|
|
112
115
|
end
|
|
113
116
|
|
|
117
|
+
# Creates a union logical type with the given member names and types.
|
|
118
|
+
#
|
|
119
|
+
# The keyword arguments map member names to types. Each type can be
|
|
120
|
+
# a symbol or a DuckDB::LogicalType instance.
|
|
121
|
+
#
|
|
122
|
+
# require 'duckdb'
|
|
123
|
+
#
|
|
124
|
+
# union_type = DuckDB::LogicalType.create_union(num: :integer, str: :varchar)
|
|
125
|
+
# union_type.type #=> :union
|
|
126
|
+
# union_type.member_count #=> 2
|
|
127
|
+
# union_type.member_name_at(0) #=> "num"
|
|
128
|
+
# union_type.member_type_at(0).type #=> :integer
|
|
129
|
+
def create_union(**members)
|
|
130
|
+
resolved = members.transform_values { |v| LogicalType.resolve(v) }
|
|
131
|
+
_create_union_type(resolved)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Creates a struct logical type with the given member names and types.
|
|
135
|
+
#
|
|
136
|
+
# The keyword arguments map member names to types. Each type can be
|
|
137
|
+
# a symbol or a DuckDB::LogicalType instance.
|
|
138
|
+
#
|
|
139
|
+
# require 'duckdb'
|
|
140
|
+
#
|
|
141
|
+
# struct_type = DuckDB::LogicalType.create_struct(name: :varchar, age: :integer)
|
|
142
|
+
# struct_type.type #=> :struct
|
|
143
|
+
# struct_type.child_count #=> 2
|
|
144
|
+
# struct_type.child_name_at(0) #=> "name"
|
|
145
|
+
# struct_type.child_type_at(0).type #=> :varchar
|
|
146
|
+
def create_struct(**members)
|
|
147
|
+
resolved = members.transform_values { |v| LogicalType.resolve(v) }
|
|
148
|
+
_create_struct_type(resolved)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Creates an enum logical type with the given members.
|
|
152
|
+
#
|
|
153
|
+
# Each member must be a String representing an enum member.
|
|
154
|
+
#
|
|
155
|
+
# require 'duckdb'
|
|
156
|
+
#
|
|
157
|
+
# enum_type = DuckDB::LogicalType.create_enum('happy', 'sad', 'neutral')
|
|
158
|
+
# enum_type.type #=> :enum
|
|
159
|
+
# enum_type.dictionary_size #=> 3
|
|
160
|
+
# enum_type.dictionary_value_at(0) #=> "happy"
|
|
161
|
+
def create_enum(*members)
|
|
162
|
+
_create_enum_type(members.map(&:to_s))
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Creates a decimal logical type with the given width and scale.
|
|
166
|
+
#
|
|
167
|
+
# require 'duckdb'
|
|
168
|
+
#
|
|
169
|
+
# decimal_type = DuckDB::LogicalType.create_decimal(18, 3)
|
|
170
|
+
# decimal_type.type #=> :decimal
|
|
171
|
+
# decimal_type.width #=> 18
|
|
172
|
+
# decimal_type.scale #=> 3
|
|
173
|
+
def create_decimal(width, scale)
|
|
174
|
+
raise DuckDB::Error, 'width must be between 1 and 38' unless RANGE_DECIMAL_WIDTH.cover?(width)
|
|
175
|
+
raise DuckDB::Error, "scale must be between 0 and width(#{width})" unless (0..width).cover?(scale)
|
|
176
|
+
|
|
177
|
+
_create_decimal_type(width, scale)
|
|
178
|
+
end
|
|
179
|
+
|
|
114
180
|
private
|
|
115
181
|
|
|
116
182
|
def raise_resolve_error(symbol)
|
|
@@ -7,7 +7,8 @@ module DuckDB
|
|
|
7
7
|
class ScalarFunction
|
|
8
8
|
# Create and configure a scalar function in one call
|
|
9
9
|
#
|
|
10
|
-
# @param name [String, Symbol] the function name
|
|
10
|
+
# @param name [String, Symbol, nil] the function name; use +nil+ when creating overloads
|
|
11
|
+
# intended for use in a +DuckDB::ScalarFunctionSet+ (the set provides the name)
|
|
11
12
|
# @param return_type [DuckDB::LogicalType|:logical_type_symbol] the return type
|
|
12
13
|
# @param parameter_type [DuckDB::LogicalType|:logical_type_symbol, nil] single fixed parameter type
|
|
13
14
|
# @param parameter_types [Array<DuckDB::LogicalType|:logical_type_symbol>, nil] multiple fixed parameter types
|
|
@@ -63,7 +64,7 @@ module DuckDB
|
|
|
63
64
|
# null_handling: true
|
|
64
65
|
# ) { |v| v.nil? ? 0 : v }
|
|
65
66
|
def self.create( # rubocop:disable Metrics/MethodLength,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity,Metrics/ParameterLists
|
|
66
|
-
|
|
67
|
+
return_type:, name: nil, parameter_type: nil, parameter_types: nil, varargs_type: nil, null_handling: false, &
|
|
67
68
|
)
|
|
68
69
|
raise ArgumentError, 'Block required' unless block_given?
|
|
69
70
|
raise ArgumentError, 'Cannot specify both parameter_type and parameter_types' if parameter_type && parameter_types
|
|
@@ -77,7 +78,7 @@ module DuckDB
|
|
|
77
78
|
end
|
|
78
79
|
|
|
79
80
|
sf = new
|
|
80
|
-
sf.name = name.to_s
|
|
81
|
+
sf.name = name.to_s if name
|
|
81
82
|
sf.return_type = return_type
|
|
82
83
|
params.each { |type| sf.add_parameter(type) }
|
|
83
84
|
sf.varargs_type = varargs_type if varargs_type
|
|
@@ -95,7 +96,9 @@ module DuckDB
|
|
|
95
96
|
date
|
|
96
97
|
double
|
|
97
98
|
float
|
|
99
|
+
hugeint
|
|
98
100
|
integer
|
|
101
|
+
interval
|
|
99
102
|
smallint
|
|
100
103
|
time
|
|
101
104
|
timestamp
|
|
@@ -106,18 +109,20 @@ module DuckDB
|
|
|
106
109
|
timestamp_tz
|
|
107
110
|
tinyint
|
|
108
111
|
ubigint
|
|
112
|
+
uhugeint
|
|
109
113
|
uinteger
|
|
110
114
|
usmallint
|
|
111
115
|
utinyint
|
|
116
|
+
uuid
|
|
112
117
|
varchar
|
|
113
118
|
].freeze
|
|
114
119
|
|
|
115
120
|
private_constant :SUPPORTED_TYPES
|
|
116
121
|
|
|
117
122
|
# Adds a parameter to the scalar function.
|
|
118
|
-
# Currently supports BIGINT, BLOB, BOOLEAN, DATE, DOUBLE, FLOAT, INTEGER, SMALLINT, TIME,
|
|
119
|
-
# TIMESTAMP_S, TIMESTAMP_MS, TIMESTAMP_NS, TIME_TZ, TIMESTAMP_TZ, TINYINT, UBIGINT,
|
|
120
|
-
# and VARCHAR types.
|
|
123
|
+
# Currently supports BIGINT, BLOB, BOOLEAN, DATE, DOUBLE, FLOAT, HUGEINT, INTEGER, INTERVAL, SMALLINT, TIME,
|
|
124
|
+
# TIMESTAMP, TIMESTAMP_S, TIMESTAMP_MS, TIMESTAMP_NS, TIME_TZ, TIMESTAMP_TZ, TINYINT, UBIGINT, UHUGEINT,
|
|
125
|
+
# UINTEGER, USMALLINT, UTINYINT, UUID, and VARCHAR types.
|
|
121
126
|
#
|
|
122
127
|
# @param logical_type [DuckDB::LogicalType | :logical_type_symbol] the parameter type
|
|
123
128
|
# @return [DuckDB::ScalarFunction] self
|
|
@@ -129,9 +134,9 @@ module DuckDB
|
|
|
129
134
|
end
|
|
130
135
|
|
|
131
136
|
# Sets the return type for the scalar function.
|
|
132
|
-
# Currently supports BIGINT, BLOB, BOOLEAN, DATE, DOUBLE, FLOAT, INTEGER, SMALLINT, TIME,
|
|
133
|
-
# TIMESTAMP_S, TIMESTAMP_MS, TIMESTAMP_NS, TIME_TZ, TIMESTAMP_TZ, TINYINT, UBIGINT,
|
|
134
|
-
# and VARCHAR types.
|
|
137
|
+
# Currently supports BIGINT, BLOB, BOOLEAN, DATE, DOUBLE, FLOAT, HUGEINT, INTEGER, INTERVAL, SMALLINT, TIME,
|
|
138
|
+
# TIMESTAMP, TIMESTAMP_S, TIMESTAMP_MS, TIMESTAMP_NS, TIME_TZ, TIMESTAMP_TZ, TINYINT, UBIGINT, UHUGEINT,
|
|
139
|
+
# UINTEGER, USMALLINT, UTINYINT, UUID, and VARCHAR types.
|
|
135
140
|
#
|
|
136
141
|
# @param logical_type [DuckDB::LogicalType | :logical_type_symbol] the return type
|
|
137
142
|
# @return [DuckDB::ScalarFunction] self
|
|
@@ -161,9 +166,9 @@ module DuckDB
|
|
|
161
166
|
# given type. Can be combined with add_parameter to add fixed leading parameters
|
|
162
167
|
# (e.g. a separator followed by a variable list of values).
|
|
163
168
|
# The block receives fixed parameters positionally, then varargs as a splat (|fixed, *rest|).
|
|
164
|
-
# Currently supports BIGINT, BLOB, BOOLEAN, DATE, DOUBLE, FLOAT, INTEGER, SMALLINT, TIME,
|
|
165
|
-
# TIMESTAMP_S, TIMESTAMP_MS, TIMESTAMP_NS, TIME_TZ, TIMESTAMP_TZ, TINYINT, UBIGINT,
|
|
166
|
-
# and VARCHAR types.
|
|
169
|
+
# Currently supports BIGINT, BLOB, BOOLEAN, DATE, DOUBLE, FLOAT, HUGEINT, INTEGER, INTERVAL, SMALLINT, TIME,
|
|
170
|
+
# TIMESTAMP, TIMESTAMP_S, TIMESTAMP_MS, TIMESTAMP_NS, TIME_TZ, TIMESTAMP_TZ, TINYINT, UBIGINT, UHUGEINT,
|
|
171
|
+
# UINTEGER, USMALLINT, UTINYINT, UUID, and VARCHAR types.
|
|
167
172
|
#
|
|
168
173
|
# @param logical_type [DuckDB::LogicalType | :logical_type_symbol] the varargs element type
|
|
169
174
|
# @return [DuckDB::ScalarFunction] self
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DuckDB
|
|
4
|
+
# DuckDB::ScalarFunctionSet encapsulates DuckDB's scalar function set,
|
|
5
|
+
# which allows registering multiple overloads of a scalar function under one name.
|
|
6
|
+
#
|
|
7
|
+
# @note DuckDB::ScalarFunctionSet is experimental.
|
|
8
|
+
class ScalarFunctionSet
|
|
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
|
+
@name = name.to_s
|
|
15
|
+
_initialize(@name)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# @param scalar_function [DuckDB::ScalarFunction] the overload to add
|
|
19
|
+
# @return [self]
|
|
20
|
+
# @raise [TypeError] if scalar_function is not a DuckDB::ScalarFunction
|
|
21
|
+
# @raise [DuckDB::Error] if the overload already exists in the set
|
|
22
|
+
def add(scalar_function)
|
|
23
|
+
unless scalar_function.is_a?(DuckDB::ScalarFunction)
|
|
24
|
+
raise TypeError, "#{scalar_function.class} is not a DuckDB::ScalarFunction"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
scalar_function.name = @name
|
|
28
|
+
_add(scalar_function)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
data/lib/duckdb/version.rb
CHANGED
data/lib/duckdb.rb
CHANGED
|
@@ -15,6 +15,7 @@ require 'duckdb/config'
|
|
|
15
15
|
require 'duckdb/column'
|
|
16
16
|
require 'duckdb/logical_type'
|
|
17
17
|
require 'duckdb/scalar_function'
|
|
18
|
+
require 'duckdb/scalar_function_set'
|
|
18
19
|
require 'duckdb/expression'
|
|
19
20
|
require 'duckdb/client_context'
|
|
20
21
|
require 'duckdb/scalar_function/bind_info'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: duckdb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.5.1.
|
|
4
|
+
version: 1.5.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Masaki Suketa
|
|
@@ -101,6 +101,8 @@ files:
|
|
|
101
101
|
- ext/duckdb/scalar_function.h
|
|
102
102
|
- ext/duckdb/scalar_function_bind_info.c
|
|
103
103
|
- ext/duckdb/scalar_function_bind_info.h
|
|
104
|
+
- ext/duckdb/scalar_function_set.c
|
|
105
|
+
- ext/duckdb/scalar_function_set.h
|
|
104
106
|
- ext/duckdb/table_function.c
|
|
105
107
|
- ext/duckdb/table_function.h
|
|
106
108
|
- ext/duckdb/table_function_bind_info.c
|
|
@@ -139,6 +141,7 @@ files:
|
|
|
139
141
|
- lib/duckdb/result.rb
|
|
140
142
|
- lib/duckdb/scalar_function.rb
|
|
141
143
|
- lib/duckdb/scalar_function/bind_info.rb
|
|
144
|
+
- lib/duckdb/scalar_function_set.rb
|
|
142
145
|
- lib/duckdb/table_function.rb
|
|
143
146
|
- lib/duckdb/table_function/bind_info.rb
|
|
144
147
|
- lib/duckdb/table_function/function_info.rb
|