duckdb 1.1.3.0 → 1.2.0.0

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.
@@ -13,15 +13,216 @@ module DuckDB
13
13
  # con.query('CREATE TABLE users (id INTEGER, name VARCHAR)')
14
14
  # appender = con.appender('users')
15
15
  # appender.append_row(1, 'Alice')
16
- #
17
16
  class Appender
18
17
  include DuckDB::Converter
19
18
 
19
+ # :stopdoc:
20
20
  RANGE_INT16 = -32_768..32_767
21
21
  RANGE_INT32 = -2_147_483_648..2_147_483_647
22
22
  RANGE_INT64 = -9_223_372_036_854_775_808..9_223_372_036_854_775_807
23
+ private_constant :RANGE_INT16, :RANGE_INT32, :RANGE_INT64
24
+ # :startdoc:
25
+
26
+ # :call-seq:
27
+ # appender.begin_row -> self
28
+ # A nop method, provided for backwards compatibility reasons.
29
+ # Does nothing. Only `end_row` is required.
30
+ def begin_row
31
+ self
32
+ end
33
+
34
+ # call-seq:
35
+ # appender.end_row -> self
36
+ #
37
+ # Finish the current row of appends. After end_row is called, the next row can be appended.
38
+ # require 'duckdb'
39
+ # db = DuckDB::Database.open
40
+ # con = db.connect
41
+ # con.query('CREATE TABLE users (id INTEGER, name VARCHAR)')
42
+ # appender = con.appender('users')
43
+ # appender
44
+ # .append_int32(1)
45
+ # .append_varchar('Alice')
46
+ # .end_row
47
+ def end_row
48
+ return self if _end_row
49
+
50
+ raise_appender_error('failed to end_row')
51
+ end
52
+
53
+ # :call-seq:
54
+ # appender.flush -> self
55
+ #
56
+ # Flushes the appender to the table, forcing the cache of the appender to be cleared.
57
+ # If flushing the data triggers a constraint violation or any other error, then all
58
+ # data is invalidated, and this method raises DuckDB::Error.
59
+ #
60
+ # require 'duckdb'
61
+ # db = DuckDB::Database.open
62
+ # con = db.connect
63
+ # con.query('CREATE TABLE users (id INTEGER, name VARCHAR)')
64
+ # appender = con.appender('users')
65
+ # appender
66
+ # .append_int32(1)
67
+ # .append_varchar('Alice')
68
+ # .end_row
69
+ # .flush
70
+ def flush
71
+ return self if _flush
72
+
73
+ raise_appender_error('failed to flush')
74
+ end
75
+
76
+ # :call-seq:
77
+ # appender.close -> self
78
+ #
79
+ # Closes the appender by flushing all intermediate states and closing it for further appends.
80
+ # If flushing the data triggers a constraint violation or any other error, then all data is
81
+ # invalidated, and this method raises DuckDB::Error.
82
+ #
83
+ # require 'duckdb'
84
+ # db = DuckDB::Database.open
85
+ # con = db.connect
86
+ # con.query('CREATE TABLE users (id INTEGER, name VARCHAR)')
87
+ # appender = con.appender('users')
88
+ # appender
89
+ # .append_int32(1)
90
+ # .append_varchar('Alice')
91
+ # .end_row
92
+ # .close
93
+ def close
94
+ return self if _close
95
+
96
+ raise_appender_error('failed to close')
97
+ end
98
+
99
+ # call-seq:
100
+ # appender.append_bool(val) -> self
101
+ #
102
+ # Appends a boolean value to the current row in the appender.
103
+ #
104
+ # require 'duckdb'
105
+ # db = DuckDB::Database.open
106
+ # con = db.connect
107
+ # con.query('CREATE TABLE users (id INTEGER, active BOOLEAN)')
108
+ # appender = con.appender('users')
109
+ # appender
110
+ # .append_int32(1)
111
+ # .append_bool(true)
112
+ # .end_row
113
+ # .flush
114
+ def append_bool(value)
115
+ return self if _append_bool(value)
116
+
117
+ raise_appender_error('failed to append_bool')
118
+ end
119
+
120
+ # call-seq:
121
+ # appender.append_int8(val) -> self
122
+ #
123
+ # Appends an int8(TINYINT) value to the current row in the appender.
124
+ #
125
+ # require 'duckdb'
126
+ # db = DuckDB::Database.open
127
+ # con = db.connect
128
+ # con.query('CREATE TABLE users (id INTEGER, age TINYINT)')
129
+ # appender = con.appender('users')
130
+ # appender
131
+ # .append_int32(1)
132
+ # .append_int8(20)
133
+ # .end_row
134
+ # .flush
135
+ #
136
+ def append_int8(value)
137
+ return self if _append_int8(value)
138
+
139
+ raise_appender_error('failed to append_int8')
140
+ end
23
141
 
