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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 249a190d19c8c4b8ff32f06621a8fd2cba6690008a593e33603761810e3f2263
4
- data.tar.gz: f0dd19a5a1b197cf22731b40741771e36b04a59b28388fb68fde6af76233d68e
3
+ metadata.gz: 35eae50625fd72c7cb1b870ff496d259c0271453404a0646fd05514b4a3f7338
4
+ data.tar.gz: e96e82fcbffa7427f63cf8a7af0ba9434b5cb7daefbd9951c3eba4866508d67e
5
5
  SHA512:
6
- metadata.gz: 381f2d2ac0b028e0a8fb2b8ab0f18f22f876f81f868d7921cb6952eaa5059a35f6d87e4bdf39b823de25fa5e0b59f349efdc77507e361aeaac032bf402211b3a
7
- data.tar.gz: 8472e39a5f8e1136193f718ad436989fba3ddffe1c5072adc775a59490c08f5a141d2a2826a314413a5a5472aea2c92af467f479e7f2f013fb06bc9630bfd3d0
6
+ metadata.gz: c7fdc287942e832ee9f33fbbf0056add1a7b7fc8db0e633356b7cf73c3d086f1d7a89ac7cf79673d0ecbbecda15ee33d365182c2874a74ac23aaf95270fe90dd
7
+ data.tar.gz: 7cc3079783a36442e43b7228223e2ded5cb453184e1eb4344788f5b7c1758e2ba665d8a9d087f6239d84dfb16a389f18fe091bd346a439e7e3c32ed9a333a9ce
@@ -20,7 +20,7 @@ jobs:
20
20
  timeout-minutes: 120
21
21
  strategy:
22
22
  matrix:
23
- ruby: ['3.2.9', '3.3.10', '3.4.9', '4.0.1', 'head']
23
+ ruby: ['3.2.9', '3.3.10', '3.4.9', '4.0.2', 'head']
24
24
  duckdb: ['1.4.4', '1.5.0']
25
25
 
26
26
  steps:
@@ -20,7 +20,7 @@ jobs:
20
20
  timeout-minutes: 120
21
21
  strategy:
22
22
  matrix:
23
- ruby: ['3.2.9', '3.3.10', '3.4.9', '4.0.1', 'head']
23
+ ruby: ['3.2.9', '3.3.10', '3.4.9', '4.0.2', 'head']
24
24
  duckdb: ['1.4.4', '1.5.0']
25
25
 
26
26
  services:
@@ -17,7 +17,7 @@ concurrency:
17
17
  jobs:
18
18
  test:
19
19
  runs-on: windows-latest
20
- timeout-minutes: 10
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
- run: |
55
- bundle exec rake test TESTOPTS="--verbose"
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.1)
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.1)
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.8.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();
@@ -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.new(1970, 1, 1, 0, 0, 0, 0)
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.local(year, month, day, hour, minute, second, microsecond)
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
- EPOCH + time
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
- tm = EPOCH + (time / 1000)
55
- Time.local(tm.year, tm.month, tm.day, tm.hour, tm.min, tm.sec, time % 1000 * 1000)
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
- tm = EPOCH + (time / 1_000_000_000)
60
- Time.local(tm.year, tm.month, tm.day, tm.hour, tm.min, tm.sec, time % 1_000_000_000 / 1000)
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)
@@ -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
- raise ArgumentError, "Unknown logical type symbol: #{symbol}"
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 (use this OR parameter_types)
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: DuckDB::LogicalType::INTEGER,
22
- # parameter_type: DuckDB::LogicalType::INTEGER
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: DuckDB::LogicalType::INTEGER,
29
- # parameter_types: [DuckDB::LogicalType::INTEGER, DuckDB::LogicalType::INTEGER]
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: DuckDB::LogicalType::INTEGER
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
- raise DuckDB::Error, 'logical_type must be a DuckDB::LogicalType' unless logical_type.is_a?(DuckDB::LogicalType)
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
- raise DuckDB::Error, 'logical_type must be a DuckDB::LogicalType' unless logical_type.is_a?(DuckDB::LogicalType)
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
- type_list = SUPPORTED_TYPES.map(&:upcase).join(', ')
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
- _set_return_type(logical_type)
114
+ logical_type
115
115
  end
116
116
  end
117
117
  end
@@ -3,5 +3,5 @@
3
3
  module DuckDB
4
4
  # The version string of ruby-duckdb.
5
5
  # Currently, ruby-duckdb is NOT semantic versioning.
6
- VERSION = '1.5.0.1'
6
+ VERSION = '1.5.0.2'
7
7
  end
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
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.0.1
4
+ version: 1.5.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Masaki Suketa