duckdb 0.3.1.0 → 0.3.4.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.
@@ -1,298 +1,244 @@
1
1
  require 'date'
2
2
  require 'time'
3
+ require_relative './converter'
3
4
 
4
5
  module DuckDB
5
- if defined?(DuckDB::Appender)
6
- # The DuckDB::Appender encapsulates DuckDB Appender.
6
+ # The DuckDB::Appender encapsulates DuckDB Appender.
7
+ #
8
+ # require 'duckdb'
9
+ # db = DuckDB::Database.open
10
+ # con = db.connect
11
+ # con.query('CREATE TABLE users (id INTEGER, name VARCHAR)')
12
+ # appender = con.appender('users')
13
+ # appender.append_row(1, 'Alice')
14
+ #
15
+ class Appender
16
+ include DuckDB::Converter
17
+
18
+ RANGE_INT16 = -32_768..32_767
19
+ RANGE_INT32 = -2_147_483_648..2_147_483_647
20
+ RANGE_INT64 = -9_223_372_036_854_775_808..9_223_372_036_854_775_807
21
+
22
+ #
23
+ # appends huge int value.
7
24
  #
8
25
  # require 'duckdb'
9
26
  # db = DuckDB::Database.open
10
27
  # con = db.connect
11
- # con.query('CREATE TABLE users (id INTEGER, name VARCHAR)')
12
- # appender = con.appender('users')
13
- # appender.append_row(1, 'Alice')
28
+ # con.query('CREATE TABLE numbers (num HUGEINT)')
29
+ # appender = con.appender('numbers')
30
+ # appender
31
+ # .begin_row
32
+ # .append_hugeint(-170_141_183_460_469_231_731_687_303_715_884_105_727)
33
+ # .end_row
14
34
  #
15
- class Appender
16
- RANGE_INT16 = -32_768..32_767
17
- RANGE_INT32 = -2_147_483_648..2_147_483_647
18
- RANGE_INT64 = -9_223_372_036_854_775_808..9_223_372_036_854_775_807
19
-
20
- #
21
- # appends huge int value.
22
- #
23
- # require 'duckdb'
24
- # db = DuckDB::Database.open
25
- # con = db.connect
26
- # con.query('CREATE TABLE numbers (num HUGEINT)')
27
- # appender = con.appender('numbers')
28
- # appender
29
- # .begin_row
30
- # .append_hugeint(-170_141_183_460_469_231_731_687_303_715_884_105_727)
31
- # .end_row
32
- #
33
- def append_hugeint(value)
34
- case value
35
- when Integer
36
- if respond_to?(:_append_hugeint, true)
37
- half = 1 << 64
38
- upper = value / half
39
- lower = value - upper * half
40
- _append_hugeint(lower, upper)
41
- else
42
- append_varchar(value.to_s)
43
- end
44
- else
45
- raise(ArgumentError, "2nd argument `#{value}` must be Integer.")
46
- end
35
+ def append_hugeint(value)
36
+ case value
37
+ when Integer
38
+ half = 1 << 64
39
+ upper = value / half
40
+ lower = value - upper * half
41
+ _append_hugeint(lower, upper)
42
+ else
43
+ raise(ArgumentError, "2nd argument `#{value}` must be Integer.")
47
44
  end
45
+ end
48
46
 
49
- #
50
- # appends date value.
51
- #
52
- # require 'duckdb'
53
- # db = DuckDB::Database.open
54
- # con = db.connect
55
- # con.query('CREATE TABLE dates (date_value DATE)')
56
- # appender = con.appender('dates')
57
- # appender.begin_row
58
- # appender.append_date(Date.today)
59
- # # or
60
- # # appender.append_date(Time.now)
61
- # # appender.append_date('2021-10-10')
62
- # appender.end_row
63
- # appender.flush
64
- #
65
- def append_date(value)
66
- date = case value
67
- when Date, Time
68
- value
69
- else
70
- begin
71
- Date.parse(value)
72
- rescue
73
- raise(ArgumentError, "Cannot parse argument `#{value}` to Date.")
74
- end
47
+ #
48
+ # appends date value.
49
+ #
50
+ # require 'duckdb'
51
+ # db = DuckDB::Database.open
52
+ # con = db.connect
53
+ # con.query('CREATE TABLE dates (date_value DATE)')
54
+ # appender = con.appender('dates')
55
+ # appender.begin_row
56
+ # appender.append_date(Date.today)
57
+ # # or
58
+ # # appender.append_date(Time.now)
59
+ # # appender.append_date('2021-10-10')
60
+ # appender.end_row
61
+ # appender.flush
62
+ #
63
+ def append_date(value)
64
+ date = case value
65
+ when Date, Time
66
+ value
67
+ else
68
+ begin
69
+ Date.parse(value)
70
+ rescue
71
+ raise(ArgumentError, "Cannot parse argument `#{value}` to Date.")
75
72
  end