142
+ # call-seq:
143
+ # appender.append_int16(val) -> self
24
144
  #
145
+ # Appends an int16(SMALLINT) value to the current row in the appender.
146
+ #
147
+ # require 'duckdb'
148
+ # db = DuckDB::Database.open
149
+ # con = db.connect
150
+ # con.query('CREATE TABLE users (id INTEGER, age SMALLINT)')
151
+ # appender = con.appender('users')
152
+ # appender
153
+ # .append_int32(1)
154
+ # .append_int16(20)
155
+ # .end_row
156
+ # .flush
157
+ def append_int16(value)
158
+ return self if _append_int16(value)
159
+
160
+ raise_appender_error('failed to append_int16')
161
+ end
162
+
163
+ # call-seq:
164
+ # appender.append_int32(val) -> self
165
+ #
166
+ # Appends an int32(INTEGER) value to the current row in the appender.
167
+ #
168
+ # require 'duckdb'
169
+ # db = DuckDB::Database.open
170
+ # con = db.connect
171
+ # con.query('CREATE TABLE users (id INTEGER, age INTEGER)')
172
+ # appender = con.appender('users')
173
+ # appender
174
+ # .append_int32(1)
175
+ # .append_int32(20)
176
+ # .end_row
177
+ # .flush
178
+ def append_int32(value)
179
+ return self if _append_int32(value)
180
+
181
+ raise_appender_error('failed to append_int32')
182
+ end
183
+
184
+ # call-seq:
185
+ # appender.append_int64(val) -> self
186
+ #
187
+ # Appends an int64(BIGINT) value to the current row in the appender.
188
+ #
189
+ # require 'duckdb'
190
+ # db = DuckDB::Database.open
191
+ # con = db.connect
192
+ # con.query('CREATE TABLE users (id INTEGER, age BIGINT)')
193
+ # appender = con.appender('users')
194
+ # appender
195
+ # .append_int32(1)
196
+ # .append_int64(20)
197
+ # .end_row
198
+ # .flush
199
+ def append_int64(value)
200
+ return self if _append_int64(value)
201
+
202
+ raise_appender_error('failed to append_int64')
203
+ end
204
+
205
+ # call-seq:
206
+ # appender.append_uint8(val) -> self
207
+ #
208
+ # Appends an uint8 value to the current row in the appender.
209
+ #
210
+ # require 'duckdb'
211
+ # db = DuckDB::Database.open
212
+ # con = db.connect
213
+ # con.query('CREATE TABLE users (id INTEGER, age UTINYINT)')
214
+ # appender = con.appender('users')
215
+ # appender
216
+ # .append_int32(1)
217
+ # .append_uint8(20)
218
+ # .end_row
219
+ # .flush
220
+ def append_uint8(value)
221
+ return self if _append_uint8(value)
222
+
223
+ raise_appender_error('failed to append_uint8')
224
+ end
225
+
25
226
  # appends huge int value.
26
227
  #
27
228
  # require 'duckdb'
@@ -30,16 +231,13 @@ module DuckDB
30
231
  # con.query('CREATE TABLE numbers (num HUGEINT)')
31
232
  # appender = con.appender('numbers')
32
233
  # appender
33
- # .begin_row
34
234
  # .append_hugeint(-170_141_183_460_469_231_731_687_303_715_884_105_727)
35
235
  # .end_row
36
- #
37
236
  def append_hugeint(value)
38
237
  lower, upper = integer_to_hugeint(value)
