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
data/lib/duckdb/error.rb
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DuckDB
|
|
4
|
+
# The exception raised by ruby-duckdb.
|
|
5
|
+
class Error < StandardError
|
|
6
|
+
# +error_type_id+ is the raw DuckDB error type id, set by the C extension when
|
|
7
|
+
# a query result fails; it defaults to +nil+ for all other errors.
|
|
8
|
+
def initialize(message = nil, error_type_id = nil)
|
|
9
|
+
super(message)
|
|
10
|
+
@error_type_id = error_type_id
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Returns the DuckDB error category as a Symbol (e.g. +:constraint+,
|
|
14
|
+
# +:catalog+, +:parser+), or +nil+ when the error did not originate from a
|
|
15
|
+
# DuckDB query result (e.g. internal binding failures).
|
|
16
|
+
#
|
|
17
|
+
# begin
|
|
18
|
+
# con.query('INSERT INTO t VALUES (1)') # duplicate primary key
|
|
19
|
+
# rescue DuckDB::Error => e
|
|
20
|
+
# e.error_type # => :constraint
|
|
21
|
+
# end
|
|
22
|
+
def error_type
|
|
23
|
+
@error_type_id && Converter::IntToSym.error_type_to_sym(@error_type_id)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
data/lib/duckdb/logical_type.rb
CHANGED
|
@@ -7,7 +7,7 @@ module DuckDB
|
|
|
7
7
|
|
|
8
8
|
@logical_types = {}
|
|
9
9
|
|
|
10
|
-
{
|
|
10
|
+
types = {
|
|
11
11
|
boolean: 1,
|
|
12
12
|
tinyint: 2,
|
|
13
13
|
smallint: 3,
|
|
@@ -47,8 +47,11 @@ module DuckDB
|
|
|
47
47
|
sqlnull: 36,
|
|
48
48
|
string_literal: 37,
|
|
49
49
|
integer_literal: 38
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
}
|
|
51
|
+
# duckdb_create_logical_type rejects TIME_NS on DuckDB < 1.5.0.
|
|
52
|
+
types[:time_ns] = 39 if Gem::Version.new(DuckDB::LIBRARY_VERSION) >= Gem::Version.new('1.5.0')
|
|
53
|
+
|
|
54
|
+
types.each do |method_name, type_id|
|
|
52
55
|
define_singleton_method(method_name) do
|
|
53
56
|
@logical_types[type_id] ||= DuckDB::LogicalType.new(type_id)
|
|
54
57
|
end
|
|
@@ -76,6 +76,21 @@ module DuckDB
|
|
|
76
76
|
Converter::IntToSym.type_to_sym(i)
|
|
77
77
|
end
|
|
78
78
|
|
|
79
|
+
# returns the column type of the result set of the prepared statement
|
|
80
|
+
# without executing it. The argument is the column index (0-based).
|
|
81
|
+
# Returns :invalid if the column index is out of range.
|
|
82
|
+
#
|
|
83
|
+
# require 'duckdb'
|
|
84
|
+
# db = DuckDB::Database.open
|
|
85
|
+
# con = db.connect
|
|
86
|
+
# con.execute('CREATE TABLE users (id INTEGER, name VARCHAR(255))')
|
|
87
|
+
# stmt = con.prepared_statement('SELECT * FROM users')
|
|
88
|
+
# stmt.column_type(0) # => :integer
|
|
89
|
+
def column_type(index)
|
|
90
|
+
i = _column_type(index)
|
|
91
|
+
Converter::IntToSym.type_to_sym(i)
|
|
92
|
+
end
|
|
93
|
+
|
|
79
94
|
# binds all parameters with SQL prepared statement.
|
|
80
95
|
#
|
|
81
96
|
# require 'duckdb'
|
|
@@ -96,8 +96,8 @@ module DuckDB
|
|
|
96
96
|
|
|
97
97
|
# Adds a parameter to the scalar function.
|
|
98
98
|
# Currently supports BIGINT, BLOB, BOOLEAN, DATE, DECIMAL, DOUBLE, FLOAT, HUGEINT, INTEGER, INTERVAL, SMALLINT,
|
|
99
|
-
# TIME, TIMESTAMP, TIMESTAMP_S, TIMESTAMP_MS, TIMESTAMP_NS, TIME_TZ, TIMESTAMP_TZ, TINYINT, UBIGINT,
|
|
100
|
-
# UINTEGER, USMALLINT, UTINYINT, UUID, and VARCHAR types.
|
|
99
|
+
# TIME, TIMESTAMP, TIMESTAMP_S, TIMESTAMP_MS, TIMESTAMP_NS, TIME_NS, TIME_TZ, TIMESTAMP_TZ, TINYINT, UBIGINT,
|
|
100
|
+
# UHUGEINT, UINTEGER, USMALLINT, UTINYINT, UUID, and VARCHAR types.
|
|
101
101
|
# For DECIMAL, pass a DuckDB::LogicalType instance created with DuckDB::LogicalType.create_decimal(width, scale).
|
|
102
102
|
#
|
|
103
103
|
# @param logical_type [DuckDB::LogicalType | :logical_type_symbol] the parameter type
|
|
@@ -111,8 +111,8 @@ module DuckDB
|
|
|
111
111
|
|
|
112
112
|
# Sets the return type for the scalar function.
|
|
113
113
|
# Currently supports BIGINT, BLOB, BOOLEAN, DATE, DECIMAL, DOUBLE, FLOAT, HUGEINT, INTEGER, INTERVAL, SMALLINT,
|
|
114
|
-
# TIME, TIMESTAMP, TIMESTAMP_S, TIMESTAMP_MS, TIMESTAMP_NS, TIME_TZ, TIMESTAMP_TZ, TINYINT, UBIGINT,
|
|
115
|
-
# UINTEGER, USMALLINT, UTINYINT, UUID, and VARCHAR types.
|
|
114
|
+
# TIME, TIMESTAMP, TIMESTAMP_S, TIMESTAMP_MS, TIMESTAMP_NS, TIME_NS, TIME_TZ, TIMESTAMP_TZ, TINYINT, UBIGINT,
|
|
115
|
+
# UHUGEINT, UINTEGER, USMALLINT, UTINYINT, UUID, and VARCHAR types.
|
|
116
116
|
# For DECIMAL, pass a DuckDB::LogicalType instance created with DuckDB::LogicalType.create_decimal(width, scale).
|
|
117
117
|
#
|
|
118
118
|
# @param logical_type [DuckDB::LogicalType | :logical_type_symbol] the return type
|
|
@@ -144,8 +144,8 @@ module DuckDB
|
|
|
144
144
|
# (e.g. a separator followed by a variable list of values).
|
|
145
145
|
# The block receives fixed parameters positionally, then varargs as a splat (|fixed, *rest|).
|
|
146
146
|
# Currently supports BIGINT, BLOB, BOOLEAN, DATE, DECIMAL, DOUBLE, FLOAT, HUGEINT, INTEGER, INTERVAL, SMALLINT,
|
|
147
|
-
# TIME, TIMESTAMP, TIMESTAMP_S, TIMESTAMP_MS, TIMESTAMP_NS, TIME_TZ, TIMESTAMP_TZ, TINYINT, UBIGINT,
|
|
148
|
-
# UINTEGER, USMALLINT, UTINYINT, UUID, and VARCHAR types.
|
|
147
|
+
# TIME, TIMESTAMP, TIMESTAMP_S, TIMESTAMP_MS, TIMESTAMP_NS, TIME_NS, TIME_TZ, TIMESTAMP_TZ, TINYINT, UBIGINT,
|
|
148
|
+
# UHUGEINT, UINTEGER, USMALLINT, UTINYINT, UUID, and VARCHAR types.
|
|
149
149
|
# For DECIMAL, pass a DuckDB::LogicalType instance created with DuckDB::LogicalType.create_decimal(width, scale).
|
|
150
150
|
#
|
|
151
151
|
# @param logical_type [DuckDB::LogicalType | :logical_type_symbol] the varargs element type
|
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
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
|
|
@@ -129,6 +130,7 @@ files:
|
|
|
129
130
|
- lib/duckdb/converter/int_to_sym.rb
|
|
130
131
|
- lib/duckdb/data_chunk.rb
|
|
131
132
|
- lib/duckdb/database.rb
|
|
133
|
+
- lib/duckdb/error.rb
|
|
132
134
|
- lib/duckdb/expression.rb
|
|
133
135
|
- lib/duckdb/extracted_statements.rb
|
|
134
136
|
- lib/duckdb/function_type_validation.rb
|
|
@@ -167,14 +169,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
167
169
|
requirements:
|
|
168
170
|
- - ">="
|
|
169
171
|
- !ruby/object:Gem::Version
|
|
170
|
-
version: 3.
|
|
172
|
+
version: 3.3.0
|
|
171
173
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
172
174
|
requirements:
|
|
173
175
|
- - ">="
|
|
174
176
|
- !ruby/object:Gem::Version
|
|
175
177
|
version: '0'
|
|
176
178
|
requirements: []
|
|
177
|
-
rubygems_version: 4.0.
|
|
179
|
+
rubygems_version: 4.0.17
|
|
178
180
|
specification_version: 4
|
|
179
181
|
summary: Ruby bindings for the DuckDB database engine.
|
|
180
182
|
test_files: []
|