sqlite3 2.10.1-arm-linux-gnu → 2.10.2-arm-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: 849f2ea6706065fc2f92a45a582235e61b60c61d5c38a79676a9e5cedbfe2ef9
4
- data.tar.gz: b1827751829e736da2855f55bec8de0dd9e19bfc6635e05fa97ebb8f6ef0efea
3
+ metadata.gz: '039ee531cfad95be518fa27af5a3a42c9897251893d671210c0e4bd77b764e1f'
4
+ data.tar.gz: 84595a5d36871d679b8f0a475d0be1f0ee8efe547eb93ee124bc1861610a5579
5
5
  SHA512:
6
- metadata.gz: c68d6796e87724c42a8e285651fe20437c9583066bdf64f6941c63ddc6a33092ffea7806d2da75cb07f1dfd884ad6c5f29358c27dd346cf17b7386c8086f8ad3
7
- data.tar.gz: 89db907e15d43a5ce3b14ef783626d1ae69ddb2de8c995d3029125bba19e7da31d419d6cfe1b28797c8d65e44e81db39a7041f7875bfd1dad53a3fdc114b1570
6
+ metadata.gz: cf66532f5a21d70a33ce2a479065ab63519216a63f814391a584ee0931d27ceb9b48c8c18f59aef0b00c36b745203f4632e730369ce78fcb6c4291449e363c17
7
+ data.tar.gz: 8d7dc04c8e1158a3f71bb116a4b231aa050e86abd63f46b864c3ac019a076498cbf59396860100679b10c03a16d7b5f55165806f7cbd75f42fe96a90ea1dc288
data/CHANGELOG.md CHANGED
@@ -1,5 +1,35 @@
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
+
3
33
  ## 2.10.1 / 2026-10-19
4
34
 
5
35
  ### 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
@@ -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
 
@@ -74,51 +74,6 @@ close_or_discard_db(sqlite3RubyPtr ctx)
74
74
  }
75
75
 
76
76
 
77
- void
78
- rb_sqlite3_pin_array_and_contents(VALUE ary)
79
- {
80
- long i;
81
-
82
- if (NIL_P(ary) || !ary) { return; }
83
-
84
- rb_gc_mark(ary);
85
- for (i = 0; i < RARRAY_LEN(ary); i++) {
86
- rb_gc_mark(RARRAY_AREF(ary, i));
87
- }
88
- }
89
-
90
- static int
91
- pin_hash_value(VALUE UNUSED(key), VALUE value, VALUE UNUSED(arg))
92
- {
93
- rb_gc_mark(value);
94
- return ST_CONTINUE;
95
- }
96
-
97
- static void
98
- pin_hash_and_contents(VALUE hash)
99
- {
100
- if (NIL_P(hash) || !hash) { return; }
101
-
102
- rb_gc_mark(hash);
103
- rb_hash_foreach(hash, pin_hash_value, 0);
104
- }
105
-
106
- /* Each wrapper also owns live aggregate instances, whose VALUEs sqlite keeps in
107
- * its own aggregate contexts. */
108
- static void
109
- pin_aggregators(VALUE aggregators)
110
- {
111
- long i;
112
-
113
- rb_sqlite3_pin_array_and_contents(aggregators);
114
-
115
- if (NIL_P(aggregators) || !aggregators) { return; }
116
-
117
- for (i = 0; i < RARRAY_LEN(aggregators); i++) {
118
- rb_sqlite3_aggregator_pin_instances(RARRAY_AREF(aggregators, i));
119
- }
120
- }
121
-
122
77
  static void
123
78
  database_mark(void *ctx)
124
79
  {
@@ -130,8 +85,8 @@ database_mark(void *ctx)
130
85
  rb_gc_mark(c->authorizer);
131
86
 
132
87
  rb_sqlite3_pin_array_and_contents(c->functions);
133
- pin_hash_and_contents(c->collations);
134
- pin_aggregators(c->aggregators);
88
+ rb_sqlite3_pin_array_and_contents(c->collations);
89
+ rb_sqlite3_aggregator_pin(c->aggregators);
135
90
  }
136
91
 
137
92
  static void
@@ -150,7 +105,7 @@ database_memsize(const void *ctx)
150
105
  }
151
106
 
