duckdb 0.8.1.3 → 0.9.0.1

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: aac4fbdeca8ed41cef872ace9dd3c8784ce43ee224a733f469fcd0907e852cd5
4
+ data.tar.gz: 56c8a5831830264fd4dfac41ba7a60bb9e6493a40536a5502c10dc0fc41b16d7
5
5
  SHA512:
6
- metadata.gz: 41fd80e244cd09100931b695533314aa29c040f7571f64747cb79533da45c8685adc1fc04bc731cff5d59e5c58f8669db2c9cb1eebb8dcdfc78f92b3d7cb49d6
7
- data.tar.gz: 75e8d32f1d467be39365f0ffe3f3f40471b1b580929f025736ec6d47db9dad62a99beb490a118066a7145837717dfcc337e5f1aaafba7f068c63279ef16770f9
6
+ metadata.gz: b94ffce97a54769bae379a3b13765b2132df1efe80fdb8f4855f77248855e95e9370651dbbe165a64c6ee45aff5bef20b3800a680f3da5287528974a243af338
7
+ data.tar.gz: 96accf18b7f4df7721efaec8e215e92dcbe97d22a99d89ffb64658f4696eb462b8ceaa27a3e558cba8ef5a7ce257fc4e3b35f83178fadf8047e19280563e7ae6
@@ -0,0 +1,3 @@
1
+ # These are supported funding model platforms
2
+
3
+ github: suketa
@@ -15,8 +15,8 @@ jobs:
15
15
  runs-on: macos-latest
16
16
  strategy:
17
17
  matrix:
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']
18
+ ruby: ['2.7.8', '3.0.6', '3.1.4', '3.2.2', '3.3.0-preview2', 'head']
19
+ duckdb: ['0.9.0', '0.8.1']
20
20
 
21
21
  steps:
22
22
  - uses: actions/checkout@v3
@@ -15,8 +15,8 @@ jobs:
15
15
  runs-on: ubuntu-latest
16
16
  strategy:
17
17
  matrix:
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']
18
+ ruby: ['2.7.8', '3.0.6', '3.1.4', '3.2.2', '3.3.0-preview2', 'head']
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,19 @@
1
1
  # ChangeLog
2
2
 
3
+ # 0.9.0.1
4
+
5
+ - add `DuckDB::PreparedStatement#bind_parameter_index`.
6
+ - DuckDB::Connection#query accepts SQL with named bind parameters.
7
+
8
+ # 0.9.0
9
+ - bump duckdb to 0.9.0.
10
+
11
+ ## Breaking Change
12
+ - deprecation warning when DuckDB::Result.each calling with `DuckDB::Result.use_chunk_each` is false.
13
+ The `each` behavior will be same as `DuckDB::Result.chunk_each` in the future.
14
+ set `DuckDB::Result.use_chunk_each = true` to suppress the warning.
15
+ - DuckDB::Result#chunck_each returns DuckDB::Interval class when the column type is INTERVAL.
16
+
3
17
  # 0.8.1.3
4
18
  - Fix BigDecimal conversion.
5
19
 
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.1)
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
 
data/README.md CHANGED
@@ -94,6 +94,17 @@ DuckDB::Database.open do |db|
94
94
  end
