sqlite3 2.9.4-aarch64-linux-gnu → 2.9.6-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 +33 -0
- data/CONTRIBUTING.md +65 -0
- data/dependencies.yml +8 -8
- data/ext/sqlite3/aggregator.c +13 -3
- data/ext/sqlite3/aggregator.h +4 -0
- data/ext/sqlite3/database.c +140 -33
- data/ext/sqlite3/database.h +8 -0
- data/ext/sqlite3/extconf.rb +4 -4
- 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/database.rb +6 -6
- 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: b27b18bdfe1a60b32baeb87e20a8a73fb62d7e60b186e03e195033e5bceb85d8
|
|
4
|
+
data.tar.gz: 5593a8fb535f2dba0799c8976b119017301a39b8d35eb0ea2118a11c4a682fd8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3ce3290e924f5db6d68ff9ed49bbd8b4ca901829e7dd31de0883018421dad78b838c63c9cd9c69fd229c6f0cdc2599bf0dae70a564f1c0b6355102d88324f81c
|
|
7
|
+
data.tar.gz: a007455459ee2e57f313227704e915dd9d41c7c0a2088cdc0af95de07105abcf7f5df8cb79bdc39c85e84b370680801088c3c3ba5cf02ce2c3ced6036e2bfe87
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
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
|
+
|
|
24
|
+
## 2.9.5 / 2026-06-07
|
|
25
|
+
|
|
26
|
+
### Dependencies
|
|
27
|
+
|
|
28
|
+
- Vendored sqlite is updated to [v3.53.2](https://www.sqlite.org/releaselog/3_53_3.html) (from v3.53.1). #709 @flavorjones
|
|
29
|
+
|
|
30
|
+
### Security / Stability
|
|
31
|
+
|
|
32
|
+
- Fix use-after-free issue with custom functions, when the same function name is used with multiple arities. See https://github.com/sparklemotion/sqlite3-ruby/security/advisories/GHSA-28hh-pr2h-2w89 for more information. #710 @flavorjones
|
|
33
|
+
- Fix use-after-free issue with aggregate functions, if a statement is used after a soft database close. See https://github.com/sparklemotion/sqlite3-ruby/security/advisories/GHSA-j7fr-3v8c-3qc3 for more information. #711 @flavorjones
|
|
34
|
+
|
|
35
|
+
|
|
3
36
|
## 2.9.4 / 2026-05-05
|
|
4
37
|
|
|
5
38
|
- Vendored sqlite is updated to [v3.53.1](https://www.sqlite.org/releaselog/3_53_1.html) (from v3.53.0). #704 @flavorjones
|
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/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
|
+
# 025328da165109f48abccc6e7478508060804412bed2bd81d47e98ba1b72983b
|
|
4
4
|
#
|
|
5
|
-
# $ sha3sum -a 256 ports/archives/sqlite-autoconf-
|
|
6
|
-
#
|
|
5
|
+
# $ sha3sum -a 256 ports/archives/sqlite-autoconf-3530200.tar.gz
|
|
6
|
+
# 025328da165109f48abccc6e7478508060804412bed2bd81d47e98ba1b72983b ports/archives/sqlite-autoconf-3530200.tar.gz
|
|
7
7
|
#
|
|
8
|
-
# $ sha256sum ports/archives/sqlite-autoconf-
|
|
9
|
-
#
|
|
10
|
-
version: "3.53.
|
|
8
|
+
# $ sha256sum ports/archives/sqlite-autoconf-3530200.tar.gz
|
|
9
|
+
# 588ad51949419a56ebe81fe56193d510c559eb94c9a57748387860b5d3069316 ports/archives/sqlite-autoconf-3530200.tar.gz
|
|
10
|
+
version: "3.53.2"
|
|
11
11
|
files:
|
|
12
|
-
- url: "https://sqlite.org/2026/sqlite-autoconf-
|
|
13
|
-
sha256: "
|
|
12
|
+
- url: "https://sqlite.org/2026/sqlite-autoconf-3530200.tar.gz"
|
|
13
|
+
sha256: "588ad51949419a56ebe81fe56193d510c559eb94c9a57748387860b5d3069316"
|
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
|
}
|
|
@@ -198,8 +276,6 @@ sqlite3_rb_close(VALUE self)
|
|
|
198
276
|
|
|
199
277
|
close_or_discard_db(ctx);
|
|
200
278
|
|
|
201
|
-
rb_iv_set(self, "-aggregators", Qnil);
|
|
202
|
-
|
|
203
279
|
return self;
|
|
204
280
|
}
|
|
205
281
|
|
|
@@ -212,8 +288,6 @@ sqlite3_rb_discard(VALUE self)
|
|
|
212
288
|
|
|
213
289
|
discard_db(ctx);
|
|
214
290
|
|
|
215
|
-
rb_iv_set(self, "-aggregators", Qnil);
|
|
216
|
-
|
|
217
291
|
return self;
|
|
218
292
|
}
|
|
219
293
|
|
|
@@ -250,9 +324,8 @@ total_changes(VALUE self)
|
|
|
250
324
|
static void
|
|
251
325
|
tracefunc(void *data, const char *sql)
|
|
252
326
|
{
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
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));
|
|
256
329
|
}
|
|
257
330
|
|
|
258
331
|
/* call-seq:
|
|
@@ -276,9 +349,9 @@ trace(int argc, VALUE *argv, VALUE self)
|
|
|
276
349
|
|
|
277
350
|
if (NIL_P(block) && rb_block_given_p()) { block = rb_block_proc(); }
|
|
278
351
|
|
|
279
|
-
|
|
352
|
+
RB_OBJ_WRITE(self, &ctx->trace_handler, block);
|
|
280
353
|
|
|
281
|
-
sqlite3_trace(ctx->db, NIL_P(block) ? NULL : tracefunc, (void *)
|
|
354
|
+
sqlite3_trace(ctx->db, NIL_P(block) ? NULL : tracefunc, (void *)ctx);
|
|
282
355
|
|
|
283
356
|
return self;
|
|
284
357
|
}
|
|
@@ -402,7 +475,9 @@ sqlite3val2rb(sqlite3_value *val)
|
|
|
402
475
|
rb_val = rb_float_new(sqlite3_value_double(val));
|
|
403
476
|
break;
|
|
404
477
|
case SQLITE_TEXT: {
|
|
405
|
-
|
|
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);
|
|
406
481
|
rb_obj_freeze(rb_val);
|
|
407
482
|
break;
|
|
408
483
|
}
|
|
@@ -470,24 +545,46 @@ set_sqlite3_func_result(sqlite3_context *ctx, VALUE result)
|
|
|
470
545
|
}
|
|
471
546
|
}
|
|
472
547
|
|
|
473
|
-
|
|
474
|
-
|
|
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)
|
|
475
556
|
{
|
|
476
|
-
|
|
477
|
-
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);
|
|
478
560
|
VALUE result;
|
|
479
561
|
int i;
|
|
480
562
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
rb_ary_push(params, param);
|
|
485
|
-
}
|
|
563
|
+
for (i = 0; i < args->argc; i++) {
|
|
564
|
+
VALUE param = sqlite3val2rb(args->argv[i]);
|
|
565
|
+
rb_ary_push(params, param);
|
|
486
566
|
}
|
|
487
567
|
|
|
488
568
|
result = rb_apply(callable, rb_intern("call"), params);
|
|
489
569
|
|
|
490
|
-
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
|
+
}
|
|
491
588
|
}
|
|
492
589
|
|
|
493
590
|
#ifndef HAVE_RB_PROC_ARITY
|
|
@@ -507,7 +604,7 @@ static VALUE
|
|
|
507
604
|
define_function_with_flags(VALUE self, VALUE name, VALUE flags)
|
|
508
605
|
{
|
|
509
606
|
sqlite3RubyPtr ctx;
|
|
510
|
-
VALUE block;
|
|
607
|
+
VALUE block, functions;
|
|
511
608
|
int status;
|
|
512
609
|
|
|
513
610
|
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
|
@@ -528,7 +625,9 @@ define_function_with_flags(VALUE self, VALUE name, VALUE flags)
|
|
|
528
625
|
|
|
529
626
|
CHECK(ctx->db, status);
|
|
530
627
|
|
|
531
|
-
|
|
628
|
+
functions = rb_iv_get(self, "@functions");
|
|
629
|
+
rb_ary_push(functions, block);
|
|
630
|
+
RB_OBJ_WRITE(self, &ctx->functions, functions);
|
|
532
631
|
|
|
533
632
|
return self;
|
|
534
633
|
}
|
|
@@ -630,14 +729,13 @@ rb_sqlite3_auth(
|
|
|
630
729
|
const char *_c,
|
|
631
730
|
const char *_d)
|
|
632
731
|
{
|
|
633
|
-
|
|
732
|
+
sqlite3RubyPtr db_ctx = (sqlite3RubyPtr)ctx;
|
|
634
733
|
VALUE action = INT2NUM(_action);
|
|
635
734
|
VALUE a = _a ? rb_str_new2(_a) : Qnil;
|
|
636
735
|
VALUE b = _b ? rb_str_new2(_b) : Qnil;
|
|
637
736
|
VALUE c = _c ? rb_str_new2(_c) : Qnil;
|
|
638
737
|
VALUE d = _d ? rb_str_new2(_d) : Qnil;
|
|
639
|
-
VALUE
|
|
640
|
-
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);
|
|
641
739
|
|
|
642
740
|
if (T_FIXNUM == TYPE(result)) { return (int)NUM2INT(result); }
|
|
643
741
|
if (Qtrue == result) { return SQLITE_OK; }
|
|
@@ -666,12 +764,12 @@ set_authorizer(VALUE self, VALUE authorizer)
|
|
|
666
764
|
REQUIRE_OPEN_DB(ctx);
|
|
667
765
|
|
|
668
766
|
status = sqlite3_set_authorizer(
|
|
669
|
-
ctx->db, NIL_P(authorizer) ? NULL : rb_sqlite3_auth, (void *)
|
|
767
|
+
ctx->db, NIL_P(authorizer) ? NULL : rb_sqlite3_auth, (void *)ctx
|
|
670
768
|
);
|
|
671
769
|
|
|
672
770
|
CHECK(ctx->db, status);
|
|
673
771
|
|
|
674
|
-
|
|
772
|
+
RB_OBJ_WRITE(self, &ctx->authorizer, authorizer);
|
|
675
773
|
|
|
676
774
|
return self;
|
|
677
775
|
}
|
|
@@ -754,6 +852,7 @@ static VALUE
|
|
|
754
852
|
collation(VALUE self, VALUE name, VALUE comparator)
|
|
755
853
|
{
|
|
756
854
|
sqlite3RubyPtr ctx;
|
|
855
|
+
VALUE collations;
|
|
757
856
|
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
|
758
857
|
REQUIRE_OPEN_DB(ctx);
|
|
759
858
|
|
|
@@ -764,8 +863,10 @@ collation(VALUE self, VALUE name, VALUE comparator)
|
|
|
764
863
|
(void *)comparator,
|
|
765
864
|
NIL_P(comparator) ? NULL : rb_comparator_func));
|
|
766
865
|
|
|
767
|
-
/*
|
|
768
|
-
|
|
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);
|
|
769
870
|
|
|
770
871
|
return self;
|
|
771
872
|
}
|
|
@@ -930,6 +1031,7 @@ rb_sqlite3_open16(VALUE self, VALUE file)
|
|
|
930
1031
|
sqlite3RubyPtr ctx;
|
|
931
1032
|
|
|
932
1033
|
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
|
1034
|
+
REQUIRE_CLOSED_DB(ctx);
|
|
933
1035
|
|
|
934
1036
|
#if defined TAINTING_SUPPORT
|
|
935
1037
|
#if defined StringValueCStr
|
|
@@ -945,7 +1047,12 @@ rb_sqlite3_open16(VALUE self, VALUE file)
|
|
|
945
1047
|
// so we do not ever set SQLITE3_RB_DATABASE_READONLY in ctx->flags
|
|
946
1048
|
status = sqlite3_open16(utf16_string_value_ptr(file), &ctx->db);
|
|
947
1049
|
|
|
948
|
-
|
|
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
|
+
}
|
|
949
1056
|
|
|
950
1057
|
return INT2NUM(status);
|
|
951
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
|
|
|
@@ -105,10 +105,10 @@ module Sqlite3
|
|
|
105
105
|
def configure_extension
|
|
106
106
|
append_cflags("-fvisibility=hidden") # see https://github.com/rake-compiler/rake-compiler-dock/issues/87
|
|
107
107
|
|
|
108
|
-
if find_header("sqlite3.h")
|
|
109
|
-
# noop
|
|
110
|
-
elsif sqlcipher? && find_header("sqlcipher/sqlite3.h")
|
|
108
|
+
if sqlcipher? && find_header("sqlcipher/sqlite3.h")
|
|
111
109
|
append_cppflags("-DUSING_SQLCIPHER_INC_SUBDIR")
|
|
110
|
+
elsif find_header("sqlite3.h")
|
|
111
|
+
# noop
|
|
112
112
|
else
|
|
113
113
|
abort_could_not_find("sqlite3.h")
|
|
114
114
|
end
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
data/lib/sqlite3/database.rb
CHANGED
|
@@ -171,11 +171,9 @@ 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
|
-
@functions =
|
|
176
|
+
@functions = []
|
|
179
177
|
@results_as_hash = options[:results_as_hash]
|
|
180
178
|
@readonly = mode & Constants::Open::READONLY != 0
|
|
181
179
|
@default_transaction_mode = options[:default_transaction_mode] || :deferred
|
|
@@ -398,6 +396,8 @@ module SQLite3
|
|
|
398
396
|
# the FunctionProxy#result= method on the +func+ parameter and
|
|
399
397
|
# indicate the return value that way.
|
|
400
398
|
#
|
|
399
|
+
# A reference to the block will be kept for the lifetime of the database object.
|
|
400
|
+
#
|
|
401
401
|
# Example:
|
|
402
402
|
#
|
|
403
403
|
# db.create_function( "maim", 1 ) do |func, value|
|
|
@@ -437,6 +437,8 @@ module SQLite3
|
|
|
437
437
|
# function invocation. It should invoke FunctionProxy#result= to
|
|
438
438
|
# store the result of the function.
|
|
439
439
|
#
|
|
440
|
+
# A reference to the block will be kept for the lifetime of the database object.
|
|
441
|
+
#
|
|
440
442
|
# Example:
|
|
441
443
|
#
|
|
442
444
|
# db.create_aggregate( "lengths", 1 ) do
|
|
@@ -454,9 +456,7 @@ module SQLite3
|
|
|
454
456
|
#
|
|
455
457
|
# See also #create_aggregate_handler for a more object-oriented approach to
|
|
456
458
|
# aggregate functions.
|
|
457
|
-
def create_aggregate(name, arity, step = nil, finalize = nil,
|
|
458
|
-
text_rep = Constants::TextRep::ANY, &block)
|
|
459
|
-
|
|
459
|
+
def create_aggregate(name, arity, step = nil, finalize = nil, text_rep = Constants::TextRep::ANY, &block)
|
|
460
460
|
proxy = Class.new do
|
|
461
461
|
def self.step(&block)
|
|
462
462
|
define_method(:step_with_ctx, &block)
|
data/lib/sqlite3/version.rb
CHANGED