duckdb 0.8.1.3 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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