95
95
  ```
96
96
 
97
+ ### using bind variables
98
+
99
+ You can use bind variables.
100
+
101
+ ```ruby
102
+ con.query('SELECT * FROM users WHERE name = ? AND email = ?', 'Alice', 'alice@example.com')
103
+ # or
104
+ con.query('SELECT * FROM users WHERE name = $name AND email = $email', name: 'Alice', email: 'alice@example.com')
105
+ ```
106
+
107
+
97
108
  ### using BLOB column
98
109
 
99
110
  BLOB is available with DuckDB v0.2.5 or later.
@@ -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')
@@ -9,6 +9,11 @@ static VALUE duckdb_prepared_statement_initialize(VALUE self, VALUE con, VALUE q
9
9
  static VALUE duckdb_prepared_statement_nparams(VALUE self);
10
10
  static VALUE duckdb_prepared_statement_execute(VALUE self);
11
11
  static idx_t check_index(VALUE vidx);
12
+
13
+ #ifdef HAVE_DUCKDB_H_GE_V090
14
+ static VALUE duckdb_prepared_statement_bind_parameter_index(VALUE self, VALUE name);
15
+ #endif
16
+
12
17
  static VALUE duckdb_prepared_statement_bind_bool(VALUE self, VALUE vidx, VALUE val);
13
18
  static VALUE duckdb_prepared_statement_bind_int8(VALUE self, VALUE vidx, VALUE val);
14
19
  static VALUE duckdb_prepared_statement_bind_int16(VALUE self, VALUE vidx, VALUE val);
@@ -93,6 +98,20 @@ static idx_t check_index(VALUE vidx) {
93
98
  return idx;
94
99
  }
95
100
 
101
+ #ifdef HAVE_DUCKDB_H_GE_V090
102
+ static VALUE duckdb_prepared_statement_bind_parameter_index(VALUE self, VALUE name) {
103
+ rubyDuckDBPreparedStatement *ctx;
104
+ idx_t idx;
105
+
106
+ TypedData_Get_Struct(self, rubyDuckDBPreparedStatement, &prepared_statement_data_type, ctx);
107
+
108
+ if (duckdb_bind_parameter_index(ctx->prepared_statement, &idx, StringValuePtr(name)) == DuckDBError) {;
109
+ rb_raise(rb_eArgError, "parameter '%s' not found", StringValuePtr(name));
110
+ }
111
+ return ULL2NUM(idx);
112
+ }
113
+ #endif
114
+
96
115
  static VALUE duckdb_prepared_statement_bind_bool(VALUE self, VALUE vidx, VALUE val) {
97
116
  rubyDuckDBPreparedStatement *ctx;
98
117
  idx_t idx = check_index(vidx);
@@ -308,6 +327,11 @@ void init_duckdb_prepared_statement(void) {
308
327
  rb_define_method(cDuckDBPreparedStatement, "initialize", duckdb_prepared_statement_initialize, 2);
309
328
  rb_define_method(cDuckDBPreparedStatement, "execute", duckdb_prepared_statement_execute, 0);
310
329
  rb_define_method(cDuckDBPreparedStatement, "nparams", duckdb_prepared_statement_nparams, 0);
330
+
331
+ #ifdef HAVE_DUCKDB_H_GE_V090
332
+ rb_define_method(cDuckDBPreparedStatement, "bind_parameter_index", duckdb_prepared_statement_bind_parameter_index, 1);
333
+ #endif
334
+
311
335
  rb_define_method(cDuckDBPreparedStatement, "bind_bool", duckdb_prepared_statement_bind_bool, 2);
312
336
  rb_define_method(cDuckDBPreparedStatement, "bind_int8", duckdb_prepared_statement_bind_int8, 2);
313
337
  rb_define_method(cDuckDBPreparedStatement, "bind_int16", duckdb_prepared_statement_bind_int16, 2);
@@ -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
@@ -12,7 +12,6 @@ module DuckDB
12
12
  # executes sql with args.
13
13
  # The first argument sql must be SQL string.
14
14
  # The rest arguments are parameters of SQL string.
15
- # The parameters must be '?' in SQL statement.
16
15
  #
17
16
  # require 'duckdb'
18
17
  # db = DuckDB::Database.open('duckdb_file')
@@ -21,12 +20,20 @@ module DuckDB
21
20
  # sql = 'SELECT * FROM users WHERE name = ? AND email = ?'
22
21
  # dave = con.query(sql, 'Dave', 'dave@example.com')
23
22
  #
24
- def query(sql, *args)
25
- return query_sql(sql) if args.empty?
23
+ # # or You can use named parameter.
24
+ #
25
+ # sql = 'SELECT * FROM users WHERE name = $name AND email = $email'
26
+ # dave = con.query(sql, name: 'Dave', email: 'dave@example.com')
27
+ #
28
+ def query(sql, *args, **hash)
29
+ return query_sql(sql) if args.empty? && hash.empty?
26
30
 
27
31
  stmt = PreparedStatement.new(self, sql)
28
- args.each_with_index do |arg, i|
29
- stmt.bind(i + 1, arg)
32
+ args.each.with_index(1) do |arg, i|
33
+ stmt.bind(i, arg)
34
+ end
35
+ hash.each do |key, value|
36
+ stmt.bind(key, value)
30
37
  end
31
38
  stmt.execute
32
39
  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
@@ -1,6 +1,6 @@
1
1
  require 'date'
2
2
  require 'bigdecimal'
3
- require_relative './converter'
3
+ require_relative 'converter'
4
4
 
5
5
  module DuckDB
6
6
  # The DuckDB::PreparedStatement encapsulates connection with DuckDB prepared
@@ -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.
@@ -172,35 +167,55 @@ module DuckDB
172
167
  # sql ='SELECT name, email FROM users WHERE email = ?'
173
168
  # stmt = PreparedStatement.new(con, sql)
174
169
  # stmt.bind(1, 'email@example.com')
175
- def bind(i, value)
170
+ def bind(index, value)
171
+ case index
172
+ when Integer
173
+ bind_with_index(index, value)
174
+ when String
175
+ bind_with_name(index, value)
176
+ when Symbol
177
+ bind_with_name(index.to_s, value)
178
+ else
179
+ raise(ArgumentError, "1st argument `#{index}` must be Integer or String or Symbol.")
180
+ end
181
+ end
182
+
183
+ private
184
+
185
+ def bind_with_index(index, value)
176
186
  case value