73
+ end
76
74
 
77
- _append_date(date.year, date.month, date.day)
78
- end
75
+ _append_date(date.year, date.month, date.day)
76
+ end
79
77
 
80
- #
81
- # appends time value.
82
- #
83
- # require 'duckdb'
84
- # db = DuckDB::Database.open
85
- # con = db.connect
86
- # con.query('CREATE TABLE times (time_value TIME)')
87
- # appender = con.appender('times')
88
- # appender.begin_row
89
- # appender.append_time(Time.now)
90
- # # or
91
- # # appender.append_time('01:01:01')
92
- # appender.end_row
93
- # appender.flush
94
- #
95
- def append_time(value)
96
- time = case value
97
- when Time
98
- value
99
- else
100
- begin
101
- Time.parse(value)
102
- rescue
103
- raise(ArgumentError, "Cannot parse argument `#{value}` to Time.")
104
- end
78
+ #
79
+ # appends time value.
80
+ #
81
+ # require 'duckdb'
82
+ # db = DuckDB::Database.open
83
+ # con = db.connect
84
+ # con.query('CREATE TABLE times (time_value TIME)')
85
+ # appender = con.appender('times')
86
+ # appender.begin_row
87
+ # appender.append_time(Time.now)
88
+ # # or
89
+ # # appender.append_time('01:01:01')
90
+ # appender.end_row
91
+ # appender.flush
92
+ #
93
+ def append_time(value)
94
+ time = case value
95
+ when Time
96
+ value
97
+ else
98
+ begin
99
+ Time.parse(value)
100
+ rescue
101
+ raise(ArgumentError, "Cannot parse argument `#{value}` to Time.")
105
102
  end
103
+ end
106
104
 
107
- _append_time(time.hour, time.min, time.sec, time.usec)
108
- end
105
+ _append_time(time.hour, time.min, time.sec, time.usec)
106
+ end
109
107
 
110
- #
111
- # appends timestamp value.
112
- #
113
- # require 'duckdb'
114
- # db = DuckDB::Database.open
115
- # con = db.connect
116
- # con.query('CREATE TABLE timestamps (timestamp_value TIMESTAMP)')
117
- # appender = con.appender('timestamps')
118
- # appender.begin_row
119
- # appender.append_time(Time.now)
120
- # # or
121
- # # appender.append_time(Date.today)
122
- # # appender.append_time('2021-08-01 01:01:01')
123
- # appender.end_row
124
- # appender.flush
125
- #
126
- def append_timestamp(value)
127
- time = case value
128
- when Time
129
- value
130
- when Date
131
- value.to_time
132
- else
133
- begin
134
- Time.parse(value)
135
- rescue
136
- raise(ArgumentError, "Cannot parse argument `#{value}` to Time or Date.")
137
- end
108
+ #
109
+ # appends timestamp value.
110
+ #
111
+ # require 'duckdb'
112
+ # db = DuckDB::Database.open
113
+ # con = db.connect
114
+ # con.query('CREATE TABLE timestamps (timestamp_value TIMESTAMP)')
115
+ # appender = con.appender('timestamps')
116
+ # appender.begin_row
117
+ # appender.append_time(Time.now)
118
+ # # or
119
+ # # appender.append_time(Date.today)
120
+ # # appender.append_time('2021-08-01 01:01:01')
121
+ # appender.end_row
122
+ # appender.flush
123
+ #
124
+ def append_timestamp(value)
125
+ time = case value
126
+ when Time
127
+ value
128
+ when Date
129
+ value.to_time
130
+ else
131
+ begin
132
+ Time.parse(value)
133
+ rescue
134
+ raise(ArgumentError, "Cannot parse argument `#{value}` to Time or Date.")
138
135
  end
