duckdb 0.8.1.3 → 0.9.0.1

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: 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