39
238
  _append_hugeint(lower, upper)
40
239
  end
41
240
 
42
- #
43
241
  # appends unsigned huge int value.
44
242
  #
45
243
  # require 'duckdb'
@@ -48,16 +246,13 @@ module DuckDB
48
246
  # con.query('CREATE TABLE numbers (num UHUGEINT)')
49
247
  # appender = con.appender('numbers')
50
248
  # appender
51
- # .begin_row
52
249
  # .append_hugeint(340_282_366_920_938_463_463_374_607_431_768_211_455)
53
250
  # .end_row
54
- #
55
251
  def append_uhugeint(value)
56
252
  lower, upper = integer_to_hugeint(value)
57
253
  _append_uhugeint(lower, upper)
58
254
  end
59
255
 
60
- #
61
256
  # appends date value.
62
257
  #
63
258
  # require 'duckdb'
@@ -65,21 +260,18 @@ module DuckDB
65
260
  # con = db.connect
66
261
  # con.query('CREATE TABLE dates (date_value DATE)')
67
262
  # appender = con.appender('dates')
68
- # appender.begin_row
69
263
  # appender.append_date(Date.today)
70
264
  # # or
71
265
  # # appender.append_date(Time.now)
72
266
  # # appender.append_date('2021-10-10')
73
267
  # appender.end_row
74
268
  # appender.flush
75
- #
76
269
  def append_date(value)
77
- date = to_date(value)
270
+ date = _parse_date(value)
78
271
 
79
272
  _append_date(date.year, date.month, date.day)
80
273
  end
81
274
 
82
- #
83
275
  # appends time value.
84
276
  #
85
277
  # require 'duckdb'
@@ -87,20 +279,17 @@ module DuckDB
87
279
  # con = db.connect
88
280
  # con.query('CREATE TABLE times (time_value TIME)')
89
281
  # appender = con.appender('times')
90
- # appender.begin_row
91
282
  # appender.append_time(Time.now)
92
283
  # # or
93
284
  # # appender.append_time('01:01:01')
94
285
  # appender.end_row
95
286
  # appender.flush
96
- #
97
287
  def append_time(value)
98
288
  time = _parse_time(value)
99
289
 
100
290
  _append_time(time.hour, time.min, time.sec, time.usec)
101
291
  end
102
292
 
103
- #
104
293
  # appends timestamp value.
105
294
  #
106
295
  # require 'duckdb'
@@ -108,21 +297,18 @@ module DuckDB
108
297
  # con = db.connect
109
298
  # con.query('CREATE TABLE timestamps (timestamp_value TIMESTAMP)')
110
299
  # appender = con.appender('timestamps')
111
- # appender.begin_row
112
300
  # appender.append_time(Time.now)
113
301
  # # or
114
302
  # # appender.append_time(Date.today)
115
303
  # # appender.append_time('2021-08-01 01:01:01')
116
304
  # appender.end_row
117
305
  # appender.flush
118
- #
119
306
  def append_timestamp(value)
120
307
  time = to_time(value)
121
308
 
122
309
  _append_timestamp(time.year, time.month, time.day, time.hour, time.min, time.sec, time.nsec / 1000)
123
310
  end
124
311
 
125
- #
126
312
  # appends interval.
127
313
  # The argument must be ISO8601 duration format.
128
314
  # WARNING: This method is expremental.
@@ -133,17 +319,14 @@ module DuckDB
133
319
  # con.query('CREATE TABLE intervals (interval_value INTERVAL)')
134
320
  # appender = con.appender('intervals')
135
321
  # appender
136
- # .begin_row
137
322
  # .append_interval('P1Y2D') # => append 1 year 2 days interval.
138
323
  # .end_row
139
324
  # .flush
140
- #
141
325
  def append_interval(value)
142
326
  value = Interval.to_interval(value)
143
327
  _append_interval(value.interval_months, value.interval_days, value.interval_micros)
144
328
  end
145
329
 
146
- #
147
330
  # appends value.
148
331
  #
149
332
  # require 'duckdb'
@@ -151,11 +334,9 @@ module DuckDB
151
334
  # con = db.connect