136
+ end
139
137
 
140
- _append_timestamp(time.year, time.month, time.day, time.hour, time.min, time.sec, time.nsec / 1000)
141
- end
138
+ _append_timestamp(time.year, time.month, time.day, time.hour, time.min, time.sec, time.nsec / 1000)
139
+ end
142
140
 
143
- #
144
- # appends interval.
145
- # The argument must be ISO8601 duration format.
146
- # WARNING: This method is expremental.
147
- #
148
- # require 'duckdb'
149
- # db = DuckDB::Database.open
150
- # con = db.connect
151
- # con.query('CREATE TABLE intervals (interval_value INTERVAL)')
152
- # appender = con.appender('intervals')
153
- # appender
154
- # .begin_row
155
- # .append_interval('P1Y2D') # => append 1 year 2 days interval.
156
- # .end_row
157
- # .flush
158
- #
159
- def append_interval(value)
160
- raise ArgumentError, "Argument `#{value}` must be a string." unless value.is_a?(String)
141
+ #
142
+ # appends interval.
143
+ # The argument must be ISO8601 duration format.
144
+ # WARNING: This method is expremental.
145
+ #
146
+ # require 'duckdb'
147
+ # db = DuckDB::Database.open
148
+ # con = db.connect
149
+ # con.query('CREATE TABLE intervals (interval_value INTERVAL)')
150
+ # appender = con.appender('intervals')
151
+ # appender
152
+ # .begin_row
153
+ # .append_interval('P1Y2D') # => append 1 year 2 days interval.
154
+ # .end_row
155
+ # .flush
156
+ #
157
+ def append_interval(value)
158
+ raise ArgumentError, "Argument `#{value}` must be a string." unless value.is_a?(String)
161
159
 
162
- hash = iso8601_interval_to_hash(value)
160
+ hash = iso8601_interval_to_hash(value)
163
161
 
164
- months, days, micros = hash_to__append_interval_args(hash)
162
+ months, days, micros = hash_to__append_interval_args(hash)
165
163
 
166
- _append_interval(months, days, micros)
167
- end
164
+ _append_interval(months, days, micros)
165
+ end
168
166
 
169
- #
170
- # appends value.
171
- #
172
- # require 'duckdb'
173
- # db = DuckDB::Database.open
174
- # con = db.connect
175
- # con.query('CREATE TABLE users (id INTEGER, name VARCHAR)')
176
- # appender = con.appender('users')
177
- # appender.begin_row
178
- # appender.append(1)
179
- # appender.append('Alice')
180
- # appender.end_row
181
- #
182
- def append(value)
167
+ #
168
+ # appends value.
169
+ #
170
+ # require 'duckdb'
171
+ # db = DuckDB::Database.open
172
+ # con = db.connect
173
+ # con.query('CREATE TABLE users (id INTEGER, name VARCHAR)')
174
+ # appender = con.appender('users')
175
+ # appender.begin_row
176
+ # appender.append(1)
177
+ # appender.append('Alice')
178
+ # appender.end_row
179
+ #
180
+ def append(value)
181
+ case value
182
+ when NilClass
183
+ append_null
184
+ when Float
185
+ append_double(value)
186
+ when Integer
183
187
  case value
