duckdb 0.3.1.0 → 0.3.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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