sqlite3 2.9.5 → 2.9.6
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 +21 -0
- data/CONTRIBUTING.md +65 -0
- data/ext/sqlite3/aggregator.c +13 -3
- data/ext/sqlite3/aggregator.h +4 -0
- data/ext/sqlite3/database.c +140 -29
- data/ext/sqlite3/database.h +8 -0
- data/ext/sqlite3/extconf.rb +1 -1
- data/lib/sqlite3/database.rb +1 -5
- data/lib/sqlite3/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dbb79f96aa8c13ac20bc538637290a2774d6b0b2d871395daba91ccf5474cdb3
|
|
4
|
+
data.tar.gz: 35015943cf52ce0db1d832622d28452f3ec9ea5eedc8e7640d9813e9eb3142be
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b626691438c66b6a9fd1eba7fa6bff93b3f761d702ec7e1c39cfbdf308ee988052e91bc0ee52c83a3b91f33847f95683eab22121882bcc8139515027333d3987
|
|
7
|
+
data.tar.gz: 5d8efaa282bae975f9a08f2abf1976f9929e98b4c174b417cff7b62a4a9a5451c9ea2cac677690187168fdf0ebc0a043e870d59ea763a9a88d57ffaa078f78b0
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# sqlite3-ruby Changelog
|
|
2
2
|
|
|
3
|
+
## 2.9.6 / 2026-08-11
|
|
4
|
+
|
|
5
|
+
### Security / Stability
|
|
6
|
+
|
|
7
|
+
- Fix a garbage collection bug where the argument array passed to a custom aggregate function's `step` was not visible to the GC, so arguments could be collected mid-conversion when the aggregate takes two or more arguments, corrupting the values passed to `step` or crashing the process. See [GHSA-mwm8-39rw-8826](https://github.com/sparklemotion/sqlite3-ruby/security/advisories/GHSA-mwm8-39rw-8826) for more information. #733 @jeremy
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Fix a leak where custom aggregate handler instances were never released, so a connection accumulated one instance per `GROUP BY` group per query for its lifetime. #722 @djmb
|
|
12
|
+
- Fix GC compaction issues with custom functions, aggregates, collations, `#trace` and `#authorizer=`. These callbacks were registered with sqlite by passing a raw Ruby object pointer as user data; keeping the object reachable prevented collection but not relocation, after which sqlite held a stale address and the next call could raise `NoMethodError`, return a wrong result, or segfault. Affects applications that call `GC.compact` or run with `GC.auto_compact = true`. The equivalent issue in `#busy_handler` was fixed in #466. #723 @djmb
|
|
13
|
+
- Fix the private methods `Database#open_v2` and `#open16` silently replacing a live connection and leaking the previous connection handle when invoked via `send` on an open database. They now raise `SQLite3::Exception`. #729 @flavorjones
|
|
14
|
+
- Fix TEXT values containing an embedded NUL byte being truncated at the first NUL when passed as arguments to functions created with `Database#define_function`. #730 @flavorjones
|
|
15
|
+
- Fix an exception raised inside a `Database#define_function` block leaving the connection's sqlite mutex held, which deadlocked any other thread that later used the connection. The exception now propagates to the caller and the connection remains usable. #731 @flavorjones
|
|
16
|
+
- `Database.new` now raises `ArgumentError` when the filename or VFS name contains an embedded NUL byte (or an embedded 0x0000 code unit in a UTF-16 filename), instead of silently opening a path truncated at the NUL. #732 @flavorjones
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Improved
|
|
20
|
+
|
|
21
|
+
- When `Database.new` fails to open the database file, the underlying sqlite3 connection handle is now closed immediately instead of waiting for the garbage collector to clean it up. #719 @katafrakt
|
|
22
|
+
|
|
23
|
+
|
|
3
24
|
## 2.9.5 / 2026-06-07
|
|
4
25
|
|
|
5
26
|
### Dependencies
|
data/CONTRIBUTING.md
CHANGED
|
@@ -27,6 +27,71 @@ explicitly closed, it will be closed when it is GCed.
|
|
|
27
27
|
will be closed/finalized when it is GCed.
|
|
28
28
|
|
|
29
29
|
|
|
30
|
+
## Debugging memory issues
|
|
31
|
+
|
|
32
|
+
Please install `valgrind` and `gdb` before you use the tools in this section.
|
|
33
|
+
|
|
34
|
+
Run the test suite under valgrind and [`ruby_memcheck`](https://github.com/Shopify/ruby_memcheck) to
|
|
35
|
+
look for memory leaks and other memory errors:
|
|
36
|
+
|
|
37
|
+
``` sh
|
|
38
|
+
bundle exec rake compile test:valgrind
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
If you can't install valgrind on your system, use the `sqlite3-dev` docker image, which contains
|
|
42
|
+
valgrind:
|
|
43
|
+
|
|
44
|
+
``` sh
|
|
45
|
+
# build the image
|
|
46
|
+
bundle exec rake docker:dev:build
|
|
47
|
+
|
|
48
|
+
# run the test suite in a container
|
|
49
|
+
bundle exec rake docker:dev:test
|
|
50
|
+
|
|
51
|
+
# run the test suite under valgrind in a container
|
|
52
|
+
bundle exec rake docker:dev:test:valgrind
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Each `docker:dev:test` task builds the image first, then mounts your working copy at `/sqlite3` in
|
|
56
|
+
the container. Note that the container compiles into the mounted working copy, so please re-run
|
|
57
|
+
`rake compile` on your machine afterwards.
|
|
58
|
+
|
|
59
|
+
Run the test suite in the debugger:
|
|
60
|
+
|
|
61
|
+
``` sh
|
|
62
|
+
bundle exec rake compile test:gdb
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
You can also run the test suite with a variety of GC behaviors, which is useful to localize some
|
|
66
|
+
classes of memory bugs. Set the `SQLITE3_TEST_GC_LEVEL` environment variable (see `test/helper.rb`
|
|
67
|
+
for more info). A more stressful level finds more bugs, but makes the suite slower:
|
|
68
|
+
|
|
69
|
+
``` sh
|
|
70
|
+
# ordinary GC behavior (the default)
|
|
71
|
+
SQLITE3_TEST_GC_LEVEL=normal bundle exec rake compile test
|
|
72
|
+
|
|
73
|
+
# minor GC after each test
|
|
74
|
+
SQLITE3_TEST_GC_LEVEL=minor bundle exec rake compile test
|
|
75
|
+
|
|
76
|
+
# major GC after each test
|
|
77
|
+
SQLITE3_TEST_GC_LEVEL=major bundle exec rake compile test
|
|
78
|
+
|
|
79
|
+
# major GC after each test, and GC compaction after every 20 tests
|
|
80
|
+
SQLITE3_TEST_GC_LEVEL=compact bundle exec rake compile test
|
|
81
|
+
|
|
82
|
+
# verify references after compaction, after every 20 tests
|
|
83
|
+
# (see https://alanwu.space/post/check-compaction/)
|
|
84
|
+
SQLITE3_TEST_GC_LEVEL=verify bundle exec rake compile test
|
|
85
|
+
|
|
86
|
+
# run each test with GC "stress mode" on
|
|
87
|
+
SQLITE3_TEST_GC_LEVEL=stress bundle exec rake compile test
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
The `compact` and `verify` levels fall back to `normal` on a platform that does not support GC
|
|
91
|
+
compaction. The `stress` level makes the suite about 150 times slower, and it makes
|
|
92
|
+
timing-sensitive tests unreliable.
|
|
93
|
+
|
|
94
|
+
|
|
30
95
|
## Building gems
|
|
31
96
|
|
|
32
97
|
As a prerequisite please make sure you have `docker` correctly installed, so that you're able to cross-compile the native gems.
|
data/ext/sqlite3/aggregator.c
CHANGED
|
@@ -97,7 +97,7 @@ rb_sqlite3_aggregate_instance_destroy(sqlite3_context *ctx)
|
|
|
97
97
|
VALUE *inst_ptr = sqlite3_aggregate_context(ctx, 0);
|
|
98
98
|
VALUE inst;
|
|
99
99
|
|
|
100
|
-
if (!inst_ptr || (inst = *inst_ptr)) {
|
|
100
|
+
if (!inst_ptr || !(inst = *inst_ptr)) {
|
|
101
101
|
return;
|
|
102
102
|
}
|
|
103
103
|
|
|
@@ -119,6 +119,7 @@ rb_sqlite3_aggregator_step(sqlite3_context *ctx, int argc, sqlite3_value **argv)
|
|
|
119
119
|
VALUE inst = rb_sqlite3_aggregate_instance(ctx);
|
|
120
120
|
VALUE handler_instance = rb_iv_get(inst, "-handler_instance");
|
|
121
121
|
VALUE *params = NULL;
|
|
122
|
+
VALUE params_handle = 0;
|
|
122
123
|
VALUE one_param;
|
|
123
124
|
int exc_status = NUM2INT(rb_iv_get(inst, "-exc_status"));
|
|
124
125
|
int i;
|
|
@@ -132,7 +133,9 @@ rb_sqlite3_aggregator_step(sqlite3_context *ctx, int argc, sqlite3_value **argv)
|
|
|
132
133
|
params = &one_param;
|
|
133
134
|
}
|
|
134
135
|
if (argc > 1) {
|
|
135
|
-
|
|
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);
|
|
136
139
|
for (i = 0; i < argc; i++) {
|
|
137
140
|
params[i] = sqlite3val2rb(argv[i]);
|
|
138
141
|
}
|
|
@@ -140,7 +143,7 @@ rb_sqlite3_aggregator_step(sqlite3_context *ctx, int argc, sqlite3_value **argv)
|
|
|
140
143
|
rb_sqlite3_protected_funcall(
|
|
141
144
|
handler_instance, rb_intern("step"), argc, params, &exc_status);
|
|
142
145
|
if (argc > 1) {
|
|
143
|
-
|
|
146
|
+
ALLOCV_END(params_handle);
|
|
144
147
|
}
|
|
145
148
|
|
|
146
149
|
rb_iv_set(inst, "-exc_status", INT2NUM(exc_status));
|
|
@@ -254,10 +257,17 @@ rb_sqlite3_define_aggregator2(VALUE self, VALUE aggregator, VALUE ruby_name)
|
|
|
254
257
|
CHECK(ctx->db, status);
|
|
255
258
|
|
|
256
259
|
rb_ary_push(aggregators, aw);
|
|
260
|
+
RB_OBJ_WRITE(self, &ctx->aggregators, aggregators);
|
|
257
261
|
|
|
258
262
|
return self;
|
|
259
263
|
}
|
|
260
264
|
|
|
265
|
+
void
|
|
266
|
+
rb_sqlite3_aggregator_pin_instances(VALUE aw)
|
|
267
|
+
{
|
|
268
|
+
rb_sqlite3_pin_array_and_contents(rb_iv_get(aw, "-instances"));
|
|
269
|
+
}
|
|
270
|
+
|
|
261
271
|
void
|
|
262
272
|
rb_sqlite3_aggregator_init(void)
|
|
263
273
|
{
|
data/ext/sqlite3/aggregator.h
CHANGED
|
@@ -7,4 +7,8 @@ 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);
|
|
13
|
+
|
|
10
14
|
#endif
|
data/ext/sqlite3/database.c
CHANGED
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
if(!_ctxt->db) \
|
|
11
11
|
rb_raise(rb_path2class("SQLite3::Exception"), "cannot use a closed database");
|
|
12
12
|
|
|
13
|
+
#define REQUIRE_CLOSED_DB(_ctxt) \
|
|
14
|
+
if(_ctxt->db) \
|
|
15
|
+
rb_raise(rb_path2class("SQLite3::Exception"), "cannot open a database that is already open");
|
|
16
|
+
|
|
13
17
|
VALUE cSqlite3Database;
|
|
14
18
|
|
|
15
19
|
/* See adr/2024-09-fork-safety.md */
|
|
@@ -70,11 +74,64 @@ close_or_discard_db(sqlite3RubyPtr ctx)
|
|
|
70
74
|
}
|
|
71
75
|
|
|
72
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
|
+
|
|
73
122
|
static void
|
|
74
123
|
database_mark(void *ctx)
|
|
75
124
|
{
|
|
76
125
|
sqlite3RubyPtr c = (sqlite3RubyPtr)ctx;
|
|
126
|
+
|
|
127
|
+
/* sqlite holds raw pointers to these, so they must not move. */
|
|
77
128
|
rb_gc_mark(c->busy_handler);
|
|
129
|
+
rb_gc_mark(c->trace_handler);
|
|
130
|
+
rb_gc_mark(c->authorizer);
|
|
131
|
+
|
|
132
|
+
rb_sqlite3_pin_array_and_contents(c->functions);
|
|
133
|
+
pin_hash_and_contents(c->collations);
|
|
134
|
+
pin_aggregators(c->aggregators);
|
|
78
135
|
}
|
|
79
136
|
|
|
80
137
|
static void
|
|
@@ -114,7 +171,21 @@ allocate(VALUE klass)
|
|
|
114
171
|
static char *
|
|
115
172
|
utf16_string_value_ptr(VALUE str)
|
|
116
173
|
{
|
|
174
|
+
VALUE utf16str, codepoints;
|
|
175
|
+
|
|
117
176
|
StringValue(str);
|
|
177
|
+
|
|
178
|
+
utf16str = rb_str_dup(str);
|
|
179
|
+
if (!UTF16_LE_P(utf16str) && !UTF16_BE_P(utf16str)) {
|
|
180
|
+
/* an untagged string (the utf16 option) holds native-byte-order UTF-16 */
|
|
181
|
+
const union { uint16_t u16; uint8_t u8[2]; } native = { 1 };
|
|
182
|
+
rb_enc_associate_index(utf16str, rb_enc_find_index(native.u8[0] ? "UTF-16LE" : "UTF-16BE"));
|
|
183
|
+
}
|
|
184
|
+
codepoints = rb_funcall(utf16str, rb_intern("codepoints"), 0);
|
|
185
|
+
if (RTEST(rb_funcall(codepoints, rb_intern("include?"), 1, INT2FIX(0)))) {
|
|
186
|
+
rb_raise(rb_eArgError, "string contains null char");
|
|
187
|
+
}
|
|
188
|
+
|
|
118
189
|
rb_str_buf_cat(str, "\x00\x00", 2L);
|
|
119
190
|
return RSTRING_PTR(str);
|
|
120
191
|
}
|
|
@@ -135,6 +206,7 @@ rb_sqlite3_open_v2(VALUE self, VALUE file, VALUE mode, VALUE zvfs)
|
|
|
135
206
|
int flags;
|
|
136
207
|
|
|
137
208
|
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
|
209
|
+
REQUIRE_CLOSED_DB(ctx);
|
|
138
210
|
|
|
139
211
|
#if defined TAINTING_SUPPORT
|
|
140
212
|
# if defined StringValueCStr
|
|
@@ -147,13 +219,19 @@ rb_sqlite3_open_v2(VALUE self, VALUE file, VALUE mode, VALUE zvfs)
|
|
|
147
219
|
|
|
148
220
|
flags = NUM2INT(mode);
|
|
149
221
|
status = sqlite3_open_v2(
|
|
150
|
-
|
|
222
|
+
StringValueCStr(file),
|
|
151
223
|
&ctx->db,
|
|
152
224
|
flags,
|
|
153
|
-
NIL_P(zvfs) ? NULL :
|
|
225
|
+
NIL_P(zvfs) ? NULL : StringValueCStr(zvfs)
|
|
154
226
|
);
|
|
155
227
|
|
|
156
|
-
|
|
228
|
+
if (status != SQLITE_OK) {
|
|
229
|
+
char *msg = sqlite3_mprintf("%s", sqlite3_errmsg(ctx->db));
|
|
230
|
+
sqlite3_close_v2(ctx->db);
|
|
231
|
+
ctx->db = NULL;
|
|
232
|
+
CHECK_MSG(ctx->db, status, msg);
|
|
233
|
+
}
|
|
234
|
+
|
|
157
235
|
if (flags & SQLITE_OPEN_READONLY) {
|
|
158
236
|
ctx->flags |= SQLITE3_RB_DATABASE_READONLY;
|
|
159
237
|
}
|
|
@@ -246,9 +324,8 @@ total_changes(VALUE self)
|
|
|
246
324
|
static void
|
|
247
325
|
tracefunc(void *data, const char *sql)
|
|
248
326
|
{
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
rb_funcall(thing, rb_intern("call"), 1, rb_str_new2(sql));
|
|
327
|
+
sqlite3RubyPtr ctx = (sqlite3RubyPtr)data;
|
|
328
|
+
rb_funcall(ctx->trace_handler, rb_intern("call"), 1, rb_str_new2(sql));
|
|
252
329
|
}
|
|
253
330
|
|
|
254
331
|
/* call-seq:
|
|
@@ -272,9 +349,9 @@ trace(int argc, VALUE *argv, VALUE self)
|
|
|
272
349
|
|
|
273
350
|
if (NIL_P(block) && rb_block_given_p()) { block = rb_block_proc(); }
|
|
274
351
|
|
|
275
|
-
|
|
352
|
+
RB_OBJ_WRITE(self, &ctx->trace_handler, block);
|
|
276
353
|
|
|
277
|
-
sqlite3_trace(ctx->db, NIL_P(block) ? NULL : tracefunc, (void *)
|
|
354
|
+
sqlite3_trace(ctx->db, NIL_P(block) ? NULL : tracefunc, (void *)ctx);
|
|
278
355
|
|
|
279
356
|
return self;
|
|
280
357
|
}
|
|
@@ -398,7 +475,9 @@ sqlite3val2rb(sqlite3_value *val)
|
|
|
398
475
|
rb_val = rb_float_new(sqlite3_value_double(val));
|
|
399
476
|
break;
|
|
400
477
|
case SQLITE_TEXT: {
|
|
401
|
-
|
|
478
|
+
const char *text = (const char *)sqlite3_value_text(val);
|
|
479
|
+
int len = sqlite3_value_bytes(val);
|
|
480
|
+
rb_val = rb_utf8_str_new(text, len);
|
|
402
481
|
rb_obj_freeze(rb_val);
|
|
403
482
|
break;
|
|
404
483
|
}
|
|
@@ -466,24 +545,46 @@ set_sqlite3_func_result(sqlite3_context *ctx, VALUE result)
|
|
|
466
545
|
}
|
|
467
546
|
}
|
|
468
547
|
|
|
469
|
-
|
|
470
|
-
|
|
548
|
+
typedef struct {
|
|
549
|
+
sqlite3_context *ctx;
|
|
550
|
+
int argc;
|
|
551
|
+
sqlite3_value **argv;
|
|
552
|
+
} rb_sqlite3_func_args_t;
|
|
553
|
+
|
|
554
|
+
static VALUE
|
|
555
|
+
rb_sqlite3_func_protected(VALUE func_args_value)
|
|
471
556
|
{
|
|
472
|
-
|
|
473
|
-
VALUE
|
|
557
|
+
rb_sqlite3_func_args_t *args = (rb_sqlite3_func_args_t *)func_args_value;
|
|
558
|
+
VALUE callable = (VALUE)sqlite3_user_data(args->ctx);
|
|
559
|
+
VALUE params = rb_ary_new2(args->argc);
|
|
474
560
|
VALUE result;
|
|
475
561
|
int i;
|
|
476
562
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
rb_ary_push(params, param);
|
|
481
|
-
}
|
|
563
|
+
for (i = 0; i < args->argc; i++) {
|
|
564
|
+
VALUE param = sqlite3val2rb(args->argv[i]);
|
|
565
|
+
rb_ary_push(params, param);
|
|
482
566
|
}
|
|
483
567
|
|
|
484
568
|
result = rb_apply(callable, rb_intern("call"), params);
|
|
485
569
|
|
|
486
|
-
set_sqlite3_func_result(ctx, result);
|
|
570
|
+
set_sqlite3_func_result(args->ctx, result);
|
|
571
|
+
|
|
572
|
+
return Qnil;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
static void
|
|
576
|
+
rb_sqlite3_func(sqlite3_context *ctx, int argc, sqlite3_value **argv)
|
|
577
|
+
{
|
|
578
|
+
rb_sqlite3_func_args_t args = { .ctx = ctx, .argc = argc, .argv = argv };
|
|
579
|
+
int exc_status;
|
|
580
|
+
|
|
581
|
+
rb_protect(rb_sqlite3_func_protected, (VALUE)&args, &exc_status);
|
|
582
|
+
|
|
583
|
+
if (exc_status) {
|
|
584
|
+
/* the user should never see this message, because Statement#step will
|
|
585
|
+
* re-raise the exception still in rb_errinfo */
|
|
586
|
+
sqlite3_result_error(ctx, "Ruby Exception occurred", -1);
|
|
587
|
+
}
|
|
487
588
|
}
|
|
488
589
|
|
|
489
590
|
#ifndef HAVE_RB_PROC_ARITY
|
|
@@ -503,7 +604,7 @@ static VALUE
|
|
|
503
604
|
define_function_with_flags(VALUE self, VALUE name, VALUE flags)
|
|
504
605
|
{
|
|
505
606
|
sqlite3RubyPtr ctx;
|
|
506
|
-
VALUE block;
|
|
607
|
+
VALUE block, functions;
|
|
507
608
|
int status;
|
|
508
609
|
|
|
509
610
|
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
|
@@ -524,7 +625,9 @@ define_function_with_flags(VALUE self, VALUE name, VALUE flags)
|
|
|
524
625
|
|
|
525
626
|
CHECK(ctx->db, status);
|
|
526
627
|
|
|
527
|
-
|
|
628
|
+
functions = rb_iv_get(self, "@functions");
|
|
629
|
+
rb_ary_push(functions, block);
|
|
630
|
+
RB_OBJ_WRITE(self, &ctx->functions, functions);
|
|
528
631
|
|
|
529
632
|
return self;
|
|
530
633
|
}
|
|
@@ -626,14 +729,13 @@ rb_sqlite3_auth(
|
|
|
626
729
|
const char *_c,
|
|
627
730
|
const char *_d)
|
|
628
731
|
{
|
|
629
|
-
|
|
732
|
+
sqlite3RubyPtr db_ctx = (sqlite3RubyPtr)ctx;
|
|
630
733
|
VALUE action = INT2NUM(_action);
|
|
631
734
|
VALUE a = _a ? rb_str_new2(_a) : Qnil;
|
|
632
735
|
VALUE b = _b ? rb_str_new2(_b) : Qnil;
|
|
633
736
|
VALUE c = _c ? rb_str_new2(_c) : Qnil;
|
|
634
737
|
VALUE d = _d ? rb_str_new2(_d) : Qnil;
|
|
635
|
-
VALUE
|
|
636
|
-
VALUE result = rb_funcall(callback, rb_intern("call"), 5, action, a, b, c, d);
|
|
738
|
+
VALUE result = rb_funcall(db_ctx->authorizer, rb_intern("call"), 5, action, a, b, c, d);
|
|
637
739
|
|
|
638
740
|
if (T_FIXNUM == TYPE(result)) { return (int)NUM2INT(result); }
|
|
639
741
|
if (Qtrue == result) { return SQLITE_OK; }
|
|
@@ -662,12 +764,12 @@ set_authorizer(VALUE self, VALUE authorizer)
|
|
|
662
764
|
REQUIRE_OPEN_DB(ctx);
|
|
663
765
|
|
|
664
766
|
status = sqlite3_set_authorizer(
|
|
665
|
-
ctx->db, NIL_P(authorizer) ? NULL : rb_sqlite3_auth, (void *)
|
|
767
|
+
ctx->db, NIL_P(authorizer) ? NULL : rb_sqlite3_auth, (void *)ctx
|
|
666
768
|
);
|
|
667
769
|
|
|
668
770
|
CHECK(ctx->db, status);
|
|
669
771
|
|
|
670
|
-
|
|
772
|
+
RB_OBJ_WRITE(self, &ctx->authorizer, authorizer);
|
|
671
773
|
|
|
672
774
|
return self;
|
|
673
775
|
}
|
|
@@ -750,6 +852,7 @@ static VALUE
|
|
|
750
852
|
collation(VALUE self, VALUE name, VALUE comparator)
|
|
751
853
|
{
|
|
752
854
|
sqlite3RubyPtr ctx;
|
|
855
|
+
VALUE collations;
|
|
753
856
|
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
|
754
857
|
REQUIRE_OPEN_DB(ctx);
|
|
755
858
|
|
|
@@ -760,8 +863,10 @@ collation(VALUE self, VALUE name, VALUE comparator)
|
|
|
760
863
|
(void *)comparator,
|
|
761
864
|
NIL_P(comparator) ? NULL : rb_comparator_func));
|
|
762
865
|
|
|
763
|
-
/*
|
|
764
|
-
|
|
866
|
+
/* sqlite holds a raw pointer to the comparator, so keep it alive and unmoved. */
|
|
867
|
+
collations = rb_iv_get(self, "@collations");
|
|
868
|
+
rb_hash_aset(collations, name, comparator);
|
|
869
|
+
RB_OBJ_WRITE(self, &ctx->collations, collations);
|
|
765
870
|
|
|
766
871
|
return self;
|
|
767
872
|
}
|
|
@@ -926,6 +1031,7 @@ rb_sqlite3_open16(VALUE self, VALUE file)
|
|
|
926
1031
|
sqlite3RubyPtr ctx;
|
|
927
1032
|
|
|
928
1033
|
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
|
1034
|
+
REQUIRE_CLOSED_DB(ctx);
|
|
929
1035
|
|
|
930
1036
|
#if defined TAINTING_SUPPORT
|
|
931
1037
|
#if defined StringValueCStr
|
|
@@ -941,7 +1047,12 @@ rb_sqlite3_open16(VALUE self, VALUE file)
|
|
|
941
1047
|
// so we do not ever set SQLITE3_RB_DATABASE_READONLY in ctx->flags
|
|
942
1048
|
status = sqlite3_open16(utf16_string_value_ptr(file), &ctx->db);
|
|
943
1049
|
|
|
944
|
-
|
|
1050
|
+
if (status != SQLITE_OK) {
|
|
1051
|
+
char *msg = sqlite3_mprintf("%s", sqlite3_errmsg(ctx->db));
|
|
1052
|
+
sqlite3_close_v2(ctx->db);
|
|
1053
|
+
ctx->db = NULL;
|
|
1054
|
+
CHECK_MSG(ctx->db, status, msg);
|
|
1055
|
+
}
|
|
945
1056
|
|
|
946
1057
|
return INT2NUM(status);
|
|
947
1058
|
}
|
data/ext/sqlite3/database.h
CHANGED
|
@@ -10,6 +10,11 @@
|
|
|
10
10
|
struct _sqlite3Ruby {
|
|
11
11
|
sqlite3 *db;
|
|
12
12
|
VALUE busy_handler;
|
|
13
|
+
VALUE functions;
|
|
14
|
+
VALUE collations;
|
|
15
|
+
VALUE aggregators;
|
|
16
|
+
VALUE trace_handler;
|
|
17
|
+
VALUE authorizer;
|
|
13
18
|
int stmt_timeout;
|
|
14
19
|
struct timespec stmt_deadline;
|
|
15
20
|
rb_pid_t owner;
|
|
@@ -19,6 +24,9 @@ struct _sqlite3Ruby {
|
|
|
19
24
|
typedef struct _sqlite3Ruby sqlite3Ruby;
|
|
20
25
|
typedef sqlite3Ruby *sqlite3RubyPtr;
|
|
21
26
|
|
|
27
|
+
/* Pinning a collection doesn't pin what's in it, hence both. */
|
|
28
|
+
void rb_sqlite3_pin_array_and_contents(VALUE ary);
|
|
29
|
+
|
|
22
30
|
void init_sqlite3_database();
|
|
23
31
|
void set_sqlite3_func_result(sqlite3_context *ctx, VALUE result);
|
|
24
32
|
|
data/ext/sqlite3/extconf.rb
CHANGED
|
@@ -66,7 +66,7 @@ module Sqlite3
|
|
|
66
66
|
"-DSQLITE_ENABLE_DBSTAT_VTAB=1"
|
|
67
67
|
]
|
|
68
68
|
env["CFLAGS"] = [user_cflags, env["CFLAGS"], more_cflags].flatten.join(" ")
|
|
69
|
-
recipe.configure_options += env.
|
|
69
|
+
recipe.configure_options += env.slice(*ENV_ALLOWLIST)
|
|
70
70
|
.map { |key, value| "#{key}=#{value.strip}" }
|
|
71
71
|
end
|
|
72
72
|
|
data/lib/sqlite3/database.rb
CHANGED
|
@@ -171,8 +171,6 @@ module SQLite3
|
|
|
171
171
|
end
|
|
172
172
|
end
|
|
173
173
|
|
|
174
|
-
@tracefunc = nil
|
|
175
|
-
@authorizer = nil
|
|
176
174
|
@progress_handler = nil
|
|
177
175
|
@collations = {}
|
|
178
176
|
@functions = []
|
|
@@ -458,9 +456,7 @@ module SQLite3
|
|
|
458
456
|
#
|
|
459
457
|
# See also #create_aggregate_handler for a more object-oriented approach to
|
|
460
458
|
# aggregate functions.
|
|
461
|
-
def create_aggregate(name, arity, step = nil, finalize = nil,
|
|
462
|
-
text_rep = Constants::TextRep::ANY, &block)
|
|
463
|
-
|
|
459
|
+
def create_aggregate(name, arity, step = nil, finalize = nil, text_rep = Constants::TextRep::ANY, &block)
|
|
464
460
|
proxy = Class.new do
|
|
465
461
|
def self.step(&block)
|
|
466
462
|
define_method(:step_with_ctx, &block)
|
data/lib/sqlite3/version.rb
CHANGED
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.9.
|
|
4
|
+
version: 2.9.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jamis Buck
|
|
@@ -103,7 +103,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
103
103
|
- !ruby/object:Gem::Version
|
|
104
104
|
version: '0'
|
|
105
105
|
requirements: []
|
|
106
|
-
rubygems_version: 4.0.
|
|
106
|
+
rubygems_version: 4.0.16
|
|
107
107
|
specification_version: 4
|
|
108
108
|
summary: Ruby library to interface with the SQLite3 database engine (http://www.sqlite.org).
|
|
109
109
|
test_files: []
|