duckdb 1.5.5.1 → 1.5.6.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: caab56325522ea94df9e9cdc12c54b20f2335c2a8d93df0d1fce965643e78cf0
4
- data.tar.gz: a572bfa7d8f4ff0dfac85c34ace463b272446e1de7f3fad6bd790de055f25787
3
+ metadata.gz: cd491bbe1e80d7e2f20455e768b0331c91cbd015e570ccc023497c74217f86a0
4
+ data.tar.gz: b3c9d5bd5b38a7f72fa7d8c00be7bd20d61505c234efc702cd365b040dcce0dd
5
5
  SHA512:
6
- metadata.gz: c150cfd69f78f5106596211b5d09432ef60678942c9fdfca4a634eff25052ec3704381ab85bbf9ae9e82dc5b177f1e226c25b95afa33e57a225e9bf4c284efad
7
- data.tar.gz: 163925f3b77e0bf445b23d37a0e3e45d0aab4d981c58b73d196af7157964c8e33f20147a8d062a13d68becd71f57a4e5906fc9d1fbbbd716c1b8319aedb7ce58
6
+ metadata.gz: acc1eb730e31250b6d8cc281923dd332b2141a4fb2ced34821126928b0c94757a6b1c5f49e29c39c01e899b196e4fe857ea529d62bd8fd88015c2dad806fbb8f
7
+ data.tar.gz: a18fc12e5e6252c32129d8ab005f75b29834711fc5790e009a8dc45fabcb9c9c9e88f1c16213a16bf346da906087ec9f57dea7e6162e51554d622a20575cdf55
data/CHANGELOG.md CHANGED
@@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  # Unreleased
6
6
 
7
+ # 1.5.6.0 - 2026-10-11
8
+ - bump up DuckDB 1.5.6 on CI.
9
+
7
10
  # 1.5.5.1 - 2026-08-29
8
11
  - fix `DuckDB::Database#close` deadlocking when DuckDB tears down a pipeline that still owes a UDF callback, as a failed aggregate window query does on DuckDB 1.4.x. `duckdb_close` joins DuckDB's worker threads, and a worker running a callback waits for the executor thread, which waits for the GVL the closing thread was holding. The GVL is now released for the duration of `duckdb_close`.
