duckdb 0.9.1.1 → 0.9.2
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/.gitattributes +1 -0
- data/.github/workflows/test_on_macos.yml +6 -6
- data/.github/workflows/test_on_ubuntu.yml +6 -6
- data/.github/workflows/test_on_windows.yml +4 -4
- data/CHANGELOG.md +11 -0
- data/Dockerfile +1 -1
- data/Gemfile.lock +5 -5
- data/README.md +17 -1
- data/benchmark/async_query.rb +90 -0
- data/ext/duckdb/appender.c +5 -5
- data/ext/duckdb/appender.h +1 -1
- data/ext/duckdb/blob.c +1 -1
- data/ext/duckdb/blob.h +1 -4
- data/ext/duckdb/column.c +2 -2
- data/ext/duckdb/column.h +2 -2
- data/ext/duckdb/config.c +1 -1
- data/ext/duckdb/config.h +1 -1
- data/ext/duckdb/connection.c +66 -6
- data/ext/duckdb/connection.h +2 -2
- data/ext/duckdb/converter.h +1 -1
- data/ext/duckdb/conveter.c +1 -1
- data/ext/duckdb/database.c +3 -3
- data/ext/duckdb/database.h +2 -2
- data/ext/duckdb/duckdb.c +11 -11
- data/ext/duckdb/error.c +1 -1
- data/ext/duckdb/error.h +1 -1
- data/ext/duckdb/extconf.rb +46 -13
- data/ext/duckdb/pending_result.c +45 -6
- data/ext/duckdb/pending_result.h +1 -1
- data/ext/duckdb/prepared_statement.c +7 -7
- data/ext/duckdb/prepared_statement.h +1 -1
- data/ext/duckdb/result.c +112 -70
- data/ext/duckdb/result.h +2 -2
- data/ext/duckdb/ruby-duckdb.h +1 -0
- data/ext/duckdb/util.c +4 -4
- data/ext/duckdb/util.h +4 -4
- data/lib/duckdb/appender.rb +1 -10
- data/lib/duckdb/connection.rb +50 -8
- data/lib/duckdb/converter.rb +27 -1
- data/lib/duckdb/prepared_statement.rb +42 -40
- data/lib/duckdb/result.rb +8 -2
- data/lib/duckdb/version.rb +1 -1
- data/sample/async_query.rb +24 -0
- data/sample/async_query_stream.rb +24 -0
- metadata +6 -2
data/lib/duckdb/connection.rb
CHANGED
@@ -25,19 +25,60 @@ module DuckDB
|
|
25
25
|
# sql = 'SELECT * FROM users WHERE name = $name AND email = $email'
|
26
26
|
# dave = con.query(sql, name: 'Dave', email: 'dave@example.com')
|
27
27
|
#
|
28
|
-
def query(sql, *args, **
|
29
|
-
return query_sql(sql) if args.empty? &&
|
28
|
+
def query(sql, *args, **kwargs)
|
29
|
+
return query_sql(sql) if args.empty? && kwargs.empty?
|
30
30
|
|
31
31
|
stmt = PreparedStatement.new(self, sql)
|
32
|
-
|
33
|
-
stmt.bind(i, arg)
|
34
|
-
end
|
35
|
-
hash.each do |key, value|
|
36
|
-
stmt.bind(key, value)
|
37
|
-
end
|
32
|
+
stmt.bind_args(*args, **kwargs)
|
38
33
|
stmt.execute
|
39
34
|
end
|
40
35
|
|
36
|
+
#
|
37
|
+
# executes sql with args asynchronously.
|
38
|
+
# The first argument sql must be SQL string.
|
39
|
+
# The rest arguments are parameters of SQL string.
|
40
|
+
# This method returns DuckDB::PendingResult object.
|
41
|
+
#
|
42
|
+
# require 'duckdb'
|
43
|
+
# db = DuckDB::Database.open('duckdb_file')
|
44
|
+
# con = db.connect
|
45
|
+
#
|
46
|
+
# sql = 'SELECT * FROM users WHERE name = $name AND email = $email'
|
47
|
+
# pending_result = con.async_query(sql, name: 'Dave', email: 'dave@example.com')
|
48
|
+
# pending_result.execute_task while pending_result.state == :not_ready
|
49
|
+
# result = pending_result.execute_pending
|
50
|
+
# result.each.first
|
51
|
+
#
|
52
|
+
def async_query(sql, *args, **kwargs)
|
53
|
+
stmt = PreparedStatement.new(self, sql)
|
54
|
+
stmt.bind_args(*args, **kwargs)
|
55
|
+
stmt.pending_prepared
|
56
|
+
end
|
57
|
+
|
58
|
+
#
|
59
|
+
# executes sql with args asynchronously and provides streaming result.
|
60
|
+
# The first argument sql must be SQL string.
|
61
|
+
# The rest arguments are parameters of SQL string.
|
62
|
+
# This method returns DuckDB::PendingResult object.
|
63
|
+
#
|
64
|
+
# require 'duckdb'
|
65
|
+
# DuckDB::Result.use_chunk_each = true # must be true
|
66
|
+
# db = DuckDB::Database.open('duckdb_file')
|
67
|
+
# con = db.connect
|
68
|
+
#
|
69
|
+
# sql = 'SELECT * FROM users WHERE name = $name AND email = $email'
|
70
|
+
# pending_result = con.async_query_stream(sql, name: 'Dave', email: 'dave@example.com')
|
71
|
+
#
|
72
|
+
# pending_result.execute_task while pending_result.state == :not_ready
|
73
|
+
# result = pending_result.execute_pending
|
74
|
+
# result.each.first
|
75
|
+
#
|
76
|
+
def async_query_stream(sql, *args, **kwargs)
|
77
|
+
stmt = PreparedStatement.new(self, sql)
|
78
|
+
stmt.bind_args(*args, **kwargs)
|
79
|
+
stmt.pending_prepared_stream
|
80
|
+
end
|
81
|
+
|
41
82
|
#
|
42
83
|
# connects DuckDB database
|
43
84
|
# The first argument is DuckDB::Database object
|
@@ -83,6 +124,7 @@ module DuckDB
|
|
83
124
|
end
|
84
125
|
|
85
126
|
alias execute query
|
127
|
+
alias async_execute async_query
|
86
128
|
alias open connect
|
87
129
|
alias close disconnect
|
88
130
|
end
|
data/lib/duckdb/converter.rb
CHANGED
@@ -42,6 +42,32 @@ module DuckDB
|
|
42
42
|
"#{str[0, 8]}-#{str[8, 4]}-#{str[12, 4]}-#{str[16, 4]}-#{str[20, 12]}"
|
43
43
|
end
|
44
44
|
|
45
|
+
def _parse_date(value)
|
46
|
+
case value
|
47
|
+
when Date, Time
|
48
|
+
value
|
49
|
+
else
|
50
|
+
begin
|
51
|
+
Date.parse(value)
|
52
|
+
rescue StandardError => e
|
53
|
+
raise(ArgumentError, "Cannot parse `#{value.inspect}` to Date object. #{e.message}")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def _parse_time(value)
|
59
|
+
case value
|
60
|
+
when Time
|
61
|
+
value
|
62
|
+
else
|
63
|
+
begin
|
64
|
+
Time.parse(value)
|
65
|
+
rescue StandardError => e
|
66
|
+
raise(ArgumentError, "Cannot parse `#{value.inspect}` to Time object. #{e.message}")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
45
71
|
private
|
46
72
|
|
47
73
|
def integer_to_hugeint(value)
|
@@ -51,7 +77,7 @@ module DuckDB
|
|
51
77
|
lower = value - (upper << HALF_HUGEINT_BIT)
|
52
78
|
[lower, upper]
|
53
79
|
else
|
54
|
-
raise(ArgumentError, "The argument `#{value}` must be Integer.")
|
80
|
+
raise(ArgumentError, "The argument `#{value.inspect}` must be Integer.")
|
55
81
|
end
|
56
82
|
end
|
57
83
|
end
|
@@ -24,21 +24,49 @@ module DuckDB
|
|
24
24
|
PendingResult.new(self)
|
25
25
|
end
|
26
26
|
|
27
|
+
def pending_prepared_stream
|
28
|
+
raise DuckDB::Error, 'DuckDB::Result.use_chunk_each must be true.' unless DuckDB::Result.use_chunk_each?
|
29
|
+
|
30
|
+
PendingResult.new(self, true)
|
31
|
+
end
|
32
|
+
|
33
|
+
# binds all parameters with SQL prepared statement.
|
34
|
+
#
|
35
|
+
# require 'duckdb'
|
36
|
+
# db = DuckDB::Database.open('duckdb_database')
|
37
|
+
# con = db.connect
|
38
|
+
# sql ='SELECT name FROM users WHERE id = ?'
|
39
|
+
# # or
|
40
|
+
# # sql ='SELECT name FROM users WHERE id = $id'
|
41
|
+
# stmt = PreparedStatement.new(con, sql)
|
42
|
+
# stmt.bind_args([1])
|
43
|
+
# # or
|
44
|
+
# # stmt.bind_args(id: 1)
|
45
|
+
def bind_args(*args, **kwargs)
|
46
|
+
args.each.with_index(1) do |arg, i|
|
47
|
+
bind(i, arg)
|
48
|
+
end
|
49
|
+
kwargs.each do |key, value|
|
50
|
+
bind(key, value)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
27
54
|
# binds i-th parameter with SQL prepared statement.
|
28
55
|
# The first argument is index of parameter.
|
29
56
|
# The index of first parameter is 1 not 0.
|
30
57
|
# The second argument value is to expected Integer value.
|
31
58
|
# This method uses bind_varchar internally.
|
59
|
+
#
|
32
60
|
# require 'duckdb'
|
33
61
|
# db = DuckDB::Database.open('duckdb_database')
|
34
62
|
# con = db.connect
|
35
63
|
# sql ='SELECT name FROM users WHERE bigint_col = ?'
|
36
64
|
# stmt = PreparedStatement.new(con, sql)
|
37
65
|
# stmt.bind_hugeint(1, 1_234_567_890_123_456_789_012_345)
|
38
|
-
def bind_hugeint(
|
66
|
+
def bind_hugeint(index, value)
|
39
67
|
case value
|
40
68
|
when Integer
|
41
|
-
bind_varchar(
|
69
|
+
bind_varchar(index, value.to_s)
|
42
70
|
else
|
43
71
|
raise(ArgumentError, "2nd argument `#{value}` must be Integer.")
|
44
72
|
end
|
@@ -49,6 +77,7 @@ module DuckDB
|
|
49
77
|
# The index of first parameter is 1 not 0.
|
50
78
|
# The second argument value must be Integer value.
|
51
79
|
# This method uses duckdb_bind_hugeint internally.
|
80
|
+
#
|
52
81
|
# require 'duckdb'
|
53
82
|
# db = DuckDB::Database.open('duckdb_database')
|
54
83
|
# con = db.connect
|
@@ -73,19 +102,10 @@ module DuckDB
|
|
73
102
|
# stmt.bind(1, Date.today)
|
74
103
|
# # or you can specify date string.
|
75
104
|
# # stmt.bind(1, '2021-02-23')
|
76
|
-
def bind_date(
|
77
|
-
|
78
|
-
when Date, Time
|
79
|
-
date = value
|
80
|
-
else
|
81
|
-
begin
|
82
|
-
date = Date.parse(value)
|
83
|
-
rescue => e
|
84
|
-
raise(ArgumentError, "Cannot parse argument value to date. #{e.message}")
|
85
|
-
end
|
86
|
-
end
|
105
|
+
def bind_date(index, value)
|
106
|
+
date = _parse_date(value)
|
87
107
|
|
88
|
-
_bind_date(
|
108
|
+
_bind_date(index, date.year, date.month, date.day)
|
89
109
|
end
|
90
110
|
|
91
111
|
# binds i-th parameter with SQL prepared statement.
|
@@ -101,19 +121,10 @@ module DuckDB
|
|
101
121
|
# stmt.bind(1, Time.now)
|
102
122
|
# # or you can specify time string.
|
103
123
|
# # stmt.bind(1, '07:39:45')
|
104
|
-
def bind_time(
|
105
|
-
|
106
|
-
when Time
|
107
|
-
time = value
|
108
|
-
else
|
109
|
-
begin
|
110
|
-
time = Time.parse(value)
|
111
|
-
rescue => e
|
112
|
-
raise(ArgumentError, "Cannot parse argument value to time. #{e.message}")
|
113
|
-
end
|
114
|
-
end
|
124
|
+
def bind_time(index, value)
|
125
|
+
time = _parse_time(value)
|
115
126
|
|
116
|
-
_bind_time(
|
127
|
+
_bind_time(index, time.hour, time.min, time.sec, time.usec)
|
117
128
|
end
|
118
129
|
|
119
130
|
# binds i-th parameter with SQL prepared statement.
|
@@ -129,19 +140,10 @@ module DuckDB
|
|
129
140
|
# stmt.bind(1, Time.now)
|
130
141
|
# # or you can specify timestamp string.
|
131
142
|
# # stmt.bind(1, '2022-02-23 07:39:45')
|
132
|
-
def bind_timestamp(
|
133
|
-
|
134
|
-
when Time
|
135
|
-
time = value
|
136
|
-
else
|
137
|
-
begin
|
138
|
-
time = Time.parse(value)
|
139
|
-
rescue => e
|
140
|
-
raise(ArgumentError, "Cannot parse argument value to time. #{e.message}")
|
141
|
-
end
|
142
|
-
end
|
143
|
+
def bind_timestamp(index, value)
|
144
|
+
time = _parse_time(value)
|
143
145
|
|
144
|
-
_bind_timestamp(
|
146
|
+
_bind_timestamp(index, time.year, time.month, time.day, time.hour, time.min, time.sec, time.usec)
|
145
147
|
end
|
146
148
|
|
147
149
|
# binds i-th parameter with SQL prepared statement.
|
@@ -155,9 +157,9 @@ module DuckDB
|
|
155
157
|
# sql ='SELECT value FROM intervals WHERE interval = ?'
|
156
158
|
# stmt = PreparedStatement.new(con, sql)
|
157
159
|
# stmt.bind(1, 'P1Y2D')
|
158
|
-
def bind_interval(
|
160
|
+
def bind_interval(index, value)
|
159
161
|
value = Interval.to_interval(value)
|
160
|
-
_bind_interval(
|
162
|
+
_bind_interval(index, value.interval_months, value.interval_days, value.interval_micros)
|
161
163
|
end
|
162
164
|
|
163
165
|
# binds i-th parameter with SQL prepared statement.
|
data/lib/duckdb/result.rb
CHANGED
@@ -58,9 +58,15 @@ module DuckDB
|
|
58
58
|
|
59
59
|
def each
|
60
60
|
if self.class.use_chunk_each?
|
61
|
-
|
61
|
+
if streaming?
|
62
|
+
return _chunk_stream unless block_given?
|
62
63
|
|
63
|
-
|
64
|
+
_chunk_stream { |row| yield row }
|
65
|
+
else
|
66
|
+
return chunk_each unless block_given?
|
67
|
+
|
68
|
+
chunk_each { |row| yield row }
|
69
|
+
end
|
64
70
|
else
|
65
71
|
warn('this `each` behavior will be deprecated in the future. set `DuckDB::Result.use_chunk_each = true` to use new `each` behavior.')
|
66
72
|
return to_enum { row_size } unless block_given?
|
data/lib/duckdb/version.rb
CHANGED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'duckdb'
|
2
|
+
|
3
|
+
DuckDB::Result.use_chunk_each = true
|
4
|
+
DuckDB::Database.open do |db|
|
5
|
+
db.connect do |con|
|
6
|
+
con.query('SET threads=1')
|
7
|
+
con.query('CREATE TABLE tbl as SELECT range a, mod(range, 10) b FROM range(10000)')
|
8
|
+
con.query('CREATE TABLE tbl2 as SELECT range a, mod(range, 10) b FROM range(10000)')
|
9
|
+
# con.query('SET ENABLE_PROGRESS_BAR=true')
|
10
|
+
# con.query('SET ENABLE_PROGRESS_BAR_PRINT=false')
|
11
|
+
pending_result = con.async_query('SELECT * FROM tbl where b = (SELECT min(b) FROM tbl2)')
|
12
|
+
|
13
|
+
# con.interrupt
|
14
|
+
while pending_result.state == :not_ready
|
15
|
+
pending_result.execute_task
|
16
|
+
print '.'
|
17
|
+
$stdout.flush
|
18
|
+
sleep 0.01
|
19
|
+
end
|
20
|
+
result = pending_result.execute_pending
|
21
|
+
puts
|
22
|
+
p result.each.first
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'duckdb'
|
2
|
+
|
3
|
+
DuckDB::Result.use_chunk_each = true
|
4
|
+
DuckDB::Database.open do |db|
|
5
|
+
db.connect do |con|
|
6
|
+
con.query('SET threads=1')
|
7
|
+
con.query('CREATE TABLE tbl as SELECT range a, mod(range, 10) b FROM range(10000)')
|
8
|
+
con.query('CREATE TABLE tbl2 as SELECT range a, mod(range, 10) b FROM range(10000)')
|
9
|
+
# con.query('SET ENABLE_PROGRESS_BAR=true')
|
10
|
+
# con.query('SET ENABLE_PROGRESS_BAR_PRINT=false')
|
11
|
+
pending_result = con.async_query_stream('SELECT * FROM tbl where b = (SELECT min(b) FROM tbl2)')
|
12
|
+
|
13
|
+
# con.interrupt
|
14
|
+
while pending_result.state == :not_ready
|
15
|
+
pending_result.execute_task
|
16
|
+
print '.'
|
17
|
+
$stdout.flush
|
18
|
+
sleep 0.01
|
19
|
+
end
|
20
|
+
result = pending_result.execute_pending
|
21
|
+
puts
|
22
|
+
p result.each.first
|
23
|
+
end
|
24
|
+
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.9.
|
4
|
+
version: 0.9.2
|
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-11-25 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
|
+
- ".gitattributes"
|
78
79
|
- ".github/FUNDING.yml"
|
79
80
|
- ".github/workflows/test_on_macos.yml"
|
80
81
|
- ".github/workflows/test_on_ubuntu.yml"
|
@@ -88,6 +89,7 @@ files:
|
|
88
89
|
- LICENSE
|
89
90
|
- README.md
|
90
91
|
- Rakefile
|
92
|
+
- benchmark/async_query.rb
|
91
93
|
- benchmark/converter_hugeint_ips.rb
|
92
94
|
- benchmark/get_converter_module_ips.rb
|
93
95
|
- benchmark/to_bigdecimal_ips.rb
|
@@ -138,6 +140,8 @@ files:
|
|
138
140
|
- lib/duckdb/prepared_statement.rb
|
139
141
|
- lib/duckdb/result.rb
|
140
142
|
- lib/duckdb/version.rb
|
143
|
+
- sample/async_query.rb
|
144
|
+
- sample/async_query_stream.rb
|
141
145
|
homepage: https://github.com/suketa/ruby-duckdb
|
142
146
|
licenses:
|
143
147
|
- MIT
|