152
107
  static const rb_data_type_t database_type = {
153
- .wrap_struct_name = "SQLite3::Backup",
108
+ .wrap_struct_name = "SQLite3::Database",
154
109
  .function = {
155
110
  .dmark = database_mark,
156
111
  .dfree = deallocate,
@@ -321,11 +276,21 @@ total_changes(VALUE self)
321
276
  return INT2NUM(sqlite3_total_changes(ctx->db));
322
277
  }
323
278
 
279
+ static VALUE
280
+ tracefunc_protected(VALUE args_value)
281
+ {
282
+ VALUE *args = (VALUE *)args_value;
283
+ return rb_funcall(args[0], rb_intern("call"), 1, rb_str_new2((const char *)args[1]));
284
+ }
285
+
324
286
  static void
325
287
  tracefunc(void *data, const char *sql)
326
288
  {
327
289
  sqlite3RubyPtr ctx = (sqlite3RubyPtr)data;
328
- rb_funcall(ctx->trace_handler, rb_intern("call"), 1, rb_str_new2(sql));
290
+ VALUE args[] = { ctx->trace_handler, (VALUE)sql };
291
+ int failed;
292
+
293
+ rb_sqlite3_protect_callback(tracefunc_protected, (VALUE)args, &failed);
329
294
  }
330
295
 
331
296
  /* call-seq:
@@ -356,15 +321,24 @@ trace(int argc, VALUE *argv, VALUE self)
356
321
  return self;
357
322
  }
358
323
 
324
+ static VALUE
325
+ rb_sqlite3_busy_handler_protected(VALUE args_value)
326
+ {
327
+ VALUE *args = (VALUE *)args_value;
328
+ return rb_funcall(args[0], rb_intern("call"), 1, INT2NUM((int)args[1]));
329
+ }
330
+
359
331
  static int
360
332
  rb_sqlite3_busy_handler(void *context, int count)
361
333
  {
362
334
  sqlite3RubyPtr ctx = (sqlite3RubyPtr)context;
335
+ VALUE args[] = { ctx->busy_handler, (VALUE)count };
336
+ VALUE result;
337
+ int failed;
363
338
 
364
- VALUE handle = ctx->busy_handler;
365
- VALUE result = rb_funcall(handle, rb_intern("call"), 1, INT2NUM(count));
339
+ result = rb_sqlite3_protect_callback(rb_sqlite3_busy_handler_protected, (VALUE)args, &failed);
366
340
 
367
- if (Qfalse == result) { return 0; }
341
+ if (failed || Qfalse == result) { return 0; }
368
342
 
369
343
  return 1;
370
344
  }
@@ -529,14 +503,14 @@ set_sqlite3_func_result(sqlite3_context *ctx, VALUE result)
529
503
  sqlite3_result_blob(
530
504
  ctx,
531
505
  (const void *)StringValuePtr(result),
532
- (int)RSTRING_LEN(result),
506
+ RSTRING_LENINT(result),
533
507
  SQLITE_TRANSIENT
534
508
  );
535
509
  } else {
536
510
  sqlite3_result_text(
537
511
  ctx,
538
512
  (const char *)StringValuePtr(result),
539
- (int)RSTRING_LEN(result),
513
+ RSTRING_LENINT(result),
540
514
  SQLITE_TRANSIENT
541
515
  );
542
516
  }
@@ -578,13 +552,13 @@ static void
578
552
  rb_sqlite3_func(sqlite3_context *ctx, int argc, sqlite3_value **argv)
579
553
  {
580
554
  rb_sqlite3_func_args_t args = { .ctx = ctx, .argc = argc, .argv = argv };
581
- int exc_status;
555
+ int failed;
582
556
 
583
- rb_protect(rb_sqlite3_func_protected, (VALUE)&args, &exc_status);
557
+ rb_sqlite3_protect_callback(rb_sqlite3_func_protected, (VALUE)&args, &failed);
584
558
 
585
- if (exc_status) {
559
+ if (failed) {
586
560
  /* the user should never see this message, because Statement#step will
587
- * re-raise the exception still in rb_errinfo */
561
+ * resume the Ruby exception instead */
588
562
  sqlite3_result_error(ctx, "Ruby Exception occurred", -1);
589
563
  }
590
564
  }
@@ -722,6 +696,33 @@ changes(VALUE self)
722
696
  return INT2NUM(sqlite3_changes(ctx->db));
723
697
  }
724
698
 
699
+ typedef struct {
700
+ VALUE authorizer;
701
+ int action;
702
+ const char *a;
703
+ const char *b;
704
+ const char *c;
705
+ const char *d;
706
+ } rb_sqlite3_auth_args_t;
707
+
708
+ static VALUE
709
+ rb_sqlite3_auth_protected(VALUE auth_args_value)
710
+ {
711
+ rb_sqlite3_auth_args_t *args = (rb_sqlite3_auth_args_t *)auth_args_value;
712
+ VALUE action = INT2NUM(args->action);
713
+ VALUE a = args->a ? rb_str_new2(args->a) : Qnil;
714
+ VALUE b = args->b ? rb_str_new2(args->b) : Qnil;
715
+ VALUE c = args->c ? rb_str_new2(args->c) : Qnil;
716
+ VALUE d = args->d ? rb_str_new2(args->d) : Qnil;
717
+ VALUE result = rb_funcall(args->authorizer, rb_intern("call"), 5, action, a, b, c, d);
718
+
719
+ if (T_FIXNUM == TYPE(result)) { return INT2FIX(NUM2INT(result)); }
720
+ if (Qtrue == result) { return INT2FIX(SQLITE_OK); }
721
+ if (Qfalse == result) { return INT2FIX(SQLITE_DENY); }
722
+
723
+ return INT2FIX(SQLITE_IGNORE);
724
+ }
725
+
725
726
  static int