184
- when NilClass
185
- append_null
186
- when Float
187
- append_double(value)
188
- when Integer
189
- case value
190
- when RANGE_INT16
191
- append_int16(value)
192
- when RANGE_INT32
193
- append_int32(value)
194
- when RANGE_INT64
195
- append_int64(value)
196
- else
197
- append_hugeint(value)
198
- end
199
- when String
200
- if defined?(DuckDB::Blob)
201
- blob?(value) ? append_blob(value) : append_varchar(value)
202
- else
203
- append_varchar(value)
204
- end
205
- when TrueClass, FalseClass
206
- append_bool(value)
207
- when Time
208
- if respond_to?(:append_timestamp)
209
- append_timestamp(value)
210
- else
211
- append_varchar(value.strftime('%Y-%m-%d %H:%M:%S.%N'))
212
- end
213
- when Date
214
- if respond_to?(:append_date)
215
- append_date(value)
216
- else
217
- append_varchar(value.strftime('%Y-%m-%d'))
218
- end
188
+ when RANGE_INT16
189
+ append_int16(value)
190
+ when RANGE_INT32
191
+ append_int32(value)
192
+ when RANGE_INT64
193
+ append_int64(value)
219
194
  else
220
- raise(DuckDB::Error, "not supported type #{value} (#{value.class})")
195
+ append_hugeint(value)
221
196
  end
222
- end
223
-
224
- #
225
- # append a row.
226
- #
227
- # appender.append_row(1, 'Alice')
228
- #
229
- # is same as:
230
- #
231
- # appender.begin_row
232
- # appender.append(1)
233
- # appender.append('Alice')
234
- # appender.end_row
235
- #
236
- def append_row(*args)
237
- begin_row
238
- args.each do |arg|
239
- append(arg)
197
+ when String
198
+ blob?(value) ? append_blob(value) : append_varchar(value)
199
+ when TrueClass, FalseClass
200
+ append_bool(value)
201
+ when Time
202
+ if respond_to?(:append_timestamp)
203
+ append_timestamp(value)
204
+ else
205
+ append_varchar(value.strftime('%Y-%m-%d %H:%M:%S.%N'))
240
206
  end
241
- end_row
242
- end
243
-
244
- private
245
-
246
- def blob?(value)
247
- value.instance_of?(DuckDB::Blob) || value.encoding == Encoding::BINARY
248
- end
249
-
250
- def iso8601_interval_to_hash(value)
251
- digit = ''
252
- time = false
253
- hash = {}
254
- hash.default = 0
255
-
256
- value.each_char do |c|
257
- if '-0123456789.'.include?(c)
258
- digit += c
259
- elsif c == 'T'
260
- time = true
261
- digit = ''
262
- elsif c == 'M'
263
- m_interval_to_hash(hash, digit, time)
264
- digit = ''
265
- elsif c == 'S'
266
- s_interval_to_hash(hash, digit)
267
- digit = ''
268
- elsif 'YDH'.include?(c)
269
- hash[c] = digit.to_i
270
- digit = ''
271
- elsif c != 'P'
272
- raise ArgumentError, "The argument `#{value}` can't be parse."
273
- end
207
+ when Date
208
+ if respond_to?(:append_date)
209
+ append_date(value)
210
+ else
211
+ append_varchar(value.strftime('%Y-%m-%d'))
274
212
  end
275
- hash
213
+ else
214
+ raise(DuckDB::Error, "not supported type #{value} (#{value.class})")
276
215
  end
216
+ end
277
217
 
278
- def m_interval_to_hash(hash, digit, time)
279
- key = time ? 'TM' : 'M'
280
- hash[key] = digit.to_i
218
+ #
219
+ # append a row.
220
+ #
221
+ # appender.append_row(1, 'Alice')
222
+ #
223
+ # is same as:
224
+ #
225
+ # appender.begin_row
226
+ # appender.append(1)
227
+ # appender.append('Alice')
228
+ # appender.end_row
229
+ #
230
+ def append_row(*args)
231
+ begin_row
232
+ args.each do |arg|
233
+ append(arg)
281
234
  end
235
+ end_row
236
+ end
282
237
 
283
- def s_interval_to_hash(hash, digit)
284
- sec, msec = digit.split('.')
285
- hash['S'] = sec.to_i
286
- hash['MS'] = "#{msec}000000"[0, 6].to_i
287
- hash['MS'] *= -1 if hash['S'].negative?
288
- end
238
+ private
289
239
 
290
- def hash_to__append_interval_args(hash)
291
- months = hash['Y'] * 12 + hash['M']
292
- days = hash['D']
293
- micros = (hash['H'] * 3600 + hash['TM'] * 60 + hash['S']) * 1_000_000 + hash['MS']
294
- [months, days, micros]
295
- end
240
+ def blob?(value)
241
+ value.instance_of?(DuckDB::Blob) || value.encoding == Encoding::BINARY
296
242
  end
