duckdb 1.1.2.0 → 1.1.2.1

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 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