726
727
  rb_sqlite3_auth(
727
728
  void *ctx,
@@ -732,18 +733,17 @@ rb_sqlite3_auth(
732
733
  const char *_d)
733
734
  {
734
735
  sqlite3RubyPtr db_ctx = (sqlite3RubyPtr)ctx;
735
- VALUE action = INT2NUM(_action);
736
- VALUE a = _a ? rb_str_new2(_a) : Qnil;
737
- VALUE b = _b ? rb_str_new2(_b) : Qnil;
738
- VALUE c = _c ? rb_str_new2(_c) : Qnil;
739
- VALUE d = _d ? rb_str_new2(_d) : Qnil;
740
- VALUE result = rb_funcall(db_ctx->authorizer, rb_intern("call"), 5, action, a, b, c, d);
741
-
742
- if (T_FIXNUM == TYPE(result)) { return (int)NUM2INT(result); }
743
- if (Qtrue == result) { return SQLITE_OK; }
744
- if (Qfalse == result) { return SQLITE_DENY; }
745
-
746
- return SQLITE_IGNORE;
736
+ rb_sqlite3_auth_args_t args = {
737
+ .authorizer = db_ctx->authorizer, .action = _action, .a = _a, .b = _b, .c = _c, .d = _d
738
+ };
739
+ VALUE result;
740
+ int failed;
741
+
742
+ result = rb_sqlite3_protect_callback(rb_sqlite3_auth_protected, (VALUE)&args, &failed);
743
+
744
+ if (failed) { return SQLITE_DENY; }
745
+
746
+ return FIX2INT(result);
747
747
  }
748
748
 
749
749
  /* call-seq: set_authorizer = auth
@@ -815,10 +815,19 @@ set_extended_result_codes(VALUE self, VALUE enable)
815
815
  return self;
816
816
  }
817
817
 
818
- int
819
- rb_comparator_func(void *ctx, int a_len, const void *a, int b_len, const void *b)
820
- {
818
+ typedef struct {
821
819
  VALUE comparator;
820
+ int a_len;
821
+ const void *a;
822
+ int b_len;
823
+ const void *b;
824
+ int comparison;
825
+ } rb_comparator_args_t;
826
+
827
+ static VALUE
828
+ rb_comparator_protected(VALUE comparator_args_value)
829
+ {
830
+ rb_comparator_args_t *args = (rb_comparator_args_t *)comparator_args_value;
822
831
  VALUE a_str;
823
832
  VALUE b_str;
824
833
  VALUE comparison;
@@ -826,9 +835,8 @@ rb_comparator_func(void *ctx, int a_len, const void *a, int b_len, const void *b
826
835
 
827
836
  internal_encoding = rb_default_internal_encoding();
828
837
 
829
- comparator = (VALUE)ctx;
830
- a_str = rb_str_new((const char *)a, a_len);
831
- b_str = rb_str_new((const char *)b, b_len);
838
+ a_str = rb_str_new((const char *)args->a, args->a_len);
839
+ b_str = rb_str_new((const char *)args->b, args->b_len);
832
840
 
833
841
  rb_enc_associate_index(a_str, rb_utf8_encindex());
834
842
  rb_enc_associate_index(b_str, rb_utf8_encindex());
@@ -838,9 +846,30 @@ rb_comparator_func(void *ctx, int a_len, const void *a, int b_len, const void *b
838
846
  b_str = rb_str_export_to_enc(b_str, internal_encoding);
839
847
  }
840
848
 
841
- comparison = rb_funcall(comparator, rb_intern("compare"), 2, a_str, b_str);
849
+ comparison = rb_funcall(args->comparator, rb_intern("compare"), 2, a_str, b_str);
850
+
851
+ args->comparison = NUM2INT(comparison);
842
852
 
843
- return NUM2INT(comparison);
853
+ return Qnil;
854
+ }
855
+
856
+ int
857
+ rb_comparator_func(void *ctx, int a_len, const void *a, int b_len, const void *b)
858
+ {
859
+ rb_comparator_args_t args = { .comparator = (VALUE)ctx, .a_len = a_len, .a = a, .b_len = b_len, .b = b };
860
+ int failed;
861
+
862
+ rb_sqlite3_protect_callback(rb_comparator_protected, (VALUE)&args, &failed);
863
+
864
+ /* sqlite has no way to report a comparator error */
865
+ return failed ? 0 : args.comparison;
866
+ }
867
+
868
+ static int
869
+ push_hash_value(VALUE UNUSED(key), VALUE value, VALUE ary)
870
+ {
871
+ rb_ary_push(ary, value);
872
+ return ST_CONTINUE;
844
873
  }
845
874
 