152
335
  # con.query('CREATE TABLE users (id INTEGER, name VARCHAR)')
153
336
  # appender = con.appender('users')
154
- # appender.begin_row
155
337
  # appender.append(1)
156
338
  # appender.append('Alice')
157
339
  # appender.end_row
158
- #
159
340
  def append(value)
160
341
  case value
161
342
  when NilClass
@@ -188,20 +369,16 @@ module DuckDB
188
369
  end
189
370
  end
190
371
 
191
- #
192
372
  # append a row.
193
373
  #
194
374
  # appender.append_row(1, 'Alice')
195
375
  #
196
376
  # is same as:
197
377
  #
198
- # appender.begin_row
199
- # appender.append(1)
378
+ # appender.append(2)
200
379
  # appender.append('Alice')
201
380
  # appender.end_row
202
- #
203
381
  def append_row(*args)
204
- begin_row
205
382
  args.each do |arg|
206
383
  append(arg)
207
384
  end
@@ -210,35 +387,21 @@ module DuckDB
210
387
 
211
388
  private
212
389
 
213
- def blob?(value)
214
- value.instance_of?(DuckDB::Blob) || value.encoding == Encoding::BINARY
390
+ def raise_appender_error(default_message) # :nodoc:
391
+ message = error_message
392
+ raise DuckDB::Error, message || default_message
215
393
  end
216
394
 
217
- def to_date(value)
218
- case value
219
- when Date, Time
220
- value
221
- else
222
- begin
223
- Date.parse(value)
224
- rescue StandardError
225
- raise(ArgumentError, "Cannot parse argument `#{value}` to Date.")
226
- end
227
- end
395
+ def blob?(value) # :nodoc:
396
+ value.instance_of?(DuckDB::Blob) || value.encoding == Encoding::BINARY
228
397
  end
229
398
 
230
- def to_time(value)
399
+ def to_time(value) # :nodoc:
231
400
  case value
232
- when Time
233
- value
234
401
  when Date
235
402
  value.to_time
236
403
  else
237
- begin
238
- Time.parse(value)
239
- rescue StandardError
240
- raise(ArgumentError, "Cannot parse argument `#{value}` to Time or Date.")
241
- end
404
+ _parse_time(value)
242
405
  end
243
406
  end
244
407
  end
data/lib/duckdb/column.rb CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  module DuckDB
4
4
  class Column
5
- #
6
5
  # returns column type symbol
7
6
  # `:unknown` means that the column type is unknown/unsupported by ruby-duckdb.
8
7
  # `:invalid` means that the column type is invalid in duckdb.
@@ -15,7 +14,6 @@ module DuckDB
15
14
  # users = con.query('SELECT * FROM users')
16
15
  # columns = users.columns
17
16
  # columns.first.type #=> :integer
18
- #
19
17
  def type
20
18
  type_id = _type
21
19
  DuckDB::Converter::IntToSym.type_to_sym(type_id)
@@ -8,7 +8,6 @@ module DuckDB
8
8
  # con = db.connect
9
9
  # con.query(sql)
10
10
  class Connection
11
- #
12
11
  # executes sql with args.
13
12
  # The first argument sql must be SQL string.
14
13
  # The rest arguments are parameters of SQL string.
@@ -24,7 +23,6 @@ module DuckDB
24
23
  #
25
24
  # sql = 'SELECT * FROM users WHERE name = $name AND email = $email'
26
25
  # dave = con.query(sql, name: 'Dave', email: 'dave@example.com')
27
- #
28
26
  def query(sql, *args, **kwargs)
29
27
  return query_multi_sql(sql) if args.empty? && kwargs.empty?
30
28
 
@@ -39,13 +37,13 @@ module DuckDB
39
37
  result = nil
40
38
  stmts.each do |stmt|
41
39
  result = stmt.execute
40
+ stmt.destroy
42
41
  end
43
42
  result
44
43
  ensure
45
44
  stmts&.destroy
46
45
  end
47
46
 
48
- #
49
47
  # executes sql with args asynchronously.
50
48
  # The first argument sql must be SQL string.
51
49
  # The rest arguments are parameters of SQL string.
