duckdb 1.5.6.1 → 2.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 74ccac3599175a19bbc80ef6e9ad8e9aae62152d96b78dd6357623f647b62ff2
4
- data.tar.gz: 9302451535c8b0b32f561a7deb45bd0f1800a77889592308c5e94714abd5fa33
3
+ metadata.gz: ee2e890f808c2ff97b099f79c12f17a8fe856253b13a3ee2378bbb21306e3587
4
+ data.tar.gz: a7081554ee0ff1ebb22d688f9aafab9b434c93cb080f2abcc7ffe1e9ae3f70d1
5
5
  SHA512:
6
- metadata.gz: 29981f3882568513062d0a59e04e434dd8b557635c70b51fb2bfc017a077bf7076a10b384b676a7c32e7bf0d6d24420622e4e04164fced341b9165fe11cce062
7
- data.tar.gz: e16e3171bd0c0f8f0e26f2c48e6f3a4d73fb1c93b00a01a4ae5d790a66ea0a198aa722d91790eb95e3b4fb1f66e167e3a69cf0377effa39138e801d7eb9eac16
6
+ metadata.gz: b13a967b1e810a51220080b227b8b6dd5098e56775567b049ed722ec3a9dec02f3fe97c399fbd8a88e292bc65888ce905ef5b908f182e110abf785d2ff2aa94e
7
+ data.tar.gz: ff96826d03f74eedb1633bc5256addb916abd737741636918748d0fcd65c0527669100b96103beff430184a6a8b21b4387fffb2351888898c1729c4c335d01be
data/CHANGELOG.md CHANGED
@@ -4,6 +4,20 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  # Unreleased
6
6
 