297
243
  end
298
244
  end
@@ -0,0 +1,55 @@
1
+ module DuckDB
2
+ class Column
3
+ #
4
+ # returns column type symbol
5
+ # `:unknown` means that the column type is unknown/unsupported by ruby-duckdb.
6
+ # `:invalid` means that the column type is invalid in duckdb.
7
+ #
8
+ # require 'duckdb'
9
+ # db = DuckDB::Database.open
10
+ # con = db.connect
11
+ # con.query('CREATE TABLE users (id INTEGER, name VARCHAR(30))')
12
+ #
13
+ # users = con.query('SELECT * FROM users')
14
+ # columns = users.columns
15
+ # columns.first.type #=> :integer
16
+ #
17
+ def type
18
+ types = %i[
19
+ invalid
20
+ boolean
21
+ tinyint
22
+ smallint
23
+ integer
24
+ bigint
25
+ utinyint
26
+ usmallint
27
+ uinteger
28
+ ubigint
29
+ float
30
+ double
31
+ timestamp
32
+ date
33
+ time
34
+ interval
35
+ hugeint
36
+ varchar
37
+ blob
38
+ decimal
39
+ timestamp_s
40
+ timestamp_ms
41
+ timestamp_ns
42
+ enum
43
+ list
44
+ struct
45
+ map
46
+ uuid
47
+ json
48
+ ]
49
+ index = _type
50
+ return :unknown if index >= types.size
51
+
52
+ types[index]
53
+ end
54
+ end
55
+ end
data/lib/duckdb/config.rb CHANGED
@@ -34,10 +34,7 @@ module DuckDB
34
34
  # configs['default_order'] # => "The order type used when none is specified ([ASC] or DESC)"
35
35
  #
36
36
  def key_descriptions
37
- return @key_descriptions if @key_descriptions
38
-
39
- n = size
40
- @key_descriptions = (0...n).each_with_object({}) do |i, hash|
37
+ @key_descriptions ||= (0...size).each_with_object({}) do |i, hash|
41
38
  key, description = key_description(i)
42
39
  hash[key] = description
43
40
  end
@@ -0,0 +1,52 @@
1
+ module DuckDB
2
+ module Converter
3
+ private
4
+
5
+ def iso8601_interval_to_hash(value)
6
+ digit = ''
7
+ time = false
8
+ hash = {}
9
+ hash.default = 0
10
+
11
+ value.each_char do |c|
12
+ if '-0123456789.'.include?(c)
13
+ digit += c
14
+ elsif c == 'T'
15
+ time = true
16
+ digit = ''
17
+ elsif c == 'M'
18
+ m_interval_to_hash(hash, digit, time)
19
+ digit = ''
20
+ elsif c == 'S'
21
+ s_interval_to_hash(hash, digit)
22
+ digit = ''
23
+ elsif 'YDH'.include?(c)
24
+ hash[c] = digit.to_i
25
+ digit = ''
26
+ elsif c != 'P'
27
+ raise ArgumentError, "The argument `#{value}` can't be parse."
28
+ end
29
+ end
30
+ hash
31
+ end
32
+
33
+ def m_interval_to_hash(hash, digit, time)
34
+ key = time ? 'TM' : 'M'
35
+ hash[key] = digit.to_i
36
+ end
37
+
38
+ def s_interval_to_hash(hash, digit)
39
+ sec, msec = digit.split('.')
40
+ hash['S'] = sec.to_i
41
+ hash['MS'] = "#{msec}000000"[0, 6].to_i
42
+ hash['MS'] *= -1 if hash['S'].negative?
43
+ end
44
+
45
+ def hash_to__append_interval_args(hash)
46
+ months = hash['Y'] * 12 + hash['M']
47
+ days = hash['D']
48
+ micros = (hash['H'] * 3600 + hash['TM'] * 60 + hash['S']) * 1_000_000 + hash['MS']
49
+ [months, days, micros]
50
+ end
51
+ end
52
+ end