duckdb 1.5.0.1 → 1.5.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/test_on_macos.yml +1 -1
- data/.github/workflows/test_on_ubuntu.yml +1 -1
- data/.github/workflows/test_on_windows.yml +6 -3
- data/CHANGELOG.md +6 -0
- data/Gemfile.lock +3 -3
- data/README.md +4 -0
- data/ext/duckdb/duckdb.c +15 -0
- data/lib/duckdb/converter.rb +22 -7
- data/lib/duckdb/logical_type.rb +20 -2
- data/lib/duckdb/scalar_function.rb +22 -22
- data/lib/duckdb/version.rb +1 -1
- data/lib/duckdb.rb +24 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 35eae50625fd72c7cb1b870ff496d259c0271453404a0646fd05514b4a3f7338
|
|
4
|
+
data.tar.gz: e96e82fcbffa7427f63cf8a7af0ba9434b5cb7daefbd9951c3eba4866508d67e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c7fdc287942e832ee9f33fbbf0056add1a7b7fc8db0e633356b7cf73c3d086f1d7a89ac7cf79673d0ecbbecda15ee33d365182c2874a74ac23aaf95270fe90dd
|
|
7
|
+
data.tar.gz: 7cc3079783a36442e43b7228223e2ded5cb453184e1eb4344788f5b7c1758e2ba665d8a9d087f6239d84dfb16a389f18fe091bd346a439e7e3c32ed9a333a9ce
|
|
@@ -17,7 +17,7 @@ concurrency:
|
|
|
17
17
|
jobs:
|
|
18
18
|
test:
|
|
19
19
|
runs-on: windows-latest
|
|
20
|
-
timeout-minutes:
|
|
20
|
+
timeout-minutes: 15
|
|
21
21
|
strategy:
|
|
22
22
|
matrix:
|
|
23
23
|
ruby: ['3.2.9', '3.3.10', '3.4.8', '4.0.1', 'ucrt', 'mingw', 'mswin', 'head']
|
|
@@ -51,8 +51,11 @@ jobs:
|
|
|
51
51
|
bundle exec rake build -- --with-duckdb-include=../../../.. --with-duckdb-lib=../../../..
|
|
52
52
|
|
|
53
53
|
- name: rake test
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
uses: nick-fields/retry@v3
|
|
55
|
+
with:
|
|
56
|
+
timeout_minutes: 3
|
|
57
|
+
max_attempts: 3
|
|
58
|
+
command: bundle exec rake test TESTOPTS="--verbose"
|
|
56
59
|
|
|
57
60
|
post-test:
|
|
58
61
|
name: All tests passed on Windows
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
# Unreleased
|
|
6
6
|
|
|
7
|
+
# 1.5.0.2 - 2026-03-22
|
|
8
|
+
|
|
9
|
+
- add `DuckDB.vector_size` to return the DuckDB vector size (number of rows processed per vectorized operation).
|
|
10
|
+
- `DuckDB::ScalarFunction.create`, `DuckDB::ScalarFunction#add_parameter`, `DuckDB::ScalarFunction#return_type=` accept logical type symbol as argument.
|
|
11
|
+
- add `DuckDB.default_timezone` configuration (defaults to `:local`) to control how TIMESTAMP and TIME values without time zone are converted to Ruby `Time` (`:utc` or `:local`).
|
|
12
|
+
|
|
7
13
|
# 1.5.0.1 - 2026-03-17
|
|
8
14
|
|
|
9
15
|
- enable `DuckDB::ScalarFunction` work with duckdb multi threads.
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
duckdb (1.5.0.
|
|
4
|
+
duckdb (1.5.0.2)
|
|
5
5
|
bigdecimal (>= 3.1.4)
|
|
6
6
|
|
|
7
7
|
GEM
|
|
@@ -13,13 +13,13 @@ GEM
|
|
|
13
13
|
bigdecimal (4.0.1)
|
|
14
14
|
csv (3.3.5)
|
|
15
15
|
drb (2.2.3)
|
|
16
|
-
json (2.19.
|
|
16
|
+
json (2.19.2)
|
|
17
17
|
json-schema (6.2.0)
|
|
18
18
|
addressable (~> 2.8)
|
|
19
19
|
bigdecimal (>= 3.1, < 5)
|
|
20
20
|
language_server-protocol (3.17.0.5)
|
|
21
21
|
lint_roller (1.1.0)
|
|
22
|
-
mcp (0.
|
|
22
|
+
mcp (0.9.0)
|
|
23
23
|
json-schema (>= 4.1)
|
|
24
24
|
minitest (6.0.2)
|
|
25
25
|
drb (~> 2.0)
|
data/README.md
CHANGED
|
@@ -263,3 +263,7 @@ con.query('INSERT INTO numbers VALUES (2), (1), (4), (3)')
|
|
|
263
263
|
res = con.query('SELECT number FROM numbers ORDER BY number')
|
|
264
264
|
res.first.first # => 4
|
|
265
265
|
```
|
|
266
|
+
|
|
267
|
+
#### Global configuration
|
|
268
|
+
|
|
269
|
+
Set `DuckDB.default_timezone` to control how TIMESTAMP and TIME values without time zone are converted to Ruby `Time` objects. The default is `:local`, but you can use `:utc` for UTC conversion.
|
data/ext/duckdb/duckdb.c
CHANGED
|
@@ -5,6 +5,7 @@ VALUE PositiveInfinity;
|
|
|
5
5
|
VALUE NegativeInfinity;
|
|
6
6
|
|
|
7
7
|
static VALUE duckdb_s_library_version(VALUE self);
|
|
8
|
+
static VALUE duckdb_s_vector_size(VALUE self);
|
|
8
9
|
|
|
9
10
|
/*
|
|
10
11
|
* call-seq:
|
|
@@ -18,6 +19,19 @@ static VALUE duckdb_s_library_version(VALUE self) {
|
|
|
18
19
|
return rb_str_new2(duckdb_library_version());
|
|
19
20
|
}
|
|
20
21
|
|
|
22
|
+
/*
|
|
23
|
+
* call-seq:
|
|
24
|
+
* DuckDB.vector_size -> Integer
|
|
25
|
+
*
|
|
26
|
+
* Returns the vector size of DuckDB. The vector size is the number of rows
|
|
27
|
+
* that are processed in a single vectorized operation.
|
|
28
|
+
*
|
|
29
|
+
* DuckDB.vector_size # => 2048
|
|
30
|
+
*/
|
|
31
|
+
static VALUE duckdb_s_vector_size(VALUE self) {
|
|
32
|
+
return ULONG2NUM(duckdb_vector_size());
|
|
33
|
+
}
|
|
34
|
+
|
|
21
35
|
void
|
|
22
36
|
Init_duckdb_native(void) {
|
|
23
37
|
mDuckDB = rb_define_module("DuckDB");
|
|
@@ -25,6 +39,7 @@ Init_duckdb_native(void) {
|
|
|
25
39
|
NegativeInfinity = rb_str_new_literal("-infinity");
|
|
26
40
|
|
|
27
41
|
rb_define_singleton_method(mDuckDB, "library_version", duckdb_s_library_version, 0);
|
|
42
|
+
rb_define_singleton_method(mDuckDB, "vector_size", duckdb_s_vector_size, 0);
|
|
28
43
|
|
|
29
44
|
rbduckdb_init_duckdb_error();
|
|
30
45
|
rbduckdb_init_duckdb_database();
|
data/lib/duckdb/converter.rb
CHANGED
|
@@ -12,10 +12,14 @@ module DuckDB
|
|
|
12
12
|
HALF_HUGEINT = 1 << HALF_HUGEINT_BIT
|
|
13
13
|
FLIP_HUGEINT = 1 << 63
|
|
14
14
|
EPOCH = Time.local(1970, 1, 1)
|
|
15
|
-
EPOCH_UTC = Time.
|
|
15
|
+
EPOCH_UTC = Time.utc(1970, 1, 1)
|
|
16
16
|
|
|
17
17
|
module_function
|
|
18
18
|
|
|
19
|
+
def default_timezone_utc?
|
|
20
|
+
defined?(DuckDB.default_timezone) && DuckDB.default_timezone == :utc
|
|
21
|
+
end
|
|
22
|
+
|
|
19
23
|
def _to_infinity(value)
|
|
20
24
|
if value.positive?
|
|
21
25
|
DuckDB::Infinity::POSITIVE
|
|
@@ -30,11 +34,16 @@ module DuckDB
|
|
|
30
34
|
|
|
31
35
|
# rubocop:disable Metrics/ParameterLists
|
|
32
36
|
def _to_time(year, month, day, hour, minute, second, microsecond)
|
|
33
|
-
Time.
|
|
37
|
+
Time.public_send(
|
|
38
|
+
default_timezone_utc? ? :utc : :local,
|
|
39
|
+
year, month, day, hour, minute, second, microsecond
|
|
40
|
+
)
|
|
34
41
|
end
|
|
35
42
|
# rubocop:enable Metrics/ParameterLists
|
|
36
43
|
|
|
37
44
|
def _to_time_from_duckdb_time(hour, minute, second, microsecond)
|
|
45
|
+
return Time.utc(1970, 1, 1, hour, minute, second, microsecond) if default_timezone_utc?
|
|
46
|
+
|
|
38
47
|
Time.parse(
|
|
39
48
|
format(
|
|
40
49
|
'%<hour>02d:%<minute>02d:%<second>02d.%<microsecond>06d',
|
|
@@ -47,17 +56,23 @@ module DuckDB
|
|
|
47
56
|
end
|
|
48
57
|
|
|
49
58
|
def _to_time_from_duckdb_timestamp_s(time)
|
|
50
|
-
|
|
59
|
+
if default_timezone_utc?
|
|
60
|
+
EPOCH_UTC + time
|
|
61
|
+
else
|
|
62
|
+
EPOCH + time
|
|
63
|
+
end
|
|
51
64
|
end
|
|
52
65
|
|
|
53
66
|
def _to_time_from_duckdb_timestamp_ms(time)
|
|
54
|
-
|
|
55
|
-
|
|
67
|
+
_to_time_from_duckdb_timestamp_s(time / 1000).then do |tm|
|
|
68
|
+
_to_time(tm.year, tm.month, tm.day, tm.hour, tm.min, tm.sec, time % 1000 * 1000)
|
|
69
|
+
end
|
|
56
70
|
end
|
|
57
71
|
|
|
58
72
|
def _to_time_from_duckdb_timestamp_ns(time)
|
|
59
|
-
|
|
60
|
-
|
|
73
|
+
_to_time_from_duckdb_timestamp_s(time / 1_000_000_000).then do |tm|
|
|
74
|
+
_to_time(tm.year, tm.month, tm.day, tm.hour, tm.min, tm.sec, time % 1_000_000_000 / 1000)
|
|
75
|
+
end
|
|
61
76
|
end
|
|
62
77
|
|
|
63
78
|
def _to_time_from_duckdb_time_tz(hour, min, sec, micro, timezone)
|
data/lib/duckdb/logical_type.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module DuckDB
|
|
4
|
-
class LogicalType
|
|
4
|
+
class LogicalType # rubocop:disable Metrics/ClassLength
|
|
5
5
|
alias :alias get_alias
|
|
6
6
|
alias :alias= set_alias
|
|
7
7
|
|
|
@@ -58,9 +58,11 @@ module DuckDB
|
|
|
58
58
|
def resolve(symbol)
|
|
59
59
|
return symbol if symbol.is_a?(DuckDB::LogicalType)
|
|
60
60
|
|
|
61
|
+
raise_resolve_error(symbol) unless symbol.respond_to?(:upcase)
|
|
62
|
+
|
|
61
63
|
DuckDB::LogicalType.const_get(symbol.upcase)
|
|
62
64
|
rescue NameError
|
|
63
|
-
|
|
65
|
+
raise_resolve_error(symbol)
|
|
64
66
|
end
|
|
65
67
|
|
|
66
68
|
# Creates an array logical type with the given child type and size.
|
|
@@ -93,6 +95,12 @@ module DuckDB
|
|
|
93
95
|
def create_list(type)
|
|
94
96
|
_create_list_type(LogicalType.resolve(type))
|
|
95
97
|
end
|
|
98
|
+
|
|
99
|
+
private
|
|
100
|
+
|
|
101
|
+
def raise_resolve_error(symbol)
|
|
102
|
+
raise DuckDB::Error, "Unknown logical type: `#{symbol.inspect}`"
|
|
103
|
+
end
|
|
96
104
|
end
|
|
97
105
|
|
|
98
106
|
# returns logical type's type symbol
|
|
@@ -239,5 +247,15 @@ module DuckDB
|
|
|
239
247
|
yield dictionary_value_at(i)
|
|
240
248
|
end
|
|
241
249
|
end
|
|
250
|
+
|
|
251
|
+
# :nodoc:
|
|
252
|
+
def inspect
|
|
253
|
+
"<#{self.class}::#{type.upcase}>"
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
# :nodoc:
|
|
257
|
+
def to_s
|
|
258
|
+
inspect
|
|
259
|
+
end
|
|
242
260
|
end
|
|
243
261
|
end
|
|
@@ -8,9 +8,9 @@ module DuckDB
|
|
|
8
8
|
# Create and configure a scalar function in one call
|
|
9
9
|
#
|
|
10
10
|
# @param name [String, Symbol] the function name
|
|
11
|
-
# @param return_type [DuckDB::LogicalType] the return type
|
|
12
|
-
# @param parameter_type [DuckDB::LogicalType, nil] single parameter type
|
|
13
|
-
# @param parameter_types [Array<DuckDB::LogicalType>, nil] multiple parameter types
|
|
11
|
+
# @param return_type [DuckDB::LogicalType|:logical_type_symbol] the return type
|
|
12
|
+
# @param parameter_type [DuckDB::LogicalType|:logical_type_symbol, nil] single parameter type
|
|
13
|
+
# @param parameter_types [Array<DuckDB::LogicalType|:logical_type_symbol>, nil] multiple parameter types
|
|
14
14
|
# @yield [*args] the function implementation
|
|
15
15
|
# @return [DuckDB::ScalarFunction] configured scalar function ready to register
|
|
16
16
|
# @raise [ArgumentError] if block is not provided or both parameter_type and parameter_types are specified
|
|
@@ -18,21 +18,21 @@ module DuckDB
|
|
|
18
18
|
# @example Single parameter function
|
|
19
19
|
# sf = DuckDB::ScalarFunction.create(
|
|
20
20
|
# name: :triple,
|
|
21
|
-
# return_type:
|
|
22
|
-
# parameter_type:
|
|
21
|
+
# return_type: :integer,
|
|
22
|
+
# parameter_type: :integer
|
|
23
23
|
# ) { |v| v * 3 }
|
|
24
24
|
#
|
|
25
25
|
# @example Multiple parameters
|
|
26
26
|
# sf = DuckDB::ScalarFunction.create(
|
|
27
27
|
# name: :add,
|
|
28
|
-
# return_type:
|
|
29
|
-
# parameter_types: [
|
|
28
|
+
# return_type: :integer,
|
|
29
|
+
# parameter_types: [:integer, :integer]
|
|
30
30
|
# ) { |a, b| a + b }
|
|
31
31
|
#
|
|
32
32
|
# @example No parameters (constant function)
|
|
33
33
|
# sf = DuckDB::ScalarFunction.create(
|
|
34
34
|
# name: :random_num,
|
|
35
|
-
# return_type:
|
|
35
|
+
# return_type: :integer
|
|
36
36
|
# ) { rand(100) }
|
|
37
37
|
def self.create(name:, return_type:, parameter_type: nil, parameter_types: nil, &) # rubocop:disable Metrics/MethodLength
|
|
38
38
|
raise ArgumentError, 'Block required' unless block_given?
|
|
@@ -80,17 +80,11 @@ module DuckDB
|
|
|
80
80
|
# Currently supports BIGINT, BLOB, BOOLEAN, DATE, DOUBLE, FLOAT, INTEGER, SMALLINT, TIME, TIMESTAMP, TINYINT,
|
|
81
81
|
# UBIGINT, UINTEGER, USMALLINT, UTINYINT, and VARCHAR types.
|
|
82
82
|
#
|
|
83
|
-
# @param logical_type [DuckDB::LogicalType] the parameter type
|
|
83
|
+
# @param logical_type [DuckDB::LogicalType | :logical_type_symbol] the parameter type
|
|
84
84
|
# @return [DuckDB::ScalarFunction] self
|
|
85
85
|
# @raise [DuckDB::Error] if the type is not supported
|
|
86
86
|
def add_parameter(logical_type)
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
unless SUPPORTED_TYPES.include?(logical_type.type)
|
|
90
|
-
type_list = SUPPORTED_TYPES.map(&:upcase).join(', ')
|
|
91
|
-
raise DuckDB::Error,
|
|
92
|
-
"Only #{type_list} parameter types are currently supported"
|
|
93
|
-
end
|
|
87
|
+
logical_type = check_supported_type!(logical_type)
|
|
94
88
|
|
|
95
89
|
_add_parameter(logical_type)
|
|
96
90
|
end
|
|
@@ -99,19 +93,25 @@ module DuckDB
|
|
|
99
93
|
# Currently supports BIGINT, BLOB, BOOLEAN, DATE, DOUBLE, FLOAT, INTEGER, SMALLINT, TIME, TIMESTAMP, TINYINT,
|
|
100
94
|
# UBIGINT, UINTEGER, USMALLINT, UTINYINT, and VARCHAR types.
|
|
101
95
|
#
|
|
102
|
-
# @param logical_type [DuckDB::LogicalType] the return type
|
|
96
|
+
# @param logical_type [DuckDB::LogicalType | :logical_type_symbol] the return type
|
|
103
97
|
# @return [DuckDB::ScalarFunction] self
|
|
104
98
|
# @raise [DuckDB::Error] if the type is not supported
|
|
105
99
|
def return_type=(logical_type)
|
|
106
|
-
|
|
100
|
+
logical_type = check_supported_type!(logical_type)
|
|
101
|
+
|
|
102
|
+
_set_return_type(logical_type)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
private
|
|
106
|
+
|
|
107
|
+
def check_supported_type!(type)
|
|
108
|
+
logical_type = DuckDB::LogicalType.resolve(type)
|
|
107
109
|
|
|
108
110
|
unless SUPPORTED_TYPES.include?(logical_type.type)
|
|
109
|
-
|
|
110
|
-
raise DuckDB::Error,
|
|
111
|
-
"Only #{type_list} return types are currently supported"
|
|
111
|
+
raise DuckDB::Error, "Type `#{type}` is not supported. Only #{SUPPORTED_TYPES.inspect} are available."
|
|
112
112
|
end
|
|
113
113
|
|
|
114
|
-
|
|
114
|
+
logical_type
|
|
115
115
|
end
|
|
116
116
|
end
|
|
117
117
|
end
|
data/lib/duckdb/version.rb
CHANGED
data/lib/duckdb.rb
CHANGED
|
@@ -27,4 +27,28 @@ require 'duckdb/casting'
|
|
|
27
27
|
|
|
28
28
|
# DuckDB provides Ruby interface of DuckDB.
|
|
29
29
|
module DuckDB
|
|
30
|
+
class << self
|
|
31
|
+
# Controls how DuckDB converts timestamp and time values without explicit
|
|
32
|
+
# time zone information.
|
|
33
|
+
#
|
|
34
|
+
# - `:utc` - interpret values as UTC
|
|
35
|
+
# - `:local` - (default) interpret values as local time, preserving existing behavior
|
|
36
|
+
#
|
|
37
|
+
# Example:
|
|
38
|
+
# DuckDB.default_timezone = :utc
|
|
39
|
+
#
|
|
40
|
+
# This setting only affects conversion of values without time zone. Values
|
|
41
|
+
# with explicit time zone are always interpreted according to their offset.
|
|
42
|
+
attr_reader :default_timezone
|
|
43
|
+
|
|
44
|
+
def default_timezone=(value)
|
|
45
|
+
raise ArgumentError, 'DuckDB.default_timezone must be either :utc or :local.' unless %i[local utc].include?(value)
|
|
46
|
+
|
|
47
|
+
@default_timezone = value
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Default to local time to preserve existing behavior unless explicitly
|
|
52
|
+
# configured otherwise.
|
|
53
|
+
self.default_timezone = :local
|
|
30
54
|
end
|