duckdb 1.1.2.0 → 1.1.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7b6ad88562489a943d5ed0a4fcf56c4f84c9c3aa1a9f28d6d1fc6431c3fcea0b
4
- data.tar.gz: ac3047cf0098642db942d3547d1e8069fdb53ac0c8eb7f757a0f15c31465b895
3
+ metadata.gz: f326104e9d96a1fce6a508d481101c37eca2b12a421eb2ddb07acb838f14c118
4
+ data.tar.gz: 999db21104bb772564ecb41bbc2fee06d728f65618bb8538d14bc20c22173424
5
5
  SHA512:
6
- metadata.gz: 5614558f8685681f3369dfb4d8e36ead416b7ba1e4b0de48ff5edcc375a99c43bc1c3d996eca1f083d2df0352ced146f1e6d23bff99fa193035c9f996674e63d
7
- data.tar.gz: 2ace009816b0dfee961d8db1b0864e512239186ebf7b8b0ffe20a360e0a247a68afd11c4b97993e350f7b2a7f8eb29d4b4b3fcb367b31c575a6163d1d9d333c6
6
+ metadata.gz: 6cb7f30f39a2b768d1d6441e324f2c86f910f7af2cff9ca403b91eeb13cacbc0bee56dc54d3786f58e27af9276ae91d4a2ce22c705727b0c8ef5cd3251ab83fe
7
+ data.tar.gz: f17ea13a5aca9640e17e8983a37f9ba88e6ace381d5ad7e979232b0158028b92aa07ddea758ffdd9868511fb2b868354f7f15c20d6c14fb4f2e537613f8ee670
data/CHANGELOG.md CHANGED
@@ -3,6 +3,21 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  # Unreleased
5
5
 
6
+ # 1.1.2.1 - 2024-11-04
7
+ - `DuckDB::Connection#query` accepts multiple SQL statement.
8
+ - When multiple SQL statements are given, `DuckDB::Connection#query` returns the last statement result.
9
+ - `DuckDB::Connection#query` does not support multiple SQL statements with bind parameters. If you pass 2 or more argument,
10
+ `DuckDB::Connection#query` will regard first argument as only one SQL statement and the rest as bind parameters.
11
+ - add `DuckDB::ExtracteStatements#each` method.
12
+ - add `DuckDB::ExtracteStatementsImpl#destroy` method.
13
+ - add `DuckDB::PreparedStatement#prepare`.
14
+ - `DuckDB::Connection#prepared_statement` accepts block and calls `PreparedStatement#destroy` after block executed.
15
+ ```ruby
16
+ con.prepared_statement('SELECT * FROM table WHERE id = ?') do |stmt|
17
+ stmt.bind(1)
18
+ stmt.execute
19
+ end
20
+ ```
6
21
  # 1.1.2.0 - 2024-10-20
7
22
  - bump duckdb to 1.1.2.
8
23
  - add `DuckDB::PreparedStatement#destroy`.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- duckdb (1.1.2.0)
4
+ duckdb (1.1.2.1)
5
5
  bigdecimal (>= 3.1.4)
6
6
 
7
7
  GEM
@@ -17,6 +17,7 @@ static VALUE allocate(VALUE klass);
17
17
  static size_t memsize(const void *p);
18
18
 
19
19
  static VALUE duckdb_extracted_statements_initialize(VALUE self, VALUE con, VALUE query);
20
+ static VALUE duckdb_extracted_statements_destroy(VALUE self);
20
21
  static VALUE duckdb_extracted_statements_size(VALUE self);
21
22
  static VALUE duckdb_extracted_statements_prepared_statement(VALUE self, VALUE con, VALUE index);
22
23
 
@@ -56,12 +57,22 @@ static VALUE duckdb_extracted_statements_initialize(VALUE self, VALUE con, VALUE
56
57
 
57
58
  if (ctx->num_statements == 0) {
58
59
  error = duckdb_extract_statements_error(ctx->extracted_statements);
59
- rb_raise(eDuckDBError, "%s", error);
60
+ rb_raise(eDuckDBError, "%s", error ? error : "Failed to extract statements(Database connection closed?).");
60
61
  }
61
62
 
62
63
  return self;
63
64
  }
64
65
 