7
+ # 2.0.0.0 - 2026-10-24
8
+ - bump Ruby to 4.0.9 on CI (Ubuntu and macOS).
9
+ - bump up DuckDB 2.0.0 on CI.
10
+ - add `DuckDB::PendingResult#destroy` (issue #1497).
11
+ - add block form to `DuckDB::Connection#async_query` that destroys the `DuckDB::PendingResult` after the block (issue #1498).
12
+
13
+ ## Breaking Changes
14
+ - remove `DuckDB::BindInfo`, `DuckDB::InitInfo` and `DuckDB::FunctionInfo` (deprecated aliases). Use `DuckDB::TableFunction::BindInfo`, `DuckDB::TableFunction::InitInfo` and `DuckDB::TableFunction::FunctionInfo`.
15
+ - remove the positional config form `DuckDB::Database.open(path, config)`. Use `DuckDB::Database.open(path, config: config)`.
16
+ - remove the 3-argument form `DuckDB::Appender.new(con, schema, table)`. Use `DuckDB::Appender.new(con, table, schema: schema)`.
17
+ - `DuckDB::Appender.new`, `DuckDB::Connection#appender` and `DuckDB::TableDescription.new` no longer split dot-notation table names such as `'schema.table'`. A name containing a dot is used literally as the table name. Use the `schema:` and `catalog:` keyword arguments to qualify a table. Quoted names (`'"a.b"'` / `"'a.b'"`) still have their quotes stripped.
18
+ - remove the private methods `DuckDB::Result#_enum_dictionary_size`, `#_enum_dictionary_value` and `#_column_type`. Use `columns[i].logical_type.dictionary_size`, `columns[i].logical_type.dictionary_value_at(j)` and `columns[i].type`.
19
+ - `DuckDB::PendingResult.new` takes exactly one argument (the prepared statement); the meaningless second argument was removed.
20
+
7
21
  # 1.5.6.1 - 2026-10-17
8
22
  - fix `DuckDB::ExtractedStatements.new`, `Connection#query` and `Connection#extract` raising `Failed to extract statements(Database connection closed?).` for SQL that contains no statements, such as only a comment, whitespace or `;` (issue #1490). `ExtractedStatements#size` is now `0`, `Connection#query` returns `nil` and `Connection#extract` returns `[]`. Calling `ExtractedStatements.new` on a closed connection now raises `Database connection closed`.
9
23
 
data/README.md CHANGED
@@ -251,7 +251,7 @@ require 'duckdb'
251
251
  config = DuckDB::Config.new
252
252
  config['default_order'] = 'DESC'
253
253
 
254
- db = DuckDB::Database.open(nil, config)
254
+ db = DuckDB::Database.open(config: config)
255
255
 
256
256
  con = db.connect
257
257
  con.query('CREATE TABLE numbers (number INTEGER)')
@@ -62,8 +62,11 @@ static size_t memsize(const void *p) {
62
62
  return sizeof(rubyDuckDBArrowArrayStream);
63
63
  }
64
64
 
65
- /* Context memory is managed with plain malloc/free because the release
66
- * callback may run outside Ruby's memory bookkeeping. */
65
+ /*
66
+ * Use malloc/free (not xmalloc/xfree): the stream callbacks are invoked by
67
+ * Arrow consumers, possibly from a non-Ruby thread where xmalloc/xfree are
68
+ * unsafe.
69
+ */
67
70
  static void stream_set_error(arrowArrayStreamContext *ctx, const char *msg) {
68
71
  size_t len = strlen(msg) + 1;
69
72
 
@@ -96,6 +99,7 @@ static int stream_get_schema(struct ArrowArrayStream *stream, struct ArrowSchema
96
99
  idx_t i;
97
100
 
98
101
  column_count = duckdb_column_count(&(ctx->presult_ctx->result));
102
+ /* Use calloc (not xcalloc): see stream_set_error. */
99
103
  types = calloc((size_t)column_count, sizeof(duckdb_logical_type));
100
104
  names = calloc((size_t)column_count, sizeof(const char *));
101
105
  if (column_count > 0 && (types == NULL || names == NULL)) {
@@ -172,6 +176,10 @@ VALUE rbduckdb_create_arrow_array_stream(VALUE oDuckDBResult) {
172
176
  TypedData_Get_Struct(obj, rubyDuckDBArrowArrayStream, &arrow_array_stream_data_type, p);
173
177
  presult_ctx = rbduckdb_get_struct_result(oDuckDBResult);
174
178
 
179
+ /*
180
+ * Use calloc (not xcalloc): stream_release frees the context, possibly
181
+ * from a non-Ruby thread where xfree is unsafe.
182
+ */
175
183
  ctx = calloc((size_t)1, sizeof(arrowArrayStreamContext));
176
184
  if (ctx == NULL) {
177
185
  rb_raise(rb_eNoMemError, "failed to allocate ArrowArrayStream context");
@@ -71,7 +71,6 @@ static VALUE expression__fold(VALUE self, VALUE client_context) {
71
71
  rubyDuckDBClientContext *cc_ctx;
72
72
  duckdb_value out_value = NULL;
73
73
  duckdb_error_data error_data;
74
- VALUE result;
75
74
 
76
75
  TypedData_Get_Struct(self, rubyDuckDBExpression, &expression_data_type, expr_ctx);
77
76
  cc_ctx = rbduckdb_get_struct_client_context(client_context);
@@ -88,9 +87,7 @@ static VALUE expression__fold(VALUE self, VALUE client_context) {
88
87
  }
89
88
  duckdb_destroy_error_data(&error_data);
90
89
 
91
- result = rbduckdb_duckdb_value_to_ruby(out_value);
92
- duckdb_destroy_value(&out_value);
93
- return result;
90
+ return rbduckdb_duckdb_value_to_ruby_and_destroy(out_value);
94
91
  }
95
92
 
96
93
  void rbduckdb_init_expression(void) {
@@ -5,12 +5,14 @@ static VALUE cDuckDBPendingResult;
5
5
  static void deallocate(void *ctx);
6
6
  static VALUE allocate(VALUE klass);
7
7
  static size_t memsize(const void *p);
8
- static VALUE pending_result_initialize(int argc, VALUE *args, VALUE self);
8
+ static VALUE pending_result_initialize(VALUE self, VALUE oDuckDBPreparedStatement);
9
9
  static VALUE pending_result_execute_task(VALUE self);
10
+ static VALUE pending_result_destroy(VALUE self);
10
11
  static VALUE pending_result_execute_pending(VALUE self);
11
12
  static VALUE pending_result_execution_finished_p(VALUE self);
12
13
  static VALUE pending_result__state(VALUE self);
13
14
  static VALUE pending_result__execute_check_state(VALUE self);
15
+ static rubyDuckDBPendingResult *get_live_pending_result(VALUE self);
14
16
 
15
17
  /*
16
18
  * No RUBY_TYPED_FREE_IMMEDIATELY: deallocate can block, and must not run
@@ -30,6 +32,28 @@ static void deallocate(void *ctx) {
30
32
  xfree(p);
31
33
  }
32
34
 
35
+ /*
36
+ * Returns the struct, raising DuckDB::Error if the pending result was
37
+ * destroyed or never initialized.
38
+ */
39
+ static rubyDuckDBPendingResult *get_live_pending_result(VALUE self) {
40
+ rubyDuckDBPendingResult *ctx = rbduckdb_get_struct_pending_result(self);
41
+ if (!ctx->pending_result) {
42
+ rb_raise(eDuckDBError, "pending result is already destroyed or not initialized");
43
+ }
44
+ return ctx;
45
+ }
46
+
47
+ /*
48
+ * duckdb_destroy_pending can block (cancelling the pipeline may run UDF
49
+ * teardown callbacks that need the GVL, or shut the database down), so it
50
+ * is called without the GVL. See database_close in database.c.
51
+ */
52
+ static void *destroy_pending_without_gvl(void *ctx) {
53
+ duckdb_destroy_pending((duckdb_pending_result *)ctx);
54
+ return NULL;
55
+ }
56
+
33
57
  static VALUE allocate(VALUE klass) {
34
58
  rubyDuckDBPendingResult *ctx = xcalloc((size_t)1, sizeof(rubyDuckDBPendingResult));
35
59
  ctx->state = DUCKDB_PENDING_RESULT_NOT_READY;
@@ -40,16 +64,9 @@ static size_t memsize(const void *p) {
40
64
  return sizeof(rubyDuckDBPendingResult);
41
65
  }
42
66
 
43
- static VALUE pending_result_initialize(int argc, VALUE *argv, VALUE self) {
44
- VALUE oDuckDBPreparedStatement;
45
- VALUE streaming_p = Qfalse;
67
+ static VALUE pending_result_initialize(VALUE self, VALUE oDuckDBPreparedStatement) {
46
68
  duckdb_state state;
47
69
 
48
- if (argc == 2) {
49
- rb_warn("The second argument of `DuckDB::PendingResult#new` is now meaningless. The result is the same when it is set to true.");
50
- }
51
- rb_scan_args(argc, argv, "11", &oDuckDBPreparedStatement, &streaming_p);
52
-
53
70
  if (rb_obj_is_kind_of(oDuckDBPreparedStatement, cDuckDBPreparedStatement) != Qtrue) {
54
71
  rb_raise(rb_eTypeError, "1st argument must be DuckDB::PreparedStatement");
55
72
  }
@@ -77,11 +94,31 @@ static VALUE pending_result_initialize(int argc, VALUE *argv, VALUE self) {
77
94
  * pending_result.execute_task
78
95
  */
79
96
  static VALUE pending_result_execute_task(VALUE self) {
80
- rubyDuckDBPendingResult *ctx = rbduckdb_get_struct_pending_result(self);
97
+ rubyDuckDBPendingResult *ctx = get_live_pending_result(self);
81
98
  ctx->state = duckdb_pending_execute_task(ctx->pending_result);
82
99
  return Qnil;
83
100
  }
84
101
 
102
+ /*
103
+ * call-seq:
104
+ * pending_result.destroy -> nil
105
+ *
106
+ * Releases the pending result without executing it. Required on DuckDB >= 2.0.0,
107
+ * where an open pending result blocks the connection. Safe to call more than once.
108
+ *
109
+ * db = DuckDB::Database.open
110
+ * conn = db.connect
111
+ * pending_result = conn.async_query("slow query")
112
+ * pending_result.destroy
113
+ * conn.query("SELECT 1")
114
+ */
115
+ static VALUE pending_result_destroy(VALUE self) {
116
+ rubyDuckDBPendingResult *ctx = rbduckdb_get_struct_pending_result(self);
117
+ rb_thread_call_without_gvl(destroy_pending_without_gvl, &(ctx->pending_result), NULL, NULL);
118
+ ctx->state = DUCKDB_PENDING_ERROR;
119
+ return Qnil;
120
+ }
121
+
85
122
  static VALUE pending_result_execution_finished_p(VALUE self) {
86
123
  rubyDuckDBPendingResult *ctx = rbduckdb_get_struct_pending_result(self);
87
124
  return duckdb_pending_execution_is_finished(ctx->state) ? Qtrue : Qfalse;
@@ -102,9 +139,10 @@ static VALUE pending_result_execution_finished_p(VALUE self) {
102
139
  static VALUE pending_result_execute_pending(VALUE self) {
103
140
  rubyDuckDBPendingResult *ctx;
104
141
  rubyDuckDBResult *ctxr;
105
- VALUE result = rbduckdb_create_result();
142
+ VALUE result;
106
143
 
107
- TypedData_Get_Struct(self, rubyDuckDBPendingResult, &pending_result_data_type, ctx);
144
+ ctx = get_live_pending_result(self);
145
+ result = rbduckdb_create_result();
108
146
  ctxr = rbduckdb_get_struct_result(result);
109
147
  if (duckdb_execute_pending(ctx->pending_result, &(ctxr->result)) == DuckDBError) {
110
148
  rb_raise(eDuckDBError, "%s", duckdb_pending_error(ctx->pending_result));
@@ -120,7 +158,7 @@ static VALUE pending_result__state(VALUE self) {
120
158
 
121
159
  /* :nodoc: */
122
160
  static VALUE pending_result__execute_check_state(VALUE self) {
123
- rubyDuckDBPendingResult *ctx = rbduckdb_get_struct_pending_result(self);
161
+ rubyDuckDBPendingResult *ctx = get_live_pending_result(self);
124
162
  return INT2FIX(duckdb_pending_execute_check_state(ctx->pending_result));
125
163
  }
126
164
 
@@ -137,8 +175,9 @@ void rbduckdb_init_pending_result(void) {
137
175
  cDuckDBPendingResult = rb_define_class_under(mDuckDB, "PendingResult", rb_cObject);
138
176
  rb_define_alloc_func(cDuckDBPendingResult, allocate);
139
177
 
140
- rb_define_method(cDuckDBPendingResult, "initialize", pending_result_initialize, -1);
178
+ rb_define_method(cDuckDBPendingResult, "initialize", pending_result_initialize, 1);
141
179
  rb_define_method(cDuckDBPendingResult, "execute_task", pending_result_execute_task, 0);
180
+ rb_define_method(cDuckDBPendingResult, "destroy", pending_result_destroy, 0);
142
181
  rb_define_method(cDuckDBPendingResult, "execute_pending", pending_result_execute_pending, 0);
143
182
  rb_define_method(cDuckDBPendingResult, "execution_finished?", pending_result_execution_finished_p, 0);
144
183
  rb_define_private_method(cDuckDBPendingResult, "_state", pending_result__state, 0);
data/ext/duckdb/result.c CHANGED
@@ -3,6 +3,30 @@
3
3
  struct chunk_arg {
4
4
  duckdb_data_chunk chunk;
5
5
  idx_t col_count;
6
+ duckdb_logical_type *types;
7
+ };
8
+
9
+ struct owned_type_arg {
10
+ duckdb_vector vector;
11
+ duckdb_logical_type type;
12
+ idx_t bgn;
13
+ idx_t end;
14
+ };
15
+
16
+ struct struct_fields_arg {
17
+ duckdb_logical_type ty;
18
+ duckdb_vector vector;
19
+ idx_t row_idx;
20
+ duckdb_logical_type child_type;
21
+ };
22
+
23
+ struct map_entries_arg {
24
+ duckdb_vector key_vector;
25
+ duckdb_vector value_vector;
26
+ duckdb_logical_type key_type;
27
+ duckdb_logical_type value_type;
28
+ idx_t bgn;
29
+ idx_t end;
6
30
  };
7
31
 
8
32
  static VALUE cDuckDBResult;
@@ -14,6 +38,7 @@ static VALUE result_column_count(VALUE oDuckDBResult);
14
38
  static VALUE result_rows_changed(VALUE oDuckDBResult);
15
39
  static VALUE result_columns(VALUE oDuckDBResult);
16
40
  static VALUE destroy_data_chunk(VALUE arg);
41
+ static duckdb_logical_type *result_column_types(rubyDuckDBResult *ctx);
17
42
 
18
43
  static VALUE result__chunk_stream(VALUE oDuckDBResult);
19
44
  static VALUE result_arrow_c_stream(VALUE oDuckDBResult);
@@ -46,7 +71,13 @@ static VALUE vector_bit(void* vector_data, idx_t row_idx);
46
71
  static VALUE vector_time_tz(void* vector_data, idx_t row_idx);
47
72
  static VALUE vector_timestamp_tz(void* vector_data, idx_t row_idx);
48
73
  static VALUE vector_uuid(void* vector_data, idx_t row_idx);
49
- static VALUE vector_value(duckdb_vector vector, idx_t row_idx);
74
+ static VALUE convert_values_in_range(VALUE varg);
75
+ static VALUE destroy_owned_type(VALUE varg);
76
+ static VALUE vector_values_with_owned_type(duckdb_vector vector, duckdb_logical_type type, idx_t bgn, idx_t end);
77
+ static VALUE convert_map_entries(VALUE varg);
78
+ static VALUE destroy_map_types(VALUE varg);
79
+ static VALUE convert_struct_fields(VALUE varg);
80
+ static VALUE destroy_struct_child_type(VALUE varg);
50
81
 
51
82
  /*
52
83
  * No RUBY_TYPED_FREE_IMMEDIATELY: deallocate can block, and must not run
@@ -66,6 +97,11 @@ static void deallocate(void *ctx) {
66
97
  }
67
98
 
68
99
  static VALUE allocate(VALUE klass) {
100
+ /*
101
+ * Use calloc (not xcalloc): rbduckdb_result_unref frees the struct, and it
102
+ * can run from the Arrow stream release callback on a non-Ruby thread
103
+ * where xfree is unsafe.
104
+ */
69
105
  rubyDuckDBResult *ctx = calloc((size_t)1, sizeof(rubyDuckDBResult));
70
106
 
71
107
  if (ctx == NULL) {
@@ -80,7 +116,13 @@ void rbduckdb_result_ref(rubyDuckDBResult *ctx) {
80
116
  }
81
117
 
82
118
  void rbduckdb_result_unref(rubyDuckDBResult *ctx) {
119
+ idx_t col_idx;
120
+
83
121
  if (RUBY_ATOMIC_FETCH_SUB(ctx->refcount, 1) == 1) {
122
+ for (col_idx = 0; col_idx < ctx->column_types_count; col_idx++) {
123
+ duckdb_destroy_logical_type(&(ctx->column_types[col_idx]));
124
+ }
125
+ free(ctx->column_types);
84
126
  duckdb_destroy_result(&(ctx->result));
85
127
  free(ctx);
86
128
  }
@@ -176,6 +218,25 @@ static VALUE destroy_data_chunk(VALUE arg) {
176
218
  return Qnil;
177
219
  }
178
220
 
221
+ static duckdb_logical_type *result_column_types(rubyDuckDBResult *ctx) {
222
+ idx_t col_idx;
223
+ idx_t column_count;
224
+
225
+ if (ctx->column_types == NULL) {
226
+ column_count = duckdb_column_count(&(ctx->result));
227
+ /* Use calloc (not xcalloc): freed by rbduckdb_result_unref (see allocate). */
228
+ ctx->column_types = calloc(column_count + 1, sizeof(duckdb_logical_type));
229
+ if (ctx->column_types == NULL) {
230
+ rb_raise(rb_eNoMemError, "failed to allocate DuckDB::Result column types");
231
+ }
232
+ for (col_idx = 0; col_idx < column_count; col_idx++) {
233
+ ctx->column_types[col_idx] = duckdb_column_logical_type(&(ctx->result), col_idx);
234
+ }
235
+ ctx->column_types_count = column_count;
236
+ }
237
+ return ctx->column_types;
238
+ }
239
+
179
240
  /* :nodoc: */
180
241
  static VALUE result__chunk_stream(VALUE oDuckDBResult) {
181
242
  rubyDuckDBResult *ctx;
@@ -186,6 +247,7 @@ static VALUE result__chunk_stream(VALUE oDuckDBResult) {
186
247
  RETURN_ENUMERATOR(oDuckDBResult, 0, 0);
187
248
 
188
249
  arg.col_count = duckdb_column_count(&(ctx->result));
250
+ arg.types = result_column_types(ctx);
189
251
 
190
252
  while((arg.chunk = duckdb_fetch_chunk(ctx->result)) != NULL) {
191
253
  rb_ensure(yield_rows, (VALUE)&arg, destroy_data_chunk, (VALUE)&arg);
@@ -208,7 +270,7 @@ static VALUE yield_rows(VALUE arg) {
208
270
  row = rb_ary_new2(p->col_count);
209
271
  for (col_idx = 0; col_idx < p->col_count; col_idx++) {
210
272
  vector = duckdb_data_chunk_get_vector(p->chunk, col_idx);
211
- val = vector_value(vector, row_idx);
273
+ val = rbduckdb_vector_value_at(vector, p->types[col_idx], row_idx);
212
274
  rb_ary_store(row, col_idx, val);
213
275
  }
214
276
  rb_yield(row);
@@ -371,23 +433,11 @@ static VALUE vector_enum(duckdb_logical_type ty, void* vector_data, idx_t row_id
371
433
  }
372
434
 
373
435
  static VALUE vector_array(duckdb_logical_type ty, duckdb_vector vector, idx_t row_idx) {
374
- VALUE ary = Qnil;
375
- VALUE value = Qnil;
376
-
377
- duckdb_logical_type child_logical_type = duckdb_array_type_child_type(ty);
378
436
  idx_t size = duckdb_array_type_array_size(ty);
379
437
  idx_t bgn = row_idx * size;
380
- idx_t end = bgn + size;
381
- duckdb_vector child = duckdb_array_vector_get_child(vector);
438
+ duckdb_vector child = duckdb_array_vector_get_child(vector);
382
439
 
383
- ary = rb_ary_new2(size);
384
- for (idx_t i = bgn; i < end; ++i) {
385
- value = rbduckdb_vector_value_at(child, child_logical_type, i);
386
- rb_ary_store(ary, i - bgn, value);
387
- }
388
-
389
- duckdb_destroy_logical_type(&child_logical_type);
390
- return ary;
440
+ return vector_values_with_owned_type(child, duckdb_array_type_child_type(ty), bgn, bgn + size);
391
441
  }
392
442
 
393
443
  VALUE rbduckdb_vector_value_at(duckdb_vector vector, duckdb_logical_type element_type, idx_t index) {
@@ -519,93 +569,121 @@ VALUE rbduckdb_vector_value_at(duckdb_vector vector, duckdb_logical_type element
519
569
  }
520
570
 
521
571
  static VALUE vector_list(duckdb_logical_type ty, duckdb_vector vector, void * vector_data, idx_t row_idx) {
522
- VALUE ary = Qnil;
523
- VALUE value = Qnil;
524
- idx_t i;
525
-
526
- duckdb_logical_type child_logical_type = duckdb_list_type_child_type(ty);
527
-
528
572
  duckdb_list_entry list_entry = ((duckdb_list_entry *)vector_data)[row_idx];
529
573
  idx_t bgn = list_entry.offset;
530
- idx_t end = bgn + list_entry.length;
531
- ary = rb_ary_new2(list_entry.length);
532
-
533
574
  duckdb_vector child = duckdb_list_vector_get_child(vector);
534
575
 
535
- for (i = bgn; i < end; ++i) {
536
- value = rbduckdb_vector_value_at(child, child_logical_type, i);
537
- rb_ary_store(ary, i - bgn, value);
538
- }
539
- duckdb_destroy_logical_type(&child_logical_type);
540
- return ary;
576
+ return vector_values_with_owned_type(child, duckdb_list_type_child_type(ty), bgn, bgn + list_entry.length);
541
577
  }
542
578
 
543
579
  static VALUE vector_map(duckdb_logical_type ty, duckdb_vector vector, void* vector_data, idx_t row_idx) {
544
- VALUE hash = rb_hash_new();
545
- VALUE key = Qnil;
546
- VALUE value = Qnil;
547
-
548
- duckdb_logical_type key_logical_type = duckdb_map_type_key_type(ty);
549
- duckdb_logical_type value_logical_type = duckdb_map_type_value_type(ty);
550
-
580
+ struct map_entries_arg arg;
551
581
  duckdb_list_entry list_entry = ((duckdb_list_entry *)vector_data)[row_idx];
582
+ duckdb_vector child = duckdb_list_vector_get_child(vector);
552
583
 
553
- idx_t bgn = list_entry.offset;
554
- idx_t end = bgn + list_entry.length;
584
+ arg.key_vector = duckdb_struct_vector_get_child(child, 0);
585
+ arg.value_vector = duckdb_struct_vector_get_child(child, 1);
586
+ arg.key_type = duckdb_map_type_key_type(ty);
587
+ arg.value_type = duckdb_map_type_value_type(ty);
588
+ arg.bgn = list_entry.offset;
589
+ arg.end = list_entry.offset + list_entry.length;
555
590
 
556
- duckdb_vector child = duckdb_list_vector_get_child(vector);
557
- duckdb_vector key_vector = duckdb_struct_vector_get_child(child, 0);
558
- duckdb_vector value_vector = duckdb_struct_vector_get_child(child, 1);
591
+ return rb_ensure(convert_map_entries, (VALUE)&arg, destroy_map_types, (VALUE)&arg);
592
+ }
559
593
 
560
- for (idx_t i = bgn; i < end; ++i) {
561
- key = rbduckdb_vector_value_at(key_vector, key_logical_type, i);
562
- value = rbduckdb_vector_value_at(value_vector, value_logical_type, i);
594
+ static VALUE convert_map_entries(VALUE varg) {
595
+ struct map_entries_arg *arg = (struct map_entries_arg *)varg;
596
+ VALUE hash = rb_hash_new();
597
+ VALUE key;
598
+ VALUE value;
599
+
600
+ for (idx_t i = arg->bgn; i < arg->end; ++i) {
601
+ key = rbduckdb_vector_value_at(arg->key_vector, arg->key_type, i);
602
+ value = rbduckdb_vector_value_at(arg->value_vector, arg->value_type, i);
563
603
  rb_hash_aset(hash, key, value);
564
604
  }
565
-
566
- duckdb_destroy_logical_type(&key_logical_type);
567
- duckdb_destroy_logical_type(&value_logical_type);
568
605
  return hash;
569
606
  }
570
607
 
608
+ static VALUE destroy_map_types(VALUE varg) {
609
+ struct map_entries_arg *arg = (struct map_entries_arg *)varg;
610
+
611
+ duckdb_destroy_logical_type(&(arg->key_type));
612
+ duckdb_destroy_logical_type(&(arg->value_type));
613
+ return Qnil;
614
+ }
615
+
571
616
  static VALUE vector_struct(duckdb_logical_type ty, duckdb_vector vector, idx_t row_idx) {
617
+ struct struct_fields_arg arg = {ty, vector, row_idx, NULL};
618
+
619
+ return rb_ensure(convert_struct_fields, (VALUE)&arg, destroy_struct_child_type, (VALUE)&arg);
620
+ }
621
+
622
+ static VALUE convert_struct_fields(VALUE varg) {
623
+ struct struct_fields_arg *arg = (struct struct_fields_arg *)varg;
572
624
  VALUE hash = rb_hash_new();
573
625
  VALUE value = Qnil;
574
626
  VALUE key = Qnil;
575
627
  duckdb_vector child;
576
- duckdb_logical_type child_type;
577
628
  char *p;
578
629
 
579
- idx_t child_count = duckdb_struct_type_child_count(ty);
630
+ idx_t child_count = duckdb_struct_type_child_count(arg->ty);
580
631
 
581
632
  for (idx_t i = 0; i < child_count; ++i) {
582
- p = duckdb_struct_type_child_name(ty, i);
633
+ p = duckdb_struct_type_child_name(arg->ty, i);
583
634
  if (p) {
584
635
  key = ID2SYM(rb_intern_const(p));
585
- child = duckdb_struct_vector_get_child(vector, i);
586
- child_type = duckdb_struct_type_child_type(ty, i);
587
- value = rbduckdb_vector_value_at(child, child_type, row_idx);
588
- rb_hash_aset(hash, key, value);
589
- duckdb_destroy_logical_type(&child_type);
590
636
  duckdb_free(p);
637
+ child = duckdb_struct_vector_get_child(arg->vector, i);
638
+ arg->child_type = duckdb_struct_type_child_type(arg->ty, i);
639
+ value = rbduckdb_vector_value_at(child, arg->child_type, arg->row_idx);
640
+ duckdb_destroy_logical_type(&(arg->child_type));
641
+ rb_hash_aset(hash, key, value);
591
642
  }
592
643
  }
593
644
 
594
645
  return hash;
595
646
  }
596
647
 
648
+ static VALUE destroy_struct_child_type(VALUE varg) {
649
+ struct struct_fields_arg *arg = (struct struct_fields_arg *)varg;
650
+
651
+ duckdb_destroy_logical_type(&(arg->child_type));
652
+ return Qnil;
653
+ }
654
+
597
655
  static VALUE vector_union(duckdb_logical_type ty, duckdb_vector vector, void* vector_data, idx_t row_idx){
598
- VALUE value = Qnil;
599
656
  duckdb_vector type_vector = duckdb_struct_vector_get_child(vector, 0);
600
657
  void *data = duckdb_vector_get_data(type_vector);
601
658
  uint8_t index = ((int8_t *)data)[row_idx];
602
659
 
603
- duckdb_logical_type child_type = duckdb_union_type_member_type(ty, index);
660
+ duckdb_vector member_vector = duckdb_struct_vector_get_child(vector, index + 1);
661
+ VALUE values = vector_values_with_owned_type(member_vector, duckdb_union_type_member_type(ty, index), row_idx, row_idx + 1);
604
662
 
605
- duckdb_vector vector_value = duckdb_struct_vector_get_child(vector, index + 1);
606
- value = rbduckdb_vector_value_at(vector_value, child_type, row_idx);
607
- duckdb_destroy_logical_type(&child_type);
608
- return value;
663
+ return rb_ary_entry(values, 0);
664
+ }
665
+
666
+ static VALUE convert_values_in_range(VALUE varg) {
667
+ struct owned_type_arg *arg = (struct owned_type_arg *)varg;
668
+ VALUE ary = rb_ary_new2(arg->end - arg->bgn);
669
+
670
+ for (idx_t i = arg->bgn; i < arg->end; ++i) {
671
+ rb_ary_store(ary, i - arg->bgn, rbduckdb_vector_value_at(arg->vector, arg->type, i));
672
+ }
673
+ return ary;
674
+ }
675
+
676
+ static VALUE destroy_owned_type(VALUE varg) {
677
+ struct owned_type_arg *arg = (struct owned_type_arg *)varg;
678
+
679
+ duckdb_destroy_logical_type(&(arg->type));
680
+ return Qnil;
681
+ }
682
+
683
+ static VALUE vector_values_with_owned_type(duckdb_vector vector, duckdb_logical_type type, idx_t bgn, idx_t end) {
684
+ struct owned_type_arg arg = {vector, type, bgn, end};
685
+
686
+ return rb_ensure(convert_values_in_range, (VALUE)&arg, destroy_owned_type, (VALUE)&arg);
609
687
  }
610
688
 
611
689
  static VALUE str_concat_byte(VALUE str, unsigned char byte, int offset) {
@@ -642,16 +720,17 @@ static VALUE vector_bit(void* vector_data, idx_t row_idx) {
642
720
  VALUE str = Qnil;
643
721
 
644
722
  if(duckdb_string_is_inlined(s)) {
645
- length = s.value.inlined.length - 1;
646
- p = &s.value.inlined.inlined[1];
647
- offset = s.value.inlined.inlined[0];
648
- str = bytes_to_string(p, length, offset);
723
+ length = s.value.inlined.length;
724
+ p = s.value.inlined.inlined;
649
725
  } else {
650
- length = s.value.pointer.length - 1;
651
- p = &s.value.pointer.ptr[1];
652
- offset = s.value.pointer.ptr[0];
653
- str = bytes_to_string(p, length, offset);
726
+ length = s.value.pointer.length;
727
+ p = s.value.pointer.ptr;
728
+ }
729
+ if (length < 2 || (unsigned char)p[0] > 7) {
730
+ rb_raise(eDuckDBError, "invalid BIT value");
654
731
  }
732
+ offset = (unsigned char)p[0];
733
+ str = bytes_to_string(p + 1, length - 1, offset);
655
734
  return str;
656
735
  }
657
736
 
@@ -697,18 +776,6 @@ static VALUE result_arrow_c_stream(VALUE oDuckDBResult) {
697
776
  return stream;
698
777
  }
699
778
 
700
- static VALUE vector_value(duckdb_vector vector, idx_t row_idx) {
701
- duckdb_logical_type ty;
702
- VALUE obj = Qnil;
703
-
704
- ty = duckdb_vector_get_column_type(vector);
705
-
706
- obj = rbduckdb_vector_value_at(vector, ty, row_idx);
707
-
708
- duckdb_destroy_logical_type(&ty);
709
- return obj;
710
- }
711
-
712
779
  void rbduckdb_init_result(void) {
713
780
  #if 0
714
781
  VALUE mDuckDB = rb_define_module("DuckDB");
data/ext/duckdb/result.h CHANGED
@@ -12,6 +12,8 @@ struct _rubyDuckDBResult {
12
12
  duckdb_result result;
13
13
  bool arrow_exported;
14
14
  rb_atomic_t refcount;
15
+ duckdb_logical_type *column_types;
16
+ idx_t column_types_count;
15
17
  };
16
18
 
17
19
  typedef struct _rubyDuckDBResult rubyDuckDBResult;
@@ -76,18 +76,13 @@ static VALUE table_function_bind_info_get_parameter(VALUE self, VALUE index) {
76
76
  rubyDuckDBBindInfo *ctx;
77
77
  idx_t idx;
78
78
  duckdb_value param_value;
79
- VALUE result;
80
79
 
81
80
  TypedData_Get_Struct(self, rubyDuckDBBindInfo, &bind_info_data_type, ctx);
82
81
 
83
82
  idx = NUM2ULL(index);
84
83
  param_value = duckdb_bind_get_parameter(ctx->bind_info, idx);
85
84
 
86
- result = rbduckdb_duckdb_value_to_ruby(param_value);
87
-
88
- duckdb_destroy_value(&param_value);
89
-
90
- return result;
85
+ return rbduckdb_duckdb_value_to_ruby_and_destroy(param_value);
91
86
  }
92
87
 
93
88
  /*
@@ -102,7 +97,6 @@ static VALUE table_function_bind_info_get_named_parameter(VALUE self, VALUE name
102
97
  rubyDuckDBBindInfo *ctx;
103
98
  const char *param_name;
104
99
  duckdb_value param_value;
105
- VALUE result;
106
100
 
107
101
  TypedData_Get_Struct(self, rubyDuckDBBindInfo, &bind_info_data_type, ctx);
108
102
 
@@ -114,11 +108,7 @@ static VALUE table_function_bind_info_get_named_parameter(VALUE self, VALUE name
114
108
  return Qnil;
115
109
  }
116
110
 
117
- result = rbduckdb_duckdb_value_to_ruby(param_value);
118
-
119
- duckdb_destroy_value(&param_value);
120
-
121
- return result;
111
+ return rbduckdb_duckdb_value_to_ruby_and_destroy(param_value);
122
112
  }
123
113
 
124
114
  /*
data/ext/duckdb/value.c CHANGED
@@ -1,5 +1,10 @@
1
1
  #include "ruby-duckdb.h"
2
2
 
3
+ struct vector_ref_arg {
4
+ duckdb_vector vec;
5
+ duckdb_logical_type logical_type;
6
+ };
7
+
3
8
  VALUE cDuckDBValue;
4
9
 
5
10
  static void deallocate(void *);
@@ -37,6 +42,10 @@ static VALUE value_s__create_bit(VALUE klass, VALUE data);
37
42
  static VALUE value_s__create_bignum(VALUE klass, VALUE data, VALUE is_negative);
38
43
  static VALUE value_s_create_null(VALUE klass);
39
44
  static VALUE to_ruby_via_vector(duckdb_logical_type logical_type, duckdb_value val);
45
+ static VALUE convert_referenced_value(VALUE varg);
46
+ static VALUE destroy_referencing_vector(VALUE varg);
47
+ static VALUE convert_owned_value(VALUE varg);
48
+ static VALUE destroy_owned_value(VALUE varg);
40
49
  static idx_t marshal_values(VALUE ary, duckdb_value **out, volatile VALUE *guard);
41
50
  static VALUE value_s__create_list(VALUE klass, VALUE ltype, VALUE values);
42
51
  static VALUE value_s__create_array(VALUE klass, VALUE ltype, VALUE values);
@@ -557,19 +566,43 @@ rubyDuckDBValue *rbduckdb_get_struct_value(VALUE obj) {
557
566
  * the C API has no direct duckdb_get_* accessors for.
558
567
  */
559
568
  static VALUE to_ruby_via_vector(duckdb_logical_type logical_type, duckdb_value val) {
560
- duckdb_vector vec = duckdb_create_vector(logical_type, 1);
561
- VALUE result;
569
+ struct vector_ref_arg arg;
562
570
 
563
- duckdb_vector_reference_value(vec, val);
564
- result = rbduckdb_vector_value_at(vec, logical_type, 0);
565
- duckdb_destroy_vector(&vec);
566
- return result;
571
+ arg.vec = duckdb_create_vector(logical_type, 1);
572
+ arg.logical_type = logical_type;
573
+ duckdb_vector_reference_value(arg.vec, val);
574
+ return rb_ensure(convert_referenced_value, (VALUE)&arg, destroy_referencing_vector, (VALUE)&arg);
575
+ }
576
+
577
+ static VALUE convert_referenced_value(VALUE varg) {
578
+ struct vector_ref_arg *arg = (struct vector_ref_arg *)varg;
579
+
580
+ return rbduckdb_vector_value_at(arg->vec, arg->logical_type, 0);
581
+ }
582
+
583
+ static VALUE destroy_referencing_vector(VALUE varg) {
584
+ struct vector_ref_arg *arg = (struct vector_ref_arg *)varg;
585
+
586
+ duckdb_destroy_vector(&(arg->vec));
587
+ return Qnil;
588
+ }
589
+
590
+ static VALUE convert_owned_value(VALUE varg) {
591
+ return rbduckdb_duckdb_value_to_ruby(*(duckdb_value *)varg);
592
+ }
593
+
594
+ static VALUE destroy_owned_value(VALUE varg) {
595
+ duckdb_destroy_value((duckdb_value *)varg);
596
+ return Qnil;
597
+ }
598
+
599
+ VALUE rbduckdb_duckdb_value_to_ruby_and_destroy(duckdb_value val) {
600
+ return rb_ensure(convert_owned_value, (VALUE)&val, destroy_owned_value, (VALUE)&val);
567
601
  }
568
602
 
569
603
  VALUE rbduckdb_duckdb_value_to_ruby(duckdb_value val) {
570
604
  duckdb_logical_type logical_type;
571
605
  duckdb_type type_id;
572
- duckdb_value child_value;
573
606
  VALUE result;
574
607
  VALUE elem;
575
608
  VALUE key;
@@ -676,9 +709,7 @@ VALUE rbduckdb_duckdb_value_to_ruby(duckdb_value val) {
676
709
  size = duckdb_get_list_size(val);
677
710
  result = rb_ary_new_capa(size);
678
711
  for (i = 0; i < size; i++) {
679
- child_value = duckdb_get_list_child(val, i);
680
- elem = rbduckdb_duckdb_value_to_ruby(child_value);
681
- duckdb_destroy_value(&child_value);
712
+ elem = rbduckdb_duckdb_value_to_ruby_and_destroy(duckdb_get_list_child(val, i));
682
713
  rb_ary_push(result, elem);
683
714
  }
684
715
  break;
@@ -687,23 +718,18 @@ VALUE rbduckdb_duckdb_value_to_ruby(duckdb_value val) {
687
718
  result = rb_hash_new();
688
719
  for (i = 0; i < size; i++) {
689
720
  str = duckdb_struct_type_child_name(logical_type, i);
690
- child_value = duckdb_get_struct_child(val, i);
691
- elem = rbduckdb_duckdb_value_to_ruby(child_value);
692
- duckdb_destroy_value(&child_value);
693
- rb_hash_aset(result, ID2SYM(rb_intern(str)), elem);
721
+ key = ID2SYM(rb_intern(str));
694
722
  duckdb_free(str);
723
+ elem = rbduckdb_duckdb_value_to_ruby_and_destroy(duckdb_get_struct_child(val, i));
724
+ rb_hash_aset(result, key, elem);
695
725
  }
696
726
  break;
697
727
  case DUCKDB_TYPE_MAP:
698
728
  size = duckdb_get_map_size(val);
699
729
  result = rb_hash_new();
700
730
  for (i = 0; i < size; i++) {
701
- child_value = duckdb_get_map_key(val, i);
702
- key = rbduckdb_duckdb_value_to_ruby(child_value);
703
- duckdb_destroy_value(&child_value);
704
- child_value = duckdb_get_map_value(val, i);
705
- elem = rbduckdb_duckdb_value_to_ruby(child_value);
706
- duckdb_destroy_value(&child_value);
731
+ key = rbduckdb_duckdb_value_to_ruby_and_destroy(duckdb_get_map_key(val, i));
732
+ elem = rbduckdb_duckdb_value_to_ruby_and_destroy(duckdb_get_map_value(val, i));
707
733
  rb_hash_aset(result, key, elem);
708
734
  }
709
735
  break;
data/ext/duckdb/value.h CHANGED
@@ -10,6 +10,7 @@ typedef struct _rubyDuckDBValue rubyDuckDBValue;
10
10
  void rbduckdb_init_value(void);
11
11
  VALUE rbduckdb_value_new(duckdb_value value);
12
12
  VALUE rbduckdb_duckdb_value_to_ruby(duckdb_value val);
13
+ VALUE rbduckdb_duckdb_value_to_ruby_and_destroy(duckdb_value val);
13
14
  rubyDuckDBValue *rbduckdb_get_struct_value(VALUE obj);
14
15
 
15
16
  #endif
@@ -7,10 +7,9 @@ require_relative 'converter'
7
7
  module DuckDB
8
8
  # The DuckDB::Appender encapsulates DuckDB Appender.
9
9
  #
10
- # The +table+ argument (2nd positional argument) supports dot-notation and quoting:
11
- #
12
- # - <tt>'schema.table'</tt> — interpreted as schema-qualified (deprecated; use +schema:+ instead)
13
- # - <tt>'"schema.table"'</tt> or <tt>"'schema.table'"</tt> — treated as a literal table name containing a dot
10
+ # The +table+ argument (2nd positional argument) is used as a literal table name,
11
+ # even if it contains a dot. Use the +schema:+ and +catalog:+ keywords to qualify the table.
12
+ # Surrounding double or single quotes (<tt>'"a.b"'</tt> or <tt>"'a.b'"</tt>) are stripped.
14
13
  #
15
14
  # require 'duckdb'
16
15
  # db = DuckDB::Database.open
@@ -26,13 +25,10 @@ module DuckDB
26
25
  alias from_query create_query
27
26
  end
28
27
 
29
- def initialize(con, table_or_schema, table = nil, schema: nil, catalog: nil)
30
- if table
31
- warn_deprecated_3arg
32
- _initialize(con, table_or_schema, table)
33
- else
34
- initialize_with_parsed_table(con, table_or_schema, schema: schema, catalog: catalog)
35
- end
28
+ def initialize(con, table, schema: nil, catalog: nil)
29
+ raise DuckDB::Error, '2nd argument must be table name.' if table.nil?
30
+
31
+ initialize_with_parsed_table(con, table, schema: schema, catalog: catalog)
36
32
  end
37
33
 
38
34
  # :call-seq:
@@ -801,14 +797,6 @@ module DuckDB
801
797
 
802
798
  private
803
799
 
804
- def warn_deprecated_3arg # :nodoc:
805
- warn(
806
- 'DuckDB::Appender.new(con, schema, table) is deprecated. ' \
807
- 'Use DuckDB::Appender.new(con, table, schema: schema) instead.',
808
- category: :deprecated
809
- )
810
- end
811
-
812
800
  def initialize_with_parsed_table(con, table_name, schema:, catalog:) # :nodoc:
813
801
  table_name, schema, catalog = parse_table_name(table_name, schema, catalog)
814
802
  if catalog
data/lib/duckdb/config.rb CHANGED
@@ -6,7 +6,7 @@ module DuckDB
6
6
  # require 'duckdb'
7
7
  # config = DuckDB::Config.new
8
8
  # config['default_order'] = 'DESC'
9
- # db = DuckDB::Database.open(nil, config)
9
+ # db = DuckDB::Database.open(config: config)
10
10
  # con = db.connect
11
11
  # con.query('CREATE TABLE numbers (number INTEGER)')
12
12
  # con.query('INSERT INTO numbers VALUES (2), (1), (4), (3)')
@@ -49,7 +49,7 @@ module DuckDB
49
49
  # # config.set_config('default_order', 'DESC')
50
50
  # config['default_order'] = 'DESC'
51
51
  #
52
- # db = DuckDB::Database.open(nil, config)
52
+ # db = DuckDB::Database.open(config: config)
53
53
  # con = db.connect
54
54
  # con.query('CREATE TABLE numbers (number INTEGER)')
55
55
  # con.query('INSERT INTO numbers VALUES (2), (1), (4), (3)')
@@ -8,8 +8,6 @@ module DuckDB
8
8
  # con = db.connect
9
9
  # con.query(sql)
10
10
  class Connection
11
- include DuckDB::TableNameParser
12
-
13
11
  # executes sql with args.
14
12
  # The first argument sql must be SQL string.
15
13
  # The rest arguments are parameters of SQL string.
@@ -84,11 +82,27 @@ module DuckDB
84
82
  # pending_result.execute_task while pending_result.state == :not_ready
85
83
  # result = pending_result.execute_pending
86
84
  # result.each.first
85
+ #
86
+ # If a block is given, the block is executed with the DuckDB::PendingResult
87
+ # object, the block's value is returned, and the object is destroyed
88
+ # after the block. The yielded PendingResult must not be used after the block.
89
+ #
90
+ # con.async_query(sql, name: 'Dave', email: 'dave@example.com') do |pending|
91
+ # pending.execute_task while pending.state == :not_ready
92
+ # pending.execute_pending.each.first
93
+ # end
87
94
  def async_query(sql, *args, **kwargs)
88
- prepare(sql) do |stmt|
95
+ pending = prepare(sql) do |stmt|
89
96
  stmt.bind_args(*args, **kwargs)
90
97
  stmt.pending_prepared
91
98
  end
99
+ return pending unless block_given?
100
+
101
+ begin
102
+ yield pending
103
+ ensure
104
+ pending.destroy
105
+ end
92
106
  end
93
107
 
94
108
  # connects DuckDB database
@@ -140,8 +154,8 @@ module DuckDB
140
154
  #
141
155
  # Raises DuckDB::Error if the table (or schema/catalog) does not exist.
142
156
  #
143
- # Table name parsing (quoting, dot-notation) is handled by DuckDB::Appender.new.
144
- # See DuckDB::Appender.new for details on quoting and dot-notation.
157
+ # Table name parsing (quoting) is handled by DuckDB::Appender.new.
158
+ # A table name containing a dot is used literally; use +schema:+ to qualify the table.
145
159
  #
146
160
  # require 'duckdb'
147
161
  # db = DuckDB::Database.open
@@ -164,7 +178,6 @@ module DuckDB
164
178
  # appender.append_row(4, 'Dave')
165
179
  # appender.close
166
180
  def appender(table, schema: nil, catalog: nil, &)
167
- table, schema, catalog = parse_connection_appender_table(table, schema, catalog)
168
181
  run_appender_block(Appender.new(self, table, schema: schema, catalog: catalog), &)
169
182
  end
170
183
 
@@ -466,17 +479,6 @@ module DuckDB
466
479
  appender.close
467
480
  end
468
481
 
469
- # Silently pre-parses dot-notation so Appender.new receives clean values
470
- # and does not emit a misleading "DuckDB::Appender.new" warning.
471
- # con.appender('a.b') has always split on dot — no warning needed.
472
- # Quoted table names pass through unchanged for Appender.new to handle.
473
- def parse_connection_appender_table(table, schema, catalog)
474
- return [table, schema, catalog] if quoted_table_name?(table)
475
- return [table, schema, catalog] unless table.include?('.')
476
-
477
- dot_notation_split(table, schema, catalog)
478
- end
479
-
480
482
  alias execute query
481
483
  alias async_execute async_query
482
484
  alias open connect
@@ -50,9 +50,7 @@ module DuckDB
50
50
  # con = db.connect
51
51
  # con.query('CREATE TABLE users (id INTEGER, name VARCHAR(30))')
52
52
  # end
53
- def open(path = :memory, *args, config: nil)
54
- path, config = _handle_deprecated_open_args(path, args, config)
55
-
53
+ def open(path = :memory, config: nil)
56
54
  db = new(path, config: config)
57
55
  return db unless block_given?
58
56
 
@@ -62,21 +60,6 @@ module DuckDB
62
60
  db.close
63
61
  end
64
62
  end
65
-
66
- private
67
-
68
- def _handle_deprecated_open_args(path, args, config)
69
- unless args.empty?
70
- raise TypeError, "expected DuckDB::Config, got #{args.first.class}" unless args.first.is_a?(DuckDB::Config)
71
-
72
- warn 'DuckDB::Database.open(path, config) is deprecated. ' \
73
- 'Use DuckDB::Database.open(path, config: config) instead.'
74
- config = args.first
75
- end
76
-
77
- path = :memory if path.nil?
78
- [path, config]
79
- end
80
63
  end
81
64
 
82
65
  private
@@ -18,6 +18,13 @@ module DuckDB
18
18
  # pending_result.execute_task
19
19
  # end
20
20
  # result = pending_result.execute_pending
21
+ #
22
+ # Call +destroy+ to release a pending result without fetching it. On
23
+ # DuckDB >= 2.0 an unfetched pending result blocks its connection until it
24
+ # is destroyed. After +destroy+, +state+ (and +execution_finished?+) report
25
+ # :error, and +execute_task+, +execute_pending+ and +execute_check_state+
26
+ # raise DuckDB::Error. A DuckDB::Result already returned by
27
+ # +execute_pending+ stays readable.
21
28
  class PendingResult
22
29
  STATES = %i[ready not_ready error no_tasks].freeze # :nodoc:
23
30
 
data/lib/duckdb/result.rb CHANGED
@@ -95,34 +95,5 @@ module DuckDB
95
95
  end
96
96
  values
97
97
  end
98
-
99
- private
100
-
101
- def _enum_dictionary_size(idx)
102
- warn(":_enum_dictionary_size is deprecated. use columns[#{idx}].logical_type.dictionary_size instead.")
103
-
104
- raise ArgumentError, "Invalid index: #{idx}" if idx.negative?
105
-
106
- columns[idx]&.logical_type&.dictionary_size
107
- end
108
-
109
- def _enum_dictionary_value(col_index, idx)
110
- warn(":_enum_dictionary_value is deprecated.\
111
- use columns[#{col_index}].logical_type.dictionary_value_at(#{idx}) instead.")
112
-
113
- raise ArgumentError, "Invalid index: #{col_index}" if col_index.negative?
114
-
115
- lt = columns[col_index]&.logical_type
116
-
117
- raise DuckDB::Error, "Column[#{col_index}] type is not enum" if lt&.type != :enum
118
-
119
- lt.dictionary_value_at(idx)
120
- end
121
-
122
- def _column_type(idx)
123
- warn(":_column_type is deprecated. use columns[#{idx}].send(:_type) instead.")
124
-
125
- columns[idx].send(:_type)
126
- end
127
98
  end
128
99
  end
@@ -28,9 +28,9 @@ module DuckDB
28
28
  # +con+ must be a DuckDB::Connection. +table+ is the table name (String).
29
29
  # Optionally pass +schema:+ and/or +catalog:+ to qualify the table.
30
30
  #
31
- # The +table+ argument supports dot-notation and quoting:
32
- # - <tt>'schema.table'</tt> — interpreted as schema-qualified (deprecated; use +schema:+ instead)
33
- # - <tt>'"a.b"'</tt> or <tt>"'a.b'"</tt> — treated as a literal table name containing a dot
31
+ # The +table+ argument is used as a literal table name, even if it contains a dot.
32
+ # Use +schema:+ and +catalog:+ to qualify the table. Surrounding double or single
33
+ # quotes (<tt>'"a.b"'</tt> or <tt>"'a.b'"</tt>) are stripped.
34
34
  #
35
35
  # Raises DuckDB::Error if the connection is invalid, the table name is nil,
36
36
  # or the table (or schema/catalog) does not exist.
@@ -4,23 +4,17 @@ module DuckDB
4
4
  # DuckDB::TableNameParser provides shared table name parsing for classes that
5
5
  # accept a table name argument, such as DuckDB::Appender and DuckDB::TableDescription.
6
6
  #
7
- # It handles:
8
- # - Dot-notation: <tt>'schema.table'</tt> is split into schema and table (deprecated).
9
- # - Quoting: <tt>'"a.b"'</tt> or <tt>"'a.b'"</tt> strips the quotes and treats the name literally.
7
+ # It handles quoting: <tt>'"a.b"'</tt> or <tt>"'a.b'"</tt> strips the quotes and treats the name literally.
8
+ # A name containing a dot (e.g. <tt>'a.b'</tt>) is used as-is, as a literal table name; it is
9
+ # never split into schema and table. Use the +schema:+ and +catalog:+ keyword arguments to qualify a table.
10
10
  module TableNameParser
11
11
  private
12
12
 
13
- # Parses +table+, +schema+, and +catalog+, handling quoting and dot-notation.
13
+ # Parses +table+, +schema+, and +catalog+, handling quoting.
14
14
  # Returns <tt>[table, schema, catalog]</tt>.
15
15
  def parse_table_name(table, schema, catalog)
16
- if quoted_table_name?(table)
17
- [unquote_table_name(table), schema, catalog]
18
- elsif table.include?('.')
19
- warn_dot_notation_deprecated(table)
20
- dot_notation_split(table, schema, catalog)
21
- else
22
- [table, schema, catalog]
23
- end
16
+ table = unquote_table_name(table) if quoted_table_name?(table)
17
+ [table, schema, catalog]
24
18
  end
25
19
 
26
20
  def quoted_table_name?(name) # :nodoc:
@@ -30,29 +24,5 @@ module DuckDB
30
24
  def unquote_table_name(name) # :nodoc:
31
25
  name[1..-2]
32
26
  end
33
-
34
- # Splits a dot-notation string into [table, schema, catalog].
35
- # Explicit keyword args take precedence over dot-notation parts.
36
- def dot_notation_split(table, schema, catalog) # :nodoc:
37
- parts = table.split('.')
38
- raise ArgumentError, "Too many dot-separated segments in '#{table}'" if parts.length > 3
39
-
40
- case parts.length
41
- when 2 then [parts[1], schema || parts[0], catalog]
42
- when 3 then [parts[2], schema || parts[1], catalog || parts[0]]
43
- else raise ArgumentError, "Unexpected segment count in '#{table}'"
44
- end
45
- end
46
-
47
- def warn_dot_notation_deprecated(table) # :nodoc:
48
- class_name = self.class.name
49
- warn(
50
- "Passing dot-notation '#{table}' to #{class_name}.new is deprecated. " \
51
- "If '#{table}' is a schema-qualified table, use #{class_name}.new(con, table, schema: schema) instead. " \
52
- "If '#{table}' is a literal table name containing a dot, " \
53
- "use #{class_name}.new(con, '\"#{table}\"') instead.",
54
- category: :deprecated
55
- )
56
- end
57
27
  end
58
28
  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.5.6.1'
6
+ VERSION = '2.0.0.0'
7
7
  end
data/lib/duckdb.rb CHANGED
@@ -59,22 +59,8 @@ module DuckDB
59
59
 
60
60
  @default_timezone = value
61
61
  end
62
-
63
- def const_missing(name)
64
- deprecated = DEPRECATED_CONSTANTS[name]
65
- return super unless deprecated
66
-
67
- warn "DuckDB::#{name} is deprecated. Use #{deprecated} instead.", uplevel: 1
68
- const_set(name, deprecated.split('::').reduce(Object) { |mod, part| mod.const_get(part) })
69
- end
70
62
  end
71
63
 
72
- DEPRECATED_CONSTANTS = {
73
- BindInfo: 'DuckDB::TableFunction::BindInfo',
74
- InitInfo: 'DuckDB::TableFunction::InitInfo',
75
- FunctionInfo: 'DuckDB::TableFunction::FunctionInfo'
76
- }.freeze
77
-
78
64
  # Default to local time to preserve existing behavior unless explicitly
79
65
  # configured otherwise.
80
66
  self.default_timezone = :local
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: duckdb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.6.1
4
+ version: 2.0.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Masaki Suketa