sqlite3 2.10.0 → 2.10.2
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 +41 -0
- data/FAQ.md +19 -0
- data/dependencies.yml +8 -8
- 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/database.rb +20 -30
- data/lib/sqlite3/resultset.rb +6 -2
- data/lib/sqlite3/statement.rb +19 -3
- data/lib/sqlite3/version.rb +1 -1
- data/ports/archives/sqlite-autoconf-3540100.tar.gz +0 -0
- metadata +2 -2
- data/ports/archives/sqlite-autoconf-3540000.tar.gz +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 04fc8d37ba845e25af3d976633e7042b376b4bd33186ca0af5950ae5999d8548
|
|
4
|
+
data.tar.gz: ffb42ff2e840bec7bcb8c790c212517b20f476988974e48b9289f9970cdb1d46
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e2aa6a5adf371a4ce60e672ae1b2903f70af4af3fbed6dec3d2ae92c5e91b63a7ff9feb598657706d0a4c3eeda8bdaf9750f74d65faa7e3184c7c45f814e39aa
|
|
7
|
+
data.tar.gz: 80a444a02a70cc3f2100dbd25be2f2c5fc9a6ed39926d3c1de40f33b5c2ce7b47b843bd8328f13ba2016b02eb815a69926d4a266a9250bc8c7a55fcaca47aa5b
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,46 @@
|
|
|
1
1
|
# sqlite3-ruby Changelog
|
|
2
2
|
|
|
3
|
+
## 2.10.2 / 2026-10-26
|
|
4
|
+
|
|
5
|
+
### Security / Stability
|
|
6
|
+
|
|
7
|
+
- Raise `RangeError` when a SQL string, a bound String or Blob value, or a String or Blob returned from a custom function or aggregate is longer than `INT_MAX` bytes. Previously the length was truncated to 32 bits, so SQLite could read only part of the string and drop trailing clauses from a query. See [GHSA-4fqr-96q8-xrfx](https://github.com/sparklemotion/sqlite3-ruby/security/advisories/GHSA-4fqr-96q8-xrfx) for more information. #762 @flavorjones
|
|
8
|
+
- Fix a crash when a custom aggregate's `finalize` returns a value that can't be converted to a SQLite result. The exception is now raised from the query and the connection remains usable. #762 @flavorjones
|
|
9
|
+
- Fix a use-after-free when the hash returned by `Database#collations` is cleared, modified or frozen. GC could free a comparator that SQLite still called. When that hash is frozen, `Database#collation` still raises `FrozenError`, but the new comparator stays registered and in use. #764 @flavorjones
|
|
10
|
+
- Fix a deadlock when a collation comparator, `trace` block, `busy_handler` block or authorizer raises, or when an interrupt such as `Thread#raise` arrives while an aggregate's handler is being created. Another thread that used the connection blocked forever. The exception is now raised from the query and the connection remains usable. See "Changed" below for what SQLite does before the exception is raised. #764 @flavorjones
|
|
11
|
+
- Fix a crash when a custom function or aggregate uses `throw`. The throw now unwinds to its `catch`. #764 @flavorjones
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- When a callback raises or throws, SQLite now finishes its current call before the exception is raised, instead of being unwound in the middle of it. #764 @flavorjones
|
|
16
|
+
- When a `trace` block raises, SQLite runs the statement up to its first row, or to completion, before the exception is raised. Previously the statement stopped where the block raised. For example, an `INSERT` whose `trace` block raises now inserts its row.
|
|
17
|
+
- The other callbacks in that SQLite call skip their Ruby code: collation comparators return 0, functions and aggregates report an error to SQLite, the busy handler stops retrying and the authorizer denies.
|
|
18
|
+
- `Database#execute_batch2` now raises the exception from a custom function, instead of `SQLite3::SQLException` "Ruby Exception occurred".
|
|
19
|
+
- An exception from a callback that SQLite runs during `Statement#reset!` or `Database#close` is discarded.
|
|
20
|
+
- The extension keeps per-fiber state in `Thread.current[:__sqlite3_callback_frames__]`. A query raises `FrozenError` on a `Thread` that was frozen before its first query. #764 @flavorjones
|
|
21
|
+
- When SQLite can't allocate an aggregate's context, the query raises `SQLite3::MemoryException` instead of aborting the process. #764 @flavorjones
|
|
22
|
+
|
|
23
|
+
### Improved
|
|
24
|
+
|
|
25
|
+
- `Statement#step` no longer allocates and discards an array when the statement has no more rows. This saves one object per statement execution. #758 @thomasklemm
|
|
26
|
+
- `ObjectSpace.dump` and heap profilers now report `SQLite3::Database` and `SQLite3::Statement` objects under their own type names, instead of `SQLite3::Backup`. #764 @flavorjones
|
|
27
|
+
|
|
28
|
+
### Documentation
|
|
29
|
+
|
|
30
|
+
- Document that parameters bound by `Statement#execute`, `#execute!`, `#bind_param` and `#bind_params` stay bound across executions, and that `Statement#clear_bindings!` clears them. #751 @flavorjones
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
## 2.10.1 / 2026-10-19
|
|
34
|
+
|
|
35
|
+
### Dependencies
|
|
36
|
+
|
|
37
|
+
- Vendored sqlite is updated to [v3.54.1](https://www.sqlite.org/releaselog/3_54_1.html) (from v3.54.0). This release contains a fix for a possible heap buffer overflow on 32-bit builds, including the precompiled `x86-linux-gnu` and `x86-linux-musl` gems. #756 @flavorjones
|
|
38
|
+
|
|
39
|
+
### Improved
|
|
40
|
+
|
|
41
|
+
- `Database#define_aggregator` no longer creates an anonymous class for each aggregate function it defines. Each invocation of the function still runs on a clone of the template object. #752 @jhawthorn
|
|
42
|
+
|
|
43
|
+
|
|
3
44
|
## 2.10.0 / 2026-10-13
|
|
4
45
|
|
|
5
46
|
### Dependencies
|
data/FAQ.md
CHANGED
|
@@ -209,6 +209,25 @@ Or do a `Database#prepare` to get the `Statement`, and then use either
|
|
|
209
209
|
stmt.bind_params( "value", "name" => "bob" )
|
|
210
210
|
```
|
|
211
211
|
|
|
212
|
+
Bound values stay bound across executions of the statement, including values
|
|
213
|
+
passed to `Statement#execute`. A later `#execute` that omits a parameter uses
|
|
214
|
+
the value bound earlier. Call `Statement#clear_bindings!` to set every
|
|
215
|
+
parameter back to NULL:
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
```ruby
|
|
219
|
+
stmt = db.prepare( "select :a, :b" )
|
|
220
|
+
|
|
221
|
+
stmt.execute( a: 1, b: 2 ).to_a # => [[1, 2]]
|
|
222
|
+
stmt.execute( a: 3 ).to_a # => [[3, 2]]
|
|
223
|
+
|
|
224
|
+
stmt.clear_bindings!
|
|
225
|
+
stmt.execute( a: 3 ).to_a # => [[3, nil]]
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
`Database#execute` and the related `Database` methods prepare a new statement
|
|
229
|
+
for each call, so values bound in one call never carry over to the next.
|
|
230
|
+
|
|
212
231
|
## How do I discover metadata about a query result?
|
|
213
232
|
|
|
214
233
|
IMPORTANT: `Database#execute` returns an Array of Array of Strings
|
data/dependencies.yml
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
sqlite3:
|
|
2
2
|
# checksum verified by first checking the published sha3(256) checksum against https://sqlite.org/download.html:
|
|
3
|
-
#
|
|
3
|
+
# b6ced585c750916b1052667239a12c25a7ff34e7f06e1e6e01ecb0427657d9c4
|
|
4
4
|
#
|
|
5
|
-
# $
|
|
6
|
-
#
|
|
5
|
+
# $ openssl dgst -sha3-256 -r ports/archives/sqlite-autoconf-3540100.tar.gz
|
|
6
|
+
# b6ced585c750916b1052667239a12c25a7ff34e7f06e1e6e01ecb0427657d9c4 *ports/archives/sqlite-autoconf-3540100.tar.gz
|
|
7
7
|
#
|
|
8
|
-
# $ sha256sum ports/archives/sqlite-autoconf-
|
|
9
|
-
#
|
|
10
|
-
version: "3.54.
|
|
8
|
+
# $ sha256sum ports/archives/sqlite-autoconf-3540100.tar.gz
|
|
9
|
+
# 926ef8d3eab93fbf6e06011901273eec076b25594a4e1cdadd50c987f4f6d64e ports/archives/sqlite-autoconf-3540100.tar.gz
|
|
10
|
+
version: "3.54.1"
|
|
11
11
|
files:
|
|
12
|
-
- url: "https://sqlite.org/2026/sqlite-autoconf-
|
|
13
|
-
sha256: "
|
|
12
|
+
- url: "https://sqlite.org/2026/sqlite-autoconf-3540100.tar.gz"
|
|
13
|
+
sha256: "926ef8d3eab93fbf6e06011901273eec076b25594a4e1cdadd50c987f4f6d64e"
|
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
|
|