@@ -60,7 +58,6 @@ module DuckDB
60
58
  # pending_result.execute_task while pending_result.state == :not_ready
61
59
  # result = pending_result.execute_pending
62
60
  # result.each.first
63
- #
64
61
  def async_query(sql, *args, **kwargs)
65
62
  prepare(sql) do |stmt|
66
63
  stmt.bind_args(*args, **kwargs)
@@ -68,7 +65,6 @@ module DuckDB
68
65
  end
69
66
  end
70
67
 
71
- #
72
68
  # executes sql with args asynchronously and provides streaming result.
73
69
  # The first argument sql must be SQL string.
74
70
  # The rest arguments are parameters of SQL string.
@@ -84,7 +80,6 @@ module DuckDB
84
80
  # pending_result.execute_task while pending_result.state == :not_ready
85
81
  # result = pending_result.execute_pending
86
82
  # result.each.first
87
- #
88
83
  def async_query_stream(sql, *args, **kwargs)
89
84
  prepare(sql) do |stmt|
90
85
  stmt.bind_args(*args, **kwargs)
@@ -92,10 +87,8 @@ module DuckDB
92
87
  end
93
88
  end
94
89
 
95
- #
96
90
  # connects DuckDB database
97
91
  # The first argument is DuckDB::Database object
98
- #
99
92
  def connect(db)
100
93
  conn = _connect(db)
101
94
  return conn unless block_given?
@@ -107,7 +100,6 @@ module DuckDB
107
100
  end
108
101
  end
109
102
 
110
- #
111
103
  # returns PreparedStatement object.
112
104
  # The first argument is SQL string.
113
105
  # If block is given, the block is executed with PreparedStatement object
@@ -127,17 +119,14 @@ module DuckDB
127
119
  # stmt.bind_args(name: 'Dave', email: 'dave@example.com')
128
120
  # stmt.execute
129
121
  # end
130
- #
131
122
  def prepared_statement(str, &)
132
123
  return PreparedStatement.new(self, str) unless block_given?
133
124
 
134
125
  PreparedStatement.prepare(self, str, &)
135
126
  end
136
127
 
137
- #
138
128
  # returns Appender object.
139
129
  # The first argument is table name
140
- #
141
130
  def appender(table)
142
131
  appender = create_appender(table)
143
132
 
@@ -20,13 +20,11 @@ module DuckDB
20
20
  # result.each do |row|
21
21
  # p row
22
22
  # end
23
- #
24
23
  class Database
25
24
  private_class_method :_open
26
25
  private_class_method :_open_ext
27
26
 
28
27
  class << self
29
- ##
30
28
  # Opens database.
31
29
  # The first argument is DuckDB database file path to open.
32
30
  # If there is no argument, the method opens DuckDB database in memory.
@@ -40,7 +38,6 @@ module DuckDB
40
38
  # con = db.connect
41
39
  # con.query('CREATE TABLE users (id INTEGER, name VARCHAR(30))')
42
40
  # end
43
- #
44
41
  def open(dbpath = nil, config = nil)
45
42
  db = _db_open(dbpath, config)
46
43
  return db unless block_given?
@@ -54,7 +51,7 @@ module DuckDB
54
51
 
55
52
  private
56
53
 
57
- def _db_open(dbpath, config)
54
+ def _db_open(dbpath, config) # :nodoc:
58
55
  if config
59
56
  _open_ext(dbpath, config)
60
57
  else
@@ -63,7 +60,6 @@ module DuckDB
63
60
  end
64
61
  end
65
62
 
66
- ##
67
63
  # connects database.
68
64
  #
69
65
  # The method yields block and disconnects the database if block given
@@ -75,7 +71,6 @@ module DuckDB
75
71
  # db.connect do |con|
76
72
  # con.query('CREATE TABLE users (id INTEGER, name VARCHAR(30))')
77
73
  # end
78
- #
79
74
  def connect
80
75
  conn = _connect
81
76
  return conn unless block_given?
@@ -24,11 +24,11 @@ module DuckDB
24
24
  # con.query('CREATE TABLE intervals (interval_value INTERVAL)')
25
25
  # appender = con.appender('intervals')