66
+ static VALUE duckdb_extracted_statements_destroy(VALUE self) {
67
+ rubyDuckDBExtractedStatements *ctx;
68
+
69
+ TypedData_Get_Struct(self, rubyDuckDBExtractedStatements, &extract_statements_data_type, ctx);
70
+
71
+ duckdb_destroy_extracted(&(ctx->extracted_statements));
72
+
73
+ return Qnil;
74
+ }
75
+
65
76
  static VALUE duckdb_extracted_statements_size(VALUE self) {
66
77
  rubyDuckDBExtractedStatements *ctx;
67
78
 
@@ -85,10 +96,11 @@ static VALUE duckdb_extracted_statements_prepared_statement(VALUE self, VALUE co
85
96
  }
86
97
 
87
98
  void rbduckdb_init_duckdb_extracted_statements(void) {
88
- cDuckDBExtractedStatements = rb_define_class_under(mDuckDB, "ExtractedStatements", rb_cObject);
99
+ cDuckDBExtractedStatements = rb_define_class_under(mDuckDB, "ExtractedStatementsImpl", rb_cObject);
89
100
 
90
101
  rb_define_alloc_func(cDuckDBExtractedStatements, allocate);
91
102
  rb_define_method(cDuckDBExtractedStatements, "initialize", duckdb_extracted_statements_initialize, 2);
103
+ rb_define_method(cDuckDBExtractedStatements, "destroy", duckdb_extracted_statements_destroy, 0);
92
104
  rb_define_method(cDuckDBExtractedStatements, "size", duckdb_extracted_statements_size, 0);
93
105
  rb_define_method(cDuckDBExtractedStatements, "prepared_statement", duckdb_extracted_statements_prepared_statement, 2);
94
106
  }
@@ -32,6 +32,7 @@ static VALUE duckdb_prepared_statement__bind_time(VALUE self, VALUE vidx, VALUE
32
32
  static VALUE duckdb_prepared_statement__bind_timestamp(VALUE self, VALUE vidx, VALUE year, VALUE month, VALUE day, VALUE hour, VALUE min, VALUE sec, VALUE micros);
33
33
  static VALUE duckdb_prepared_statement__bind_interval(VALUE self, VALUE vidx, VALUE months, VALUE days, VALUE micros);
34
34
  static VALUE duckdb_prepared_statement__bind_hugeint(VALUE self, VALUE vidx, VALUE lower, VALUE upper);
35
+ static VALUE duckdb_prepared_statement__bind_decimal(VALUE self, VALUE vidx, VALUE lower, VALUE upper, VALUE width, VALUE scale);
35
36
 
36
37
  static const rb_data_type_t prepared_statement_data_type = {
37
38
  "DuckDB/PreparedStatement",
@@ -71,7 +72,7 @@ VALUE rbduckdb_prepared_statement_new(duckdb_connection con, duckdb_extracted_st
71
72
  TypedData_Get_Struct(obj, rubyDuckDBPreparedStatement, &prepared_statement_data_type, ctx);
72
73
 
73
74
  if (duckdb_prepare_extracted_statement(con, extracted_statements, index, &(ctx->prepared_statement)) == DuckDBError) {
74
- rb_raise(eDuckDBError, "Fail to get DuckDB::PreparedStatement object from ExtractedStatements object");
75
+ rb_raise(eDuckDBError, "Failed to create DuckDB::PreparedStatement object.");
75
76
  }
76
77
  return obj;
77
78
  }
@@ -104,11 +105,16 @@ static VALUE duckdb_prepared_statement_execute(VALUE self) {
104
105
  rubyDuckDBPreparedStatement *ctx;
105
106
  rubyDuckDBResult *ctxr;
106
107
  VALUE result = rbduckdb_create_result();
108
+ const char *p = NULL;
107
109
 
108
110
  TypedData_Get_Struct(self, rubyDuckDBPreparedStatement, &prepared_statement_data_type, ctx);
109
111
  ctxr = get_struct_result(result);
110
112
  if (duckdb_execute_prepared(ctx->prepared_statement, &(ctxr->result)) == DuckDBError) {
111
- rb_raise(eDuckDBError, "%s", duckdb_result_error(&(ctxr->result)));
113
+ p = duckdb_result_error(&(ctxr->result));
114
+ if (p == NULL) {
115
+ p = duckdb_prepare_error(ctx->prepared_statement);
116
+ }
117
+ rb_raise(eDuckDBError, "%s", p ? p : "Failed to execute prepared statement.");
112
118
  }
113
119
  return result;
114
120
  }
@@ -399,6 +405,26 @@ static VALUE duckdb_prepared_statement__bind_hugeint(VALUE self, VALUE vidx, VAL
399
405
  return self;
400
406
  }
401
407
 
408
+ static VALUE duckdb_prepared_statement__bind_decimal(VALUE self, VALUE vidx, VALUE lower, VALUE upper, VALUE width, VALUE scale) {
409
+ duckdb_hugeint hugeint;
410
+ duckdb_decimal decimal;
411
+ rubyDuckDBPreparedStatement *ctx;
412
+ idx_t idx = check_index(vidx);
413
+
414
+ TypedData_Get_Struct(self, rubyDuckDBPreparedStatement, &prepared_statement_data_type, ctx);
415
+ hugeint.lower = NUM2ULL(lower);
416
+ hugeint.upper = NUM2LL(upper);
417
+ decimal.value = hugeint;
418
+ decimal.width = (uint8_t)NUM2UINT(width);
419
+ decimal.scale = (uint8_t)NUM2UINT(scale);
420
+
421
+ if (duckdb_bind_decimal(ctx->prepared_statement, idx, decimal) == DuckDBError) {
422
+ rb_raise(eDuckDBError, "fail to bind %llu parameter", (unsigned long long)idx);
423
+ }
424
+
425
+ return self;
426
+ }
427
+
402
428
  rubyDuckDBPreparedStatement *get_struct_prepared_statement(VALUE self) {
403
429
  rubyDuckDBPreparedStatement *ctx;
404
430
  TypedData_Get_Struct(self, rubyDuckDBPreparedStatement, &prepared_statement_data_type, ctx);
@@ -434,4 +460,5 @@ void rbduckdb_init_duckdb_prepared_statement(void) {
434
460
  rb_define_private_method(cDuckDBPreparedStatement, "_bind_timestamp", duckdb_prepared_statement__bind_timestamp, 8);
435
461
  rb_define_private_method(cDuckDBPreparedStatement, "_bind_interval", duckdb_prepared_statement__bind_interval, 4);
436
462
  rb_define_private_method(cDuckDBPreparedStatement, "_bind_hugeint", duckdb_prepared_statement__bind_hugeint, 3);
463
+ rb_define_private_method(cDuckDBPreparedStatement, "_bind_decimal", duckdb_prepared_statement__bind_decimal, 5);
437
464
  }
@@ -74,16 +74,7 @@ module DuckDB
74
74
  # appender.flush
75
75
  #
76
76
  def append_date(value)
77
- date = case value
78
- when Date, Time
79
- value
80
- else
81
- begin
82
- Date.parse(value)
83
- rescue
84
- raise(ArgumentError, "Cannot parse argument `#{value}` to Date.")
85
- end
86
- end
77
+ date = to_date(value)
87
78
 
88
79
  _append_date(date.year, date.month, date.day)
89
80
  end
@@ -126,18 +117,7 @@ module DuckDB
126
117
  # appender.flush
127
118
  #
128
119
  def append_timestamp(value)
129
- time = case value
130
- when Time
131
- value
132
- when Date
133
- value.to_time
134
- else
135
- begin
136
- Time.parse(value)
137
- rescue
138
- raise(ArgumentError, "Cannot parse argument `#{value}` to Time or Date.")
139
- end
140
- end
120
+ time = to_time(value)
141
121
 
142
122
  _append_timestamp(time.year, time.month, time.day, time.hour, time.min, time.sec, time.nsec / 1000)
143
123
  end
@@ -233,5 +213,33 @@ module DuckDB
233
213
  def blob?(value)
234
214
  value.instance_of?(DuckDB::Blob) || value.encoding == Encoding::BINARY
235
215
  end
216
+
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
228
+ end
229
+
230
+ def to_time(value)
231
+ case value
232
+ when Time
233
+ value
234
+ when Date
235
+ value.to_time
236
+ else
237
+ begin
238
+ Time.parse(value)
239
+ rescue StandardError
240
+ raise(ArgumentError, "Cannot parse argument `#{value}` to Time or Date.")
241
+ end
242
+ end
243
+ end
236
244
  end
237
245
  end
@@ -26,13 +26,23 @@ module DuckDB
26
26
  # dave = con.query(sql, name: 'Dave', email: 'dave@example.com')
27
27
  #
28
28
  def query(sql, *args, **kwargs)
29
- return query_sql(sql) if args.empty? && kwargs.empty?
29
+ return query_multi_sql(sql) if args.empty? && kwargs.empty?
30
30
 
31
- stmt = PreparedStatement.new(self, sql)
32
- stmt.bind_args(*args, **kwargs)
33
- stmt.execute
31
+ prepare(sql) do |stmt|
32
+ stmt.bind_args(*args, **kwargs)
33
+ stmt.execute
34
+ end
35
+ end
36
+
37
+ def query_multi_sql(sql)
38
+ stmts = ExtractedStatements.new(self, sql)
39
+ result = nil
40
+ stmts.each do |stmt|
41
+ result = stmt.execute
42
+ end
43
+ result
34
44
  ensure
35
- stmt&.destroy
45
+ stmts&.destroy
36
46
  end
37
47
 
38
48
  #
@@ -52,11 +62,10 @@ module DuckDB
52
62
  # result.each.first
53
63
  #
54
64
  def async_query(sql, *args, **kwargs)
55
- stmt = PreparedStatement.new(self, sql)
56
- stmt.bind_args(*args, **kwargs)
57
- stmt.pending_prepared
58
- ensure
59
- stmt&.destroy
65
+ prepare(sql) do |stmt|
66
+ stmt.bind_args(*args, **kwargs)
67
+ stmt.pending_prepared
68
+ end
60
69
  end
61
70
 
62
71
  #
@@ -77,11 +86,10 @@ module DuckDB
77
86
  # result.each.first
78
87
  #
79
88
  def async_query_stream(sql, *args, **kwargs)
80
- stmt = PreparedStatement.new(self, sql)
81
- stmt.bind_args(*args, **kwargs)
82
- stmt.pending_prepared_stream
83
- ensure
84
- stmt&.destroy
89
+ prepare(sql) do |stmt|
90
+ stmt.bind_args(*args, **kwargs)
91
+ stmt.pending_prepared_stream
92
+ end
85
93
  end
86
94
 
87
95
  #
@@ -102,6 +110,8 @@ module DuckDB
102
110
  #
103
111
  # returns PreparedStatement object.
104
112
  # The first argument is SQL string.
113
+ # If block is given, the block is executed with PreparedStatement object
114
+ # and the object is cleaned up immediately.
105
115
  #
106
116
  # require 'duckdb'
107
117
  # db = DuckDB::Database.open('duckdb_file')
@@ -111,8 +121,17 @@ module DuckDB
111
121
  # stmt = con.prepared_statement(sql)
112
122
  # stmt.bind_args(name: 'Dave', email: 'dave@example.com')
113
123
  # result = stmt.execute
114
- def prepared_statement(str)
115
- PreparedStatement.new(self, str)
124
+ #
125
+ # # or
126
+ # result = con.prepared_statement(sql) do |stmt|
127
+ # stmt.bind_args(name: 'Dave', email: 'dave@example.com')
128
+ # stmt.execute
129
+ # end
130
+ #
131
+ def prepared_statement(str, &)
132
+ return PreparedStatement.new(self, str) unless block_given?
133
+
134
+ PreparedStatement.prepare(self, str, &)
116
135
  end
117
136
 
118
137
  #
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DuckDB
4
+ class ExtractedStatements < ExtractedStatementsImpl
5
+ include Enumerable
6
+
7
+ def initialize(con, sql)
8
+ @con = con
9
+ super(con, sql)
10
+ end
11
+
12
+ def each
13
+ return to_enum(__method__) { size } unless block_given?
14
+
15
+ size.times do |i|
16
+ yield prepared_statement(@con, i)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -22,6 +22,31 @@ module DuckDB
22
22
  RANGE_INT32 = -2_147_483_648..2_147_483_647
23
23
  RANGE_INT64 = -9_223_372_036_854_775_808..9_223_372_036_854_775_807
24
24
 
25
+ class << self
26
+ # return DuckDB::PreparedStatement object.
27
+ # The first argument is DuckDB::Connection object.
28
+ # The second argument is SQL string.
29
+ # If block is given, the block is executed and the statement is destroyed.
30
+ #
31
+ # require 'duckdb'
32
+ # db = DuckDB::Database.open('duckdb_database')
33
+ # con = db.connection
34
+ # DuckDB::PreparedStatement.prepare(con, 'SELECT * FROM users WHERE id = ?') do |stmt|
35
+ # stmt.bind(1, 1)
36
+ # stmt.execute
37
+ # end
38
+ def prepare(con, sql)
39
+ stmt = new(con, sql)
40
+ return stmt unless block_given?
41
+
42
+ begin
43
+ yield stmt
44
+ ensure
45
+ stmt.destroy
46
+ end
47
+ end
48
+ end
49
+
25
50
  def pending_prepared
26
51
  PendingResult.new(self)
27
52
  end
@@ -3,5 +3,5 @@
3
3
  module DuckDB
4
4
  # The version string of ruby-duckdb.
5
5
  # Currently, ruby-duckdb is NOT semantic versioning.
6
- VERSION = '1.1.2.0'
6
+ VERSION = '1.1.2.1'
7
7
  end
data/lib/duckdb.rb CHANGED
@@ -6,6 +6,7 @@ require 'duckdb/version'
6
6
  require 'duckdb/converter'
7
7
  require 'duckdb/database'
8
8
  require 'duckdb/connection'
9
+ require 'duckdb/extracted_statements'
9
10
  require 'duckdb/result'
10
11
  require 'duckdb/prepared_statement'
11
12
  require 'duckdb/pending_result'
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: 1.1.2.0
4
+ version: 1.1.2.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: 2024-10-20 00:00:00.000000000 Z
11
+ date: 2024-11-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bigdecimal
@@ -149,6 +149,7 @@ files:
149
149
  - lib/duckdb/converter.rb
150
150
  - lib/duckdb/converter/int_to_sym.rb
151
151
  - lib/duckdb/database.rb
152
+ - lib/duckdb/extracted_statements.rb
152
153
  - lib/duckdb/infinity.rb
153
154
  - lib/duckdb/interval.rb
154
155
  - lib/duckdb/library_version.rb