846
875
  /* call-seq: db.collation(name, comparator)
@@ -854,21 +883,42 @@ static VALUE
854
883
  collation(VALUE self, VALUE name, VALUE comparator)
855
884
  {
856
885
  sqlite3RubyPtr ctx;
857
- VALUE collations;
886
+ VALUE registry, key, comparators;
887
+ char *key_ptr;
888
+ long i;
858
889
  TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
859
890
  REQUIRE_OPEN_DB(ctx);
860
891
 
892
+ /* sqlite holds a raw pointer to the comparator, so keep it alive and
893
+ * unmoved. The registry is private because callers can mutate the public
894
+ * @collations hash, and the snapshot is an array so database_mark can pin
895
+ * it without iterating a hash. sqlite matches collation names without
896
+ * regard to ASCII case, so the registry does too. The key and registry
897
+ * are prepared before sqlite takes the pointer, and the snapshot is
898
+ * published before the public hash, whose write can raise. */
899
+ key = rb_str_new_cstr(StringValuePtr(name));
900
+ key_ptr = RSTRING_PTR(key);
901
+ for (i = 0; i < RSTRING_LEN(key); i++) {
902
+ if (key_ptr[i] >= 'A' && key_ptr[i] <= 'Z') { key_ptr[i] += 'a' - 'A'; }
903
+ }
904
+ if (!rb_ivar_defined(self, rb_intern("-collations"))) {
905
+ rb_iv_set(self, "-collations", rb_hash_new());
906
+ }
907
+ registry = rb_iv_get(self, "-collations");
908
+
861
909
  CHECK(ctx->db, sqlite3_create_collation(
862
910
  ctx->db,
863
- StringValuePtr(name),
911
+ RSTRING_PTR(name),
864
912
  SQLITE_UTF8,
865
913
  (void *)comparator,
866
914
  NIL_P(comparator) ? NULL : rb_comparator_func));
867
915
 
868
- /* sqlite holds a raw pointer to the comparator, so keep it alive and unmoved. */
869
- collations = rb_iv_get(self, "@collations");
870
- rb_hash_aset(collations, name, comparator);
871
- RB_OBJ_WRITE(self, &ctx->collations, collations);
916
+ rb_hash_aset(registry, key, comparator);
917
+ comparators = rb_ary_new();
918
+ rb_hash_foreach(registry, push_hash_value, comparators);
919
+ RB_OBJ_WRITE(self, &ctx->collations, comparators);
920
+
921
+ rb_hash_aset(rb_iv_get(self, "@collations"), name, comparator);
872
922
 
873
923
  return self;
874
924
  }
@@ -934,42 +984,68 @@ transaction_active_p(VALUE self)
934
984
  return sqlite3_get_autocommit(ctx->db) ? Qfalse : Qtrue;
935
985
  }
936
986
 
