duckdb 2.0.0.0 → 2.0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/ext/duckdb/aggregate_function.c +40 -78
- data/ext/duckdb/appender.c +39 -126
- data/ext/duckdb/connection.c +17 -54
- data/ext/duckdb/conveter.c +39 -76
- data/ext/duckdb/function_executor.c +214 -248
- data/ext/duckdb/function_executor.h +10 -0
- data/ext/duckdb/function_vector.c +26 -0
- data/ext/duckdb/function_vector.h +18 -0
- data/ext/duckdb/logical_type.c +88 -170
- data/ext/duckdb/prepared_statement.c +37 -138
- data/ext/duckdb/result.c +15 -84
- data/ext/duckdb/scalar_function.c +9 -69
- data/ext/duckdb/table_function.c +33 -82
- data/ext/duckdb/util.c +53 -2
- data/lib/duckdb/interval.rb +2 -28
- data/lib/duckdb/prepared_statement.rb +5 -0
- data/lib/duckdb/scalar_function_set.rb +1 -2
- data/lib/duckdb/version.rb +1 -1
- metadata +1 -1
|
@@ -18,11 +18,6 @@ static VALUE scalar_function__set_bind(VALUE self);
|
|
|
18
18
|
static void scalar_function_callback(duckdb_function_info info, duckdb_data_chunk input, duckdb_vector output);
|
|
19
19
|
static void scalar_function_bind_callback(duckdb_bind_info info);
|
|
20
20
|
#ifdef HAVE_DUCKDB_H_GE_V1_5_0
|
|
21
|
-
/*
|
|
22
|
-
* Thread detection functions (available since Ruby 2.3).
|
|
23
|
-
* Used to skip the proxy on Ruby threads.
|
|
24
|
-
*/
|
|
25
|
-
extern int ruby_native_thread_p(void);
|
|
26
21
|
static void scalar_function_init_callback(duckdb_init_info info);
|
|
27
22
|
#endif
|
|
28
23
|
|
|
@@ -251,15 +246,8 @@ static VALUE process_rows(VALUE varg) {
|
|
|
251
246
|
* a GC, and the earlier columns' objects must stay reachable. */
|
|
252
247
|
VALUE args = rb_ary_new_capa((long)arg->col_count);
|
|
253
248
|
|
|
254
|
-
/* Allocate arrays to hold input vectors and their types */
|
|
255
|
-
arg->input_vectors = ALLOC_N(duckdb_vector, arg->col_count);
|
|
256
|
-
arg->input_types = ALLOC_N(duckdb_logical_type, arg->col_count);
|
|
257
|
-
|
|
258
249
|
/* Get all input vectors and their types */
|
|
259
|
-
|
|
260
|
-
arg->input_vectors[j] = duckdb_data_chunk_get_vector(arg->input, j);
|
|
261
|
-
arg->input_types[j] = duckdb_vector_get_column_type(arg->input_vectors[j]);
|
|
262
|
-
}
|
|
250
|
+
rbduckdb_input_columns_load(arg->input, arg->col_count, &arg->input_vectors, &arg->input_types);
|
|
263
251
|
|
|
264
252
|
/* Process each row */
|
|
265
253
|
for (i = 0; i < arg->row_count; i++) {
|
|
@@ -282,24 +270,10 @@ static VALUE process_rows(VALUE varg) {
|
|
|
282
270
|
|
|
283
271
|
static VALUE cleanup_callback(VALUE varg) {
|
|
284
272
|
struct callback_arg *arg = (struct callback_arg *)varg;
|
|
285
|
-
idx_t j;
|
|
286
273
|
|
|
287
|
-
|
|
288
|
-
if (arg->input_types != NULL) {
|
|
289
|
-
for (j = 0; j < arg->col_count; j++) {
|
|
290
|
-
duckdb_destroy_logical_type(&arg->input_types[j]);
|
|
291
|
-
}
|
|
292
|
-
}
|
|
274
|
+
rbduckdb_input_columns_free(arg->input_vectors, arg->input_types, arg->col_count);
|
|
293
275
|
duckdb_destroy_logical_type(&arg->output_type);
|
|
294
276
|
|
|
295
|
-
/* Free allocated memory */
|
|
296
|
-
if (arg->input_types != NULL) {
|
|
297
|
-
xfree(arg->input_types);
|
|
298
|
-
}
|
|
299
|
-
if (arg->input_vectors != NULL) {
|
|
300
|
-
xfree(arg->input_vectors);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
277
|
return Qnil;
|
|
304
278
|
}
|
|
305
279
|
|
|
@@ -307,50 +281,16 @@ static VALUE cleanup_callback(VALUE varg) {
|
|
|
307
281
|
/*
|
|
308
282
|
* Per-worker init for the execute path (DuckDB >= 1.5.0).
|
|
309
283
|
*
|
|
310
|
-
*
|
|
311
|
-
*
|
|
312
|
-
*
|
|
313
|
-
*
|
|
314
|
-
* instead of the shared global executor, so workers run callbacks concurrently.
|
|
315
|
-
* DuckDB invokes rbduckdb_worker_proxy_destroy when the state is freed.
|
|
284
|
+
* Runs once on each worker thread that will execute the callback. Creates the
|
|
285
|
+
* worker's proxy and stores it as per-worker state, so the execute callback
|
|
286
|
+
* dispatches through it rather than the shared global executor. DuckDB calls
|
|
287
|
+
* rbduckdb_worker_proxy_destroy when the state is freed.
|
|
316
288
|
*/
|
|
317
|
-
struct create_proxy_callback_arg {
|
|
318
|
-
struct worker_proxy *proxy;
|
|
319
|
-
};
|
|
320
|
-
|
|
321
|
-
static VALUE create_proxy_callback(VALUE varg) {
|
|
322
|
-
struct create_proxy_callback_arg *arg = (struct create_proxy_callback_arg *)varg;
|
|
323
|
-
arg->proxy = rbduckdb_worker_proxy_create();
|
|
324
|
-
return Qnil;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
/*
|
|
328
|
-
* rbduckdb_worker_proxy_create may raise (NoMemError, Thread.new failure),
|
|
329
|
-
* and the executor runs callbacks unprotected — a raise would longjmp past
|
|
330
|
-
* its done-signaling and block the waiting DuckDB worker forever. Swallow
|
|
331
|
-
* the exception instead: the proxy stays NULL, init sets no state, and the
|
|
332
|
-
* execute callback falls back to the global executor.
|
|
333
|
-
*/
|
|
334
|
-
static void create_proxy_callback_protected(void *user_data) {
|
|
335
|
-
int exception_state;
|
|
336
|
-
|
|
337
|
-
rb_protect(create_proxy_callback, (VALUE)user_data, &exception_state);
|
|
338
|
-
if (exception_state) {
|
|
339
|
-
rb_set_errinfo(Qnil);
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
|
|
343
289
|
static void scalar_function_init_callback(duckdb_init_info info) {
|
|
344
|
-
struct
|
|
345
|
-
|
|
346
|
-
/* A Ruby calling thread runs the callback inline (Case 1/2); no proxy needed. */
|
|
347
|
-
if (ruby_native_thread_p()) return;
|
|
348
|
-
|
|
349
|
-
arg.proxy = NULL;
|
|
350
|
-
rbduckdb_function_executor_dispatch(create_proxy_callback_protected, &arg);
|
|
290
|
+
struct worker_proxy *proxy = rbduckdb_worker_proxy_create_for_current_thread();
|
|
351
291
|
|
|
352
|
-
if (
|
|
353
|
-
duckdb_scalar_function_init_set_state(info,
|
|
292
|
+
if (proxy != NULL) {
|
|
293
|
+
duckdb_scalar_function_init_set_state(info, proxy, rbduckdb_worker_proxy_destroy);
|
|
354
294
|
}
|
|
355
295
|
}
|
|
356
296
|
#endif
|
data/ext/duckdb/table_function.c
CHANGED
|
@@ -22,8 +22,6 @@ static void table_function_init_callback(duckdb_init_info info);
|
|
|
22
22
|
static VALUE table_function_execute(VALUE self);
|
|
23
23
|
static void table_function_execute_callback(duckdb_function_info info, duckdb_data_chunk output);
|
|
24
24
|
#ifdef HAVE_DUCKDB_H_GE_V1_5_0
|
|
25
|
-
/* Thread detection (declared in function_executor.c); used to skip the proxy on Ruby threads. */
|
|
26
|
-
extern int ruby_native_thread_p(void);
|
|
27
25
|
static void table_function_local_init_callback(duckdb_init_info info);
|
|
28
26
|
#endif
|
|
29
27
|
|
|
@@ -185,6 +183,27 @@ static VALUE table_function_add_named_parameter(VALUE self, VALUE name, VALUE lo
|
|
|
185
183
|
return self;
|
|
186
184
|
}
|
|
187
185
|
|
|
186
|
+
static rubyDuckDBTableFunction *table_function_get_settable_ctx(VALUE self, const char *block_msg) {
|
|
187
|
+
rubyDuckDBTableFunction *ctx;
|
|
188
|
+
|
|
189
|
+
if (!rb_block_given_p()) {
|
|
190
|
+
rb_raise(rb_eArgError, "%s", block_msg);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
TypedData_Get_Struct(self, rubyDuckDBTableFunction, &table_function_data_type, ctx);
|
|
194
|
+
|
|
195
|
+
if (!ctx->table_function) {
|
|
196
|
+
rb_raise(eDuckDBError, "Table function is destroyed");
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return ctx;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
static VALUE call_proc_with_one_arg(VALUE arg) {
|
|
203
|
+
VALUE *args = (VALUE *)arg;
|
|
204
|
+
return rb_funcall(args[0], rb_intern("call"), 1, args[1]);
|
|
205
|
+
}
|
|
206
|
+
|
|
188
207
|
/*
|
|
189
208
|
* call-seq:
|
|
190
209
|
* table_function.bind { |bind_info| ... } -> self
|
|
@@ -198,17 +217,7 @@ static VALUE table_function_add_named_parameter(VALUE self, VALUE name, VALUE lo
|
|
|
198
217
|
* end
|
|
199
218
|
*/
|
|
200
219
|
static VALUE table_function_bind(VALUE self) {
|
|
201
|
-
rubyDuckDBTableFunction *ctx;
|
|
202
|
-
|
|
203
|
-
if (!rb_block_given_p()) {
|
|
204
|
-
rb_raise(rb_eArgError, "block is required");
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
TypedData_Get_Struct(self, rubyDuckDBTableFunction, &table_function_data_type, ctx);
|
|
208
|
-
|
|
209
|
-
if (!ctx->table_function) {
|
|
210
|
-
rb_raise(eDuckDBError, "Table function is destroyed");
|
|
211
|
-
}
|
|
220
|
+
rubyDuckDBTableFunction *ctx = table_function_get_settable_ctx(self, "block is required");
|
|
212
221
|
|
|
213
222
|
RB_OBJ_WRITE(self, &ctx->bind_proc, rb_block_proc());
|
|
214
223
|
|
|
@@ -219,11 +228,6 @@ static VALUE table_function_bind(VALUE self) {
|
|
|
219
228
|
return self;
|
|
220
229
|
}
|
|
221
230
|
|
|
222
|
-
static VALUE call_bind_proc(VALUE arg) {
|
|
223
|
-
VALUE *args = (VALUE *)arg;
|
|
224
|
-
return rb_funcall(args[0], rb_intern("call"), 1, args[1]);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
231
|
struct bind_dispatch_arg {
|
|
228
232
|
rubyDuckDBTableFunction *ctx;
|
|
229
233
|
duckdb_bind_info info;
|
|
@@ -240,7 +244,7 @@ static void execute_bind_callback_protected(void *user_data) {
|
|
|
240
244
|
bind_info_ctx->bind_info = darg->info;
|
|
241
245
|
|
|
242
246
|
VALUE call_args[2] = { darg->ctx->bind_proc, bind_info_obj };
|
|
243
|
-
rb_protect(
|
|
247
|
+
rb_protect(call_proc_with_one_arg, (VALUE)call_args, &state);
|
|
244
248
|
|
|
245
249
|
if (state) {
|
|
246
250
|
VALUE msg = rbduckdb_pending_error_message();
|
|
@@ -274,17 +278,7 @@ static void table_function_bind_callback(duckdb_bind_info info) {
|
|
|
274
278
|
* end
|
|
275
279
|
*/
|
|
276
280
|
static VALUE table_function_init(VALUE self) {
|
|
277
|
-
rubyDuckDBTableFunction *ctx;
|
|
278
|
-
|
|
279
|
-
if (!rb_block_given_p()) {
|
|
280
|
-
rb_raise(rb_eArgError, "block is required for init");
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
TypedData_Get_Struct(self, rubyDuckDBTableFunction, &table_function_data_type, ctx);
|
|
284
|
-
|
|
285
|
-
if (!ctx->table_function) {
|
|
286
|
-
rb_raise(eDuckDBError, "Table function is destroyed");
|
|
287
|
-
}
|
|
281
|
+
rubyDuckDBTableFunction *ctx = table_function_get_settable_ctx(self, "block is required for init");
|
|
288
282
|
|
|
289
283
|
RB_OBJ_WRITE(self, &ctx->init_proc, rb_block_proc());
|
|
290
284
|
duckdb_table_function_set_init(ctx->table_function, table_function_init_callback);
|
|
@@ -294,11 +288,6 @@ static VALUE table_function_init(VALUE self) {
|
|
|
294
288
|
return self;
|
|
295
289
|
}
|
|
296
290
|
|
|
297
|
-
static VALUE call_init_proc(VALUE args_val) {
|
|
298
|
-
VALUE *args = (VALUE *)args_val;
|
|
299
|
-
return rb_funcall(args[0], rb_intern("call"), 1, args[1]);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
291
|
struct init_dispatch_arg {
|
|
303
292
|
rubyDuckDBTableFunction *ctx;
|
|
304
293
|
duckdb_init_info info;
|
|
@@ -315,7 +304,7 @@ static void execute_init_callback_protected(void *user_data) {
|
|
|
315
304
|
init_info_ctx->info = darg->info;
|
|
316
305
|
|
|
317
306
|
VALUE call_args[2] = { darg->ctx->init_proc, init_info_obj };
|
|
318
|
-
rb_protect(
|
|
307
|
+
rb_protect(call_proc_with_one_arg, (VALUE)call_args, &state);
|
|
319
308
|
|
|
320
309
|
if (state) {
|
|
321
310
|
VALUE msg = rbduckdb_pending_error_message();
|
|
@@ -351,13 +340,7 @@ static void table_function_init_callback(duckdb_init_info info) {
|
|
|
351
340
|
* end
|
|
352
341
|
*/
|
|
353
342
|
static VALUE table_function_execute(VALUE self) {
|
|
354
|
-
rubyDuckDBTableFunction *ctx;
|
|
355
|
-
|
|
356
|
-
if (!rb_block_given_p()) {
|
|
357
|
-
rb_raise(rb_eArgError, "block is required for execute");
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
TypedData_Get_Struct(self, rubyDuckDBTableFunction, &table_function_data_type, ctx);
|
|
343
|
+
rubyDuckDBTableFunction *ctx = table_function_get_settable_ctx(self, "block is required for execute");
|
|
361
344
|
|
|
362
345
|
RB_OBJ_WRITE(self, &ctx->execute_proc, rb_block_proc());
|
|
363
346
|
duckdb_table_function_set_function(ctx->table_function, table_function_execute_callback);
|
|
@@ -431,48 +414,16 @@ static void table_function_execute_callback(duckdb_function_info info, duckdb_da
|
|
|
431
414
|
/*
|
|
432
415
|
* Per-worker init for the execute path (DuckDB >= 1.5.0).
|
|
433
416
|
*
|
|
434
|
-
*
|
|
435
|
-
*
|
|
436
|
-
*
|
|
437
|
-
*
|
|
438
|
-
* instead of the shared global executor, so workers run callbacks concurrently.
|
|
439
|
-
* DuckDB invokes rbduckdb_worker_proxy_destroy when the local state is freed.
|
|
440
|
-
*/
|
|
441
|
-
struct create_proxy_callback_arg {
|
|
442
|
-
struct worker_proxy *proxy;
|
|
443
|
-
};
|
|
444
|
-
|
|
445
|
-
static VALUE create_proxy_callback(VALUE varg) {
|
|
446
|
-
struct create_proxy_callback_arg *arg = (struct create_proxy_callback_arg *)varg;
|
|
447
|
-
arg->proxy = rbduckdb_worker_proxy_create();
|
|
448
|
-
return Qnil;
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
/*
|
|
452
|
-
* rbduckdb_worker_proxy_create may raise (NoMemError, Thread.new failure).
|
|
453
|
-
* Swallow it: the proxy stays NULL, local_init sets no state, and the execute
|
|
454
|
-
* callback falls back to the global executor.
|
|
417
|
+
* Runs once on each worker thread that will execute the callback. Creates the
|
|
418
|
+
* worker's proxy and stores it as thread-local init data, so the execute callback
|
|
419
|
+
* dispatches through it rather than the shared global executor. DuckDB calls
|
|
420
|
+
* rbduckdb_worker_proxy_destroy when the local state is freed.
|
|
455
421
|
*/
|
|
456
|
-
static void create_proxy_callback_protected(void *user_data) {
|
|
457
|
-
int exception_state;
|
|
458
|
-
|
|
459
|
-
rb_protect(create_proxy_callback, (VALUE)user_data, &exception_state);
|
|
460
|
-
if (exception_state) {
|
|
461
|
-
rb_set_errinfo(Qnil);
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
|
|
465
422
|
static void table_function_local_init_callback(duckdb_init_info info) {
|
|
466
|
-
struct
|
|
467
|
-
|
|
468
|
-
/* A Ruby calling thread runs the callback inline (Case 1/2); no proxy needed. */
|
|
469
|
-
if (ruby_native_thread_p()) return;
|
|
470
|
-
|
|
471
|
-
arg.proxy = NULL;
|
|
472
|
-
rbduckdb_function_executor_dispatch(create_proxy_callback_protected, &arg);
|
|
423
|
+
struct worker_proxy *proxy = rbduckdb_worker_proxy_create_for_current_thread();
|
|
473
424
|
|
|
474
|
-
if (
|
|
475
|
-
duckdb_init_set_init_data(info,
|
|
425
|
+
if (proxy != NULL) {
|
|
426
|
+
duckdb_init_set_init_data(info, proxy, rbduckdb_worker_proxy_destroy);
|
|
476
427
|
}
|
|
477
428
|
}
|
|
478
429
|
#endif
|
data/ext/duckdb/util.c
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
#include "ruby-duckdb.h"
|
|
2
2
|
|
|
3
|
+
#define MICROS_PER_DAY 86400000000LL
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
* Mirrors DuckDB's Date::IsValid. duckdb_to_date throws a C++ exception
|
|
7
|
+
* for dates it rejects, which would abort the process.
|
|
8
|
+
*/
|
|
9
|
+
static int is_valid_date(int32_t year, int32_t month, int32_t day) {
|
|
10
|
+
static const int32_t days_in_month[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
|
11
|
+
int32_t max_day;
|
|
12
|
+
|
|
13
|
+
if (month < 1 || month > 12 || day < 1) {
|
|
14
|
+
return 0;
|
|
15
|
+
}
|
|
16
|
+
if (year < -5877641 || (year == -5877641 && (month < 6 || (month == 6 && day < 25)))) {
|
|
17
|
+
return 0;
|
|
18
|
+
}
|
|
19
|
+
if (year > 5881580 || (year == 5881580 && (month > 7 || (month == 7 && day > 10)))) {
|
|
20
|
+
return 0;
|
|
21
|
+
}
|
|
22
|
+
max_day = days_in_month[month];
|
|
23
|
+
if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)) {
|
|
24
|
+
max_day = 29;
|
|
25
|
+
}
|
|
26
|
+
return day <= max_day;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
static duckdb_date to_duckdb_date(duckdb_date_struct dt_struct) {
|
|
30
|
+
if (!is_valid_date(dt_struct.year, dt_struct.month, dt_struct.day)) {
|
|
31
|
+
rb_raise(rb_eRangeError, "date out of range: %d-%02d-%02d", dt_struct.year, dt_struct.month, dt_struct.day);
|
|
32
|
+
}
|
|
33
|
+
return duckdb_to_date(dt_struct);
|
|
34
|
+
}
|
|
35
|
+
|
|
3
36
|
duckdb_date rbduckdb_to_duckdb_date_from_value(VALUE year, VALUE month, VALUE day) {
|
|
4
37
|
duckdb_date_struct dt_struct;
|
|
5
38
|
|
|
@@ -7,7 +40,7 @@ duckdb_date rbduckdb_to_duckdb_date_from_value(VALUE year, VALUE month, VALUE da
|
|
|
7
40
|
dt_struct.month = NUM2INT(month);
|
|
8
41
|
dt_struct.day = NUM2INT(day);
|
|
9
42
|
|
|
10
|
-
return
|
|
43
|
+
return to_duckdb_date(dt_struct);
|
|
11
44
|
}
|
|
12
45
|
|
|
13
46
|
duckdb_time rbduckdb_to_duckdb_time_from_value(VALUE hour, VALUE min, VALUE sec, VALUE micros) {
|
|
@@ -47,6 +80,9 @@ duckdb_timestamp rbduckdb_to_duckdb_timestamp_from_time_value(VALUE time_obj) {
|
|
|
47
80
|
|
|
48
81
|
duckdb_timestamp rbduckdb_to_duckdb_timestamp_from_value(VALUE year, VALUE month, VALUE day, VALUE hour, VALUE min, VALUE sec, VALUE micros) {
|
|
49
82
|
duckdb_timestamp_struct timestamp_st;
|
|
83
|
+
duckdb_timestamp result;
|
|
84
|
+
int64_t days;
|
|
85
|
+
int64_t time_micros;
|
|
50
86
|
|
|
51
87
|
timestamp_st.date.year = NUM2INT(year);
|
|
52
88
|
timestamp_st.date.month = NUM2INT(month);
|
|
@@ -56,7 +92,22 @@ duckdb_timestamp rbduckdb_to_duckdb_timestamp_from_value(VALUE year, VALUE month
|
|
|
56
92
|
timestamp_st.time.sec = NUM2INT(sec);
|
|
57
93
|
timestamp_st.time.micros = NUM2INT(micros);
|
|
58
94
|
|
|
59
|
-
|
|
95
|
+
/* Mirrors DuckDB's Timestamp::TryFromDatetime; duckdb_to_timestamp throws on failure. */
|
|
96
|
+
days = to_duckdb_date(timestamp_st.date).days;
|
|
97
|
+
time_micros = duckdb_to_time(timestamp_st.time).micros;
|
|
98
|
+
if (days > INT64_MAX / MICROS_PER_DAY || days < INT64_MIN / MICROS_PER_DAY) {
|
|
99
|
+
rb_raise(rb_eRangeError, "timestamp out of range");
|
|
100
|
+
}
|
|
101
|
+
result.micros = days * MICROS_PER_DAY;
|
|
102
|
+
if ((time_micros > 0 && result.micros > INT64_MAX - time_micros) ||
|
|
103
|
+
(time_micros < 0 && result.micros < INT64_MIN - time_micros)) {
|
|
104
|
+
rb_raise(rb_eRangeError, "timestamp out of range");
|
|
105
|
+
}
|
|
106
|
+
result.micros += time_micros;
|
|
107
|
+
if (!duckdb_is_finite_timestamp(result)) {
|
|
108
|
+
rb_raise(rb_eRangeError, "timestamp out of range");
|
|
109
|
+
}
|
|
110
|
+
return result;
|
|
60
111
|
}
|
|
61
112
|
|
|
62
113
|
void rbduckdb_to_duckdb_interval_from_value(duckdb_interval* interval, VALUE months, VALUE days, VALUE micros) {
|
data/lib/duckdb/interval.rb
CHANGED
|
@@ -103,12 +103,10 @@ module DuckDB
|
|
|
103
103
|
|
|
104
104
|
def matched_to_i(matched) # :nodoc:
|
|
105
105
|
sign = to_sign(matched)
|
|
106
|
-
sec =
|
|
106
|
+
year, month, day, hour, min, sec = %i[year month day hour min sec].map { |k| matched[k].to_i }
|
|
107
107
|
usec = to_usec(matched)
|
|
108
108
|
usec *= -1 if sec.negative?
|
|
109
|
-
value = [
|
|
110
|
-
to_year(matched), to_month(matched), to_day(matched), to_hour(matched), to_min(matched), sec, usec
|
|
111
|
-
]
|
|
109
|
+
value = [year, month, day, hour, min, sec, usec]
|
|
112
110
|
sign.positive? ? value : value.map { |v| v * sign }
|
|
113
111
|
end
|
|
114
112
|
|
|
@@ -116,30 +114,6 @@ module DuckDB
|
|
|
116
114
|
matched[:negativ] == '-' ? -1 : 1
|
|
117
115
|
end
|
|
118
116
|
|
|
119
|
-
def to_year(matched) # :nodoc:
|
|
120
|
-
matched[:year].to_i
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
def to_month(matched) # :nodoc:
|
|
124
|
-
matched[:month].to_i
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
def to_day(matched) # :nodoc:
|
|
128
|
-
matched[:day].to_i
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
def to_hour(matched) # :nodoc:
|
|
132
|
-
matched[:hour].to_i
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
def to_min(matched) # :nodoc:
|
|
136
|
-
matched[:min].to_i
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
def to_sec(matched) # :nodoc:
|
|
140
|
-
matched[:sec].to_i
|
|
141
|
-
end
|
|
142
|
-
|
|
143
117
|
def to_usec(matched) # :nodoc:
|
|
144
118
|
matched[:usec].to_s.ljust(6, '0')[0, 6].to_i
|
|
145
119
|
end
|
|
@@ -332,6 +332,11 @@ module DuckDB
|
|
|
332
332
|
decimal = _parse_deciaml(value)
|
|
333
333
|
lower, upper = decimal_to_hugeint(decimal)
|
|
334
334
|
width = _decimal_width(decimal)
|
|
335
|
+
# _bind_decimal narrows width to uint8, and DuckDB aborts on a mismatched width.
|
|
336
|
+
unless RANGE_DECIMAL_WIDTH.cover?(width)
|
|
337
|
+
raise ArgumentError, "value out of range for DECIMAL width (#{RANGE_DECIMAL_WIDTH})"
|
|
338
|
+
end
|
|
339
|
+
|
|
335
340
|
_bind_decimal(index, lower, upper, width, decimal.scale)
|
|
336
341
|
end
|
|
337
342
|
|
|
@@ -11,8 +11,7 @@ module DuckDB
|
|
|
11
11
|
def initialize(name)
|
|
12
12
|
raise TypeError, "#{name.class} is not a String or Symbol" unless name.is_a?(String) || name.is_a?(Symbol)
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
_initialize(@name)
|
|
14
|
+
_initialize(name.to_s)
|
|
16
15
|
end
|
|
17
16
|
|
|
18
17
|
# @param scalar_function [DuckDB::ScalarFunction] the overload to add
|
data/lib/duckdb/version.rb
CHANGED