sqlite3 2.10.0-aarch64-linux-gnu → 2.10.2-aarch64-linux-gnu

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: 68be8ed04d85ef7ec4bc345dab650d6095ae2837c063d7ec3308f8d7e606c8e5
4
- data.tar.gz: 9c91642427ea0cb898504dce1ed777a1d68867edf06882c1e4af08f60cc4cc3d
3
+ metadata.gz: f9127458a40883ef9822507597470fd696e1d9a8bd00b14d43a9b9544d92d558
4
+ data.tar.gz: d827e966f0ab36f8ec315dab41b1f2e2a8fbc292b451b7a77d6974e81c33eca4
5
5
  SHA512:
6
- metadata.gz: 2c0c80f5b87738ce375e61065d473130e77812351f340baf3635c17dc8e5915d753b037fb6b2c55300cb8241727874f19020dbaa1f3587780cab047005554dec
7
- data.tar.gz: 42da023fb70c7e5cc0747405034eacb9e62ff36542acd58d67ad4c012a5d25dc1e43578343a66113a2856c99d87917c1e9d4f3a248eba0019ce92b799c5b5dd3
6
+ metadata.gz: e263760d40f9a5002da1ddccb43a84a2bb484226c14b4a68f67db801d61a22f6df42ae26d27259c4157107c2d0e872fdae17942e066a4e8e293237e5fc0ef267
7
+ data.tar.gz: 8665adb90d725c905d79bf1dc81b64d92a7e972037b2e849c3b64b8695f260f735079f6fd52ba991f4ee131f17d255af41b60d40b9d05e4184444371a2245aa8
data/CHANGELOG.md CHANGED
@@ -1,5 +1,46 @@
1
1
  # sqlite3-ruby Changelog
2
2
 
