duckdb 0.8.1.3 → 0.9.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a9ce44263b8239010b8a203c7cebadf9e1b07323efc89164877e8ded4e37f768
4
- data.tar.gz: 44dc20be5aa79c3b29f7bd7b57aa5d9e30524919499a88e7b7d5d50318468554
3
+ metadata.gz: f7738df010c1fb1aab32b61077ff9409566cfaddecfef3b27aa6e7ee7a8651f2
4
+ data.tar.gz: 6357f43da56b7c4fbea72ec7b3c549f598f3a91b88c1c91ab5320a01606f3589
5
5
  SHA512:
6
- metadata.gz: 41fd80e244cd09100931b695533314aa29c040f7571f64747cb79533da45c8685adc1fc04bc731cff5d59e5c58f8669db2c9cb1eebb8dcdfc78f92b3d7cb49d6
7
- data.tar.gz: 75e8d32f1d467be39365f0ffe3f3f40471b1b580929f025736ec6d47db9dad62a99beb490a118066a7145837717dfcc337e5f1aaafba7f068c63279ef16770f9
6
+ metadata.gz: e417badc1b301fdea9f3f4b27648e7e1c7d8e125a0a1323bb69e93a992467eca0a57a320b0bb14f7fda45bf8d9d3706b59eb586a71e3c9f77bd2ec18c88b8b3a
7
+ data.tar.gz: b1486d0d525fa93a8485504eac95015a07ac064a4dfb15bb0689aa7be4cb41edcf03436324e822bda26a8c3c6e8a9e2a690efcd49f7180f675777f75284df5e8
@@ -0,0 +1,3 @@
1
+ # These are supported funding model platforms
2
+
3
+ github: suketa
@@ -16,7 +16,7 @@ jobs:
16
16
  strategy:
17
17
  matrix:
18
18
  ruby: ['2.7.8', '3.0.6', '3.1.4', '3.2.2', '3.3.0-preview1', 'head']
19
- duckdb: ['0.7.1', '0.8.1']
19
+ duckdb: ['0.9.0', '0.8.1']
20
20
 
21
21
  steps:
22
22
  - uses: actions/checkout@v3
@@ -16,7 +16,7 @@ jobs:
16
16
  strategy:
17
17
  matrix:
18
18
  ruby: ['2.7.8', '3.0.6', '3.1.4', '3.2.2', '3.3.0-preview1', 'head']
19
- duckdb: ['0.7.1', '0.8.1']
19
+ duckdb: ['0.9.0', '0.8.1']
20
20
 
21
21
  steps:
22
22
  - uses: actions/checkout@v3
@@ -16,7 +16,7 @@ jobs:
16
16
  strategy:
17
17
  matrix:
18
18
  ruby: ['2.7.8', '3.0.6', '3.1.4', '3.2.2', 'ucrt', 'mingw', 'mswin', 'head']
19
- duckdb: ['0.7.1', '0.8.1']
19
+ duckdb: ['0.9.0', '0.8.1']
20
20
 
21
21
  steps:
22
22
  - uses: actions/checkout@v3
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # ChangeLog
2
2
 
3
+ # 0.9.0
4
+ - bump duckdb to 0.9.0.
5
+
6
+ ## Breaking Change
7
+ - deprecation warning when DuckDB::Result.each calling with `DuckDB::Result.use_chunk_each` is false.
8
+ The `each` behavior will be same as `DuckDB::Result.chunk_each` in the future.
9
+ set `DuckDB::Result.use_chunk_each = true` to suppress the warning.
10
+ - DuckDB::Result#chunck_each returns DuckDB::Interval class when the column type is INTERVAL.
11
+
3
12
  # 0.8.1.3
4
13
  - Fix BigDecimal conversion.
5
14
 
data/CONTRIBUTION.md CHANGED
@@ -8,8 +8,8 @@
8
8
  2. Build and access to docker container
9
9
 
10
10
  ```
11
- docker-compose build ubuntu
12
- docker-compose run --rm ubuntu bash
11
+ docker compose build ubuntu
12
+ docker compose run --rm ubuntu bash
13
13
  ```
14
14
 
15
15
  In case you want custom ruby or duckdb versions, use `--build-arg` options
data/Dockerfile CHANGED
@@ -1,7 +1,7 @@
1
1
  ARG RUBY_VERSION=3.2.2
2
2
  FROM ruby:${RUBY_VERSION}
3
3
 
4
- ARG DUCKDB_VERSION=0.8.0
4
+ ARG DUCKDB_VERSION=0.9.0
5
5
 
6
6
  RUN apt update -qq && \
7
7
  apt install -y build-essential curl git wget
