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 +4 -4
- data/.github/FUNDING.yml +3 -0
- data/.github/workflows/test_on_macos.yml +2 -2
- data/.github/workflows/test_on_ubuntu.yml +2 -2
- data/.github/workflows/test_on_windows.yml +1 -1
- data/CHANGELOG.md +14 -0
- data/CONTRIBUTION.md +2 -2
- data/Dockerfile +1 -1
- data/Gemfile.lock +7 -7
- data/README.md +11 -0
- data/benchmark/converter_hugeint_ips.rb +27 -0
- data/ext/duckdb/duckdb.c +9 -1
- data/ext/duckdb/extconf.rb +3 -0
- data/ext/duckdb/prepared_statement.c +24 -0
- data/ext/duckdb/ruby-duckdb.h +4 -0
- data/lib/duckdb/appender.rb +4 -7
- data/lib/duckdb/connection.rb +12 -5
- data/lib/duckdb/converter.rb +8 -61
- data/lib/duckdb/interval.rb +161 -0
- data/lib/duckdb/prepared_statement.rb +34 -19
- data/lib/duckdb/result.rb +2 -1
- data/lib/duckdb/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aac4fbdeca8ed41cef872ace9dd3c8784ce43ee224a733f469fcd0907e852cd5
|
4
|
+
data.tar.gz: 56c8a5831830264fd4dfac41ba7a60bb9e6493a40536a5502c10dc0fc41b16d7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b94ffce97a54769bae379a3b13765b2132df1efe80fdb8f4855f77248855e95e9370651dbbe165a64c6ee45aff5bef20b3800a680f3da5287528974a243af338
|
7
|
+
data.tar.gz: 96accf18b7f4df7721efaec8e215e92dcbe97d22a99d89ffb64658f4696eb462b8ceaa27a3e558cba8ef5a7ce257fc4e3b35f83178fadf8047e19280563e7ae6
|
data/.github/FUNDING.yml
ADDED
@@ -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-
|
19
|
-
duckdb: ['0.
|
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-
|
19
|
-
duckdb: ['0.
|
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
|
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
|
12
|
-
docker
|
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
data/Gemfile.lock
CHANGED
@@ -1,24 +1,24 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
duckdb (0.
|
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.
|
11
|
-
minitest (5.
|
12
|
-
nokogiri (1.15.
|
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.
|
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.
|
19
|
+
rake-compiler (1.2.5)
|
20
20
|
rake
|
21
|
-
ruby_memcheck (
|
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
|
-
|
16
|
+
return rb_str_new2(duckdb_library_version());
|
9
17
|
}
|
10
18
|
|
11
19
|
void
|
data/ext/duckdb/extconf.rb
CHANGED
@@ -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);
|
data/ext/duckdb/ruby-duckdb.h
CHANGED
data/lib/duckdb/appender.rb
CHANGED
@@ -150,13 +150,8 @@ module DuckDB
|
|
150
150
|
# .flush
|
151
151
|
#
|
152
152
|
def append_interval(value)
|
153
|
-
|
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
|
data/lib/duckdb/connection.rb
CHANGED
@@ -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
|
-
|
25
|
-
|
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.
|
29
|
-
stmt.bind(i
|
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
|
data/lib/duckdb/converter.rb
CHANGED
@@ -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
|
-
|
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
|
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
|
-
|
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
|
57
|
-
lower = value - upper
|
50
|
+
upper = value >> HALF_HUGEINT_BIT
|
51
|
+
lower = value - (upper << HALF_HUGEINT_BIT)
|
58
52
|
[lower, upper]
|
59
53
|
else
|
60
|
-
raise(ArgumentError, "
|
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 '
|
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
|
-
|
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(
|
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(
|
188
|
+
bind_null(index)
|
179
189
|
when Float
|
180
|
-
bind_double(
|
190
|
+
bind_double(index, value)
|
181
191
|
when Integer
|
182
192
|
case value
|
183
193
|
when RANGE_INT64
|
184
|
-
bind_int64(
|
194
|
+
bind_int64(index, value)
|
185
195
|
else
|
186
|
-
bind_varchar(
|
196
|
+
bind_varchar(index, value.to_s)
|
187
197
|
end
|
188
198
|
when String
|
189
|
-
blob?(value) ? bind_blob(
|
199
|
+
blob?(value) ? bind_blob(index, value) : bind_varchar(index, value)
|
190
200
|
when TrueClass, FalseClass
|
191
|
-
bind_bool(
|
201
|
+
bind_bool(index, value)
|
192
202
|
when Time
|
193
|
-
bind_varchar(
|
203
|
+
bind_varchar(index, value.strftime('%Y-%m-%d %H:%M:%S.%N'))
|
194
204
|
when Date
|
195
|
-
bind_varchar(
|
205
|
+
bind_varchar(index, value.strftime('%Y-%m-%d'))
|
196
206
|
when BigDecimal
|
197
|
-
bind_varchar(
|
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
|
-
|
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
|
-
|
98
|
+
Converter._to_hugeint_from_vector(lower, upper)
|
98
99
|
end
|
99
100
|
|
100
101
|
def _to_decimal(row, col)
|
data/lib/duckdb/version.rb
CHANGED
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.
|
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-
|
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
|