26
26
  # appender
27
- # .begin_row
28
27
  # .append_interval(interval)
29
28
  # .end_row
30
29
  # .flush
31
30
  class Interval
31
+ # :stopdoc:
32
32
  ISO8601_REGEXP = Regexp.compile(
33
33
  '(?<negativ>-{0,1})P
34
34
  (?<year>-{0,1}\d+Y){0,1}
@@ -40,6 +40,8 @@ module DuckDB
40
40
  ((?<sec>-{0,1}\d+)\.{0,1}(?<usec>\d*)S){0,1}',
41
41
  Regexp::EXTENDED
42
42
  )
43
+ private_constant :ISO8601_REGEXP
44
+ # :startdoc:
43
45
 
44
46
  class << self
45
47
  # parses the ISO8601 format string and return the Interval object.
@@ -93,7 +95,7 @@ module DuckDB
93
95
 
94
96
  private
95
97
 
96
- def matched_to_i(matched)
98
+ def matched_to_i(matched) # :nodoc:
97
99
  sign = to_sign(matched)
98
100
  sec = to_sec(matched)
99
101
  usec = to_usec(matched)
@@ -104,35 +106,35 @@ module DuckDB
104
106
  sign.positive? ? value : value.map { |v| v * sign }
105
107
  end
106
108
 
107
- def to_sign(matched)
109
+ def to_sign(matched) # :nodoc:
108
110
  matched[:negativ] == '-' ? -1 : 1
109
111
  end
110
112
 
111
- def to_year(matched)
113
+ def to_year(matched) # :nodoc:
112
114
  matched[:year].to_i
113
115
  end
114
116
 
115
- def to_month(matched)
117
+ def to_month(matched) # :nodoc:
116
118
  matched[:month].to_i
117
119
  end
118
120
 
119
- def to_day(matched)
121
+ def to_day(matched) # :nodoc:
120
122
  matched[:day].to_i
121
123
  end
122
124
 
123
- def to_hour(matched)
125
+ def to_hour(matched) # :nodoc:
124
126
  matched[:hour].to_i
125
127
  end
126
128
 
127
- def to_min(matched)
129
+ def to_min(matched) # :nodoc:
128
130
  matched[:min].to_i
129
131
  end
130
132
 
131
- def to_sec(matched)
133
+ def to_sec(matched) # :nodoc:
132
134
  matched[:sec].to_i
133
135
  end
134
136
 
135
- def to_usec(matched)
137
+ def to_usec(matched) # :nodoc:
136
138
  matched[:usec].to_s.ljust(6, '0')[0, 6].to_i
137
139
  end
138
140
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DuckDB
4
+ # represents the version of the DuckDB library.
5
+ # If DuckDB.library_version is v0.2.0, then DuckDB::LIBRARY_VERSION is 0.2.0.
4
6
  LIBRARY_VERSION = library_version[1..]
5
7
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DuckDB
4
+ class LogicalType
5
+ # returns logical type's type symbol
6
+ # `:unknown` means that the logical type's type is unknown/unsupported by ruby-duckdb.
7
+ # `:invalid` means that the logical type's type is invalid in duckdb.
8
+ #
9
+ # require 'duckdb'
10
+ # db = DuckDB::Database.open
11
+ # con = db.connect
12
+ # con.query('CREATE TABLE climates (id INTEGER, temperature DECIMAIL)')
13
+ #
14
+ # users = con.query('SELECT * FROM climates')
15
+ # columns = users.columns
16
+ # columns.second.logical_type.type #=> :decimal
17
+ def type
18
+ type_id = _type
19
+ DuckDB::Converter::IntToSym.type_to_sym(type_id)
20
+ end
21
+ end
22
+ end
@@ -19,7 +19,7 @@ module DuckDB
19
19
  # end
20
20
  # result = pending_result.execute_pending
21
21
  class PendingResult
22
- STATES = %i[ready not_ready error no_tasks].freeze
22
+ STATES = %i[ready not_ready error no_tasks].freeze # :nodoc:
23
23
 
24
24
  # returns the state of the pending result.
25
25
  # the result can be :ready, :not_ready, :error, :no_tasks.