937
- static int
938
- hash_callback_function(VALUE callback_ary, int count, char **data, char **columns)
987
+ typedef struct {
988
+ VALUE callback_ary;
989
+ VALUE (*build_row)(VALUE);
990
+ } rb_sqlite3_exec_batch_t;
991
+
992
+ typedef struct {
993
+ VALUE callback_ary;
994
+ int count;
995
+ char **data;
996
+ char **columns;
997
+ } rb_sqlite3_exec_row_t;
998
+
999
+ static VALUE
1000
+ hash_callback_protected(VALUE row_value)
939
1001
  {
1002
+ rb_sqlite3_exec_row_t *row = (rb_sqlite3_exec_row_t *)row_value;
940
1003
  VALUE new_hash = rb_hash_new();
941
1004
  int i;
942
1005
 
943
- for (i = 0; i < count; i++) {
944
- if (data[i] == NULL) {
945
- rb_hash_aset(new_hash, rb_str_new_cstr(columns[i]), Qnil);
1006
+ for (i = 0; i < row->count; i++) {
1007
+ if (row->data[i] == NULL) {
1008
+ rb_hash_aset(new_hash, rb_str_new_cstr(row->columns[i]), Qnil);
946
1009
  } else {
947
- rb_hash_aset(new_hash, rb_str_new_cstr(columns[i]), rb_str_new_cstr(data[i]));
1010
+ rb_hash_aset(new_hash, rb_str_new_cstr(row->columns[i]), rb_str_new_cstr(row->data[i]));
948
1011
  }
949
1012
  }
950
1013
 
951
- rb_ary_push(callback_ary, new_hash);
1014
+ rb_ary_push(row->callback_ary, new_hash);
952
1015
 
953
- return 0;
1016
+ return Qnil;
954
1017
  }
955
1018
 
956
- static int
957
- regular_callback_function(VALUE callback_ary, int count, char **data, char **columns)
1019
+ static VALUE
1020
+ regular_callback_protected(VALUE row_value)
958
1021
  {
1022
+ rb_sqlite3_exec_row_t *row = (rb_sqlite3_exec_row_t *)row_value;
959
1023
  VALUE new_ary = rb_ary_new();
960
1024
  int i;
961
1025
 
962
- for (i = 0; i < count; i++) {
963
- if (data[i] == NULL) {
1026
+ for (i = 0; i < row->count; i++) {
1027
+ if (row->data[i] == NULL) {
964
1028
  rb_ary_push(new_ary, Qnil);
965
1029
  } else {
966
- rb_ary_push(new_ary, rb_str_new_cstr(data[i]));
1030
+ rb_ary_push(new_ary, rb_str_new_cstr(row->data[i]));
967
1031
  }
968
1032
  }
969
1033
 
970
- rb_ary_push(callback_ary, new_ary);
1034
+ rb_ary_push(row->callback_ary, new_ary);
971
1035
 
972
- return 0;
1036
+ return Qnil;
1037
+ }
1038
+
1039
+ static int
1040
+ exec_batch_callback_function(void *batch_ptr, int count, char **data, char **columns)
1041
+ {
1042
+ rb_sqlite3_exec_batch_t *batch = (rb_sqlite3_exec_batch_t *)batch_ptr;
1043
+ rb_sqlite3_exec_row_t row = { .callback_ary = batch->callback_ary, .count = count, .data = data, .columns = columns };
1044
+ int failed;
1045
+
1046
+ rb_sqlite3_protect_callback(batch->build_row, (VALUE)&row, &failed);
1047
+
1048
+ return failed;
973
1049
  }
974
1050
 
975
1051
 
@@ -987,21 +1063,27 @@ exec_batch(VALUE self, VALUE sql, VALUE results_as_hash)
987
1063
  sqlite3RubyPtr ctx;
988
1064
  int status;
989
1065
  VALUE callback_ary = rb_ary_new();
1066
+ rb_sqlite3_exec_batch_t batch = {
1067
+ .callback_ary = callback_ary,
1068
+ .build_row = results_as_hash == Qtrue ? hash_callback_protected : regular_callback_protected
1069
+ };
1070
+ rb_sqlite3_callback_frame_t frame;
1071
+ int callback_state;
1072
+ const char *sql_cstr;
990
1073
  char *errMsg;
991
1074
 
992
1075
  TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
993
1076
  REQUIRE_OPEN_DB(ctx);
994
1077
 
1078
+ sql_cstr = StringValuePtr(sql);
995
1079
  timespecclear(&ctx->stmt_deadline);
996
1080
 
997
- if (results_as_hash == Qtrue) {
998
- status = sqlite3_exec(ctx->db, StringValuePtr(sql), (sqlite3_callback)hash_callback_function,
999
- (void *)callback_ary,
1000
- &errMsg);
1001
- } else {
1002
- status = sqlite3_exec(ctx->db, StringValuePtr(sql), (sqlite3_callback)regular_callback_function,
1003
- (void *)callback_ary,
1004
- &errMsg);
1081
+ rb_sqlite3_push_callback_frame(&frame);
1082
+ status = sqlite3_exec(ctx->db, sql_cstr, exec_batch_callback_function, (void *)&batch, &errMsg);
1083
+
1084
+ if ((callback_state = rb_sqlite3_pop_callback_frame(&frame))) {
1085
+ sqlite3_free(errMsg);
1086
+ rb_jump_tag(callback_state);
1005
1087
  }
1006
1088
 
1007
1089
  CHECK_MSG(ctx->db, status, errMsg);
@@ -10,6 +10,12 @@
10
10
  struct _sqlite3Ruby {
11
11
  sqlite3 *db;
12
12
  VALUE busy_handler;
13
+ /* sqlite holds raw pointers to everything in these arrays. functions and
14
+ * aggregators are append-only, so redefining a name keeps the old object
15
+ * for the life of the connection: sqlite keys them by name and arity, and
16
+ * releasing on name alone was GHSA-28hh-pr2h-2w89. collations is rebuilt
17
+ * from a private hash keyed by name without regard to ASCII case, as
18
+ * sqlite keys them, so it releases a replaced comparator. */
13
19
  VALUE functions;
14
20
  VALUE collations;
15
21
  VALUE aggregators;
@@ -24,9 +30,6 @@ struct _sqlite3Ruby {
24
30
  typedef struct _sqlite3Ruby sqlite3Ruby;
25
31
  typedef sqlite3Ruby *sqlite3RubyPtr;
26
32
 
27
- /* Pinning a collection doesn't pin what's in it, hence both. */
28
- void rb_sqlite3_pin_array_and_contents(VALUE ary);
29
-
30
33
  void init_sqlite3_database();
31
34
  void set_sqlite3_func_result(sqlite3_context *ctx, VALUE result);
32
35
 
@@ -120,3 +120,98 @@ rb_sqlite3_raise_with_sql(sqlite3 *db, int status, const char *sql)
120
120
 
121
121
  rb_exc_raise(exception);
122
122
  }
123
+
124
+ /* Each call into sqlite that can run callbacks pushes a frame on the current
125
+ * fiber's chain. A callback that fails records its rb_protect state in the top
126
+ * frame, and the call resumes Ruby's pending jump once sqlite returns. Ruby
127
+ * code that runs in between, such as a finalizer during an allocation, pushes
128
+ * its own frame for any nested call, so it never takes the outer call's jump.
129
+ * The chain is fiber-local because Ruby code inside a callback can switch
130
+ * threads or fibers, and a Ruby thread can move between native threads inside
131
+ * rb_protect. */
132
+ typedef struct {
133
+ rb_sqlite3_callback_frame_t *top;
134
+ } callback_frames_t;
135
+
136
+ static const rb_data_type_t callback_frames_type = {
137
+ .wrap_struct_name = "SQLite3::CallbackFrames",
138
+ .function = {
139
+ .dfree = RUBY_TYPED_DEFAULT_FREE,
140
+ },
141
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
142
+ };
143
+
144
+ static ID id_callback_frames;
145
+
146
+ /* Ruby code can overwrite or delete the fiber-local, so check its type. */
147
+ static VALUE
148
+ current_callback_frames_holder(void)
149
+ {
150
+ VALUE holder = rb_thread_local_aref(rb_thread_current(), id_callback_frames);
151
+ return rb_typeddata_is_kind_of(holder, &callback_frames_type) ? holder : Qnil;
152
+ }
153
+
154
+ static callback_frames_t *
155
+ current_callback_frames(void)
156
+ {
157
+ VALUE holder = current_callback_frames_holder();
158
+ return NIL_P(holder) ? NULL : (callback_frames_t *)RTYPEDDATA_DATA(holder);
159
+ }
160
+
161
+ void
162
+ rb_sqlite3_push_callback_frame(rb_sqlite3_callback_frame_t *frame)
163
+ {
164
+ VALUE holder = current_callback_frames_holder();
165
+ callback_frames_t *frames;
166
+
167
+ if (NIL_P(holder)) {
168
+ holder = TypedData_Make_Struct(rb_cObject, callback_frames_t, &callback_frames_type, frames);
169
+ rb_thread_local_aset(rb_thread_current(), id_callback_frames, holder);
170
+ }
171
+ frames = (callback_frames_t *)RTYPEDDATA_DATA(holder);
172
+
173
+ frame->state = 0;
174
+ frame->prev = frames->top;
175
+ frame->holder = holder;
176
+ frames->top = frame;
177
+ }
178
+
179
+ int
180
+ rb_sqlite3_pop_callback_frame(rb_sqlite3_callback_frame_t *frame)
181
+ {
182
+ ((callback_frames_t *)RTYPEDDATA_DATA(frame->holder))->top = frame->prev;
183
+ return frame->state;
184
+ }
185
+
186
+ VALUE
187
+ rb_sqlite3_protect_callback(VALUE (*body)(VALUE), VALUE data, int *failed)
188
+ {
189
+ callback_frames_t *frames = current_callback_frames();
190
+ rb_sqlite3_callback_frame_t *frame = frames ? frames->top : NULL;
191
+ VALUE result;
192
+ int state;
193
+
194
+ if (frame && frame->state) {
195
+ *failed = 1;
196
+ return Qnil;
197
+ }
198
+
199
+ result = rb_protect(body, data, &state);
200
+ *failed = state != 0;
201
+
202
+ if (frame) {
203
+ frame->state = state;
204
+ } else if (state) {
205
+ /* sqlite ran the callback from a call that cannot resume the jump, such
206
+ * as Statement#reset!, so the error is dropped */
207
+ rb_set_errinfo(Qnil);
208
+ }
209
+
210
+ return result;
211
+ }
212
+
213
+ void
214
+ init_sqlite3_exception(void)
215
+ {
216
+ id_callback_frames = rb_intern("__sqlite3_callback_frames__");
217
+ }
@@ -9,4 +9,25 @@ void rb_sqlite3_raise(sqlite3 *db, int status);
9
9
  void rb_sqlite3_raise_msg(sqlite3 *db, int status, const char *msg);
10
10
  void rb_sqlite3_raise_with_sql(sqlite3 *db, int status, const char *sql);
11
11
 
12
+ typedef struct rb_sqlite3_callback_frame {
13
+ int state;
14
+ struct rb_sqlite3_callback_frame *prev;
15
+ /* the frame lives on the C stack, so GC marks and pins this */
16
+ VALUE holder;
17
+ } rb_sqlite3_callback_frame_t;
18
+
19
+ /* A call into sqlite that can run callbacks is wrapped in a frame. sqlite
20
+ * cannot carry a raise or a throw, so a failed callback records its rb_protect
21
+ * state in the frame, and later callbacks skip their Ruby code. Pop the frame
22
+ * when sqlite returns, then resume the jump with rb_jump_tag if the returned
23
+ * state is nonzero. Run nothing but the sqlite call in between, because a
24
+ * raise there would skip the pop. */
25
+ void rb_sqlite3_push_callback_frame(rb_sqlite3_callback_frame_t *frame);
26
+ int rb_sqlite3_pop_callback_frame(rb_sqlite3_callback_frame_t *frame);
27
+
28
+ /* Runs a callback's Ruby code for sqlite. Sets *failed if it did not complete. */
29
+ VALUE rb_sqlite3_protect_callback(VALUE (*body)(VALUE), VALUE data, int *failed);
30
+
31
+ void init_sqlite3_exception(void);
32
+
12
33
  #endif
@@ -60,6 +60,19 @@ bignum_to_int64(VALUE value, sqlite3_int64 *result)
60
60
  #endif
61
61
  }
62
62
 
63
+ void
64
+ rb_sqlite3_pin_array_and_contents(VALUE ary)
65
+ {
66
+ long i;
67
+
68
+ if (NIL_P(ary) || !ary) { return; }
69
+
70
+ rb_gc_mark(ary);
71
+ for (i = 0; i < RARRAY_LEN(ary); i++) {
72
+ rb_gc_mark(RARRAY_AREF(ary, i));
73
+ }
74
+ }
75
+
63
76
  static VALUE
64
77
  libversion(VALUE UNUSED(klass))
65
78
  {
@@ -191,6 +204,7 @@ Init_sqlite3_native(void)
191
204
  sqlite3_initialize();
192
205
  #endif
193
206
 
207
+ init_sqlite3_exception();
194
208
  init_sqlite3_constants();
195
209
  init_sqlite3_database();
196
210
  init_sqlite3_statement();
@@ -45,4 +45,7 @@ extern VALUE cSqlite3Blob;
45
45
 
46
46
  int bignum_to_int64(VALUE big, sqlite3_int64 *result);
47
47
 
48
+ /* Pinning a collection doesn't pin what's in it, hence both. */
49
+ void rb_sqlite3_pin_array_and_contents(VALUE ary);
50
+
48
51
  #endif
@@ -31,7 +31,7 @@ statement_memsize(const void *data)
31
31
  }
32
32
 
33
33
  static const rb_data_type_t statement_type = {
34
- "SQLite3::Backup",
34
+ "SQLite3::Statement",
35
35
  {
36
36
  NULL,
37
37
  statement_deallocate,
@@ -54,8 +54,9 @@ prepare(VALUE self, VALUE db, VALUE sql)
54
54
  {
55
55
  sqlite3RubyPtr db_ctx = sqlite3_database_unwrap(db);
56
56
  sqlite3StmtRubyPtr ctx;
57
- const char *tail = NULL;
58
- int status;
57
+ const char *sql_cstr, *tail = NULL;
58
+ rb_sqlite3_callback_frame_t frame;
59
+ int sql_len, status, callback_state;
59
60
 
60
61
  StringValue(sql);
61
62
 
@@ -68,17 +69,22 @@ prepare(VALUE self, VALUE db, VALUE sql)
68
69
 
69
70
  timespecclear(&db_ctx->stmt_deadline);
70
71
 
72
+ sql_cstr = StringValuePtr(sql);
73
+ sql_len = RSTRING_LENINT(sql);
74
+
75
+ rb_sqlite3_push_callback_frame(&frame);
71
76
  #ifdef HAVE_SQLITE3_PREPARE_V2
72
77
  status = sqlite3_prepare_v2(
73
78
  #else
74
79
  status = sqlite3_prepare(
75
80
  #endif
76
81
  db_ctx->db,
77
- (const char *)StringValuePtr(sql),
78
- (int)RSTRING_LEN(sql),
82
+ sql_cstr,
83
+ sql_len,
79
84
  &ctx->st,
80
85
  &tail
81
86
  );
87
+ if ((callback_state = rb_sqlite3_pop_callback_frame(&frame))) { rb_jump_tag(callback_state); }
82
88
 
83
89
  CHECK_PREPARE(db_ctx->db, status, StringValuePtr(sql));
84
90
 
@@ -125,7 +131,8 @@ step(VALUE self)
125
131
  {
126
132
  sqlite3StmtRubyPtr ctx;
127
133
  sqlite3_stmt *stmt;
128
- int value, length;
134
+ rb_sqlite3_callback_frame_t frame;
135
+ int value, length, callback_state;
129
136
  VALUE list;
130
137
  rb_encoding *internal_encoding;
131
138
 
@@ -144,14 +151,13 @@ step(VALUE self)
144
151
  timespecclear(&ctx->db->stmt_deadline);
145
152
  }
146
153
 
154
+ rb_sqlite3_push_callback_frame(&frame);
147
155
  value = sqlite3_step(stmt);
148
- if (rb_errinfo() != Qnil) {
149
- /* some user defined function was invoked as a callback during step and
150
- * it raised an exception that has been suppressed until step returns.
151
- * Now re-raise it. */
152
- VALUE exception = rb_errinfo();
153
- rb_set_errinfo(Qnil);
154
- rb_exc_raise(exception);
156
+ if ((callback_state = rb_sqlite3_pop_callback_frame(&frame))) { rb_jump_tag(callback_state); }
157
+
158
+ if (value == SQLITE_DONE) {
159
+ ctx->done_p = 1;
160
+ return Qnil;
155
161
  }
156
162
 
157
163
  length = sqlite3_column_count(stmt);
@@ -200,10 +206,6 @@ step(VALUE self)
200
206
  }
201
207
  }
202
208
  break;
203
- case SQLITE_DONE:
204
- ctx->done_p = 1;
205
- return Qnil;
206
- break;
207
209
  default:
208
210
  sqlite3_reset(stmt);
209
211
  ctx->done_p = 0;
@@ -236,6 +238,10 @@ step(VALUE self)
236
238
  *
237
239
  * stmt.bind_param(1, my_binary_str.encode(Encoding::UTF_8))
238
240
  *
241
+ * The value that this method binds stays bound when you execute or reset the
242
+ * statement. To change the value, bind a new value to the same parameter. To
243
+ * clear all values, call #clear_bindings!.
244
+ *
239
245
  * See also #bind_params.
240
246
  */
241
247
  static VALUE
@@ -274,7 +280,7 @@ bind_param(VALUE self, VALUE key, VALUE value)
274
280
  ctx->st,
275
281
  index,
276
282
  (const char *)StringValuePtr(value),
277
- (int)RSTRING_LEN(value),
283
+ RSTRING_LENINT(value),
278
284
  SQLITE_TRANSIENT
279
285
  );
280
286
  } else {
@@ -285,7 +291,7 @@ bind_param(VALUE self, VALUE key, VALUE value)
285
291
  ctx->st,
286
292
  index,
287
293
  (const char *)StringValuePtr(value),
288
- (int)RSTRING_LEN(value),
294
+ RSTRING_LENINT(value),
289
295
  SQLITE_TRANSIENT
290
296
  );
291
297
  } else {
@@ -296,7 +302,7 @@ bind_param(VALUE self, VALUE key, VALUE value)
296
302
  ctx->st,
297
303
  index,
298
304
  (const char *)StringValuePtr(value),
299
- (int)RSTRING_LEN(value),
305
+ RSTRING_LENINT(value),
300
306
  SQLITE_TRANSIENT
301
307
  );
302
308
  }
@@ -331,8 +337,11 @@ bind_param(VALUE self, VALUE key, VALUE value)
331
337
 
332
338
  /* call-seq: stmt.reset!
333
339
  *
334
- * Resets the statement. This is typically done internally, though it might
335
- * occasionally be necessary to manually reset the statement.
340
+ * Resets the statement. The library usually calls this method for you, but
341
+ * you can also call it yourself.
342
+ *
343
+ * This method does not clear the values that you bound to the parameters. To
344
+ * clear them, call #clear_bindings!.
336
345
  */
337
346
  static VALUE
338
347
  reset_bang(VALUE self)
@@ -353,8 +362,11 @@ reset_bang(VALUE self)
353
362
 
354
363
  /* call-seq: stmt.clear_bindings!
355
364
  *
356
- * Resets the statement. This is typically done internally, though it might
357
- * occasionally be necessary to manually reset the statement.
365
+ * Sets the value of each parameter in the statement to NULL.
366
+ *
367
+ * The values that you bind with #bind_param, #bind_params, or #execute stay
368
+ * bound when you execute or reset the statement. Call this method to clear
369
+ * them.
358
370
  */
359
371
  static VALUE
360
372
  clear_bindings_bang(VALUE self)
Binary file
Binary file
Binary file
Binary file
@@ -16,8 +16,12 @@ module SQLite3
16
16
  @stmt = stmt
17
17
  end
18
18
 
19
- # Reset the cursor, so that a result set which has reached end-of-file
20
- # can be rewound and reiterated.
19
+ # Resets the cursor to the first row, so that you can iterate over the
20
+ # result set again after it reaches the end.
21
+ #
22
+ # This method binds each argument to the statement with
23
+ # Statement#bind_params. The values that you bound before stay bound
24
+ # unless you bind new values to the same parameters.
21
25
  def reset(*bind_params)
22
26
  @stmt.reset!
23
27
  @stmt.bind_params(*bind_params)
@@ -65,6 +65,10 @@ module SQLite3
65
65
  # stmt = db.prepare( "select * from table where a=? and b=?" )
66
66
  # stmt.bind_params( 15, "hello" )
67
67
  #
68
+ # The values that this method binds stay bound when you execute or reset
69
+ # the statement. To change a value, bind a new value to the same
70
+ # parameter. To clear all values, call #clear_bindings!.
71
+ #
68
72
  # See also #execute, #bind_param, Statement#bind_param, and
69
73
  # Statement#bind_params.
70
74
  def bind_params(*bind_vars)
@@ -83,7 +87,17 @@ module SQLite3
83
87
  # statement's virtual machine. If a block was given, the new ResultSet will
84
88
  # be yielded to it; otherwise, the ResultSet will be returned.
85
89
  #
86
- # Any parameters will be bound to the statement using #bind_params.
90
+ # This method binds each argument to the statement with #bind_params. The
91
+ # values stay bound after this method returns. If a later call to #execute
92
+ # does not give a value for a parameter, the statement uses the value from
93
+ # the earlier call. To set all parameters to NULL before you execute the
94
+ # statement again, call #clear_bindings!:
95
+ #
96
+ # stmt = db.prepare( "select :a, :b" )
97
+ # stmt.execute( a: 1, b: 2 ).to_a # => [[1, 2]]
98
+ # stmt.execute( a: 3 ).to_a # => [[3, 2]]
99
+ # stmt.clear_bindings!
100
+ # stmt.execute( a: 3 ).to_a # => [[3, nil]]
87
101
  #
88
102
  # Example:
89
103
  #
@@ -92,7 +106,7 @@ module SQLite3
92
106
  # ...
93
107
  # end
94
108
  #
95
- # See also #bind_params, #execute!.
109
+ # See also #bind_params, #execute!, #clear_bindings!.
96
110
  def execute(*bind_vars)
97
111
  reset! if active? || done?
98
112
 
@@ -109,7 +123,9 @@ module SQLite3
109
123
  # rows returned by executing the statement. Otherwise, each row will be
110
124
  # yielded to the block.
111
125
  #
112
- # Any parameters will be bound to the statement using #bind_params.
126
+ # This method binds each argument to the statement with #bind_params. The
127
+ # values stay bound after this method returns. For more information, see
128
+ # #execute.
113
129
  #
114
130
  # Example:
115
131
  #
@@ -1,4 +1,4 @@
1
1
  module SQLite3
2
2
  # (String) the version of the sqlite3 gem, e.g. "2.1.1"
3
- VERSION = "2.10.1"
3
+ VERSION = "2.10.2"
4
4
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sqlite3
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.10.1
4
+ version: 2.10.2
5
5
  platform: arm-linux-gnu
6
6
  authors:
7
7
  - Jamis Buck