9
12
  - fix a segfault when an aggregate UDF was used as a window function over a constant frame, such as `OVER ()` or `OVER (PARTITION BY x)` (issue #1446). DuckDB's `CAPIAggregateUpdate` does not flatten the state vector the way its combine and finalize counterparts do, so it hands the update callback a single state while reporting the partition's full row count; reading one state per row then ran off the end of the allocation. Such a chunk is now recognised and every row aggregated into the one state DuckDB supplied.
@@ -9,7 +9,7 @@ VALUE cDuckDBAggregateFunction;
9
9
  * VALUE returned from the user's init_proc and later passed to
10
10
  * finalize_proc.
11
11
  *
12
- * Protected from GC via rb_gc_register_mark_object on init.
12
+ * Protected from GC via rb_global_variable on init.
13
13
  */
14
14
  static VALUE g_aggregate_state_registry;
15
15
 
@@ -37,7 +37,7 @@ static unsigned long long g_next_state_id = 0;
37
37
  * reach state_id is the crash; membership here is the only test that does not.
38
38
  * See issue #1446.
39
39
  *
40
- * Protected from GC via rb_gc_register_mark_object on init.
40
+ * Protected from GC via rb_global_variable on init.
41
41
  */
42
42
  static VALUE g_aggregate_state_addresses;
43
43
 
@@ -69,7 +69,7 @@ static VALUE aggregate_function__set_special_handling(VALUE self);
69
69
  static const rb_data_type_t aggregate_function_data_type = {
70
70
  "DuckDB/AggregateFunction",
71
71
  {mark, deallocate, memsize, compact},
72
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
72
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
73
73
  };
74
74
 
75
75
  static void mark(void *ctx) {
@@ -895,7 +895,7 @@ static VALUE aggregate_function__set_init(VALUE self) {
895
895
  }
896
896
 
897
897
  TypedData_Get_Struct(self, rubyDuckDBAggregateFunction, &aggregate_function_data_type, p);
898
- p->init_proc = rb_block_proc();
898
+ RB_OBJ_WRITE(self, &p->init_proc, rb_block_proc());
899
899
 
900
900
  maybe_set_functions(p);
901
901
 
@@ -911,7 +911,7 @@ static VALUE aggregate_function__set_update(VALUE self) {
911
911
  }
912
912
 
913
913
  TypedData_Get_Struct(self, rubyDuckDBAggregateFunction, &aggregate_function_data_type, p);
914
- p->update_proc = rb_block_proc();
914
+ RB_OBJ_WRITE(self, &p->update_proc, rb_block_proc());
915
915
 
916
916
  maybe_set_functions(p);
917
917
 
@@ -927,7 +927,7 @@ static VALUE aggregate_function__set_combine(VALUE self) {
927
927
  }
928
928
 
929
929
  TypedData_Get_Struct(self, rubyDuckDBAggregateFunction, &aggregate_function_data_type, p);
930
- p->combine_proc = rb_block_proc();
930
+ RB_OBJ_WRITE(self, &p->combine_proc, rb_block_proc());
931
931
 
932
932
  maybe_set_functions(p);
933
933
 
@@ -943,7 +943,7 @@ static VALUE aggregate_function__set_finalize(VALUE self) {
943
943
  }
944
944
 
945
945
  TypedData_Get_Struct(self, rubyDuckDBAggregateFunction, &aggregate_function_data_type, p);
946
- p->finalize_proc = rb_block_proc();
946
+ RB_OBJ_WRITE(self, &p->finalize_proc, rb_block_proc());
947
947
 
948
948
  maybe_set_functions(p);
949
949
 
@@ -983,8 +983,8 @@ void rbduckdb_init_aggregate_function(void) {
983
983
  rb_define_singleton_method(cDuckDBAggregateFunction, "_state_registry_size",
984
984
  aggregate_function_s__state_registry_size, 0);
985
985
 
986
+ rb_global_variable(&g_aggregate_state_registry);
986
987
  g_aggregate_state_registry = rb_hash_new();
987
- rb_gc_register_mark_object(g_aggregate_state_registry);
988
+ rb_global_variable(&g_aggregate_state_addresses);
988
989
  g_aggregate_state_addresses = rb_hash_new();
989
- rb_gc_register_mark_object(g_aggregate_state_addresses);
990
990
  }
@@ -13,12 +13,12 @@ static VALUE aggregate_function_set__add(VALUE self, VALUE aggregate_function);
13
13
  static const rb_data_type_t aggregate_function_set_data_type = {
14
14
  "DuckDB/AggregateFunctionSet",
15
15
  {mark, deallocate, memsize, compact},
16
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
16
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
17
17
  };
18
18
 
19
19
  static void mark(void *ctx) {
20
20
  rubyDuckDBAggregateFunctionSet *p = (rubyDuckDBAggregateFunctionSet *)ctx;
21
- rb_gc_mark(p->functions);
21
+ rb_gc_mark_movable(p->functions);
22
22
  }
23
23
 
24
24
  static void deallocate(void *ctx) {
@@ -35,8 +35,7 @@ static void compact(void *ctx) {
35
35
  static VALUE allocate(VALUE klass) {
36
36
  rubyDuckDBAggregateFunctionSet *ctx = xcalloc((size_t)1, sizeof(rubyDuckDBAggregateFunctionSet));
37
37
  VALUE obj = TypedData_Wrap_Struct(klass, &aggregate_function_set_data_type, ctx);
38
- ctx->functions = rb_ary_new();
39
- RB_GC_GUARD(ctx->functions);
38
+ RB_OBJ_WRITE(obj, &ctx->functions, rb_ary_new());
40
39
  return obj;
41
40
  }
42
41
 
@@ -50,10 +50,20 @@ static VALUE appender__clear_columns(VALUE self);
50
50
  static VALUE appender__close(VALUE self);
51
51
  static VALUE state_to_rbool(duckdb_state state);
52
52
 
53
+ /*
54
+ * Keeps RUBY_TYPED_FREE_IMMEDIATELY although duckdb_appender_destroy flushes
55
+ * buffered rows (blocking I/O): when an unclosed appender is collected in the
56
+ * same GC as its connection, freeing it during sweep runs before the deferred
57
+ * Connection/Database free, so its rows still reach the table. Deferring it too
58
+ * would let the connection close first and lose rows. (The appender holds no
59
+ * reference to its connection, so if the connection is collected in an earlier
60
+ * GC the flush is lost anyway.) Freeing in sweep cannot shut the database down:
61
+ * DuckDB's BaseAppender holds only a weak_ptr<ClientContext>.
62
+ */
53
63
  static const rb_data_type_t appender_data_type = {
54
64
  "DuckDB/Appender",
55
65
  {NULL, deallocate, memsize,},
56
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
66
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
57
67
  };
58
68
 
59
69
  static void deallocate(void * ctx) {
@@ -13,7 +13,7 @@ typedef struct {
13
13
  * the Ruby DuckDB::ArrowArrayStream object is gone, so this context is
14
14
  * freed only by the stream release callback, and it holds a reference on
15
15
  * the result struct (rbduckdb_result_ref) until then. The release callback
16
- * must not call any Ruby API: it can run during GC sweep (via deallocate
16
+ * must not call any Ruby API: it can run during GC finalization (via deallocate
17
17
  * of an unconsumed stream) or from a non-Ruby thread.
18
18
  */
19
19
  typedef struct {
@@ -34,10 +34,14 @@ static int stream_get_next(struct ArrowArrayStream *stream, struct ArrowArray *o
34
34
  static const char *stream_get_last_error(struct ArrowArrayStream *stream);
35
35
  static void stream_release(struct ArrowArrayStream *stream);
36
36
 
37
+ /*
38
+ * No RUBY_TYPED_FREE_IMMEDIATELY: deallocate can block, and must not run
39
+ * during GC sweep: releasing the stream may destroy a streaming result (see result.c).
40
+ */
37
41
  static const rb_data_type_t arrow_array_stream_data_type = {
38
42
  "DuckDB/ArrowArrayStream",
39
43
  {NULL, deallocate, memsize,},
40
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
44
+ 0, 0, RUBY_TYPED_WB_PROTECTED
41
45
  };
42
46
 
43
47
  static void deallocate(void *ctx) {
@@ -26,7 +26,7 @@ static VALUE connection__arrow_release(VALUE self, VALUE address);
26
26
  static const rb_data_type_t arrow_converted_schema_data_type = {
27
27
  "DuckDB/ArrowConvertedSchema",
28
28
  {NULL, deallocate, memsize,},
29
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
29
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
30
30
  };
31
31
 
32
32
  static void deallocate(void *ctx) {
@@ -10,7 +10,7 @@ static VALUE client_context_connection_id(VALUE self);
10
10
  static const rb_data_type_t client_context_data_type = {
11
11
  "DuckDB/ClientContext",
12
12
  {NULL, deallocate, memsize,},
13
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
13
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
14
14
  };
15
15
 
16
16
  static void deallocate(void *ctx) {
data/ext/duckdb/column.c CHANGED
@@ -12,7 +12,7 @@ static VALUE column_name(VALUE oDuckDBColumn);
12
12
  static const rb_data_type_t column_data_type = {
13
13
  "DuckDB/Column",
14
14
  {NULL, deallocate, memsize,},
15
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
15
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
16
16
  };
17
17
 
18
18
  static void deallocate(void *ctx) {
data/ext/duckdb/column.h CHANGED
@@ -2,7 +2,6 @@
2
2
  #define RUBY_DUCKDB_COLUMN_H
3
3
 
4
4
  struct _rubyDuckDBColumn {
5
- VALUE result;
6
5
  idx_t col;
7
6
  };
8
7
 
data/ext/duckdb/config.c CHANGED
@@ -13,7 +13,7 @@ static VALUE config_set_config(VALUE self, VALUE key, VALUE value);
13
13
  static const rb_data_type_t config_data_type = {
14
14
  "DuckDB/Config",
15
15
  {NULL, deallocate, memsize,},
16
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
16
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
17
17
  };
18
18
 
19
19
  static void deallocate(void * ctx) {
@@ -4,6 +4,7 @@ VALUE cDuckDBConnection;
4
4
 
5
5
  static void deallocate(void *ctx);
6
6
  static void mark(void *ctx);
7
+ static void compact(void *ctx);
7
8
  static VALUE allocate(VALUE klass);
8
9
  static size_t memsize(const void *p);
9
10
  static VALUE connection_disconnect(VALUE self);
@@ -19,10 +20,15 @@ static VALUE connection__register_aggregate_function_set(VALUE self, VALUE aggre
19
20
  static VALUE connection__register_table_function(VALUE self, VALUE table_function);
20
21
  static VALUE connection__get_table_names(VALUE self, VALUE query, VALUE qualified);
21
22
 
23
+ /*
24
+ * No RUBY_TYPED_FREE_IMMEDIATELY: deallocate can block, and must not run
25
+ * during GC sweep: duckdb_disconnect rolls back an open transaction and may drop the
26
+ * last reference to the database (checkpoint on shutdown).
27
+ */
22
28
  static const rb_data_type_t connection_data_type = {
23
29
  "DuckDB/Connection",
24
- {mark, deallocate, memsize,},
25
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
30
+ {mark, deallocate, memsize, compact},
31
+ 0, 0, RUBY_TYPED_WB_PROTECTED
26
32
  };
27
33
 
28
34
  static void deallocate(void *ctx) {
@@ -34,7 +40,12 @@ static void deallocate(void *ctx) {
34
40
 
35
41
  static void mark(void *ctx) {
36
42
  rubyDuckDBConnection *p = (rubyDuckDBConnection *)ctx;
37
- rb_gc_mark(p->database);
43
+ rb_gc_mark_movable(p->database);
44
+ }
45
+
46
+ static void compact(void *ctx) {
47
+ rubyDuckDBConnection *p = (rubyDuckDBConnection *)ctx;
48
+ p->database = rb_gc_location(p->database);
38
49
  }
39
50
 
40
51
  static VALUE allocate(VALUE klass) {
@@ -77,7 +88,7 @@ VALUE rbduckdb_create_connection(VALUE oDuckDBDatabase) {
77
88
  if (duckdb_connect(ctxdb->db, &(ctxcon->con)) == DuckDBError) {
78
89
  rb_raise(eDuckDBError, "connection error");
79
90
  }
80
- ctxcon->database = oDuckDBDatabase;
91
+ RB_OBJ_WRITE(obj, &ctxcon->database, oDuckDBDatabase);
81
92
 
82
93
  return obj;
83
94
  }
@@ -93,7 +104,7 @@ static VALUE connection_disconnect(VALUE self) {
93
104
  * reachable from other connections. They are released with the database.
94
105
  * This connection no longer needs the database, so stop retaining it.
95
106
  */
96
- ctx->database = Qnil;
107
+ RB_OBJ_WRITE(self, &ctx->database, Qnil);
97
108
 
98
109
  return self;
99
110
  }
@@ -162,7 +173,7 @@ static VALUE connection__connect(VALUE self, VALUE oDuckDBDatabase) {
162
173
  if (duckdb_connect(ctxdb->db, &(ctx->con)) == DuckDBError) {
163
174
  rb_raise(eDuckDBError, "connection error");
164
175
  }
165
- ctx->database = oDuckDBDatabase;
176
+ RB_OBJ_WRITE(self, &ctx->database, oDuckDBDatabase);
166
177
 
167
178
  return self;
168
179
  }
@@ -16,7 +16,7 @@ static VALUE data_chunk__reset(VALUE self);
16
16
  static const rb_data_type_t data_chunk_data_type = {
17
17
  "DuckDB/DataChunk",
18
18
  {NULL, deallocate, memsize,},
19
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
19
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
20
20
  };
21
21
 
22
22
  static void deallocate(void *ctx) {
@@ -5,6 +5,7 @@ VALUE cDuckDBDatabase;
5
5
  static void close_database(rubyDuckDB *p);
6
6
  static void deallocate(void * ctx);
7
7
  static void mark(void *ctx);
8
+ static void compact(void *ctx);
8
9
  static VALUE allocate(VALUE klass);
9
10
  static size_t memsize(const void *p);
10
11
  static duckdb_config create_config_with_ruby_api(void);
@@ -12,10 +13,14 @@ static VALUE database__initialize(VALUE self, VALUE file, VALUE config);
12
13
  static VALUE database__connect(VALUE self);
13
14
  static VALUE database_close(VALUE self);
14
15
 
16
+ /*
17
+ * No RUBY_TYPED_FREE_IMMEDIATELY: deallocate can block, and must not run
18
+ * during GC sweep: duckdb_close may checkpoint to disk and joins worker threads.
19
+ */
15
20
  static const rb_data_type_t database_data_type = {
16
21
  "DuckDB/Database",
17
- {mark, deallocate, memsize,},
18
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
22
+ {mark, deallocate, memsize, compact},
23
+ 0, 0, RUBY_TYPED_WB_PROTECTED
19
24
  };
20
25
 
21
26
  static void close_database(rubyDuckDB *p) {
@@ -31,7 +36,7 @@ static void close_database(rubyDuckDB *p) {
31
36
  * for us, and we wait for the worker.
32
37
  *
33
38
  * The GC free function calls close_database directly and cannot do this;
34
- * releasing the GVL mid-sweep is not allowed. A database reaching close via
39
+ * a free function must not release the GVL. A database reaching close via
35
40
  * the GC with callbacks still pending would deadlock the same way.
36
41
  */
37
42
  static void *close_database_without_gvl(void *ctx) {
@@ -49,9 +54,17 @@ static void deallocate(void * ctx) {
49
54
  static void mark(void *ctx) {
50
55
  rubyDuckDB *p = (rubyDuckDB *)ctx;
51
56
 
52
- rb_gc_mark(p->registered_functions);
53
- rb_gc_mark(p->cached_path);
54
- rb_gc_mark(p->cache_wrappers);
57
+ rb_gc_mark_movable(p->registered_functions);
58
+ rb_gc_mark_movable(p->cached_path);
59
+ rb_gc_mark_movable(p->cache_wrappers);
60
+ }
61
+
62
+ static void compact(void *ctx) {
63
+ rubyDuckDB *p = (rubyDuckDB *)ctx;
64
+
65
+ p->registered_functions = rb_gc_location(p->registered_functions);
66
+ p->cached_path = rb_gc_location(p->cached_path);
67
+ p->cache_wrappers = rb_gc_location(p->cache_wrappers);
55
68
  }
56
69
 
57
70
  static size_t memsize(const void *p) {
@@ -61,11 +74,9 @@ static size_t memsize(const void *p) {
61
74
  static VALUE allocate(VALUE klass) {
62
75
  rubyDuckDB *ctx = xcalloc((size_t)1, sizeof(rubyDuckDB));
63
76
  VALUE obj = TypedData_Wrap_Struct(klass, &database_data_type, ctx);
64
- VALUE registered_functions = rb_ary_new();
65
- ctx->registered_functions = registered_functions;
66
77
  ctx->cached_path = Qnil;
67
78
  ctx->cache_wrappers = Qnil;
68
- RB_GC_GUARD(registered_functions);
79
+ RB_OBJ_WRITE(obj, &ctx->registered_functions, rb_ary_new());
69
80
  return obj;
70
81
  }
71
82
 
@@ -89,8 +100,8 @@ void rbduckdb_database_set_cache_entry(VALUE database, VALUE path, VALUE wrapper
89
100
  rubyDuckDB *ctx;
90
101
 
91
102
  TypedData_Get_Struct(database, rubyDuckDB, &database_data_type, ctx);
92
- ctx->cached_path = rb_str_new_frozen(path);
93
- ctx->cache_wrappers = wrappers;
103
+ RB_OBJ_WRITE(database, &ctx->cached_path, rb_str_new_frozen(path));
104
+ RB_OBJ_WRITE(database, &ctx->cache_wrappers, wrappers);
94
105
  }
95
106
 
96
107
  rubyDuckDB *rbduckdb_get_struct_database(VALUE obj) {
@@ -181,8 +192,8 @@ static VALUE database_close(VALUE self) {
181
192
  */
182
193
  if (!NIL_P(ctx->cache_wrappers)) {
183
194
  rb_funcall(ctx->cache_wrappers, rb_intern("delete"), 1, self);
184
- ctx->cache_wrappers = Qnil;
185
- ctx->cached_path = Qnil;
195
+ RB_OBJ_WRITE(self, &ctx->cache_wrappers, Qnil);
196
+ RB_OBJ_WRITE(self, &ctx->cached_path, Qnil);
186
197
  }
187
198
  rb_thread_call_without_gvl(close_database_without_gvl, ctx, NULL, NULL);
188
199
  return self;
@@ -11,7 +11,7 @@ static VALUE expression__fold(VALUE self, VALUE client_context);
11
11
  static const rb_data_type_t expression_data_type = {
12
12
  "DuckDB/Expression",
13
13
  {NULL, deallocate, memsize,},
14
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
14
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
15
15
  };
16
16
 
17
17
  static void deallocate(void *ctx) {
@@ -9,7 +9,7 @@ static size_t memsize(const void *p);
9
9
  static const rb_data_type_t extract_statements_data_type = {
10
10
  "DuckDB/ExtractedStatements",
11
11
  {NULL, deallocate, memsize,},
12
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
12
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
13
13
  };
14
14
 
15
15
  static void deallocate(void *ctx);
@@ -4,6 +4,7 @@ VALUE cDuckDBInstanceCache;
4
4
 
5
5
  static void deallocate(void * ctx);
6
6
  static void mark(void *ctx);
7
+ static void compact(void *ctx);
7
8
  static VALUE allocate(VALUE klass);
8
9
  static VALUE memoizable_path(VALUE vpath);
9
10
  static VALUE find_cached_wrapper(rubyDuckDBInstanceCache *ctx, VALUE vpath);
@@ -14,8 +15,8 @@ static VALUE instance_cache_destroy(VALUE self);
14
15
 
15
16
  static const rb_data_type_t instance_cache_data_type = {
16
17
  "DuckDB/InstanceCache",
17
- {mark, deallocate, memsize,},
18
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
18
+ {mark, deallocate, memsize, compact},
19
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
19
20
  };
20
21
 
21
22
  static void deallocate(void * ctx) {
@@ -30,7 +31,13 @@ static void deallocate(void * ctx) {
30
31
  static void mark(void *ctx) {
31
32
  rubyDuckDBInstanceCache *p = (rubyDuckDBInstanceCache *)ctx;
32
33
 
33
- rb_gc_mark(p->wrappers);
34
+ rb_gc_mark_movable(p->wrappers);
35
+ }
36
+
37
+ static void compact(void *ctx) {
38
+ rubyDuckDBInstanceCache *p = (rubyDuckDBInstanceCache *)ctx;
39
+
40
+ p->wrappers = rb_gc_location(p->wrappers);
34
41
  }
35
42
 
36
43
  static size_t memsize(const void *p) {
@@ -87,9 +94,10 @@ static VALUE instance_cache_initialize(VALUE self) {
87
94
  rb_raise(eDuckDBError, "Failed to create instance cache");
88
95
  }
89
96
 
90
- ctx->wrappers = rb_funcall(rb_const_get(rb_const_get(rb_cObject, rb_intern("ObjectSpace")),
91
- rb_intern("WeakMap")),
92
- rb_intern("new"), 0);
97
+ RB_OBJ_WRITE(self, &ctx->wrappers,
98
+ rb_funcall(rb_const_get(rb_const_get(rb_cObject, rb_intern("ObjectSpace")),
99
+ rb_intern("WeakMap")),
100
+ rb_intern("new"), 0));
93
101
 
94
102
  return self;
95
103
  }
@@ -35,7 +35,7 @@ static VALUE logical_type_initialize(VALUE self, VALUE type_id_arg);
35
35
  static const rb_data_type_t logical_type_data_type = {
36
36
  "DuckDB/LogicalType",
37
37
  {NULL, deallocate, memsize,},
38
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
38
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
39
39
  };
40
40
 
41
41
  static void deallocate(void *ctx) {
@@ -12,10 +12,15 @@ static VALUE pending_result_execution_finished_p(VALUE self);
12
12
  static VALUE pending_result__state(VALUE self);
13
13
  static VALUE pending_result__execute_check_state(VALUE self);
14
14
 
15
+ /*
16
+ * No RUBY_TYPED_FREE_IMMEDIATELY: deallocate can block, and must not run
17
+ * during GC sweep: the pending result holds a ClientContext reference; dropping the last
18
+ * one rolls back an open transaction and may shut the database down.
19
+ */
15
20
  static const rb_data_type_t pending_result_data_type = {
16
21
  "DuckDB/PendingResult",
17
22
  {NULL, deallocate, memsize,},
18
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
23
+ 0, 0, RUBY_TYPED_WB_PROTECTED
19
24
  };
20
25
 
21
26
  static void deallocate(void *ctx) {
@@ -47,10 +47,15 @@ static VALUE prepared_statement__bind_uuid(VALUE self, VALUE vidx, VALUE val);
47
47
  static VALUE prepared_statement__bind_decimal(VALUE self, VALUE vidx, VALUE lower, VALUE upper, VALUE width, VALUE scale);
48
48
  static VALUE prepared_statement__bind_value(VALUE self, VALUE vidx, VALUE val);
49
49
 
50
+ /*
51
+ * No RUBY_TYPED_FREE_IMMEDIATELY: deallocate can block, and must not run
52
+ * during GC sweep: the statement holds a ClientContext reference; dropping the last one
53
+ * rolls back an open transaction and may shut the database down.
54
+ */
50
55
  static const rb_data_type_t prepared_statement_data_type = {
51
56
  "DuckDB/PreparedStatement",
52
57
  {NULL, deallocate, memsize,},
53
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
58
+ 0, 0, RUBY_TYPED_WB_PROTECTED
54
59
  };
55
60
 
56
61
  static void destroy_prepared_statement(rubyDuckDBPreparedStatement *p) {
data/ext/duckdb/result.c CHANGED
@@ -48,10 +48,15 @@ static VALUE vector_timestamp_tz(void* vector_data, idx_t row_idx);
48
48
  static VALUE vector_uuid(void* vector_data, idx_t row_idx);
49
49
  static VALUE vector_value(duckdb_vector vector, idx_t row_idx);
50
50
 
51
+ /*
52
+ * No RUBY_TYPED_FREE_IMMEDIATELY: deallocate can block, and must not run
53
+ * during GC sweep: a streaming result holds a ClientContext reference; dropping the last
54
+ * one rolls back an open transaction and may shut the database down.
55
+ */
51
56
  static const rb_data_type_t result_data_type = {
52
57
  "DuckDB/Result",
53
58
  {NULL, deallocate, memsize,},
54
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
59
+ 0, 0, RUBY_TYPED_WB_PROTECTED
55
60
  };
56
61
 
57
62
  static void deallocate(void *ctx) {
data/ext/duckdb/result.h CHANGED
@@ -6,7 +6,7 @@
6
6
  * object holds one reference, and each exported Arrow stream holds another,
7
7
  * so the duckdb_result stays valid for consumers that outlive the Ruby
8
8
  * objects. rbduckdb_result_unref() must not call any Ruby API: it runs from
9
- * GC sweep (deallocate) and from Arrow stream release callbacks.
9
+ * GC finalization (deallocate) and from Arrow stream release callbacks.
10
10
  */
11
11
  struct _rubyDuckDBResult {
12
12
  duckdb_result result;
@@ -79,13 +79,13 @@ static void execute_callback_protected(void *user_data) {
79
79
  static const rb_data_type_t scalar_function_data_type = {
80
80
  "DuckDB/ScalarFunction",
81
81
  {mark, deallocate, memsize, compact},
82
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
82
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
83
83
  };
84
84
 
85
85
  static void mark(void *ctx) {
86
86
  rubyDuckDBScalarFunction *p = (rubyDuckDBScalarFunction *)ctx;
87
- rb_gc_mark(p->function_proc);
88
- rb_gc_mark(p->bind_proc);
87
+ rb_gc_mark_movable(p->function_proc);
88
+ rb_gc_mark_movable(p->bind_proc);
89
89
  }
90
90
 
91
91
  static void deallocate(void * ctx) {
@@ -371,7 +371,7 @@ static VALUE scalar_function_set_function(VALUE self) {
371
371
 
372
372
  TypedData_Get_Struct(self, rubyDuckDBScalarFunction, &scalar_function_data_type, p);
373
373
 
374
- p->function_proc = rb_block_proc();
374
+ RB_OBJ_WRITE(self, &p->function_proc, rb_block_proc());
375
375
 
376
376
  duckdb_scalar_function_set_extra_info(p->scalar_function, p, NULL);
377
377
  duckdb_scalar_function_set_function(p->scalar_function, scalar_function_callback);
@@ -398,7 +398,7 @@ static VALUE scalar_function__set_bind(VALUE self) {
398
398
 
399
399
  TypedData_Get_Struct(self, rubyDuckDBScalarFunction, &scalar_function_data_type, p);
400
400
 
401
- p->bind_proc = rb_block_proc();
401
+ RB_OBJ_WRITE(self, &p->bind_proc, rb_block_proc());
402
402
 
403
403
  duckdb_scalar_function_set_extra_info(p->scalar_function, p, NULL);
404
404
  duckdb_scalar_function_set_bind(p->scalar_function, scalar_function_bind_callback);
@@ -13,7 +13,7 @@ static VALUE scalar_function_bind_info_client_context(VALUE self);
13
13
  static const rb_data_type_t scalar_function_bind_info_data_type = {
14
14
  "DuckDB/ScalarFunction/BindInfo",
15
15
  {NULL, deallocate, memsize,},
16
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
16
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
17
17
  };
18
18
 
19
19
  static void deallocate(void *ctx) {
@@ -13,12 +13,12 @@ static VALUE scalar_function_set__add(VALUE self, VALUE scalar_function);
13
13
  static const rb_data_type_t scalar_function_set_data_type = {
14
14
  "DuckDB/ScalarFunctionSet",
15
15
  {mark, deallocate, memsize, compact},
16
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
16
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
17
17
  };
18
18
 
19
19
  static void mark(void *ctx) {
20
20
  rubyDuckDBScalarFunctionSet *p = (rubyDuckDBScalarFunctionSet *)ctx;
21
- rb_gc_mark(p->functions);
21
+ rb_gc_mark_movable(p->functions);
22
22
  }
23
23
 
24
24
  static void deallocate(void *ctx) {
@@ -35,8 +35,7 @@ static void compact(void *ctx) {
35
35
  static VALUE allocate(VALUE klass) {
36
36
  rubyDuckDBScalarFunctionSet *ctx = xcalloc((size_t)1, sizeof(rubyDuckDBScalarFunctionSet));
37
37
  VALUE obj = TypedData_Wrap_Struct(klass, &scalar_function_set_data_type, ctx);
38
- ctx->functions = rb_ary_new();
39
- RB_GC_GUARD(ctx->functions);
38
+ RB_OBJ_WRITE(obj, &ctx->functions, rb_ary_new());
40
39
  return obj;
41
40
  }
42
41
 
@@ -19,7 +19,7 @@ static VALUE table_description__column_has_default(VALUE self, VALUE idx);
19
19
  static const rb_data_type_t table_description_data_type = {
20
20
  "DuckDB/TableDescription",
21
21
  {NULL, deallocate, memsize,},
22
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
22
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
23
23
  };
24
24
 
25
25
  static void deallocate(void *ctx) {
@@ -30,14 +30,14 @@ static void table_function_local_init_callback(duckdb_init_info info);
30
30
  static const rb_data_type_t table_function_data_type = {
31
31
  "DuckDB/TableFunction",
32
32
  {mark, deallocate, memsize, compact},
33
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
33
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
34
34
  };
35
35
 
36
36
  static void mark(void *ctx) {
37
37
  rubyDuckDBTableFunction *p = (rubyDuckDBTableFunction *)ctx;
38
- rb_gc_mark(p->bind_proc);
39
- rb_gc_mark(p->init_proc);
40
- rb_gc_mark(p->execute_proc);
38
+ rb_gc_mark_movable(p->bind_proc);
39
+ rb_gc_mark_movable(p->init_proc);
40
+ rb_gc_mark_movable(p->execute_proc);
41
41
  }
42
42
 
43
43
  static void deallocate(void *ctx) {
@@ -210,7 +210,7 @@ static VALUE table_function_bind(VALUE self) {
210
210
  rb_raise(eDuckDBError, "Table function is destroyed");
211
211
  }
212
212
 
213
- ctx->bind_proc = rb_block_proc();
213
+ RB_OBJ_WRITE(self, &ctx->bind_proc, rb_block_proc());
214
214
 
215
215
  duckdb_table_function_set_bind(ctx->table_function, table_function_bind_callback);
216
216
 
@@ -286,7 +286,7 @@ static VALUE table_function_init(VALUE self) {
286
286
  rb_raise(eDuckDBError, "Table function is destroyed");
287
287
  }
288
288
 
289
- ctx->init_proc = rb_block_proc();
289
+ RB_OBJ_WRITE(self, &ctx->init_proc, rb_block_proc());
290
290
  duckdb_table_function_set_init(ctx->table_function, table_function_init_callback);
291
291
 
292
292
  rbduckdb_function_executor_ensure_started();
@@ -359,7 +359,7 @@ static VALUE table_function_execute(VALUE self) {
359
359
 
360
360
  TypedData_Get_Struct(self, rubyDuckDBTableFunction, &table_function_data_type, ctx);
361
361
 
362
- ctx->execute_proc = rb_block_proc();
362
+ RB_OBJ_WRITE(self, &ctx->execute_proc, rb_block_proc());
363
363
  duckdb_table_function_set_function(ctx->table_function, table_function_execute_callback);
364
364
  #ifdef HAVE_DUCKDB_H_GE_V1_5_0
365
365
  /* Per-worker proxy threads for the execute path (DuckDB >= 1.5.0). */
@@ -16,7 +16,7 @@ static VALUE table_function_bind_info_set_error(VALUE self, VALUE error);
16
16
  static const rb_data_type_t bind_info_data_type = {
17
17
  "DuckDB/TableFunctionBindInfo",
18
18
  {NULL, deallocate, memsize,},
19
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
19
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
20
20
  };
21
21
 
22
22
  static void deallocate(void *ctx) {
@@ -11,7 +11,7 @@ static VALUE table_function_function_info_set_error(VALUE self, VALUE error);
11
11
  static const rb_data_type_t function_info_data_type = {
12
12
  "DuckDB/TableFunctionFunctionInfo",
13
13
  {NULL, deallocate, memsize,},
14
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
14
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
15
15
  };
16
16
 
17
17
  static void deallocate(void *ctx) {
@@ -14,7 +14,7 @@ static VALUE table_function_init_info_bind_data(VALUE self);
14
14
  static const rb_data_type_t init_info_data_type = {
15
15
  "DuckDB/TableFunctionInitInfo",
16
16
  {NULL, deallocate, memsize,},
17
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
17
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
18
18
  };
19
19
 
20
20
  static void deallocate(void *ctx) {
data/ext/duckdb/value.c CHANGED
@@ -53,7 +53,7 @@ static VALUE value_to_ruby(VALUE self);
53
53
  static const rb_data_type_t value_data_type = {
54
54
  "DuckDB/Value",
55
55
  {NULL, deallocate, memsize,},
56
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
56
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
57
57
  };
58
58
 
59
59
  static void deallocate(void * ctx) {
@@ -332,7 +332,7 @@ static idx_t marshal_values(VALUE ary, duckdb_value **out, volatile VALUE *guard
332
332
  idx_t i;
333
333
 
334
334
  for (i = 0; i < n; i++) {
335
- buf[i] = rbduckdb_get_struct_value(RARRAY_AREF(ary, i))->value;
335
+ buf[i] = rbduckdb_get_struct_value(rb_ary_entry(ary, (long)i))->value;
336
336
  }
337
337
  *out = buf;
338
338
  return n;
data/ext/duckdb/vector.c CHANGED
@@ -15,7 +15,7 @@ static VALUE vector_set_validity(VALUE self, VALUE index, VALUE valid);
15
15
  static const rb_data_type_t vector_data_type = {
16
16
  "DuckDB/Vector",
17
17
  {NULL, deallocate, memsize,},
18
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
18
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
19
19
  };
20
20
 
21
21
  static void deallocate(void *ctx) {
@@ -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.5.1'
6
+ VERSION = '1.5.6.0'
7
7
  end
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.5.1
4
+ version: 1.5.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Masaki Suketa
@@ -176,7 +176,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
176
176
  - !ruby/object:Gem::Version
177
177
  version: '0'
178
178
  requirements: []
179
- rubygems_version: 4.0.17
179
+ rubygems_version: 4.0.20
180
180
  specification_version: 4
181
181
  summary: Ruby bindings for the DuckDB database engine.
182
182
  test_files: []