duckdb 1.5.4.0 → 1.5.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +36 -0
- data/CLAUDE.md +4 -0
- data/duckdb.gemspec +1 -1
- data/ext/duckdb/connection.c +23 -1
- data/ext/duckdb/error.c +12 -0
- data/ext/duckdb/error.h +1 -0
- data/ext/duckdb/function_vector.c +14 -0
- data/ext/duckdb/prepared_statement.c +120 -5
- data/ext/duckdb/util.c +8 -0
- data/ext/duckdb/util.h +1 -0
- data/ext/duckdb/value.c +526 -1
- data/lib/duckdb/connection.rb +19 -0
- data/lib/duckdb/converter/int_to_sym.rb +54 -1
- data/lib/duckdb/converter.rb +7 -7
- data/lib/duckdb/error.rb +26 -0
- data/lib/duckdb/function_type_validation.rb +1 -0
- data/lib/duckdb/logical_type.rb +6 -3
- data/lib/duckdb/prepared_statement.rb +15 -0
- data/lib/duckdb/scalar_function.rb +6 -6
- data/lib/duckdb/value.rb +400 -0
- data/lib/duckdb/version.rb +1 -1
- data/lib/duckdb.rb +1 -0
- metadata +5 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 534e22d8df0424b7551b9ca68678d4da9f2e7a8055272dabe7c31d9f63c7b5b4
|
|
4
|
+
data.tar.gz: 5a1a9289dd5b20de755dd2c5c17242a353fe695ff27e47e5c862bc62004d1875
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5afc359f5b6f5c972a34f0662e0f94360d91a12fd6a3db3e116f54eea486436c2855e0ee8f1949d8aff42c7b171e2efbeded84989b5bf335b57c7b9e7d5ee497
|
|
7
|
+
data.tar.gz: 7f5d628c60766814253cf9971b90f3fc1911d63d376cbce414dcff92f8e550dbccc676832435349b925c26449aa18d2dff394bd0e33d300ee1e57cc8ff8f5f5f
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,42 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
# Unreleased
|
|
6
6
|
|
|
7
|
+
# 1.5.5.0 - 2026-07-27
|
|
8
|
+
|
|
9
|
+
- bump up DuckDB 1.5.5 on CI.
|
|
10
|
+
- support TIME_NS columns in `DuckDB::ScalarFunction` and `DuckDB::AggregateFunction`: TIME_NS can now be used as parameter and return type (with `DuckDB::LogicalType::TIME_NS`). Requires DuckDB >= 1.5.0 for full TIME_NS support in the C API.
|
|
11
|
+
- bump Ruby to 3.4.10 on CI.
|
|
12
|
+
- add `DuckDB::Connection#table_names(sql, qualified: false)` returning the table names referenced by a SQL query without executing it (binds `duckdb_get_table_names`). With `qualified: true`, names are returned as written in the query (including schema/catalog prefixes and aliases). Result order is unspecified.
|
|
13
|
+
- add `DuckDB::Value.create_list(child_type, values)` and `DuckDB::Value.create_array(child_type, values)` to create LIST/ARRAY values from an element type (a Symbol like `:integer` or a `DuckDB::LogicalType`) and an Array of `DuckDB::Value` elements. The created values can be bound to prepared statements with `#bind_value`.
|
|
14
|
+
- add `DuckDB::Value#list_size` and `DuckDB::Value#list_child(index)` to read LIST value elements as `DuckDB::Value`.
|
|
15
|
+
- add `DuckDB::Value#to_ruby` converting a `DuckDB::Value` to a Ruby object. LIST/ARRAY values are converted to Ruby Arrays recursively; NULL becomes `nil`.
|
|
16
|
+
- add `DuckDB::Value.create_struct(struct_type, values)` to create STRUCT values from a field spec (a Hash like `{ a: :integer, b: :varchar }` or a STRUCT `DuckDB::LogicalType`) and an Array of `DuckDB::Value` field values (positional, matching field order).
|
|
17
|
+
- add `DuckDB::Value#struct_child(index)` to read STRUCT fields as `DuckDB::Value`. `DuckDB::Value#to_ruby` converts STRUCT values to a Hash with Symbol keys recursively.
|
|
18
|
+
- add `DuckDB::Value.create_map(map_type, entries)` to create MAP values from a type spec (a one-pair Hash like `{ varchar: :integer }` or a MAP `DuckDB::LogicalType`) and a Hash of `DuckDB::Value` keys to `DuckDB::Value` values.
|
|
19
|
+
- add `DuckDB::Value#map_size`, `#map_key(index)` and `#map_value(index)` to read MAP entries as `DuckDB::Value`. `DuckDB::Value#to_ruby` converts MAP values to a Hash recursively (keys keep their natural Ruby type).
|
|
20
|
+
- add `DuckDB::PreparedStatement#column_count`, `#column_name(col_index)`, `#column_type(col_index)` and `#column_logical_type(col_index)` to get the result-set column metadata of a prepared statement without executing it (column index is 0-based). Useful for PG-style statement caching where column types must be known before execution.
|
|
21
|
+
- add `DuckDB::Error#error_type` returning the DuckDB error category as a Symbol (e.g. `:constraint`, `:catalog`, `:parser`), or `nil` for errors not originating from a query result. Helps the ActiveRecord adapter map failures to `RecordNotUnique` / `NotNullViolation` etc. without parsing error messages.
|
|
22
|
+
- add `DuckDB::PreparedStatement#param_logical_type(param_index)` returning the `DuckDB::LogicalType` of a bind parameter (1-based index), giving richer type information than `#param_type` (e.g. decimal width/scale, nested types).
|
|
23
|
+
- add `DuckDB::Value.create_date(value)` to create DATE values from a Date, a Time, or a parseable String (same lenient input as `Appender#append_date`).
|
|
24
|
+
- add `DuckDB::Value.create_time(value)` to create TIME values (microsecond precision) from a Time or a parseable String.
|
|
25
|
+
- add `DuckDB::Value.create_time_ns(value)` to create TIME_NS values (nanosecond precision) from a Time or a parseable String. TIME_NS values are now converted to Ruby Time with full nanosecond precision (previously truncated to microseconds).
|
|
26
|
+
- add `DuckDB::Value.create_time_tz(value)` to create TIMETZ values from a Time (the UTC offset is taken from the Time) or a parseable String with offset.
|
|
27
|
+
- add `DuckDB::Value.create_timestamp(value)` to create TIMESTAMP values (microsecond precision) from a Time, a Date, or a parseable String (same lenient input as `Appender#append_timestamp`).
|
|
28
|
+
- add `DuckDB::Value.create_timestamp_s(value)` to create TIMESTAMP_S values (second precision; sub-second input is truncated).
|
|
29
|
+
- add `DuckDB::Value.create_timestamp_ms(value)` to create TIMESTAMP_MS values (millisecond precision).
|
|
30
|
+
- add `DuckDB::Value.create_timestamp_ns(value)` to create TIMESTAMP_NS values (nanosecond precision). TIMESTAMP_NS values (including query results) are now converted to Ruby Time with full nanosecond precision (previously truncated to microseconds).
|
|
31
|
+
- add `DuckDB::Value.create_timestamp_tz(value)` to create TIMESTAMP WITH TIME ZONE values; the instant is stored correctly regardless of the input UTC offset.
|
|
32
|
+
- add `DuckDB::Value.create_interval(value)` to create INTERVAL values from a `DuckDB::Interval`.
|
|
33
|
+
- add `DuckDB::Value.create_enum(enum_type, member)` to create ENUM values from an Array of member names (or an ENUM `DuckDB::LogicalType`) and a member name or 0-based index.
|
|
34
|
+
- `DuckDB::Value#to_ruby` converts ENUM values to the member String.
|
|
35
|
+
- add `DuckDB::Value.create_union(union_type, tag, value)` to create UNION values from a member spec Hash like `{ num: :integer, str: :varchar }` (or a UNION `DuckDB::LogicalType`), a member tag, and a `DuckDB::Value`. `DuckDB::Value#to_ruby` converts UNION values to the member value.
|
|
36
|
+
- add `DuckDB::Value.create_bit(value)` to create BIT values from a String of '0'/'1' characters.
|
|
37
|
+
- `DuckDB::Value#to_ruby` converts BIT values to a String of '0'/'1' characters.
|
|
38
|
+
- add `DuckDB::Value.create_bignum(value)` to create BIGNUM (arbitrary-precision integer) values from a Ruby Integer.
|
|
39
|
+
- `DuckDB::Value#to_ruby` converts BIGNUM values to a Ruby Integer.
|
|
40
|
+
- `DuckDB::Value#to_ruby` converts BLOB values to a BINARY-encoded String.
|
|
41
|
+
- `DuckDB::Value#to_ruby` converts DECIMAL values to a BigDecimal.
|
|
42
|
+
|
|
7
43
|
# 1.5.4.0 - 2026-06-20
|
|
8
44
|
- bump up DuckDB 1.5.4 and 1.4.5 on CI.
|
|
9
45
|
- add experimental `DuckDB::Result#arrow_c_stream` returning `DuckDB::ArrowArrayStream` to export a query result as an Arrow C stream (Arrow C Data Interface). The stream can be consumed directly by ruby-polars (`Polars::DataFrame.new(result)`) and red-arrow (`Arrow::RecordBatchReader.import(stream.to_i)`).
|
data/CLAUDE.md
ADDED
data/duckdb.gemspec
CHANGED
data/ext/duckdb/connection.c
CHANGED
|
@@ -17,6 +17,7 @@ static VALUE connection__register_scalar_function_set(VALUE self, VALUE scalar_f
|
|
|
17
17
|
static VALUE connection__register_aggregate_function(VALUE self, VALUE aggregate_function);
|
|
18
18
|
static VALUE connection__register_aggregate_function_set(VALUE self, VALUE aggregate_function_set);
|
|
19
19
|
static VALUE connection__register_table_function(VALUE self, VALUE table_function);
|
|
20
|
+
static VALUE connection__get_table_names(VALUE self, VALUE query, VALUE qualified);
|
|
20
21
|
|
|
21
22
|
static const rb_data_type_t connection_data_type = {
|
|
22
23
|
"DuckDB/Connection",
|
|
@@ -198,11 +199,31 @@ static VALUE connection__query_sql(VALUE self, VALUE str) {
|
|
|
198
199
|
RB_GC_GUARD(str);
|
|
199
200
|
|
|
200
201
|
if (args.retval == DuckDBError) {
|
|
201
|
-
|
|
202
|
+
rbduckdb_raise_result_error(&(ctxr->result));
|
|
202
203
|
}
|
|
203
204
|
return result;
|
|
204
205
|
}
|
|
205
206
|
|
|
207
|
+
/* :nodoc: */
|
|
208
|
+
static VALUE connection__get_table_names(VALUE self, VALUE query, VALUE qualified) {
|
|
209
|
+
rubyDuckDBConnection *ctx;
|
|
210
|
+
duckdb_value table_names;
|
|
211
|
+
|
|
212
|
+
ctx = rbduckdb_get_struct_connection(self);
|
|
213
|
+
|
|
214
|
+
if (!(ctx->con)) {
|
|
215
|
+
rb_raise(eDuckDBError, "Database connection closed");
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
table_names = duckdb_get_table_names(ctx->con, StringValueCStr(query), RTEST(qualified));
|
|
219
|
+
|
|
220
|
+
if (!table_names) {
|
|
221
|
+
rb_raise(eDuckDBError, "Failed to get table names from query");
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return rbduckdb_value_new(table_names);
|
|
225
|
+
}
|
|
226
|
+
|
|
206
227
|
/* :nodoc: */
|
|
207
228
|
static VALUE connection__register_logical_type(VALUE self, VALUE logical_type) {
|
|
208
229
|
rubyDuckDBConnection *ctxcon;
|
|
@@ -346,4 +367,5 @@ void rbduckdb_init_connection(void) {
|
|
|
346
367
|
rb_define_private_method(cDuckDBConnection, "_register_table_function", connection__register_table_function, 1);
|
|
347
368
|
rb_define_private_method(cDuckDBConnection, "_connect", connection__connect, 1);
|
|
348
369
|
rb_define_private_method(cDuckDBConnection, "_query_sql", connection__query_sql, 1);
|
|
370
|
+
rb_define_private_method(cDuckDBConnection, "_get_table_names", connection__get_table_names, 2);
|
|
349
371
|
}
|
data/ext/duckdb/error.c
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
VALUE eDuckDBError;
|
|
4
4
|
|
|
5
|
+
/* Raises DuckDB::Error from a failed result. C only reads the message and error
|
|
6
|
+
* type id off the result (the result is destroyed before Ruby sees it) and passes
|
|
7
|
+
* them to DuckDB::Error.new(message, error_type_id). */
|
|
8
|
+
void rbduckdb_raise_result_error(duckdb_result *result) {
|
|
9
|
+
const char *msg = duckdb_result_error(result);
|
|
10
|
+
if (!msg) {
|
|
11
|
+
msg = "DuckDB error";
|
|
12
|
+
}
|
|
13
|
+
VALUE exc = rb_funcall(eDuckDBError, rb_intern("new"), 2, rb_str_new_cstr(msg), INT2FIX(duckdb_result_error_type(result)));
|
|
14
|
+
rb_exc_raise(exc);
|
|
15
|
+
}
|
|
16
|
+
|
|
5
17
|
void rbduckdb_init_error(void) {
|
|
6
18
|
#if 0
|
|
7
19
|
VALUE mDuckDB = rb_define_module("DuckDB");
|
data/ext/duckdb/error.h
CHANGED
|
@@ -137,6 +137,20 @@ void rbduckdb_vector_set_value_at(duckdb_vector vector, duckdb_logical_type elem
|
|
|
137
137
|
((duckdb_time *)vector_data)[index] = time;
|
|
138
138
|
break;
|
|
139
139
|
}
|
|
140
|
+
case DUCKDB_TYPE_TIME_NS: {
|
|
141
|
+
if (!rb_obj_is_kind_of(value, rb_cTime)) {
|
|
142
|
+
rb_raise(rb_eTypeError, "Expected Time object for TIME_NS");
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
VALUE hour = rb_funcall(value, rb_intern("hour"), 0);
|
|
146
|
+
VALUE min = rb_funcall(value, rb_intern("min"), 0);
|
|
147
|
+
VALUE sec = rb_funcall(value, rb_intern("sec"), 0);
|
|
148
|
+
VALUE nsec = rb_funcall(value, rb_intern("nsec"), 0);
|
|
149
|
+
|
|
150
|
+
duckdb_time_ns time_ns = rbduckdb_to_duckdb_time_ns_from_value(hour, min, sec, nsec);
|
|
151
|
+
((duckdb_time_ns *)vector_data)[index] = time_ns;
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
140
154
|
case DUCKDB_TYPE_TIME_TZ: {
|
|
141
155
|
if (!rb_obj_is_kind_of(value, rb_cTime)) {
|
|
142
156
|
rb_raise(rb_eTypeError, "Expected Time object for TIME_TZ");
|
|
@@ -14,6 +14,7 @@ static idx_t check_index(VALUE vidx);
|
|
|
14
14
|
|
|
15
15
|
static VALUE prepared_statement_bind_parameter_index(VALUE self, VALUE name);
|
|
16
16
|
static VALUE prepared_statement_parameter_name(VALUE self, VALUE vidx);
|
|
17
|
+
static VALUE prepared_statement_param_logical_type(VALUE self, VALUE vidx);
|
|
17
18
|
static VALUE prepared_statement_clear_bindings(VALUE self);
|
|
18
19
|
static VALUE prepared_statement_bind_bool(VALUE self, VALUE vidx, VALUE val);
|
|
19
20
|
static VALUE prepared_statement_bind_int8(VALUE self, VALUE vidx, VALUE val);
|
|
@@ -27,6 +28,10 @@ static VALUE prepared_statement_bind_blob(VALUE self, VALUE vidx, VALUE blob);
|
|
|
27
28
|
static VALUE prepared_statement_bind_null(VALUE self, VALUE vidx);
|
|
28
29
|
static VALUE prepared_statement__statement_type(VALUE self);
|
|
29
30
|
static VALUE prepared_statement__param_type(VALUE self, VALUE vidx);
|
|
31
|
+
static VALUE prepared_statement_column_count(VALUE self);
|
|
32
|
+
static VALUE prepared_statement_column_name(VALUE self, VALUE vidx);
|
|
33
|
+
static VALUE prepared_statement__column_type(VALUE self, VALUE vidx);
|
|
34
|
+
static VALUE prepared_statement_column_logical_type(VALUE self, VALUE vidx);
|
|
30
35
|
static VALUE prepared_statement__bind_uint8(VALUE self, VALUE vidx, VALUE val);
|
|
31
36
|
static VALUE prepared_statement__bind_uint16(VALUE self, VALUE vidx, VALUE val);
|
|
32
37
|
static VALUE prepared_statement__bind_uint32(VALUE self, VALUE vidx, VALUE val);
|
|
@@ -129,7 +134,6 @@ static VALUE prepared_statement_execute(VALUE self) {
|
|
|
129
134
|
rubyDuckDBResult *ctxr;
|
|
130
135
|
const char *error;
|
|
131
136
|
VALUE result = rbduckdb_create_result();
|
|
132
|
-
VALUE msg;
|
|
133
137
|
|
|
134
138
|
TypedData_Get_Struct(self, rubyDuckDBPreparedStatement, &prepared_statement_data_type, ctx);
|
|
135
139
|
ctxr = rbduckdb_get_struct_result(result);
|
|
@@ -146,11 +150,10 @@ static VALUE prepared_statement_execute(VALUE self) {
|
|
|
146
150
|
if (state == DuckDBError) {
|
|
147
151
|
error = duckdb_prepare_error(args.prepared_statement);
|
|
148
152
|
if (error == NULL) {
|
|
149
|
-
error
|
|
153
|
+
/* error originates from the result, which carries an error type */
|
|
154
|
+
rbduckdb_raise_result_error(args.out_result);
|
|
150
155
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
rb_raise(eDuckDBError, "%s", StringValuePtr(msg));
|
|
156
|
+
rb_raise(eDuckDBError, "%s", error);
|
|
154
157
|
}
|
|
155
158
|
|
|
156
159
|
return result;
|
|
@@ -206,6 +209,28 @@ static VALUE prepared_statement_parameter_name(VALUE self, VALUE vidx) {
|
|
|
206
209
|
return vname;
|
|
207
210
|
}
|
|
208
211
|
|
|
212
|
+
/*
|
|
213
|
+
* call-seq:
|
|
214
|
+
* prepared_statement.param_logical_type(param_index) -> DuckDB::LogicalType
|
|
215
|
+
*
|
|
216
|
+
* Returns the logical type of the parameter at the given (1-based) index.
|
|
217
|
+
* This provides richer type information than #param_type, e.g. decimal
|
|
218
|
+
* width/scale, nested types.
|
|
219
|
+
*/
|
|
220
|
+
static VALUE prepared_statement_param_logical_type(VALUE self, VALUE vidx) {
|
|
221
|
+
rubyDuckDBPreparedStatement *ctx;
|
|
222
|
+
duckdb_logical_type logical_type;
|
|
223
|
+
idx_t idx = check_index(vidx);
|
|
224
|
+
|
|
225
|
+
TypedData_Get_Struct(self, rubyDuckDBPreparedStatement, &prepared_statement_data_type, ctx);
|
|
226
|
+
|
|
227
|
+
logical_type = duckdb_param_logical_type(ctx->prepared_statement, idx);
|
|
228
|
+
if (logical_type == NULL) {
|
|
229
|
+
rb_raise(eDuckDBError, "fail to get logical type of the parameter at %llu. parameter index is out of range.", (unsigned long long)idx);
|
|
230
|
+
}
|
|
231
|
+
return rbduckdb_create_logical_type(logical_type);
|
|
232
|
+
}
|
|
233
|
+
|
|
209
234
|
/*
|
|
210
235
|
* call-seq:
|
|
211
236
|
* prepared_statement.clear_bindings -> DuckDB::PreparedStatement
|
|
@@ -360,6 +385,91 @@ static VALUE prepared_statement__statement_type(VALUE self) {
|
|
|
360
385
|
return INT2FIX(duckdb_prepared_statement_type(ctx->prepared_statement));
|
|
361
386
|
}
|
|
362
387
|
|
|
388
|
+
/*
|
|
389
|
+
* call-seq:
|
|
390
|
+
* prepared_statement.column_count -> Integer
|
|
391
|
+
*
|
|
392
|
+
* Returns the number of columns in the result set of the prepared statement
|
|
393
|
+
* without executing it.
|
|
394
|
+
*
|
|
395
|
+
* require 'duckdb'
|
|
396
|
+
* db = DuckDB::Database.open
|
|
397
|
+
* con = db.connect
|
|
398
|
+
* stmt = con.prepared_statement('SELECT 1 AS a, 2 AS b')
|
|
399
|
+
* stmt.column_count # => 2
|
|
400
|
+
*/
|
|
401
|
+
static VALUE prepared_statement_column_count(VALUE self) {
|
|
402
|
+
rubyDuckDBPreparedStatement *ctx;
|
|
403
|
+
TypedData_Get_Struct(self, rubyDuckDBPreparedStatement, &prepared_statement_data_type, ctx);
|
|
404
|
+
return ULL2NUM(duckdb_prepared_statement_column_count(ctx->prepared_statement));
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/*
|
|
408
|
+
* call-seq:
|
|
409
|
+
* prepared_statement.column_name(col_index) -> String
|
|
410
|
+
*
|
|
411
|
+
* Returns the name of the column at the specified index (0-based) of the
|
|
412
|
+
* result set of the prepared statement without executing it.
|
|
413
|
+
* Raises DuckDB::Error if the column index is out of range.
|
|
414
|
+
*
|
|
415
|
+
* require 'duckdb'
|
|
416
|
+
* db = DuckDB::Database.open
|
|
417
|
+
* con = db.connect
|
|
418
|
+
* stmt = con.prepared_statement('SELECT 1 AS a, 2 AS b')
|
|
419
|
+
* stmt.column_name(1) # => 'b'
|
|
420
|
+
*/
|
|
421
|
+
static VALUE prepared_statement_column_name(VALUE self, VALUE vidx) {
|
|
422
|
+
rubyDuckDBPreparedStatement *ctx;
|
|
423
|
+
VALUE vname;
|
|
424
|
+
const char *name;
|
|
425
|
+
idx_t idx = NUM2ULL(vidx);
|
|
426
|
+
|
|
427
|
+
TypedData_Get_Struct(self, rubyDuckDBPreparedStatement, &prepared_statement_data_type, ctx);
|
|
428
|
+
|
|
429
|
+
name = duckdb_prepared_statement_column_name(ctx->prepared_statement, idx);
|
|
430
|
+
if (name == NULL) {
|
|
431
|
+
rb_raise(eDuckDBError, "fail to get column name at %llu. column index is out of range.", (unsigned long long)idx);
|
|
432
|
+
}
|
|
433
|
+
vname = rb_str_new2(name);
|
|
434
|
+
duckdb_free((void *)name);
|
|
435
|
+
return vname;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/* :nodoc: */
|
|
439
|
+
static VALUE prepared_statement__column_type(VALUE self, VALUE vidx) {
|
|
440
|
+
rubyDuckDBPreparedStatement *ctx;
|
|
441
|
+
TypedData_Get_Struct(self, rubyDuckDBPreparedStatement, &prepared_statement_data_type, ctx);
|
|
442
|
+
return INT2FIX(duckdb_prepared_statement_column_type(ctx->prepared_statement, NUM2ULL(vidx)));
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/*
|
|
446
|
+
* call-seq:
|
|
447
|
+
* prepared_statement.column_logical_type(col_index) -> DuckDB::LogicalType
|
|
448
|
+
*
|
|
449
|
+
* Returns the logical type of the column at the specified index (0-based) of
|
|
450
|
+
* the result set of the prepared statement without executing it.
|
|
451
|
+
* Raises DuckDB::Error if the column index is out of range.
|
|
452
|
+
*
|
|
453
|
+
* require 'duckdb'
|
|
454
|
+
* db = DuckDB::Database.open
|
|
455
|
+
* con = db.connect
|
|
456
|
+
* stmt = con.prepared_statement('SELECT 1.5::DECIMAL(9, 4) AS a')
|
|
457
|
+
* stmt.column_logical_type(0).type # => :decimal
|
|
458
|
+
*/
|
|
459
|
+
static VALUE prepared_statement_column_logical_type(VALUE self, VALUE vidx) {
|
|
460
|
+
rubyDuckDBPreparedStatement *ctx;
|
|
461
|
+
duckdb_logical_type logical_type;
|
|
462
|
+
idx_t idx = NUM2ULL(vidx);
|
|
463
|
+
|
|
464
|
+
TypedData_Get_Struct(self, rubyDuckDBPreparedStatement, &prepared_statement_data_type, ctx);
|
|
465
|
+
|
|
466
|
+
logical_type = duckdb_prepared_statement_column_logical_type(ctx->prepared_statement, idx);
|
|
467
|
+
if (logical_type == NULL) {
|
|
468
|
+
rb_raise(eDuckDBError, "fail to get column logical type at %llu. column index is out of range.", (unsigned long long)idx);
|
|
469
|
+
}
|
|
470
|
+
return rbduckdb_create_logical_type(logical_type);
|
|
471
|
+
}
|
|
472
|
+
|
|
363
473
|
/* :nodoc: */
|
|
364
474
|
static VALUE prepared_statement__param_type(VALUE self, VALUE vidx) {
|
|
365
475
|
rubyDuckDBPreparedStatement *ctx;
|
|
@@ -613,7 +723,11 @@ void rbduckdb_init_prepared_statement(void) {
|
|
|
613
723
|
rb_define_method(cDuckDBPreparedStatement, "nparams", prepared_statement_nparams, 0);
|
|
614
724
|
rb_define_method(cDuckDBPreparedStatement, "bind_parameter_index", prepared_statement_bind_parameter_index, 1);
|
|
615
725
|
rb_define_method(cDuckDBPreparedStatement, "parameter_name", prepared_statement_parameter_name, 1);
|
|
726
|
+
rb_define_method(cDuckDBPreparedStatement, "param_logical_type", prepared_statement_param_logical_type, 1);
|
|
616
727
|
rb_define_method(cDuckDBPreparedStatement, "clear_bindings", prepared_statement_clear_bindings, 0);
|
|
728
|
+
rb_define_method(cDuckDBPreparedStatement, "column_count", prepared_statement_column_count, 0);
|
|
729
|
+
rb_define_method(cDuckDBPreparedStatement, "column_name", prepared_statement_column_name, 1);
|
|
730
|
+
rb_define_method(cDuckDBPreparedStatement, "column_logical_type", prepared_statement_column_logical_type, 1);
|
|
617
731
|
rb_define_method(cDuckDBPreparedStatement, "bind_bool", prepared_statement_bind_bool, 2);
|
|
618
732
|
rb_define_method(cDuckDBPreparedStatement, "bind_int8", prepared_statement_bind_int8, 2);
|
|
619
733
|
rb_define_method(cDuckDBPreparedStatement, "bind_int16", prepared_statement_bind_int16, 2);
|
|
@@ -630,6 +744,7 @@ void rbduckdb_init_prepared_statement(void) {
|
|
|
630
744
|
rb_define_private_method(cDuckDBPreparedStatement, "_bind_uint64", prepared_statement__bind_uint64, 2);
|
|
631
745
|
rb_define_private_method(cDuckDBPreparedStatement, "_statement_type", prepared_statement__statement_type, 0);
|
|
632
746
|
rb_define_private_method(cDuckDBPreparedStatement, "_param_type", prepared_statement__param_type, 1);
|
|
747
|
+
rb_define_private_method(cDuckDBPreparedStatement, "_column_type", prepared_statement__column_type, 1);
|
|
633
748
|
rb_define_private_method(cDuckDBPreparedStatement, "_bind_date", prepared_statement__bind_date, 4);
|
|
634
749
|
rb_define_private_method(cDuckDBPreparedStatement, "_bind_time", prepared_statement__bind_time, 5);
|
|
635
750
|
rb_define_private_method(cDuckDBPreparedStatement, "_bind_timestamp", prepared_statement__bind_timestamp, 8);
|
data/ext/duckdb/util.c
CHANGED
|
@@ -21,6 +21,14 @@ 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_time_ns rbduckdb_to_duckdb_time_ns_from_value(VALUE hour, VALUE min, VALUE sec, VALUE nanos) {
|
|
25
|
+
duckdb_time_ns time_ns;
|
|
26
|
+
|
|
27
|
+
time_ns.nanos = ((NUM2LL(hour) * 60 + NUM2LL(min)) * 60 + NUM2LL(sec)) * 1000000000LL + NUM2LL(nanos);
|
|
28
|
+
|
|
29
|
+
return time_ns;
|
|
30
|
+
}
|
|
31
|
+
|
|
24
32
|
duckdb_timestamp rbduckdb_to_duckdb_timestamp_from_time_value(VALUE time_obj) {
|
|
25
33
|
if (!rb_obj_is_kind_of(time_obj, rb_cTime)) {
|
|
26
34
|
rb_raise(rb_eTypeError, "Expected Time object");
|
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_time_ns rbduckdb_to_duckdb_time_ns_from_value(VALUE hour, VALUE min, VALUE sec, VALUE nanos);
|
|
6
7
|
duckdb_timestamp rbduckdb_to_duckdb_timestamp_from_time_value(VALUE time_obj);
|
|
7
8
|
duckdb_timestamp rbduckdb_to_duckdb_timestamp_from_value(VALUE year, VALUE month, VALUE day, VALUE hour, VALUE min, VALUE sec, VALUE micros);
|
|
8
9
|
void rbduckdb_to_duckdb_interval_from_value(duckdb_interval* interval, VALUE months, VALUE days, VALUE micros);
|