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 +4 -4
- data/CHANGELOG.md +14 -0
- data/README.md +1 -1
- data/ext/duckdb/arrow_array_stream.c +10 -2
- data/ext/duckdb/expression.c +1 -4
- data/ext/duckdb/pending_result.c +53 -14
- data/ext/duckdb/result.c +150 -83
- data/ext/duckdb/result.h +2 -0
- data/ext/duckdb/table_function_bind_info.c +2 -12
- data/ext/duckdb/value.c +46 -20
- data/ext/duckdb/value.h +1 -0
- data/lib/duckdb/appender.rb +7 -19
- data/lib/duckdb/config.rb +2 -2
- data/lib/duckdb/connection.rb +19 -17
- data/lib/duckdb/database.rb +1 -18
- data/lib/duckdb/pending_result.rb +7 -0
- data/lib/duckdb/result.rb +0 -29
- data/lib/duckdb/table_description.rb +3 -3
- data/lib/duckdb/table_name_parser.rb +6 -36
- data/lib/duckdb/version.rb +1 -1
- data/lib/duckdb.rb +0 -14
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ee2e890f808c2ff97b099f79c12f17a8fe856253b13a3ee2378bbb21306e3587
|
|
4
|
+
data.tar.gz: a7081554ee0ff1ebb22d688f9aafab9b434c93cb080f2abcc7ffe1e9ae3f70d1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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(
|
|
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
|
-
/*
|
|
66
|
-
*
|
|
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");
|
data/ext/duckdb/expression.c
CHANGED
|
@@ -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
|
-
|
|
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) {
|
data/ext/duckdb/pending_result.c
CHANGED
|
@@ -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(
|
|
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(
|
|
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 =
|
|
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
|
|
142
|
+
VALUE result;
|
|
106
143
|
|
|
107
|
-
|
|
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 =
|
|
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,
|
|
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
|
|
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 =
|
|
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
|
-
|
|
381
|
-
duckdb_vector child = duckdb_array_vector_get_child(vector);
|
|
438
|
+
duckdb_vector child = duckdb_array_vector_get_child(vector);
|
|
382
439
|
|
|
383
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
554
|
-
|
|
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
|
-
|
|
557
|
-
|
|
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
|
-
|
|
561
|
-
|
|
562
|
-
|
|
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
|
-
|
|
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
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
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
|
|
646
|
-
p =
|
|
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
|
|
651
|
-
p =
|
|
652
|
-
|
|
653
|
-
|
|
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
|
@@ -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
|
-
|
|
87
|
-
|
|
88
|
-
duckdb_destroy_value(¶m_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
|
-
|
|
118
|
-
|
|
119
|
-
duckdb_destroy_value(¶m_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
|
-
|
|
561
|
-
VALUE result;
|
|
569
|
+
struct vector_ref_arg arg;
|
|
562
570
|
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
702
|
-
|
|
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
|
data/lib/duckdb/appender.rb
CHANGED
|
@@ -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)
|
|
11
|
-
#
|
|
12
|
-
#
|
|
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,
|
|
30
|
-
if table
|
|
31
|
-
|
|
32
|
-
|
|
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(
|
|
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(
|
|
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)')
|
data/lib/duckdb/connection.rb
CHANGED
|
@@ -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
|
|
144
|
-
#
|
|
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
|
data/lib/duckdb/database.rb
CHANGED
|
@@ -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,
|
|
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
|
|
32
|
-
#
|
|
33
|
-
#
|
|
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
|
-
#
|
|
9
|
-
#
|
|
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
|
|
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
|
-
|
|
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
|
data/lib/duckdb/version.rb
CHANGED
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
|