duckdb 1.5.3.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 +47 -0
- data/CLAUDE.md +4 -0
- data/README.md +52 -0
- data/duckdb.gemspec +1 -1
- data/ext/duckdb/aggregate_function.c +0 -1
- data/ext/duckdb/appender.c +17 -0
- data/ext/duckdb/arrow_array_stream.c +226 -0
- data/ext/duckdb/arrow_array_stream.h +61 -0
- data/ext/duckdb/arrow_import.c +165 -0
- data/ext/duckdb/arrow_import.h +6 -0
- data/ext/duckdb/blob.c +1 -1
- data/ext/duckdb/blob.h +1 -2
- data/ext/duckdb/config.c +1 -1
- data/ext/duckdb/config.h +1 -1
- data/ext/duckdb/connection.c +26 -4
- data/ext/duckdb/converter.h +1 -0
- data/ext/duckdb/conveter.c +39 -9
- data/ext/duckdb/data_chunk.c +10 -0
- data/ext/duckdb/data_chunk.h +1 -0
- data/ext/duckdb/duckdb.c +13 -11
- data/ext/duckdb/error.c +13 -1
- data/ext/duckdb/error.h +2 -3
- data/ext/duckdb/function_executor.c +308 -2
- data/ext/duckdb/function_executor.h +44 -0
- data/ext/duckdb/function_vector.c +14 -0
- data/ext/duckdb/prepared_statement.c +141 -5
- data/ext/duckdb/result.c +49 -3
- data/ext/duckdb/result.h +11 -0
- data/ext/duckdb/ruby-duckdb.h +3 -0
- data/ext/duckdb/scalar_function.c +97 -29
- data/ext/duckdb/scalar_function.h +2 -4
- data/ext/duckdb/scalar_function_bind_info.c +13 -13
- data/ext/duckdb/scalar_function_bind_info.h +1 -1
- data/ext/duckdb/scalar_function_set.c +9 -9
- data/ext/duckdb/scalar_function_set.h +2 -2
- data/ext/duckdb/table_description.c +19 -19
- data/ext/duckdb/table_description.h +1 -1
- data/ext/duckdb/table_function.c +94 -28
- data/ext/duckdb/table_function.h +2 -2
- data/ext/duckdb/table_function_bind_info.c +20 -20
- data/ext/duckdb/table_function_bind_info.h +2 -2
- data/ext/duckdb/table_function_function_info.c +5 -5
- data/ext/duckdb/table_function_function_info.h +2 -2
- data/ext/duckdb/table_function_init_info.c +70 -5
- data/ext/duckdb/table_function_init_info.h +2 -2
- data/ext/duckdb/util.c +8 -0
- data/ext/duckdb/util.h +1 -0
- data/ext/duckdb/value.c +526 -1
- data/lib/duckdb/appender.rb +23 -0
- data/lib/duckdb/arrow_array_stream.rb +33 -0
- data/lib/duckdb/connection.rb +73 -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 +32 -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 +2 -0
- metadata +10 -3
data/lib/duckdb/value.rb
CHANGED
|
@@ -238,8 +238,408 @@ module DuckDB
|
|
|
238
238
|
_create_decimal(lower, upper, width, value.scale)
|
|
239
239
|
end
|
|
240
240
|
|
|
241
|
+
# Creates a DuckDB::Value of DATE type.
|
|
242
|
+
# The argument is parsed leniently: a Date, a Time, or a String
|
|
243
|
+
# accepted by Date.parse, matching Appender#append_date.
|
|
244
|
+
#
|
|
245
|
+
# value = DuckDB::Value.create_date(Date.new(2026, 7, 12))
|
|
246
|
+
# value = DuckDB::Value.create_date('2026-07-12')
|
|
247
|
+
#
|
|
248
|
+
# @param value [Date, Time, String] the date value.
|
|
249
|
+
# @return [DuckDB::Value] the created Value object.
|
|
250
|
+
# @raise [ArgumentError] if +value+ cannot be parsed to a Date.
|
|
251
|
+
def create_date(value)
|
|
252
|
+
date = _parse_date(value)
|
|
253
|
+
_create_date(date.year, date.month, date.day)
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
# Creates a DuckDB::Value of TIME type (microsecond precision).
|
|
257
|
+
# The argument is parsed leniently: a Time or a String accepted by
|
|
258
|
+
# Time.parse, matching Appender#append_time.
|
|
259
|
+
#
|
|
260
|
+
# value = DuckDB::Value.create_time(Time.now)
|
|
261
|
+
# value = DuckDB::Value.create_time('12:34:56.789')
|
|
262
|
+
#
|
|
263
|
+
# @param value [Time, String] the time value.
|
|
264
|
+
# @return [DuckDB::Value] the created Value object.
|
|
265
|
+
# @raise [ArgumentError] if +value+ cannot be parsed to a Time.
|
|
266
|
+
def create_time(value)
|
|
267
|
+
time = _parse_time(value)
|
|
268
|
+
_create_time(time.hour, time.min, time.sec, time.usec)
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
# Creates a DuckDB::Value of TIME_NS type (nanosecond precision).
|
|
272
|
+
# The argument is parsed leniently: a Time or a String accepted by
|
|
273
|
+
# Time.parse. Nanoseconds are preserved.
|
|
274
|
+
#
|
|
275
|
+
# value = DuckDB::Value.create_time_ns(Time.now)
|
|
276
|
+
# value = DuckDB::Value.create_time_ns('12:34:56.123456789')
|
|
277
|
+
#
|
|
278
|
+
# @param value [Time, String] the time value.
|
|
279
|
+
# @return [DuckDB::Value] the created Value object.
|
|
280
|
+
# @raise [ArgumentError] if +value+ cannot be parsed to a Time.
|
|
281
|
+
def create_time_ns(value)
|
|
282
|
+
time = _parse_time(value)
|
|
283
|
+
_create_time_ns(time.hour, time.min, time.sec, time.nsec)
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
# Creates a DuckDB::Value of TIMETZ type (time with UTC offset).
|
|
287
|
+
# The argument is parsed leniently: a Time (the offset is taken from
|
|
288
|
+
# the Time) or a String accepted by Time.parse.
|
|
289
|
+
#
|
|
290
|
+
# value = DuckDB::Value.create_time_tz(Time.now)
|
|
291
|
+
# value = DuckDB::Value.create_time_tz('12:34:56.789012+05:30')
|
|
292
|
+
#
|
|
293
|
+
# @param value [Time, String] the time value.
|
|
294
|
+
# @return [DuckDB::Value] the created Value object.
|
|
295
|
+
# @raise [ArgumentError] if +value+ cannot be parsed to a Time.
|
|
296
|
+
def create_time_tz(value)
|
|
297
|
+
time = _parse_time(value)
|
|
298
|
+
micros = (((((time.hour * 60) + time.min) * 60) + time.sec) * 1_000_000) + time.usec
|
|
299
|
+
_create_time_tz(micros, time.utc_offset)
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
# Creates a DuckDB::Value of TIMESTAMP type (microsecond precision).
|
|
303
|
+
# The argument is parsed leniently: a Time, a Date, or a String
|
|
304
|
+
# accepted by Time.parse, matching Appender#append_timestamp.
|
|
305
|
+
#
|
|
306
|
+
# value = DuckDB::Value.create_timestamp(Time.now)
|
|
307
|
+
# value = DuckDB::Value.create_timestamp('2026-07-12 12:34:56.789')
|
|
308
|
+
#
|
|
309
|
+
# @param value [Time, Date, String] the timestamp value.
|
|
310
|
+
# @return [DuckDB::Value] the created Value object.
|
|
311
|
+
# @raise [ArgumentError] if +value+ cannot be parsed to a Time.
|
|
312
|
+
def create_timestamp(value)
|
|
313
|
+
time = to_time(value)
|
|
314
|
+
_create_timestamp(time.year, time.month, time.day, time.hour, time.min, time.sec, time.usec)
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
# Creates a DuckDB::Value of TIMESTAMP_S type (second precision).
|
|
318
|
+
# The argument is parsed leniently: a Time, a Date, or a String
|
|
319
|
+
# accepted by Time.parse. Sub-second input is truncated to seconds.
|
|
320
|
+
#
|
|
321
|
+
# value = DuckDB::Value.create_timestamp_s(Time.now)
|
|
322
|
+
# value = DuckDB::Value.create_timestamp_s('2026-07-12 12:34:56')
|
|
323
|
+
#
|
|
324
|
+
# @param value [Time, Date, String] the timestamp value.
|
|
325
|
+
# @return [DuckDB::Value] the created Value object.
|
|
326
|
+
# @raise [ArgumentError] if +value+ cannot be parsed to a Time.
|
|
327
|
+
def create_timestamp_s(value)
|
|
328
|
+
time = to_time(value)
|
|
329
|
+
_create_timestamp_s(time.year, time.month, time.day, time.hour, time.min, time.sec)
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
# Creates a DuckDB::Value of TIMESTAMP_MS type (millisecond precision).
|
|
333
|
+
# The argument is parsed leniently: a Time, a Date, or a String
|
|
334
|
+
# accepted by Time.parse. Sub-millisecond input is truncated.
|
|
335
|
+
#
|
|
336
|
+
# value = DuckDB::Value.create_timestamp_ms(Time.now)
|
|
337
|
+
# value = DuckDB::Value.create_timestamp_ms('2026-07-12 12:34:56.789')
|
|
338
|
+
#
|
|
339
|
+
# @param value [Time, Date, String] the timestamp value.
|
|
340
|
+
# @return [DuckDB::Value] the created Value object.
|
|
341
|
+
# @raise [ArgumentError] if +value+ cannot be parsed to a Time.
|
|
342
|
+
def create_timestamp_ms(value)
|
|
343
|
+
time = to_time(value)
|
|
344
|
+
_create_timestamp_ms(time.year, time.month, time.day, time.hour, time.min, time.sec, time.usec)
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
# Creates a DuckDB::Value of TIMESTAMP_NS type (nanosecond precision).
|
|
348
|
+
# The argument is parsed leniently: a Time, a Date, or a String
|
|
349
|
+
# accepted by Time.parse. Nanoseconds are preserved.
|
|
350
|
+
#
|
|
351
|
+
# value = DuckDB::Value.create_timestamp_ns(Time.now)
|
|
352
|
+
# value = DuckDB::Value.create_timestamp_ns('2026-07-12 12:34:56.123456789')
|
|
353
|
+
#
|
|
354
|
+
# @param value [Time, Date, String] the timestamp value.
|
|
355
|
+
# @return [DuckDB::Value] the created Value object.
|
|
356
|
+
# @raise [ArgumentError] if +value+ cannot be parsed to a Time.
|
|
357
|
+
def create_timestamp_ns(value)
|
|
358
|
+
time = to_time(value)
|
|
359
|
+
_create_timestamp_ns(time.year, time.month, time.day, time.hour, time.min, time.sec, time.nsec)
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
# Creates a DuckDB::Value of TIMESTAMP WITH TIME ZONE type.
|
|
363
|
+
# The argument is parsed leniently: a Time, a Date, or a String
|
|
364
|
+
# accepted by Time.parse. The instant is stored (as microseconds since
|
|
365
|
+
# the Unix epoch), so the input's UTC offset is honored.
|
|
366
|
+
#
|
|
367
|
+
# value = DuckDB::Value.create_timestamp_tz(Time.now)
|
|
368
|
+
# value = DuckDB::Value.create_timestamp_tz('2026-07-12 12:34:56.789+05:30')
|
|
369
|
+
#
|
|
370
|
+
# @param value [Time, Date, String] the timestamp value.
|
|
371
|
+
# @return [DuckDB::Value] the created Value object.
|
|
372
|
+
# @raise [ArgumentError] if +value+ cannot be parsed to a Time.
|
|
373
|
+
def create_timestamp_tz(value)
|
|
374
|
+
time = to_time(value)
|
|
375
|
+
_create_timestamp_tz((time.to_i * 1_000_000) + time.usec)
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
# Creates a DuckDB::Value of INTERVAL type.
|
|
379
|
+
# Unlike the other temporal creators, the input is strict: it must be
|
|
380
|
+
# a DuckDB::Interval.
|
|
381
|
+
#
|
|
382
|
+
# interval = DuckDB::Interval.new(interval_months: 14, interval_days: 3, interval_micros: 12_000_000)
|
|
383
|
+
# value = DuckDB::Value.create_interval(interval)
|
|
384
|
+
#
|
|
385
|
+
# @param value [DuckDB::Interval] the interval value.
|
|
386
|
+
# @return [DuckDB::Value] the created Value object.
|
|
387
|
+
# @raise [ArgumentError] if +value+ is not a DuckDB::Interval.
|
|
388
|
+
def create_interval(value)
|
|
389
|
+
check_type!(value, DuckDB::Interval)
|
|
390
|
+
|
|
391
|
+
_create_interval(value.interval_months, value.interval_days, value.interval_micros)
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
# Creates a DuckDB::Value of ENUM type.
|
|
395
|
+
# The first argument is the enum type: an Array of member names (as
|
|
396
|
+
# accepted by DuckDB::LogicalType.create_enum) or an ENUM
|
|
397
|
+
# DuckDB::LogicalType.
|
|
398
|
+
# The second argument is the member: its name (String or Symbol) or
|
|
399
|
+
# its 0-based index (Integer).
|
|
400
|
+
#
|
|
401
|
+
# value = DuckDB::Value.create_enum(%w[happy sad neutral], 'sad')
|
|
402
|
+
#
|
|
403
|
+
# # equivalent, with an explicit ENUM logical type:
|
|
404
|
+
# enum_type = DuckDB::LogicalType.create_enum('happy', 'sad', 'neutral')
|
|
405
|
+
# value = DuckDB::Value.create_enum(enum_type, 'sad')
|
|
406
|
+
#
|
|
407
|
+
# @param enum_type [Array<String>, DuckDB::LogicalType] the member names or ENUM logical type.
|
|
408
|
+
# @param member [String, Symbol, Integer] the member name or 0-based index.
|
|
409
|
+
# @return [DuckDB::Value] the created Value object.
|
|
410
|
+
# @raise [ArgumentError] if enum_type is not an Array or an ENUM
|
|
411
|
+
# DuckDB::LogicalType, or member is not a member of the enum.
|
|
412
|
+
def create_enum(enum_type, member)
|
|
413
|
+
enum_type = DuckDB::LogicalType.create_enum(*enum_type) if enum_type.is_a?(Array)
|
|
414
|
+
check_type!(enum_type, DuckDB::LogicalType)
|
|
415
|
+
raise ArgumentError, "expected ENUM LogicalType, got #{enum_type.type}" unless enum_type.type == :enum
|
|
416
|
+
|
|
417
|
+
_create_enum(enum_type, enum_member_index(enum_type, member))
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
# Creates a DuckDB::Value of UNION type.
|
|
421
|
+
# The first argument is the union type: a Hash of member name to
|
|
422
|
+
# member type (as accepted by DuckDB::LogicalType.create_union) or a
|
|
423
|
+
# UNION DuckDB::LogicalType.
|
|
424
|
+
# The second argument is the member tag (String or Symbol), and the
|
|
425
|
+
# third is the member value as a DuckDB::Value of the tagged type.
|
|
426
|
+
#
|
|
427
|
+
# value = DuckDB::Value.create_union({ num: :integer, str: :varchar }, :num, DuckDB::Value.create_int32(42))
|
|
428
|
+
# value.to_ruby #=> 42
|
|
429
|
+
#
|
|
430
|
+
# # equivalent, with an explicit UNION logical type:
|
|
431
|
+
# union_type = DuckDB::LogicalType.create_union(num: :integer, str: :varchar)
|
|
432
|
+
# value = DuckDB::Value.create_union(union_type, :num, DuckDB::Value.create_int32(42))
|
|
433
|
+
#
|
|
434
|
+
# @param union_type [Hash, DuckDB::LogicalType] the member spec or UNION logical type.
|
|
435
|
+
# @param tag [String, Symbol] the member tag.
|
|
436
|
+
# @param member_value [DuckDB::Value] the member value.
|
|
437
|
+
# @return [DuckDB::Value] the created Value object.
|
|
438
|
+
# @raise [ArgumentError] if union_type is not a Hash or a UNION
|
|
439
|
+
# DuckDB::LogicalType, tag is not a member of the union, or
|
|
440
|
+
# member_value is not a DuckDB::Value.
|
|
441
|
+
# @raise [DuckDB::Error] if member_value does not match the tagged
|
|
442
|
+
# member's type.
|
|
443
|
+
def create_union(union_type, tag, member_value)
|
|
444
|
+
union_type = DuckDB::LogicalType.create_union(**union_type) if union_type.is_a?(Hash)
|
|
445
|
+
check_type!(union_type, DuckDB::LogicalType)
|
|
446
|
+
raise ArgumentError, "expected UNION LogicalType, got #{union_type.type}" unless union_type.type == :union
|
|
447
|
+
|
|
448
|
+
check_type!(member_value, DuckDB::Value)
|
|
449
|
+
tag_index = union_type.each_member_name.find_index(tag.to_s)
|
|
450
|
+
raise ArgumentError, "`#{tag}` is not a member of the union" if tag_index.nil?
|
|
451
|
+
|
|
452
|
+
_create_union(union_type, tag_index, member_value)
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
# Creates a DuckDB::Value of BIT (bitstring) type.
|
|
456
|
+
#
|
|
457
|
+
# value = DuckDB::Value.create_bit('0101')
|
|
458
|
+
#
|
|
459
|
+
# @param value [String] the bitstring: '0'/'1' characters, MSB first.
|
|
460
|
+
# @return [DuckDB::Value] the created Value object.
|
|
461
|
+
# @raise [ArgumentError] if +value+ is not a non-empty String of '0'
|
|
462
|
+
# and '1' characters.
|
|
463
|
+
def create_bit(value)
|
|
464
|
+
check_type!(value, String)
|
|
465
|
+
raise ArgumentError, "expected a String of '0'/'1' characters" unless value.match?(/\A[01]+\z/)
|
|
466
|
+
|
|
467
|
+
pad = -value.length % 8
|
|
468
|
+
_create_bit("#{pad.chr}#{[('1' * pad) + value].pack('B*')}".b)
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
# Creates a DuckDB::Value of BIGNUM (arbitrary-precision integer)
|
|
472
|
+
# type.
|
|
473
|
+
#
|
|
474
|
+
# value = DuckDB::Value.create_bignum(2**200)
|
|
475
|
+
#
|
|
476
|
+
# @param value [Integer] the integer value, of arbitrary size.
|
|
477
|
+
# @return [DuckDB::Value] the created Value object.
|
|
478
|
+
# @raise [ArgumentError] if +value+ is not an Integer.
|
|
479
|
+
def create_bignum(value)
|
|
480
|
+
check_type!(value, Integer)
|
|
481
|
+
|
|
482
|
+
hex = value.abs.to_s(16)
|
|
483
|
+
hex = "0#{hex}" if hex.length.odd?
|
|
484
|
+
_create_bignum([hex].pack('H*'), value.negative?)
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
# Creates a DuckDB::Value of LIST type.
|
|
488
|
+
# The first argument is the element type: a Symbol (e.g. :integer) or a
|
|
489
|
+
# DuckDB::LogicalType.
|
|
490
|
+
# The second argument is an Array of DuckDB::Value elements.
|
|
491
|
+
#
|
|
492
|
+
# values = [1, 2, 3].map { |i| DuckDB::Value.create_int32(i) }
|
|
493
|
+
# list = DuckDB::Value.create_list(:integer, values)
|
|
494
|
+
#
|
|
495
|
+
# @param child_type [Symbol, DuckDB::LogicalType] the element logical type.
|
|
496
|
+
# @param values [Array<DuckDB::Value>] the list elements.
|
|
497
|
+
# @return [DuckDB::Value] the created Value object.
|
|
498
|
+
# @raise [DuckDB::Error] if child_type cannot be resolved to a
|
|
499
|
+
# DuckDB::LogicalType.
|
|
500
|
+
# @raise [ArgumentError] if values is not an Array of DuckDB::Value.
|
|
501
|
+
def create_list(child_type, values)
|
|
502
|
+
check_value_array!(values)
|
|
503
|
+
|
|
504
|
+
_create_list(DuckDB::LogicalType.resolve(child_type), values)
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
# Creates a DuckDB::Value of ARRAY type. The array size is the number
|
|
508
|
+
# of elements given.
|
|
509
|
+
# The first argument is the element type: a Symbol (e.g. :integer) or a
|
|
510
|
+
# DuckDB::LogicalType.
|
|
511
|
+
# The second argument is an Array of DuckDB::Value elements.
|
|
512
|
+
#
|
|
513
|
+
# values = [1, 2, 3].map { |i| DuckDB::Value.create_int32(i) }
|
|
514
|
+
# array = DuckDB::Value.create_array(:integer, values)
|
|
515
|
+
#
|
|
516
|
+
# @param child_type [Symbol, DuckDB::LogicalType] the element logical type.
|
|
517
|
+
# @param values [Array<DuckDB::Value>] the array elements.
|
|
518
|
+
# @return [DuckDB::Value] the created Value object.
|
|
519
|
+
# @raise [DuckDB::Error] if child_type cannot be resolved to a
|
|
520
|
+
# DuckDB::LogicalType.
|
|
521
|
+
# @raise [ArgumentError] if values is not an Array of DuckDB::Value.
|
|
522
|
+
def create_array(child_type, values)
|
|
523
|
+
check_value_array!(values)
|
|
524
|
+
|
|
525
|
+
_create_array(DuckDB::LogicalType.resolve(child_type), values)
|
|
526
|
+
end
|
|
527
|
+
|
|
528
|
+
# Creates a DuckDB::Value of STRUCT type.
|
|
529
|
+
# The first argument is the field spec: a Hash of field name to field
|
|
530
|
+
# type (a Symbol or a DuckDB::LogicalType, as accepted by
|
|
531
|
+
# DuckDB::LogicalType.create_struct) or a STRUCT DuckDB::LogicalType.
|
|
532
|
+
# The second argument is an Array of DuckDB::Value field values,
|
|
533
|
+
# positional, matching the struct's field order.
|
|
534
|
+
#
|
|
535
|
+
# values = [DuckDB::Value.create_int32(1), DuckDB::Value.create_varchar('x')]
|
|
536
|
+
# struct = DuckDB::Value.create_struct({ a: :integer, b: :varchar }, values)
|
|
537
|
+
#
|
|
538
|
+
# # equivalent, with an explicit STRUCT logical type:
|
|
539
|
+
# struct_type = DuckDB::LogicalType.create_struct(a: :integer, b: :varchar)
|
|
540
|
+
# struct = DuckDB::Value.create_struct(struct_type, values)
|
|
541
|
+
#
|
|
542
|
+
# @param struct_type [Hash, DuckDB::LogicalType] the field spec or STRUCT logical type.
|
|
543
|
+
# @param values [Array<DuckDB::Value>] the field values, in field order.
|
|
544
|
+
# @return [DuckDB::Value] the created Value object.
|
|
545
|
+
# @raise [DuckDB::Error] if a field type in the Hash cannot be resolved
|
|
546
|
+
# to a DuckDB::LogicalType.
|
|
547
|
+
# @raise [ArgumentError] if struct_type is not a Hash or a STRUCT
|
|
548
|
+
# DuckDB::LogicalType, values is not an Array of DuckDB::Value, or
|
|
549
|
+
# values.size does not match the struct's field count.
|
|
550
|
+
def create_struct(struct_type, values)
|
|
551
|
+
struct_type = DuckDB::LogicalType.create_struct(**struct_type) if struct_type.is_a?(Hash)
|
|
552
|
+
check_type!(struct_type, DuckDB::LogicalType)
|
|
553
|
+
raise ArgumentError, "expected STRUCT LogicalType, got #{struct_type.type}" unless struct_type.type == :struct
|
|
554
|
+
|
|
555
|
+
check_value_array!(values)
|
|
556
|
+
# The C API's duckdb_create_struct_value reads exactly child_count
|
|
557
|
+
# values from the buffer with no count argument, so a size mismatch
|
|
558
|
+
# here would cause an out-of-bounds read in C.
|
|
559
|
+
n = struct_type.child_count
|
|
560
|
+
raise ArgumentError, "expected #{n} values for STRUCT, got #{values.size}" unless values.size == n
|
|
561
|
+
|
|
562
|
+
_create_struct(struct_type, values)
|
|
563
|
+
end
|
|
564
|
+
|
|
565
|
+
# Creates a DuckDB::Value of MAP type.
|
|
566
|
+
# The first argument is the type spec: a one-pair Hash of key type to
|
|
567
|
+
# value type (each a Symbol or a DuckDB::LogicalType, as accepted by
|
|
568
|
+
# DuckDB::LogicalType.create_map) or a MAP DuckDB::LogicalType.
|
|
569
|
+
# The second argument is a Hash of DuckDB::Value keys to DuckDB::Value
|
|
570
|
+
# values.
|
|
571
|
+
#
|
|
572
|
+
# entries = {
|
|
573
|
+
# DuckDB::Value.create_varchar('a') => DuckDB::Value.create_int32(1),
|
|
574
|
+
# DuckDB::Value.create_varchar('b') => DuckDB::Value.create_int32(2)
|
|
575
|
+
# }
|
|
576
|
+
# map = DuckDB::Value.create_map({ varchar: :integer }, entries)
|
|
577
|
+
#
|
|
578
|
+
# # equivalent, with an explicit MAP logical type:
|
|
579
|
+
# map_type = DuckDB::LogicalType.create_map(:varchar, :integer)
|
|
580
|
+
# map = DuckDB::Value.create_map(map_type, entries)
|
|
581
|
+
#
|
|
582
|
+
# @param map_type [Hash, DuckDB::LogicalType] the type spec or MAP logical type.
|
|
583
|
+
# @param entries [Hash{DuckDB::Value => DuckDB::Value}] the map entries.
|
|
584
|
+
# @return [DuckDB::Value] the created Value object.
|
|
585
|
+
# @raise [DuckDB::Error] if a type in the Hash spec cannot be resolved
|
|
586
|
+
# to a DuckDB::LogicalType.
|
|
587
|
+
# @raise [ArgumentError] if map_type is not a one-pair Hash or a MAP
|
|
588
|
+
# DuckDB::LogicalType, or entries is not a Hash of DuckDB::Value to
|
|
589
|
+
# DuckDB::Value.
|
|
590
|
+
def create_map(map_type, entries)
|
|
591
|
+
map_type = resolve_map_type(map_type)
|
|
592
|
+
check_type!(entries, Hash)
|
|
593
|
+
check_value_array!(entries.keys)
|
|
594
|
+
check_value_array!(entries.values)
|
|
595
|
+
|
|
596
|
+
_create_map(map_type, entries.keys, entries.values)
|
|
597
|
+
end
|
|
598
|
+
|
|
241
599
|
private
|
|
242
600
|
|
|
601
|
+
def to_time(value)
|
|
602
|
+
value.is_a?(Date) ? value.to_time : _parse_time(value)
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
def enum_member_index(enum_type, member)
|
|
606
|
+
case member
|
|
607
|
+
when Integer
|
|
608
|
+
check_range!(member, 0...enum_type.dictionary_size, 'ENUM index')
|
|
609
|
+
member
|
|
610
|
+
when String, Symbol
|
|
611
|
+
enum_member_index_by_name(enum_type, member)
|
|
612
|
+
else
|
|
613
|
+
raise ArgumentError, "expected String, Symbol or Integer, got #{member.class.name}"
|
|
614
|
+
end
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
def enum_member_index_by_name(enum_type, member)
|
|
618
|
+
index = enum_type.each_dictionary_value.find_index(member.to_s)
|
|
619
|
+
raise ArgumentError, "`#{member}` is not a member of the enum" if index.nil?
|
|
620
|
+
|
|
621
|
+
index
|
|
622
|
+
end
|
|
623
|
+
|
|
624
|
+
def resolve_map_type(map_type)
|
|
625
|
+
if map_type.is_a?(Hash)
|
|
626
|
+
unless map_type.size == 1
|
|
627
|
+
raise ArgumentError, "expected exactly one key type => value type pair, got #{map_type.size}"
|
|
628
|
+
end
|
|
629
|
+
|
|
630
|
+
map_type = DuckDB::LogicalType.create_map(*map_type.first)
|
|
631
|
+
end
|
|
632
|
+
check_type!(map_type, DuckDB::LogicalType)
|
|
633
|
+
raise ArgumentError, "expected MAP LogicalType, got #{map_type.type}" unless map_type.type == :map
|
|
634
|
+
|
|
635
|
+
map_type
|
|
636
|
+
end
|
|
637
|
+
|
|
638
|
+
def check_value_array!(values)
|
|
639
|
+
check_type!(values, Array)
|
|
640
|
+
values.each { |value| check_type!(value, DuckDB::Value) }
|
|
641
|
+
end
|
|
642
|
+
|
|
243
643
|
def check_range!(value, range, type_name)
|
|
244
644
|
raise ArgumentError, "value out of range for #{type_name} (#{range})" unless range.cover?(value)
|
|
245
645
|
end
|
data/lib/duckdb/version.rb
CHANGED
data/lib/duckdb.rb
CHANGED
|
@@ -4,11 +4,13 @@ require 'duckdb/duckdb_native'
|
|
|
4
4
|
require 'duckdb/library_version'
|
|
5
5
|
require 'duckdb/version'
|
|
6
6
|
require 'duckdb/converter'
|
|
7
|
+
require 'duckdb/error'
|
|
7
8
|
require 'duckdb/table_name_parser'
|
|
8
9
|
require 'duckdb/database'
|
|
9
10
|
require 'duckdb/connection'
|
|
10
11
|
require 'duckdb/extracted_statements'
|
|
11
12
|
require 'duckdb/result'
|
|
13
|
+
require 'duckdb/arrow_array_stream'
|
|
12
14
|
require 'duckdb/prepared_statement'
|
|
13
15
|
require 'duckdb/pending_result'
|
|
14
16
|
require 'duckdb/appender'
|
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.
|
|
4
|
+
version: 1.5.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Masaki Suketa
|
|
@@ -35,6 +35,7 @@ extra_rdoc_files: []
|
|
|
35
35
|
files:
|
|
36
36
|
- ".rdoc_options"
|
|
37
37
|
- CHANGELOG.md
|
|
38
|
+
- CLAUDE.md
|
|
38
39
|
- LICENSE
|
|
39
40
|
- README.md
|
|
40
41
|
- Rakefile
|
|
@@ -47,6 +48,10 @@ files:
|
|
|
47
48
|
- ext/duckdb/aggregate_function_set.h
|
|
48
49
|
- ext/duckdb/appender.c
|
|
49
50
|
- ext/duckdb/appender.h
|
|
51
|
+
- ext/duckdb/arrow_array_stream.c
|
|
52
|
+
- ext/duckdb/arrow_array_stream.h
|
|
53
|
+
- ext/duckdb/arrow_import.c
|
|
54
|
+
- ext/duckdb/arrow_import.h
|
|
50
55
|
- ext/duckdb/blob.c
|
|
51
56
|
- ext/duckdb/blob.h
|
|
52
57
|
- ext/duckdb/client_context.c
|
|
@@ -114,6 +119,7 @@ files:
|
|
|
114
119
|
- lib/duckdb/aggregate_function.rb
|
|
115
120
|
- lib/duckdb/aggregate_function_set.rb
|
|
116
121
|
- lib/duckdb/appender.rb
|
|
122
|
+
- lib/duckdb/arrow_array_stream.rb
|
|
117
123
|
- lib/duckdb/casting.rb
|
|
118
124
|
- lib/duckdb/client_context.rb
|
|
119
125
|
- lib/duckdb/column.rb
|
|
@@ -124,6 +130,7 @@ files:
|
|
|
124
130
|
- lib/duckdb/converter/int_to_sym.rb
|
|
125
131
|
- lib/duckdb/data_chunk.rb
|
|
126
132
|
- lib/duckdb/database.rb
|
|
133
|
+
- lib/duckdb/error.rb
|
|
127
134
|
- lib/duckdb/expression.rb
|
|
128
135
|
- lib/duckdb/extracted_statements.rb
|
|
129
136
|
- lib/duckdb/function_type_validation.rb
|
|
@@ -162,14 +169,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
162
169
|
requirements:
|
|
163
170
|
- - ">="
|
|
164
171
|
- !ruby/object:Gem::Version
|
|
165
|
-
version: 3.
|
|
172
|
+
version: 3.3.0
|
|
166
173
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
167
174
|
requirements:
|
|
168
175
|
- - ">="
|
|
169
176
|
- !ruby/object:Gem::Version
|
|
170
177
|
version: '0'
|
|
171
178
|
requirements: []
|
|
172
|
-
rubygems_version: 4.0.
|
|
179
|
+
rubygems_version: 4.0.17
|
|
173
180
|
specification_version: 4
|
|
174
181
|
summary: Ruby bindings for the DuckDB database engine.
|
|
175
182
|
test_files: []
|