3
+ ## 2.10.2 / 2026-10-26
4
+
5
+ ### Security / Stability
6
+
7
+ - Raise `RangeError` when a SQL string, a bound String or Blob value, or a String or Blob returned from a custom function or aggregate is longer than `INT_MAX` bytes. Previously the length was truncated to 32 bits, so SQLite could read only part of the string and drop trailing clauses from a query. See [GHSA-4fqr-96q8-xrfx](https://github.com/sparklemotion/sqlite3-ruby/security/advisories/GHSA-4fqr-96q8-xrfx) for more information. #762 @flavorjones
8
+ - Fix a crash when a custom aggregate's `finalize` returns a value that can't be converted to a SQLite result. The exception is now raised from the query and the connection remains usable. #762 @flavorjones
9
+ - Fix a use-after-free when the hash returned by `Database#collations` is cleared, modified or frozen. GC could free a comparator that SQLite still called. When that hash is frozen, `Database#collation` still raises `FrozenError`, but the new comparator stays registered and in use. #764 @flavorjones
10
+ - Fix a deadlock when a collation comparator, `trace` block, `busy_handler` block or authorizer raises, or when an interrupt such as `Thread#raise` arrives while an aggregate's handler is being created. Another thread that used the connection blocked forever. The exception is now raised from the query and the connection remains usable. See "Changed" below for what SQLite does before the exception is raised. #764 @flavorjones
11
+ - Fix a crash when a custom function or aggregate uses `throw`. The throw now unwinds to its `catch`. #764 @flavorjones
12
+
13
+ ### Changed
14
+
15
+ - When a callback raises or throws, SQLite now finishes its current call before the exception is raised, instead of being unwound in the middle of it. #764 @flavorjones
16
+ - When a `trace` block raises, SQLite runs the statement up to its first row, or to completion, before the exception is raised. Previously the statement stopped where the block raised. For example, an `INSERT` whose `trace` block raises now inserts its row.
17
+ - The other callbacks in that SQLite call skip their Ruby code: collation comparators return 0, functions and aggregates report an error to SQLite, the busy handler stops retrying and the authorizer denies.
18
+ - `Database#execute_batch2` now raises the exception from a custom function, instead of `SQLite3::SQLException` "Ruby Exception occurred".
19
+ - An exception from a callback that SQLite runs during `Statement#reset!` or `Database#close` is discarded.
20
+ - The extension keeps per-fiber state in `Thread.current[:__sqlite3_callback_frames__]`. A query raises `FrozenError` on a `Thread` that was frozen before its first query. #764 @flavorjones
21
+ - When SQLite can't allocate an aggregate's context, the query raises `SQLite3::MemoryException` instead of aborting the process. #764 @flavorjones
22
+
23
+ ### Improved
24
+
25
+ - `Statement#step` no longer allocates and discards an array when the statement has no more rows. This saves one object per statement execution. #758 @thomasklemm
26
+ - `ObjectSpace.dump` and heap profilers now report `SQLite3::Database` and `SQLite3::Statement` objects under their own type names, instead of `SQLite3::Backup`. #764 @flavorjones
27
+
28
+ ### Documentation
29
+
30
+ - Document that parameters bound by `Statement#execute`, `#execute!`, `#bind_param` and `#bind_params` stay bound across executions, and that `Statement#clear_bindings!` clears them. #751 @flavorjones
31
+
32
+
33
+ ## 2.10.1 / 2026-10-19
34
+
35
+ ### Dependencies
36
+
37
+ - Vendored sqlite is updated to [v3.54.1](https://www.sqlite.org/releaselog/3_54_1.html) (from v3.54.0). This release contains a fix for a possible heap buffer overflow on 32-bit builds, including the precompiled `x86-linux-gnu` and `x86-linux-musl` gems. #756 @flavorjones
38
+
39
+ ### Improved
40
+
41
+ - `Database#define_aggregator` no longer creates an anonymous class for each aggregate function it defines. Each invocation of the function still runs on a clone of the template object. #752 @jhawthorn
42
+
43
+
3
44
  ## 2.10.0 / 2026-10-13
4
45
 
5
46
  ### Dependencies
data/FAQ.md CHANGED
@@ -209,6 +209,25 @@ Or do a `Database#prepare` to get the `Statement`, and then use either
209
209
  stmt.bind_params( "value", "name" => "bob" )
210
210
  ```
211
211
 
212
+ Bound values stay bound across executions of the statement, including values
213
+ passed to `Statement#execute`. A later `#execute` that omits a parameter uses
214
+ the value bound earlier. Call `Statement#clear_bindings!` to set every
215
+ parameter back to NULL:
216
+
217
+
218
+ ```ruby
219
+ stmt = db.prepare( "select :a, :b" )
220
+
221
+ stmt.execute( a: 1, b: 2 ).to_a # => [[1, 2]]
222
+ stmt.execute( a: 3 ).to_a # => [[3, 2]]
223
+
224
+ stmt.clear_bindings!
225
+ stmt.execute( a: 3 ).to_a # => [[3, nil]]
226
+ ```
227
+
228
+ `Database#execute` and the related `Database` methods prepare a new statement
229
+ for each call, so values bound in one call never carry over to the next.
230
+
212
231
  ## How do I discover metadata about a query result?
213
232
 
214
233
  IMPORTANT: `Database#execute` returns an Array of Array of Strings
data/dependencies.yml CHANGED
@@ -1,13 +1,13 @@
1
1
  sqlite3:
2
2
  # checksum verified by first checking the published sha3(256) checksum against https://sqlite.org/download.html:
3
- # f5e3e724e9c9b3b5d512c76b02082091a59eeea9becd169a9fbf4d376403acce
3
+ # b6ced585c750916b1052667239a12c25a7ff34e7f06e1e6e01ecb0427657d9c4
4
4
  #
5
- # $ sha3sum -a 256 ports/archives/sqlite-autoconf-3540000.tar.gz
6
- # f5e3e724e9c9b3b5d512c76b02082091a59eeea9becd169a9fbf4d376403acce ports/archives/sqlite-autoconf-3540000.tar.gz
5
+ # $ openssl dgst -sha3-256 -r ports/archives/sqlite-autoconf-3540100.tar.gz
6
+ # b6ced585c750916b1052667239a12c25a7ff34e7f06e1e6e01ecb0427657d9c4 *ports/archives/sqlite-autoconf-3540100.tar.gz
7
7
  #
8
- # $ sha256sum ports/archives/sqlite-autoconf-3540000.tar.gz
9
- # 134ec0802dda5795816e25d25872d20b312cb3973438c49b30bc40b7705ea9ed ports/archives/sqlite-autoconf-3540000.tar.gz
10
- version: "3.54.0"
8
+ # $ sha256sum ports/archives/sqlite-autoconf-3540100.tar.gz
9
+ # 926ef8d3eab93fbf6e06011901273eec076b25594a4e1cdadd50c987f4f6d64e ports/archives/sqlite-autoconf-3540100.tar.gz
10
+ version: "3.54.1"
11
11
  files:
12
- - url: "https://sqlite.org/2026/sqlite-autoconf-3540000.tar.gz"
13
- sha256: "134ec0802dda5795816e25d25872d20b312cb3973438c49b30bc40b7705ea9ed"
12
+ - url: "https://sqlite.org/2026/sqlite-autoconf-3540100.tar.gz"
13
+ sha256: "926ef8d3eab93fbf6e06011901273eec076b25594a4e1cdadd50c987f4f6d64e"
@@ -14,165 +14,149 @@ static VALUE cAggregatorWrapper;
14
14
  * the xFinal callback.
15
15
  *
16
16
  * An AggregatorInstance holds the following instance variables:
17
- * -handler_instance: the instance to call `step` and `finalize` on.
18
- * -exc_status: status returned by rb_protect.
19
- * != 0 if an exception occurred. If an exception occurred
20
- * `step` and `finalize` won't be called any more. */
17
+ * -handler_instance: the instance to call `step` and `finalize` on. */
21
18
  static VALUE cAggregatorInstance;
22
19
 
23
- typedef struct rb_sqlite3_protected_funcall_args {
24
- VALUE self;
25
- ID method;
26
- int argc;
27
- VALUE *params;
28
- } protected_funcall_args_t;
29
-
30
- /* why isn't there something like this in the ruby API? */
31
- static VALUE
32
- rb_sqlite3_protected_funcall_body(VALUE protected_funcall_args_ptr)
33
- {
34
- protected_funcall_args_t *args =
35
- (protected_funcall_args_t *)protected_funcall_args_ptr;
36
-
37
- return rb_funcall2(args->self, args->method, args->argc, args->params);
38
- }
20
+ /* interned at init, because interning can allocate and rb_sqlite3_aggregator_pin
21
+ * runs during GC */
22
+ static ID id_instances;
23
+ static ID id_handler_instance;
39
24
 
40
- static VALUE
41
- rb_sqlite3_protected_funcall(VALUE self, ID method, int argc, VALUE *params,
42
- int *exc_status)
43
- {
44
- protected_funcall_args_t args = {
45
- .self = self, .method = method, .argc = argc, .params = params
46
- };
47
- return rb_protect(rb_sqlite3_protected_funcall_body, (VALUE)(&args), exc_status);
48
- }
25
+ /* sqlite's aggregate context for one invocation of an aggregate function. */
26
+ typedef struct {
27
+ /* the AggregatorInstance: Qfalse until the first callback creates it, Qnil
28
+ * once xFinal destroys it */
29
+ VALUE inst;
30
+ /* != 0 once a callback for this invocation raised, threw or was skipped.
31
+ * After that, `step` and `finalize` won't be called any more. */
32
+ int exc_status;
33
+ } aggregate_context_t;
34
+
35
+ typedef struct {
36
+ sqlite3_context *ctx;
37
+ aggregate_context_t *agg;
38
+ int argc;
39
+ sqlite3_value **argv;
40
+ } aggregator_call_t;
49
41
 
50
- /* called in rb_sqlite3_aggregator_step and rb_sqlite3_aggregator_final. It
51
- * checks if the execution context already has an associated instance. If it
52
- * has one, it returns it. If there is no instance yet, it creates one and
53
- * associates it with the context. */
42
+ /* called in the step and final callbacks. Returns the invocation's handler
43
+ * instance, creating it on the first call. */
54
44
  static VALUE
55
- rb_sqlite3_aggregate_instance(sqlite3_context *ctx)
45
+ rb_sqlite3_aggregate_handler_instance(aggregator_call_t *call)
56
46
  {
57
- VALUE aw = (VALUE) sqlite3_user_data(ctx);
58
- VALUE handler_klass = rb_iv_get(aw, "-handler_klass");
59
- VALUE inst;
60
- VALUE *inst_ptr = sqlite3_aggregate_context(ctx, (int)sizeof(VALUE));
47
+ VALUE aw, handler_instance, inst;
61
48
 
62
- if (!inst_ptr) {
63
- rb_fatal("SQLite is out-of-memory");
49
+ if (call->agg->inst == Qnil) {
50
+ rb_fatal("SQLite called us back on an already destroyed aggregate instance");
64
51
  }
65
52
 
66
- inst = *inst_ptr;
67
-
68
- if (inst == Qfalse) { /* Qfalse == 0 */
69
- VALUE instances = rb_iv_get(aw, "-instances");
70
- int exc_status;
53
+ if (call->agg->inst == Qfalse) { /* Qfalse == 0 */
54
+ aw = (VALUE) sqlite3_user_data(call->ctx);
55
+ handler_instance = rb_funcall(rb_ivar_get(aw, rb_intern("-handler_klass")), rb_intern("new"), 0);
71
56
 
72
57
  inst = rb_class_new_instance(0, NULL, cAggregatorInstance);
73
- rb_iv_set(inst, "-handler_instance", rb_sqlite3_protected_funcall(
74
- handler_klass, rb_intern("new"), 0, NULL, &exc_status));
75
- rb_iv_set(inst, "-exc_status", INT2NUM(exc_status));
76
-
77
- rb_ary_push(instances, inst);
78
-
79
- *inst_ptr = inst;
80
- }
58
+ rb_ivar_set(inst, id_handler_instance, handler_instance);
59
+ rb_ary_push(rb_ivar_get(aw, id_instances), inst);
81
60
 
82
- if (inst == Qnil) {
83
- rb_fatal("SQLite called us back on an already destroyed aggregate instance");
61
+ call->agg->inst = inst;
84
62
  }
85
63
 
86
- return inst;
64
+ return rb_ivar_get(call->agg->inst, id_handler_instance);
87
65
  }
88
66
 
89
- /* called by rb_sqlite3_aggregator_final. Unlinks and frees the
90
- * aggregator_instance_t, so the handler_instance won't be marked any more
91
- * and Ruby's GC may free it. */
67
+ /* called by rb_sqlite3_aggregator_final, after any skipped or failed callback
68
+ * too, so it runs no Ruby code. Unlinks the instance, so the handler_instance
69
+ * won't be marked any more and Ruby's GC may free it. */
92
70
  static void
93
- rb_sqlite3_aggregate_instance_destroy(sqlite3_context *ctx)
71
+ rb_sqlite3_aggregate_instance_destroy(sqlite3_context *ctx, aggregate_context_t *agg)
94
72
  {
95
- VALUE aw = (VALUE) sqlite3_user_data(ctx);
96
- VALUE instances = rb_iv_get(aw, "-instances");
97
- VALUE *inst_ptr = sqlite3_aggregate_context(ctx, 0);
98
- VALUE inst;
73
+ VALUE instances = rb_ivar_get((VALUE) sqlite3_user_data(ctx), id_instances);
74
+ long i;
99
75
 
100
- if (!inst_ptr || !(inst = *inst_ptr)) {
76
+ if (!agg->inst) {
101
77
  return;
102
78
  }
103
79
 
104
- if (inst == Qnil) {
80
+ if (agg->inst == Qnil) {
105
81
  rb_fatal("attempt to destroy aggregate instance twice");
106
82
  }
107
83
 
108
- rb_iv_set(inst, "-handler_instance", Qnil); // may catch use-after-free
109
- if (rb_ary_delete(instances, inst) == Qnil) {
84
+ rb_ivar_set(agg->inst, id_handler_instance, Qnil); // may catch use-after-free
85
+ for (i = 0; i < RARRAY_LEN(instances) && RARRAY_AREF(instances, i) != agg->inst; i++);
86
+ if (i == RARRAY_LEN(instances)) {
110
87
  rb_fatal("must be in instances at that point");
111
88
  }
89
+ rb_ary_delete_at(instances, i);
112
90
 
113
- *inst_ptr = Qnil;
91
+ agg->inst = Qnil;
92
+ }
93
+
94
+ static VALUE
95
+ rb_sqlite3_aggregator_step_protected(VALUE call_value)
96
+ {
97
+ aggregator_call_t *call = (aggregator_call_t *)call_value;
98
+ VALUE handler_instance = rb_sqlite3_aggregate_handler_instance(call);
99
+ VALUE params = rb_ary_new2(call->argc);
100
+ int i;
101
+
102
+ for (i = 0; i < call->argc; i++) {
103
+ rb_ary_push(params, sqlite3val2rb(call->argv[i]));
104
+ }
105
+
106
+ return rb_apply(handler_instance, rb_intern("step"), params);
107
+ }
108
+
109
+ static VALUE
110
+ rb_sqlite3_aggregator_final_protected(VALUE call_value)
111
+ {
112
+ aggregator_call_t *call = (aggregator_call_t *)call_value;
113
+ VALUE handler_instance = rb_sqlite3_aggregate_handler_instance(call);
114
+
115
+ set_sqlite3_func_result(call->ctx, rb_funcall(handler_instance, rb_intern("finalize"), 0));
116
+
117
+ return Qnil;
114
118
  }
115
119
 
116
120
  static void
117
121
  rb_sqlite3_aggregator_step(sqlite3_context *ctx, int argc, sqlite3_value **argv)
118
122
  {
119
- VALUE inst = rb_sqlite3_aggregate_instance(ctx);
120
- VALUE handler_instance = rb_iv_get(inst, "-handler_instance");
121
- VALUE *params = NULL;
122
- VALUE params_handle = 0;
123
- VALUE one_param;
124
- int exc_status = NUM2INT(rb_iv_get(inst, "-exc_status"));
125
- int i;
123
+ aggregator_call_t call = {
124
+ .ctx = ctx, .agg = sqlite3_aggregate_context(ctx, (int)sizeof(aggregate_context_t)), .argc = argc, .argv = argv
125
+ };
126
126
 
127
- if (exc_status) {
127
+ if (!call.agg) {
128
+ sqlite3_result_error_nomem(ctx);
128
129
  return;
129
130
  }
130
131
 
131
- if (argc == 1) {
132
- one_param = sqlite3val2rb(argv[0]);
133
- params = &one_param;
132
+ if (!call.agg->exc_status) {
133
+ rb_sqlite3_protect_callback(rb_sqlite3_aggregator_step_protected, (VALUE)&call, &call.agg->exc_status);
134
134
  }
135
- if (argc > 1) {
136
- /* ALLOCV memory is conservatively marked, so stored VALUEs survive a
137
- * GC raised by a later sqlite3val2rb call. */
138
- params = ALLOCV_N(VALUE, params_handle, argc);
139
- for (i = 0; i < argc; i++) {
140
- params[i] = sqlite3val2rb(argv[i]);
141
- }
142
- }
143
- rb_sqlite3_protected_funcall(
144
- handler_instance, rb_intern("step"), argc, params, &exc_status);
145
- if (argc > 1) {
146
- ALLOCV_END(params_handle);
147
- }
148
-
149
- rb_iv_set(inst, "-exc_status", INT2NUM(exc_status));
150
135
  }
151
136
 
152
137
  /* we assume that this function is only called once per execution context */
153
138
  static void
154
139
  rb_sqlite3_aggregator_final(sqlite3_context *ctx)
155
140
  {
156
- VALUE inst = rb_sqlite3_aggregate_instance(ctx);
157
- VALUE handler_instance = rb_iv_get(inst, "-handler_instance");
158
- int exc_status = NUM2INT(rb_iv_get(inst, "-exc_status"));
159
-
160
- if (!exc_status) {
161
- VALUE result = rb_sqlite3_protected_funcall(
162
- handler_instance, rb_intern("finalize"), 0, NULL, &exc_status);
163
- if (!exc_status) {
164
- set_sqlite3_func_result(ctx, result);
165
- }
141
+ aggregator_call_t call = { .ctx = ctx, .agg = sqlite3_aggregate_context(ctx, (int)sizeof(aggregate_context_t)) };
142
+
143
+ if (!call.agg) {
144
+ sqlite3_result_error_nomem(ctx);
145
+ return;
166
146
  }
167
147
 
168
- if (exc_status) {
148
+ if (!call.agg->exc_status) {
149
+ rb_sqlite3_protect_callback(rb_sqlite3_aggregator_final_protected, (VALUE)&call, &call.agg->exc_status);
150
+ }
151
+
152
+ if (call.agg->exc_status) {
169
153
  /* the user should never see this, as Statement.step() will pick up the
170
154
  * outstanding exception and raise it instead of generating a new one
171
155
  * for SQLITE_ERROR with message "Ruby Exception occurred" */
172
156
  sqlite3_result_error(ctx, "Ruby Exception occurred", -1);
173
157
  }
174
158
 
175
- rb_sqlite3_aggregate_instance_destroy(ctx);
159
+ rb_sqlite3_aggregate_instance_destroy(ctx, call.agg);
176
160
  }
177
161
 
178
162
  /* call-seq: define_aggregator2(aggregator)
@@ -263,9 +247,16 @@ rb_sqlite3_define_aggregator2(VALUE self, VALUE aggregator, VALUE ruby_name)
263
247
  }
264
248
 
265
249
  void
266
- rb_sqlite3_aggregator_pin_instances(VALUE aw)
250
+ rb_sqlite3_aggregator_pin(VALUE aggregators)
267
251
  {
268
- rb_sqlite3_pin_array_and_contents(rb_iv_get(aw, "-instances"));
252
+ long i;
253
+
254
+ if (NIL_P(aggregators) || !aggregators) { return; }
255
+
256
+ rb_sqlite3_pin_array_and_contents(aggregators);
257
+ for (i = 0; i < RARRAY_LEN(aggregators); i++) {
258
+ rb_sqlite3_pin_array_and_contents(rb_ivar_get(RARRAY_AREF(aggregators, i), id_instances));
259
+ }
269
260
  }
270
261
 
271
262
  void
@@ -277,4 +268,7 @@ rb_sqlite3_aggregator_init(void)
277
268
 
278
269
  cAggregatorInstance = rb_funcall(rb_cClass, rb_intern("new"), 0);
279
270
  rb_gc_register_mark_object(cAggregatorInstance);
271
+
272
+ id_instances = rb_intern("-instances");
273
+ id_handler_instance = rb_intern("-handler_instance");
280
274
  }
@@ -7,8 +7,9 @@ VALUE rb_sqlite3_define_aggregator2(VALUE self, VALUE aggregator, VALUE ruby_nam
7
7
 
8
8
  void rb_sqlite3_aggregator_init(void);
9
9
 
10
- /* sqlite stores each live instance's VALUE in its aggregate context, so those
11
- * must not move either. */
12
- void rb_sqlite3_aggregator_pin_instances(VALUE aw);
10
+ /* Pins every aggregator wrapper and its live instances. sqlite holds raw
11
+ * pointers to the wrappers, and stores each instance's VALUE in its aggregate
12
+ * context. */
13
+ void rb_sqlite3_aggregator_pin(VALUE aggregators);
13
14
 
14
15
  #endif
data/ext/sqlite3/backup.c CHANGED
@@ -113,11 +113,16 @@ static VALUE
113
113
  step(VALUE self, VALUE nPage)
114
114
  {
115
115
  sqlite3BackupRubyPtr ctx;
116
- int status;
116
+ rb_sqlite3_callback_frame_t frame;
117
+ int pages, status, callback_state;
117
118
 
118
119
  TypedData_Get_Struct(self, sqlite3BackupRuby, &backup_type, ctx);
119
120
  REQUIRE_OPEN_BACKUP(ctx);
120
- status = sqlite3_backup_step(ctx->p, NUM2INT(nPage));
121
+ pages = NUM2INT(nPage);
122
+
123
+ rb_sqlite3_push_callback_frame(&frame);
124
+ status = sqlite3_backup_step(ctx->p, pages);
125
+ if ((callback_state = rb_sqlite3_pop_callback_frame(&frame))) { rb_jump_tag(callback_state); }
121
126
  return INT2NUM(status);
122
127
  }
123
128