sqlite3 2.10.1-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 +4 -4
- data/CHANGELOG.md +30 -0
- data/FAQ.md +19 -0
- data/ext/sqlite3/aggregator.c +103 -109
- data/ext/sqlite3/aggregator.h +4 -3
- data/ext/sqlite3/backup.c +7 -2
- data/ext/sqlite3/database.c +189 -107
- data/ext/sqlite3/database.h +6 -3
- data/ext/sqlite3/exception.c +95 -0
- data/ext/sqlite3/exception.h +21 -0
- data/ext/sqlite3/sqlite3.c +14 -0
- data/ext/sqlite3/sqlite3_ruby.h +3 -0
- data/ext/sqlite3/statement.c +36 -24
- data/lib/sqlite3/3.2/sqlite3_native.so +0 -0
- data/lib/sqlite3/3.3/sqlite3_native.so +0 -0
- data/lib/sqlite3/3.4/sqlite3_native.so +0 -0
- data/lib/sqlite3/4.0/sqlite3_native.so +0 -0
- data/lib/sqlite3/resultset.rb +6 -2
- data/lib/sqlite3/statement.rb +19 -3
- data/lib/sqlite3/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f9127458a40883ef9822507597470fd696e1d9a8bd00b14d43a9b9544d92d558
|
|
4
|
+
data.tar.gz: d827e966f0ab36f8ec315dab41b1f2e2a8fbc292b451b7a77d6974e81c33eca4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e263760d40f9a5002da1ddccb43a84a2bb484226c14b4a68f67db801d61a22f6df42ae26d27259c4157107c2d0e872fdae17942e066a4e8e293237e5fc0ef267
|
|
7
|
+
data.tar.gz: 8665adb90d725c905d79bf1dc81b64d92a7e972037b2e849c3b64b8695f260f735079f6fd52ba991f4ee131f17d255af41b60d40b9d05e4184444371a2245aa8
|
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
|
data/ext/sqlite3/aggregator.c
CHANGED
|
@@ -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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
|
51
|
-
*
|
|
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
|
-
|
|
45
|
+
rb_sqlite3_aggregate_handler_instance(aggregator_call_t *call)
|
|
56
46
|
{
|
|
57
|
-
VALUE aw
|
|
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 (
|
|
63
|
-
rb_fatal("SQLite
|
|
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
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
74
|
-
|
|
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
|
-
|
|
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
|
|
90
|
-
*
|
|
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
|
|
96
|
-
|
|
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 (!
|
|
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
|
-
|
|
109
|
-
|
|
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
|
-
|
|
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
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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 (
|
|
127
|
+
if (!call.agg) {
|
|
128
|
+
sqlite3_result_error_nomem(ctx);
|
|
128
129
|
return;
|
|
129
130
|
}
|
|
130
131
|
|
|
131
|
-
if (
|
|
132
|
-
|
|
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
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
-
|
|
250
|
+
rb_sqlite3_aggregator_pin(VALUE aggregators)
|
|
267
251
|
{
|
|
268
|
-
|
|
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
|
}
|
data/ext/sqlite3/aggregator.h
CHANGED
|
@@ -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
|
-
/*
|
|
11
|
-
*
|
|
12
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
data/ext/sqlite3/database.c
CHANGED
|
@@ -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
|
-
|
|
134
|
-
|
|
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::
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
(
|
|
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
|
-
(
|
|
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
|
|
555
|
+
int failed;
|
|
582
556
|
|
|
583
|
-
|
|
557
|
+
rb_sqlite3_protect_callback(rb_sqlite3_func_protected, (VALUE)&args, &failed);
|
|
584
558
|
|
|
585
|
-
if (
|
|
559
|
+
if (failed) {
|
|
586
560
|
/* the user should never see this message, because Statement#step will
|
|
587
|
-
*
|
|
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
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
VALUE
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
if (
|
|
744
|
-
|
|
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
|
-
|
|
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
|
-
|
|
830
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
911
|
+
RSTRING_PTR(name),
|
|
864
912
|
SQLITE_UTF8,
|
|
865
913
|
(void *)comparator,
|
|
866
914
|
NIL_P(comparator) ? NULL : rb_comparator_func));
|
|
867
915
|
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
RB_OBJ_WRITE(self, &ctx->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
|
-
|
|
938
|
-
|
|
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
|
|
1016
|
+
return Qnil;
|
|
954
1017
|
}
|
|
955
1018
|
|
|
956
|
-
static
|
|
957
|
-
|
|
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
|
|
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
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
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);
|
data/ext/sqlite3/database.h
CHANGED
|
@@ -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
|
|
data/ext/sqlite3/exception.c
CHANGED
|
@@ -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
|
+
}
|
data/ext/sqlite3/exception.h
CHANGED
|
@@ -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
|
data/ext/sqlite3/sqlite3.c
CHANGED
|
@@ -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();
|
data/ext/sqlite3/sqlite3_ruby.h
CHANGED
data/ext/sqlite3/statement.c
CHANGED
|
@@ -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::
|
|
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
|
-
|
|
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
|
-
|
|
78
|
-
|
|
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
|
-
|
|
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 (
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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
|
-
(
|
|
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
|
-
(
|
|
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
|
-
(
|
|
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.
|
|
335
|
-
*
|
|
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
|
-
*
|
|
357
|
-
*
|
|
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
|
data/lib/sqlite3/resultset.rb
CHANGED
|
@@ -16,8 +16,12 @@ module SQLite3
|
|
|
16
16
|
@stmt = stmt
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
#
|
|
20
|
-
#
|
|
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)
|
data/lib/sqlite3/statement.rb
CHANGED
|
@@ -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
|
-
#
|
|
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
|
-
#
|
|
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
|
#
|
data/lib/sqlite3/version.rb
CHANGED