177
187
  when NilClass
178
- bind_null(i)
188
+ bind_null(index)
179
189
  when Float
180
- bind_double(i, value)
190
+ bind_double(index, value)
181
191
  when Integer
182
192
  case value
183
193
  when RANGE_INT64
184
- bind_int64(i, value)
194
+ bind_int64(index, value)
185
195
  else
186
- bind_varchar(i, value.to_s)
196
+ bind_varchar(index, value.to_s)
187
197
  end
188
198
  when String
189
- blob?(value) ? bind_blob(i, value) : bind_varchar(i, value)
199
+ blob?(value) ? bind_blob(index, value) : bind_varchar(index, value)
190
200
  when TrueClass, FalseClass
191
- bind_bool(i, value)
201
+ bind_bool(index, value)
192
202
  when Time
193
- bind_varchar(i, value.strftime('%Y-%m-%d %H:%M:%S.%N'))
203
+ bind_varchar(index, value.strftime('%Y-%m-%d %H:%M:%S.%N'))
194
204
  when Date
195
- bind_varchar(i, value.strftime('%Y-%m-%d'))
205
+ bind_varchar(index, value.strftime('%Y-%m-%d'))
196
206
  when BigDecimal
197
- bind_varchar(i, value.to_s('F'))
207
+ bind_varchar(index, value.to_s('F'))
198
208
  else
199
209
  raise(DuckDB::Error, "not supported type `#{value}` (#{value.class})")
200
210
  end
201
211
  end
202
212
 
203
- private
213
+ def bind_with_name(name, value)
214
+ raise DuckDB::Error, 'not supported binding with name' unless respond_to?(:bind_parameter_index)
215
+
216
+ i = bind_parameter_index(name)
217
+ bind_with_index(i, value)
218
+ end
204
219
 
205
220
  def blob?(value)
206
221
  value.instance_of?(DuckDB::Blob) || value.encoding == Encoding::BINARY
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.1'
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.1
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-10-08 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