data/Gemfile.lock CHANGED
@@ -1,24 +1,24 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- duckdb (0.8.1.3)
4
+ duckdb (0.9.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
9
  benchmark-ips (2.12.0)
10
- mini_portile2 (2.8.2)
11
- minitest (5.18.1)
12
- nokogiri (1.15.3)
10
+ mini_portile2 (2.8.4)
11
+ minitest (5.20.0)
12
+ nokogiri (1.15.4)
13
13
  mini_portile2 (~> 2.8.2)
14
14
  racc (~> 1.4)
15
- nokogiri (1.15.3-x86_64-linux)
15
+ nokogiri (1.15.4-x86_64-linux)
16
16
  racc (~> 1.4)
17
17
  racc (1.7.1)
18
18
  rake (13.0.6)
19
- rake-compiler (1.2.3)
19
+ rake-compiler (1.2.5)
20
20
  rake
21
- ruby_memcheck (1.3.2)
21
+ ruby_memcheck (2.2.0)
22
22
  nokogiri
23
23
  stackprof (0.2.25)
24
24
 
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'duckdb'
5
+ require 'benchmark/ips'
6
+
7
+ Benchmark.ips do |x|
8
+ x.report('hugeint_convert') { DuckDB::Converter._to_hugeint_from_vector(123_456_789, 123_456_789) }
9
+ end
10
+
11
+ __END__
12
+
13
+ ## before
14
+ ```
15
+ ✦ ❯ ruby benchmark/converter_hugeint_ips.rb
16
+ Warming up --------------------------------------
17
+ hugeint_convert 318.524k i/100ms
18
+ Calculating -------------------------------------
19
+ hugeint_convert 3.940M (± 0.7%) i/s - 19.748M in 5.012440s
20
+ ```
21
+
22
+ ## after (use bit shift)
23
+ ✦ ❯ ruby benchmark/converter_hugeint_ips.rb
24
+ Warming up --------------------------------------
25
+ hugeint_convert 347.419k i/100ms
26
+ Calculating -------------------------------------
27
+ hugeint_convert 4.171M (± 0.3%) i/s - 21.193M in 5.081131s
data/ext/duckdb/duckdb.c CHANGED
@@ -4,8 +4,16 @@ VALUE mDuckDB;
4
4
 
5
5
  static VALUE duckdb_s_library_version(VALUE self);
6
6
 
7
+ /*
8
+ * call-seq:
9
+ * DuckDB.library_version -> String
10
+ *
11
+ * Returns the version of the DuckDB library.
12
+ *
13
+ * DuckDB.library_version # => "0.2.0"
14
+ */
7
15
  static VALUE duckdb_s_library_version(VALUE self) {
8
- return rb_str_new2(duckdb_library_version());
16
+ return rb_str_new2(duckdb_library_version());
9
17
  }
10
18
 
11
19
  void
@@ -28,4 +28,7 @@ have_func('duckdb_extract_statements', 'duckdb.h')
28
28
  # check duckdb >= 0.8.0
29
29
  have_func('duckdb_string_is_inlined', 'duckdb.h')
30
30
 
31
+ # check duckdb >= 0.9.0
32
+ have_func('duckdb_bind_parameter_index', 'duckdb.h')
33
+
31
34
  create_makefile('duckdb/duckdb_native')
@@ -12,6 +12,10 @@
12
12
  #define HAVE_DUCKDB_H_GE_V080 1
13
13
  #endif
14
14
 
15
+ #ifdef HAVE_DUCKDB_BIND_PARAMETER_INDEX
16
+ #define HAVE_DUCKDB_H_GE_V090 1
17
+ #endif
18
+
15
19
  #include "./error.h"
16
20
  #include "./database.h"
17
21
  #include "./connection.h"
@@ -150,13 +150,8 @@ module DuckDB
150
150
  # .flush
151
151
  #
152
152
  def append_interval(value)
153
- raise ArgumentError, "Argument `#{value}` must be a string." unless value.is_a?(String)
154
-
155
- hash = iso8601_interval_to_hash(value)
156
-
157
- months, days, micros = hash_to__append_interval_args(hash)
158
-
159
- _append_interval(months, days, micros)
153
+ value = Interval.to_interval(value)
154
+ _append_interval(value.interval_months, value.interval_days, value.interval_micros)
160
155
  end
161
156
 
162
157
  #
@@ -197,6 +192,8 @@ module DuckDB
197
192
  append_timestamp(value)
198
193
  when Date
199
194
  append_date(value)
195
+ when DuckDB::Interval
196
+ append_interval(value)
200
197
  else
201
198
  raise(DuckDB::Error, "not supported type #{value} (#{value.class})")
202
199
  end
@@ -1,10 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'date'
4
+ require_relative 'interval'
4
5
 
5
6
  module DuckDB
6
7
  module Converter
7
- HALF_HUGEINT = 1 << 64
8
+ HALF_HUGEINT_BIT = 64
9
+ HALF_HUGEINT = 1 << HALF_HUGEINT_BIT
8
10
  FLIP_HUGEINT = 1 << 63
9
11
 
10
12
  module_function
@@ -18,7 +20,7 @@ module DuckDB
18
20
  end
19
21
 
20
22
  def _to_hugeint_from_vector(lower, upper)
21
- (upper * HALF_HUGEINT) + lower
23
+ (upper << HALF_HUGEINT_BIT) + lower
22
24
  end
23
25
 
24
26
  def _to_decimal_from_vector(_width, scale, lower, upper)
@@ -29,15 +31,7 @@ module DuckDB
29
31
  end
30
32
 
31
33
  def _to_interval_from_vector(months, days, micros)
32
- hash = { year: 0, month: 0, day: 0, hour: 0, min: 0, sec: 0, usec: 0 }
33
- hash[:year] = months / 12
34
- hash[:month] = months % 12
35
- hash[:day] = days
36
- hash[:hour] = micros / 3_600_000_000
37
- hash[:min] = (micros % 3_600_000_000) / 60_000_000
38
- hash[:sec] = (micros % 60_000_000) / 1_000_000
39
- hash[:usec] = micros % 1_000_000
40
- hash
34
+ Interval.new(interval_months: months, interval_days: days, interval_micros: micros)
41
35
  end
42
36
 
43
37
  def _to_uuid_from_vector(lower, upper)
@@ -53,59 +47,12 @@ module DuckDB
53
47
  def integer_to_hugeint(value)
54
48
  case value
55
49
  when Integer
56
- upper = value / HALF_HUGEINT
57
- lower = value - upper * HALF_HUGEINT
50
+ upper = value >> HALF_HUGEINT_BIT
51
+ lower = value - (upper << HALF_HUGEINT_BIT)
58
52
  [lower, upper]
59
53
  else
60
- raise(ArgumentError, "2nd argument `#{value}` must be Integer.")
54
+ raise(ArgumentError, "The argument `#{value}` must be Integer.")
61
55
  end
62
56
  end
63
-
64
- def iso8601_interval_to_hash(value)
65
- digit = ''
66
- time = false
67
- hash = {}
68
- hash.default = 0
69
-
70
- value.each_char do |c|
71
- if '-0123456789.'.include?(c)
72
- digit += c
73
- elsif c == 'T'
74
- time = true
75
- digit = ''
76
- elsif c == 'M'
77
- m_interval_to_hash(hash, digit, time)
78
- digit = ''
79
- elsif c == 'S'
80
- s_interval_to_hash(hash, digit)
81
- digit = ''
82
- elsif 'YDH'.include?(c)
83
- hash[c] = digit.to_i
84
- digit = ''
85
- elsif c != 'P'
86
- raise ArgumentError, "The argument `#{value}` can't be parse."
87
- end
88
- end
89
- hash
90
- end
91
-
92
- def m_interval_to_hash(hash, digit, time)
93
- key = time ? 'TM' : 'M'
94
- hash[key] = digit.to_i
95
- end
96
-
97
- def s_interval_to_hash(hash, digit)
98
- sec, msec = digit.split('.')
99
- hash['S'] = sec.to_i
100
- hash['MS'] = "#{msec}000000"[0, 6].to_i
101
- hash['MS'] *= -1 if hash['S'].negative?
102
- end
103
-
104
- def hash_to__append_interval_args(hash)
105
- months = hash['Y'] * 12 + hash['M']
106
- days = hash['D']
107
- micros = (hash['H'] * 3600 + hash['TM'] * 60 + hash['S']) * 1_000_000 + hash['MS']
108
- [months, days, micros]
109
- end
110
57
  end
111
58
  end
@@ -0,0 +1,161 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DuckDB
4
+ # Interval class represents DuckDB's interval type value.
5
+ #
6
+ # The usage is as follows:
7
+ #
8
+ # require 'duckdb'
9
+ #
10
+ # interval = DuckDB::Interval.new(interval_months: 14, interval_days: 3, interval_micros: 14706123456)
11
+ # # or
12
+ # # interval = DuckDB::Interval.mk_interval(year: 1, month: 2, day: 3, hour: 4, min: 5, sec: 6, usec: 123456)
13
+ # # or
14
+ # # interval = DuckDB::Interval.iso8601_parse('P1Y2M3DT4H5M6.123456S')
15
+ #
16
+ # db = DuckDB::Database.open # database in memory
17
+ # con = db.connect
18
+ #
19
+ # con.execute('CREATE TABLE users (value INTERVAL)')
20
+ #
21
+ # require 'duckdb'
22
+ # db = DuckDB::Database.open
23
+ # con = db.connect
24
+ # con.query('CREATE TABLE intervals (interval_value INTERVAL)')
25
+ # appender = con.appender('intervals')
26
+ # appender
27
+ # .begin_row
28
+ # .append_interval(interval)
29
+ # .end_row
30
+ # .flush
31
+ class Interval
32
+ ISO8601_REGEXP = Regexp.compile(
33
+ '(?<negativ>-{0,1})P
34
+ (?<year>-{0,1}\d+Y){0,1}
35
+ (?<month>-{0,1}\d+M){0,1}
36
+ (?<day>-{0,1}\d+D){0,1}
37
+ T{0,1}
38
+ (?<hour>-{0,1}\d+H){0,1}
39
+ (?<min>-{0,1}\d+M){0,1}
40
+ ((?<sec>-{0,1}\d+)\.{0,1}(?<usec>\d*)S){0,1}',
41
+ Regexp::EXTENDED
42
+ )
43
+
44
+ class << self
45
+ # parses the ISO8601 format string and return the Interval object.
46
+ #
47
+ # DuckDB::Interval.iso8601_parse('P1Y2M3DT4H5M6.123456S')
48
+ # => #<DuckDB::Interval:0x00007f9b9c0b3b60 @interval_months=14, @interval_days=3, @interval_micros=14706123456>
49
+ def iso8601_parse(value)
50
+ m = ISO8601_REGEXP.match(value)
51
+
52
+ raise ArgumentError, "The argument `#{value}` can't be parse." if m.nil?
53
+
54
+ year, month, day, hour, min, sec, usec = matched_to_i(m)
55
+
56
+ mk_interval(year: year, month: month, day: day, hour: hour, min: min, sec: sec, usec: usec)
57
+ end
58
+
59
+ # creates the Interval object.
60
+ #
61
+ # DuckDB::Interval.mk_interval(year: 1, month: 2, day: 3, hour: 4, min: 5, sec: 6, usec: 123456)
62
+ # => #<DuckDB::Interval:0x00007f9b9c0b3b60 @interval_months=14, @interval_days=3, @interval_micros=14706123456>
63
+ def mk_interval(year: 0, month: 0, day: 0, hour: 0, min: 0, sec: 0, usec: 0)
64
+ Interval.new(
65
+ interval_months: (year * 12) + month,
66
+ interval_days: day,
67
+ interval_micros: (((hour * 3600) + (min * 60) + sec) * 1_000_000) + usec
68
+ )
69
+ end
70
+
71
+ # Convert the value to the Interval object.
72
+ # The value can be String or Interval object.
73
+ # If the value is String, it is parsed as ISO8601 format.
74
+ # If the value is Interval object, it is returned as is.
75
+ # Otherwise, ArgumentError is raised.
76
+ #
77
+ # DuckDB::Interval.to_interval('P1Y2M3DT4H5M6.123456S')
78
+ # => #<DuckDB::Interval:0x00007f9b9c0b3b60 @interval_months=14, @interval_days=3, @interval_micros=14706123456>
79
+ #
80
+ # interval = DuckDB::Interval.to_interval('P1Y2M3DT4H5M6.123456S')
81
+ # DuckDB::Interval.to_interval(interval)
82
+ # => #<DuckDB::Interval:0x00007f9b9c0b3b60 @interval_months=14, @interval_days=3, @interval_micros=14706123456>
83
+ def to_interval(value)
84
+ case value
85
+ when String
86
+ iso8601_parse(value)
87
+ when Interval
88
+ value
89
+ else
90
+ raise ArgumentError, "The argument `#{value}` can't be parse."
91
+ end
92
+ end
93
+
94
+ private
95
+
96
+ def matched_to_i(matched)
97
+ sign = to_sign(matched)
98
+ sec = to_sec(matched)
99
+ usec = to_usec(matched)
100
+ usec *= -1 if sec.negative?
101
+ value = [
102
+ to_year(matched), to_month(matched), to_day(matched), to_hour(matched), to_min(matched), sec, usec
103
+ ]
104
+ sign.positive? ? value : value.map { |v| v * sign }
105
+ end
106
+
107
+ def to_sign(matched)
108
+ matched[:negativ] == '-' ? -1 : 1
109
+ end
110
+
111
+ def to_year(matched)
112
+ matched[:year].to_i
113
+ end
114
+
115
+ def to_month(matched)
116
+ matched[:month].to_i
117
+ end
118
+
119
+ def to_day(matched)
120
+ matched[:day].to_i
121
+ end
122
+
123
+ def to_hour(matched)
124
+ matched[:hour].to_i
125
+ end
126
+
127
+ def to_min(matched)
128
+ matched[:min].to_i
129
+ end
130
+
131
+ def to_sec(matched)
132
+ matched[:sec].to_i
133
+ end
134
+
135
+ def to_usec(matched)
136
+ matched[:usec].to_s.ljust(6, '0')[0, 6].to_i
137
+ end
138
+ end
139
+
140
+ attr_reader :interval_months, :interval_days, :interval_micros
141
+
142
+ # creates the Interval object.
143
+ # The arguments are the number of months, days, and microseconds.
144
+ # The default value is 0.
145
+ #
146
+ # DuckDB::Interval.new(interval_months: 1, interval_days: 2, interval_micros: 3)
147
+ # => #<DuckDB::Interval:0x00007f9b9c0b3b60 @interval_months=1, @interval_days=2, @interval_micros=3>
148
+ def initialize(interval_months: 0, interval_days: 0, interval_micros: 0)
149
+ @interval_months = interval_months
150
+ @interval_days = interval_days
151
+ @interval_micros = interval_micros
152
+ end
153
+
154
+ def ==(other)
155
+ other.is_a?(Interval) &&
156
+ @interval_months == other.interval_months &&
157
+ @interval_days == other.interval_days &&
158
+ @interval_micros == other.interval_micros
159
+ end
160
+ end
161
+ end
@@ -152,13 +152,8 @@ module DuckDB
152
152
  # stmt = PreparedStatement.new(con, sql)
153
153
  # stmt.bind(1, 'P1Y2D')
154
154
  def bind_interval(i, value)
155
- raise ArgumentError, "Argument `#{value}` must be a string." unless value.is_a?(String)
156
-
157
- hash = iso8601_interval_to_hash(value)
158
-
159
- months, days, micros = hash_to__append_interval_args(hash)
160
-
161
- _bind_interval(i, months, days, micros)
155
+ value = Interval.to_interval(value)
156
+ _bind_interval(i, value.interval_months, value.interval_days, value.interval_micros)
162
157
  end
163
158
 
164
159
  # binds i-th parameter with SQL prepared statement.
data/lib/duckdb/result.rb CHANGED
@@ -58,6 +58,7 @@ module DuckDB
58
58
 
59
59
  chunk_each { |row| yield row }
60
60
  else
61
+ warn('this `each` behavior will be deprecated in the future. set `Result.use_chunk_each = true` to use new `each` behavior.')
61
62
  return to_enum { row_size } unless block_given?
62
63
 
63
64
  row_count.times do |row_index|
@@ -94,7 +95,7 @@ module DuckDB
94
95
 
95
96
  def _to_hugeint_internal(row, col)
96
97
  lower, upper = __to_hugeint_internal(row, col)
97
- upper * Converter::HALF_HUGEINT + lower
98
+ Converter._to_hugeint_from_vector(lower, upper)
98
99
  end
99
100
 
100
101
  def _to_decimal(row, col)
@@ -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 = '0.8.1.3'
6
+ VERSION = '0.9.0'
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: duckdb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.1.3
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Masaki Suketa
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-07-15 00:00:00.000000000 Z
11
+ date: 2023-09-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -75,6 +75,7 @@ extensions:
75
75
  - ext/duckdb/extconf.rb
76
76
  extra_rdoc_files: []
77
77
  files:
78
+ - ".github/FUNDING.yml"
78
79
  - ".github/workflows/test_on_macos.yml"
79
80
  - ".github/workflows/test_on_ubuntu.yml"
80
81
  - ".github/workflows/test_on_windows.yml"
@@ -87,6 +88,7 @@ files:
87
88
  - LICENSE
88
89
  - README.md
89
90
  - Rakefile
91
+ - benchmark/converter_hugeint_ips.rb
90
92
  - benchmark/get_converter_module_ips.rb
91
93
  - benchmark/to_bigdecimal_ips.rb
92
94
  - benchmark/to_hugeint_ips.rb
@@ -128,6 +130,7 @@ files:
128
130
  - lib/duckdb/connection.rb
129
131
  - lib/duckdb/converter.rb
130
132
  - lib/duckdb/database.rb
133
+ - lib/duckdb/interval.rb
131
134
  - lib/duckdb/library_version.rb
132
135
  - lib/duckdb/prepared_statement.rb
133
136
  - lib/duckdb/result.rb