sqlite3 2.9.5-x86-linux-gnu → 2.10.0-x86-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 +53 -1
- 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 +287 -31
- data/ext/sqlite3/database.h +8 -0
- data/ext/sqlite3/extconf.rb +9 -3
- data/ext/sqlite3/statement.c +69 -3
- data/ext/sqlite3/timespec.h +9 -0
- 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 +1 -5
- data/lib/sqlite3/resultset.rb +10 -4
- data/lib/sqlite3/statement.rb +29 -10
- 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: dcc1773a27e9a4a7868b50b5c466c6825efc1aef50630e1ffe048f3d00543645
|
|
4
|
+
data.tar.gz: d14d21b19660313063a229b22c1b14346efab6f7fb5824e53e6df59cb0e7a1ff
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f6bd211b4d308eda12ba18a18d6ad5948545aa9e4722d4153fec3693ef85b7ddd37c14c741fd6eee519e23f4d268398469ec91a87404b9e134651ef4ec59c655
|
|
7
|
+
data.tar.gz: 97a661cc557f1be02a71fa942fd61e567401dc82bb6738311d4dee1b3dbc6a970811d662cefa901fc5651a0f9310b45909ecae3237c03b33fdec98bb1cd1a29a
|
data/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,62 @@
|
|
|
1
1
|
# sqlite3-ruby Changelog
|
|
2
2
|
|
|
3
|
+
## 2.10.0 / 2026-10-13
|
|
4
|
+
|
|
5
|
+
### Dependencies
|
|
6
|
+
|
|
7
|
+
- Vendored sqlite is updated to [v3.54.0](https://www.sqlite.org/releaselog/3_54_0.html) (from v3.53.2). #750 @flavorjones
|
|
8
|
+
|
|
9
|
+
Notable changes in this version include:
|
|
10
|
+
- `ALTER TABLE` now raises an error when adding, renaming, or dropping a column named `ROWID`, `_ROWID_`, or `OID`.
|
|
11
|
+
- The authorizer callback is now invoked for SQL functions in the `DEFAULT` clauses of `CREATE TABLE` statements.
|
|
12
|
+
- SQLite no longer supports Windows XP or earlier. On Windows, the precompiled gem and any installation that compiles the vendored sqlite now require Windows Vista or later.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- `ResultSet#each` and `#each_hash` return an `Enumerator` when called without a block. Previously they raised `LocalJumpError`. #656 @kch
|
|
17
|
+
- Introduce `Database#serialize`, which returns a database as a binary string, and `Database#deserialize`, which replaces a database with the contents of such a string. These wrap [`sqlite3_serialize()`](https://www.sqlite.org/c3ref/serialize.html) and [`sqlite3_deserialize()`](https://www.sqlite.org/c3ref/deserialize.html). Both methods are defined only when SQLite was compiled without `SQLITE_OMIT_DESERIALIZE`, and `Database#deserialize` also requires that the loaded SQLite is v3.54.0 or later. An image of a database in WAL mode cannot be queried after deserializing; see the `Database#deserialize` documentation. #685 @Maaarcocr
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- Fix `Database#statement_timeout=` interrupting statements almost immediately instead of after the timeout elapsed, including re-executed prepared statements and calls to `Database#execute_batch2`. #737 @palkan @flavorjones
|
|
22
|
+
|
|
23
|
+
### Improved
|
|
24
|
+
|
|
25
|
+
- On CRuby, `Statement#types` allocates fewer strings. Each declared type is downcased and interned in C, instead of allocating two strings per column. #743 @jhawthorn
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
|
|
29
|
+
- `ResultSet#each` and `#each_hash` return the result set when called with a block. As a consequence, `Database#execute` and `#execute2` called with a block return the closed `ResultSet` instead of `nil`. #656 @kch
|
|
30
|
+
- `Statement#columns` and `Statement#types` return frozen arrays, and the strings in `Statement#types` are frozen. Code that mutates them now raises `FrozenError`. #743 @jhawthorn
|
|
31
|
+
- `Statement#column_decltype` and `Statement#types` return UTF-8 strings. Previously they returned ASCII-8BIT strings. #743 @jhawthorn
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
## 2.9.6 / 2026-08-11
|
|
35
|
+
|
|
36
|
+
### Security / Stability
|
|
37
|
+
|
|
38
|
+
- 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
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
|
|
42
|
+
- 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
|
|
43
|
+
- 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
|
|
44
|
+
- 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
|
|
45
|
+
- 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
|
|
46
|
+
- 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
|
|
47
|
+
- `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
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
### Improved
|
|
51
|
+
|
|
52
|
+
- 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
|
|
53
|
+
|
|
54
|
+
|
|
3
55
|
## 2.9.5 / 2026-06-07
|
|
4
56
|
|
|
5
57
|
### Dependencies
|
|
6
58
|
|
|
7
|
-
- Vendored sqlite is updated to [v3.53.2](https://www.sqlite.org/releaselog/
|
|
59
|
+
- Vendored sqlite is updated to [v3.53.2](https://www.sqlite.org/releaselog/3_53_2.html) (from v3.53.1). #709 @flavorjones
|
|
8
60
|
|
|
9
61
|
### Security / Stability
|
|
10
62
|
|
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
|
+
# f5e3e724e9c9b3b5d512c76b02082091a59eeea9becd169a9fbf4d376403acce
|
|
4
4
|
#
|
|
5
|
-
# $ sha3sum -a 256 ports/archives/sqlite-autoconf-
|
|
6
|
-
#
|
|
5
|
+
# $ sha3sum -a 256 ports/archives/sqlite-autoconf-3540000.tar.gz
|
|
6
|
+
# f5e3e724e9c9b3b5d512c76b02082091a59eeea9becd169a9fbf4d376403acce ports/archives/sqlite-autoconf-3540000.tar.gz
|
|
7
7
|
#
|
|
8
|
-
# $ sha256sum ports/archives/sqlite-autoconf-
|
|
9
|
-
#
|
|
10
|
-
version: "3.
|
|
8
|
+
# $ sha256sum ports/archives/sqlite-autoconf-3540000.tar.gz
|
|
9
|
+
# 134ec0802dda5795816e25d25872d20b312cb3973438c49b30bc40b7705ea9ed ports/archives/sqlite-autoconf-3540000.tar.gz
|
|
10
|
+
version: "3.54.0"
|
|
11
11
|
files:
|
|
12
|
-
- url: "https://sqlite.org/2026/sqlite-autoconf-
|
|
13
|
-
sha256: "
|
|
12
|
+
- url: "https://sqlite.org/2026/sqlite-autoconf-3540000.tar.gz"
|
|
13
|
+
sha256: "134ec0802dda5795816e25d25872d20b312cb3973438c49b30bc40b7705ea9ed"
|
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
|
}
|
|
@@ -340,8 +417,10 @@ rb_sqlite3_statement_timeout(void *context)
|
|
|
340
417
|
clock_gettime(CLOCK_MONOTONIC, ¤tTime);
|
|
341
418
|
|
|
342
419
|
if (!timespecisset(&ctx->stmt_deadline)) {
|
|
343
|
-
|
|
344
|
-
ctx->
|
|
420
|
+
struct timespec timeout;
|
|
421
|
+
timeout.tv_sec = ctx->stmt_timeout / 1000;
|
|
422
|
+
timeout.tv_nsec = (ctx->stmt_timeout % 1000) * 1000000L;
|
|
423
|
+
timespecadd(¤tTime, &timeout, &ctx->stmt_deadline);
|
|
345
424
|
} else if (timespecafter(¤tTime, &ctx->stmt_deadline)) {
|
|
346
425
|
return 1;
|
|
347
426
|
}
|
|
@@ -398,7 +477,9 @@ sqlite3val2rb(sqlite3_value *val)
|
|
|
398
477
|
rb_val = rb_float_new(sqlite3_value_double(val));
|
|
399
478
|
break;
|
|
400
479
|
case SQLITE_TEXT: {
|
|
401
|
-
|
|
480
|
+
const char *text = (const char *)sqlite3_value_text(val);
|
|
481
|
+
int len = sqlite3_value_bytes(val);
|
|
482
|
+
rb_val = rb_utf8_str_new(text, len);
|
|
402
483
|
rb_obj_freeze(rb_val);
|
|
403
484
|
break;
|
|
404
485
|
}
|
|
@@ -466,24 +547,46 @@ set_sqlite3_func_result(sqlite3_context *ctx, VALUE result)
|
|
|
466
547
|
}
|
|
467
548
|
}
|
|
468
549
|
|
|
469
|
-
|
|
470
|
-
|
|
550
|
+
typedef struct {
|
|
551
|
+
sqlite3_context *ctx;
|
|
552
|
+
int argc;
|
|
553
|
+
sqlite3_value **argv;
|
|
554
|
+
} rb_sqlite3_func_args_t;
|
|
555
|
+
|
|
556
|
+
static VALUE
|
|
557
|
+
rb_sqlite3_func_protected(VALUE func_args_value)
|
|
471
558
|
{
|
|
472
|
-
|
|
473
|
-
VALUE
|
|
559
|
+
rb_sqlite3_func_args_t *args = (rb_sqlite3_func_args_t *)func_args_value;
|
|
560
|
+
VALUE callable = (VALUE)sqlite3_user_data(args->ctx);
|
|
561
|
+
VALUE params = rb_ary_new2(args->argc);
|
|
474
562
|
VALUE result;
|
|
475
563
|
int i;
|
|
476
564
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
rb_ary_push(params, param);
|
|
481
|
-
}
|
|
565
|
+
for (i = 0; i < args->argc; i++) {
|
|
566
|
+
VALUE param = sqlite3val2rb(args->argv[i]);
|
|
567
|
+
rb_ary_push(params, param);
|
|
482
568
|
}
|
|
483
569
|
|
|
484
570
|
result = rb_apply(callable, rb_intern("call"), params);
|
|
485
571
|
|
|
486
|
-
set_sqlite3_func_result(ctx, result);
|
|
572
|
+
set_sqlite3_func_result(args->ctx, result);
|
|
573
|
+
|
|
574
|
+
return Qnil;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
static void
|
|
578
|
+
rb_sqlite3_func(sqlite3_context *ctx, int argc, sqlite3_value **argv)
|
|
579
|
+
{
|
|
580
|
+
rb_sqlite3_func_args_t args = { .ctx = ctx, .argc = argc, .argv = argv };
|
|
581
|
+
int exc_status;
|
|
582
|
+
|
|
583
|
+
rb_protect(rb_sqlite3_func_protected, (VALUE)&args, &exc_status);
|
|
584
|
+
|
|
585
|
+
if (exc_status) {
|
|
586
|
+
/* the user should never see this message, because Statement#step will
|
|
587
|
+
* re-raise the exception still in rb_errinfo */
|
|
588
|
+
sqlite3_result_error(ctx, "Ruby Exception occurred", -1);
|
|
589
|
+
}
|
|
487
590
|
}
|
|
488
591
|
|
|
489
592
|
#ifndef HAVE_RB_PROC_ARITY
|
|
@@ -503,7 +606,7 @@ static VALUE
|
|
|
503
606
|
define_function_with_flags(VALUE self, VALUE name, VALUE flags)
|
|
504
607
|
{
|
|
505
608
|
sqlite3RubyPtr ctx;
|
|
506
|
-
VALUE block;
|
|
609
|
+
VALUE block, functions;
|
|
507
610
|
int status;
|
|
508
611
|
|
|
509
612
|
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
|
@@ -524,7 +627,9 @@ define_function_with_flags(VALUE self, VALUE name, VALUE flags)
|
|
|
524
627
|
|
|
525
628
|
CHECK(ctx->db, status);
|
|
526
629
|
|
|
527
|
-
|
|
630
|
+
functions = rb_iv_get(self, "@functions");
|
|
631
|
+
rb_ary_push(functions, block);
|
|
632
|
+
RB_OBJ_WRITE(self, &ctx->functions, functions);
|
|
528
633
|
|
|
529
634
|
return self;
|
|
530
635
|
}
|
|
@@ -626,14 +731,13 @@ rb_sqlite3_auth(
|
|
|
626
731
|
const char *_c,
|
|
627
732
|
const char *_d)
|
|
628
733
|
{
|
|
629
|
-
|
|
734
|
+
sqlite3RubyPtr db_ctx = (sqlite3RubyPtr)ctx;
|
|
630
735
|
VALUE action = INT2NUM(_action);
|
|
631
736
|
VALUE a = _a ? rb_str_new2(_a) : Qnil;
|
|
632
737
|
VALUE b = _b ? rb_str_new2(_b) : Qnil;
|
|
633
738
|
VALUE c = _c ? rb_str_new2(_c) : Qnil;
|
|
634
739
|
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);
|
|
740
|
+
VALUE result = rb_funcall(db_ctx->authorizer, rb_intern("call"), 5, action, a, b, c, d);
|
|
637
741
|
|
|
638
742
|
if (T_FIXNUM == TYPE(result)) { return (int)NUM2INT(result); }
|
|
639
743
|
if (Qtrue == result) { return SQLITE_OK; }
|
|
@@ -662,12 +766,12 @@ set_authorizer(VALUE self, VALUE authorizer)
|
|
|
662
766
|
REQUIRE_OPEN_DB(ctx);
|
|
663
767
|
|
|
664
768
|
status = sqlite3_set_authorizer(
|
|
665
|
-
ctx->db, NIL_P(authorizer) ? NULL : rb_sqlite3_auth, (void *)
|
|
769
|
+
ctx->db, NIL_P(authorizer) ? NULL : rb_sqlite3_auth, (void *)ctx
|
|
666
770
|
);
|
|
667
771
|
|
|
668
772
|
CHECK(ctx->db, status);
|
|
669
773
|
|
|
670
|
-
|
|
774
|
+
RB_OBJ_WRITE(self, &ctx->authorizer, authorizer);
|
|
671
775
|
|
|
672
776
|
return self;
|
|
673
777
|
}
|
|
@@ -750,6 +854,7 @@ static VALUE
|
|
|
750
854
|
collation(VALUE self, VALUE name, VALUE comparator)
|
|
751
855
|
{
|
|
752
856
|
sqlite3RubyPtr ctx;
|
|
857
|
+
VALUE collations;
|
|
753
858
|
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
|
754
859
|
REQUIRE_OPEN_DB(ctx);
|
|
755
860
|
|
|
@@ -760,8 +865,10 @@ collation(VALUE self, VALUE name, VALUE comparator)
|
|
|
760
865
|
(void *)comparator,
|
|
761
866
|
NIL_P(comparator) ? NULL : rb_comparator_func));
|
|
762
867
|
|
|
763
|
-
/*
|
|
764
|
-
|
|
868
|
+
/* sqlite holds a raw pointer to the comparator, so keep it alive and unmoved. */
|
|
869
|
+
collations = rb_iv_get(self, "@collations");
|
|
870
|
+
rb_hash_aset(collations, name, comparator);
|
|
871
|
+
RB_OBJ_WRITE(self, &ctx->collations, collations);
|
|
765
872
|
|
|
766
873
|
return self;
|
|
767
874
|
}
|
|
@@ -885,6 +992,8 @@ exec_batch(VALUE self, VALUE sql, VALUE results_as_hash)
|
|
|
885
992
|
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
|
886
993
|
REQUIRE_OPEN_DB(ctx);
|
|
887
994
|
|
|
995
|
+
timespecclear(&ctx->stmt_deadline);
|
|
996
|
+
|
|
888
997
|
if (results_as_hash == Qtrue) {
|
|
889
998
|
status = sqlite3_exec(ctx->db, StringValuePtr(sql), (sqlite3_callback)hash_callback_function,
|
|
890
999
|
(void *)callback_ary,
|
|
@@ -919,6 +1028,137 @@ db_filename(VALUE self, VALUE db_name)
|
|
|
919
1028
|
return Qnil;
|
|
920
1029
|
}
|
|
921
1030
|
|
|
1031
|
+
#ifdef HAVE_SQLITE3_SERIALIZE
|
|
1032
|
+
/* call-seq: db.serialize(database_name = "main")
|
|
1033
|
+
*
|
|
1034
|
+
* Serialize the database into a binary string.
|
|
1035
|
+
*
|
|
1036
|
+
* [database_name] The name of the database to serialize. The default value
|
|
1037
|
+
* is +main+ which is the main database file. For ATTACHed
|
|
1038
|
+
* databases, use the name given in the ATTACH statement.
|
|
1039
|
+
*
|
|
1040
|
+
* Returns the contents of the database as a binary string. For an ordinary
|
|
1041
|
+
* on-disk database file, this is just a copy of the disk file. For an
|
|
1042
|
+
* in-memory database or a "TEMP" database, this is the same sequence of bytes
|
|
1043
|
+
* which would be written to disk if that database were backed up to disk.
|
|
1044
|
+
*
|
|
1045
|
+
* Note: This method will only be defined if SQLite was compiled without
|
|
1046
|
+
* +SQLITE_OMIT_DESERIALIZE+.
|
|
1047
|
+
*
|
|
1048
|
+
* See also: https://www.sqlite.org/c3ref/serialize.html
|
|
1049
|
+
*/
|
|
1050
|
+
static VALUE
|
|
1051
|
+
serialize(int argc, VALUE *argv, VALUE self)
|
|
1052
|
+
{
|
|
1053
|
+
sqlite3RubyPtr ctx;
|
|
1054
|
+
VALUE db_name;
|
|
1055
|
+
const char *zSchema;
|
|
1056
|
+
sqlite3_int64 size;
|
|
1057
|
+
unsigned char *data;
|
|
1058
|
+
VALUE result;
|
|
1059
|
+
|
|
1060
|
+
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
|
1061
|
+
REQUIRE_OPEN_DB(ctx);
|
|
1062
|
+
|
|
1063
|
+
rb_scan_args(argc, argv, "01", &db_name);
|
|
1064
|
+
|
|
1065
|
+
zSchema = NIL_P(db_name) ? NULL : StringValueCStr(db_name);
|
|
1066
|
+
|
|
1067
|
+
data = sqlite3_serialize(ctx->db, zSchema, &size, 0);
|
|
1068
|
+
|
|
1069
|
+
if (data == NULL && size < 0) {
|
|
1070
|
+
rb_raise(rb_path2class("SQLite3::Exception"), "unable to serialize database \"%s\"",
|
|
1071
|
+
zSchema ? zSchema : "main");
|
|
1072
|
+
}
|
|
1073
|
+
if (data == NULL && size > 0) {
|
|
1074
|
+
rb_raise(rb_eNoMemError, "failed to allocate %lld bytes for serialization", (long long)size);
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
result = rb_str_new((const char *)data, (long)size);
|
|
1078
|
+
sqlite3_free(data);
|
|
1079
|
+
|
|
1080
|
+
return result;
|
|
1081
|
+
}
|
|
1082
|
+
#endif
|
|
1083
|
+
|
|
1084
|
+
#ifdef HAVE_SQLITE3_DESERIALIZE
|
|
1085
|
+
/* call-seq: db.deserialize(data, database_name = "main")
|
|
1086
|
+
*
|
|
1087
|
+
* Deserialize a binary string into the database. The database is replaced with
|
|
1088
|
+
* the contents of the serialized data.
|
|
1089
|
+
*
|
|
1090
|
+
* [data] A binary string containing the serialized database content.
|
|
1091
|
+
* This is typically obtained from a prior call to #serialize.
|
|
1092
|
+
*
|
|
1093
|
+
* [database_name] The name of the database to replace. The default value is
|
|
1094
|
+
* +main+ which is the main database file. For ATTACHed
|
|
1095
|
+
* databases, use the name given in the ATTACH statement.
|
|
1096
|
+
*
|
|
1097
|
+
* Raises SQLite3::BusyException if the database has an open read or write
|
|
1098
|
+
* transaction, including one held by an unfinished statement, or is involved in
|
|
1099
|
+
* a backup.
|
|
1100
|
+
*
|
|
1101
|
+
* Deserializing into a database backed by a file detaches the connection from
|
|
1102
|
+
* that file. The file is left unchanged, and #filename(database_name) returns an
|
|
1103
|
+
* empty string afterwards.
|
|
1104
|
+
*
|
|
1105
|
+
* ⚠ An image of a database in WAL mode deserializes without error, but later
|
|
1106
|
+
* statements, even <tt>SELECT 1</tt>, raise SQLite3::CantOpenException. Rails
|
|
1107
|
+
* enables WAL mode by default.
|
|
1108
|
+
*
|
|
1109
|
+
* ⚠ Deserializing untrusted data carries the same risks as opening an untrusted
|
|
1110
|
+
* database file. See https://www.sqlite.org/security.html
|
|
1111
|
+
*
|
|
1112
|
+
* Note: This method will only be defined if the loaded SQLite is version 3.54.0
|
|
1113
|
+
* or later and was compiled without +SQLITE_OMIT_DESERIALIZE+. Older versions do
|
|
1114
|
+
* not raise SQLite3::BusyException; they free memory that an unfinished statement
|
|
1115
|
+
* still uses.
|
|
1116
|
+
*
|
|
1117
|
+
* See also: https://www.sqlite.org/c3ref/deserialize.html
|
|
1118
|
+
*/
|
|
1119
|
+
static VALUE
|
|
1120
|
+
deserialize(int argc, VALUE *argv, VALUE self)
|
|
1121
|
+
{
|
|
1122
|
+
sqlite3RubyPtr ctx;
|
|
1123
|
+
VALUE data, db_name;
|
|
1124
|
+
const char *zSchema;
|
|
1125
|
+
unsigned char *pData;
|
|
1126
|
+
sqlite3_int64 szDb, szBuf;
|
|
1127
|
+
int status;
|
|
1128
|
+
|
|
1129
|
+
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
|
1130
|
+
REQUIRE_OPEN_DB(ctx);
|
|
1131
|
+
|
|
1132
|
+
rb_scan_args(argc, argv, "11", &data, &db_name);
|
|
1133
|
+
|
|
1134
|
+
StringValue(data);
|
|
1135
|
+
zSchema = NIL_P(db_name) ? NULL : StringValueCStr(db_name);
|
|
1136
|
+
szDb = RSTRING_LEN(data);
|
|
1137
|
+
|
|
1138
|
+
/* SQLite's docs advise padding the buffer with about 20 extra bytes, because a maliciously
|
|
1139
|
+
* malformed image may make SQLite read past its end. See https://www.sqlite.org/c3ref/deserialize.html
|
|
1140
|
+
*
|
|
1141
|
+
* With SQLITE_DESERIALIZE_RESIZEABLE set, SQLite copies pages out of the buffer with bounds
|
|
1142
|
+
* checks instead of reading it in place, so the padding is defensive. It also keeps the
|
|
1143
|
+
* allocation nonzero for an empty string, for which sqlite3_malloc64(0) returns NULL.
|
|
1144
|
+
*/
|
|
1145
|
+
szBuf = szDb + 20;
|
|
1146
|
+
pData = sqlite3_malloc64(szBuf);
|
|
1147
|
+
if (pData == NULL) {
|
|
1148
|
+
rb_raise(rb_eNoMemError, "failed to allocate %lld bytes for deserialization", (long long)szBuf);
|
|
1149
|
+
}
|
|
1150
|
+
memcpy(pData, RSTRING_PTR(data), (size_t)szDb);
|
|
1151
|
+
memset(pData + szDb, 0, (size_t)(szBuf - szDb));
|
|
1152
|
+
|
|
1153
|
+
status = sqlite3_deserialize(ctx->db, zSchema, pData, szDb, szDb,
|
|
1154
|
+
SQLITE_DESERIALIZE_FREEONCLOSE | SQLITE_DESERIALIZE_RESIZEABLE);
|
|
1155
|
+
|
|
1156
|
+
CHECK(ctx->db, status);
|
|
1157
|
+
|
|
1158
|
+
return self;
|
|
1159
|
+
}
|
|
1160
|
+
#endif
|
|
1161
|
+
|
|
922
1162
|
static VALUE
|
|
923
1163
|
rb_sqlite3_open16(VALUE self, VALUE file)
|
|
924
1164
|
{
|
|
@@ -926,6 +1166,7 @@ rb_sqlite3_open16(VALUE self, VALUE file)
|
|
|
926
1166
|
sqlite3RubyPtr ctx;
|
|
927
1167
|
|
|
928
1168
|
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
|
1169
|
+
REQUIRE_CLOSED_DB(ctx);
|
|
929
1170
|
|
|
930
1171
|
#if defined TAINTING_SUPPORT
|
|
931
1172
|
#if defined StringValueCStr
|
|
@@ -941,7 +1182,12 @@ rb_sqlite3_open16(VALUE self, VALUE file)
|
|
|
941
1182
|
// so we do not ever set SQLITE3_RB_DATABASE_READONLY in ctx->flags
|
|
942
1183
|
status = sqlite3_open16(utf16_string_value_ptr(file), &ctx->db);
|
|
943
1184
|
|
|
944
|
-
|
|
1185
|
+
if (status != SQLITE_OK) {
|
|
1186
|
+
char *msg = sqlite3_mprintf("%s", sqlite3_errmsg(ctx->db));
|
|
1187
|
+
sqlite3_close_v2(ctx->db);
|
|
1188
|
+
ctx->db = NULL;
|
|
1189
|
+
CHECK_MSG(ctx->db, status, msg);
|
|
1190
|
+
}
|
|
945
1191
|
|
|
946
1192
|
return INT2NUM(status);
|
|
947
1193
|
}
|
|
@@ -993,6 +1239,16 @@ init_sqlite3_database(void)
|
|
|
993
1239
|
#endif
|
|
994
1240
|
#endif
|
|
995
1241
|
|
|
1242
|
+
#ifdef HAVE_SQLITE3_SERIALIZE
|
|
1243
|
+
rb_define_method(cSqlite3Database, "serialize", serialize, -1);
|
|
1244
|
+
#endif
|
|
1245
|
+
|
|
1246
|
+
#ifdef HAVE_SQLITE3_DESERIALIZE
|
|
1247
|
+
/* Before 3.54.0, sqlite3_deserialize frees memory that an unfinished statement still uses. */
|
|
1248
|
+
if (sqlite3_libversion_number() >= 3054000) {
|
|
1249
|
+
rb_define_method(cSqlite3Database, "deserialize", deserialize, -1);
|
|
1250
|
+
}
|
|
1251
|
+
#endif
|
|
996
1252
|
|
|
997
1253
|
rb_sqlite3_aggregator_init();
|
|
998
1254
|
}
|
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
|
@@ -65,8 +65,11 @@ module Sqlite3
|
|
|
65
65
|
"-DSQLITE_ENABLE_DBPAGE_VTAB=1",
|
|
66
66
|
"-DSQLITE_ENABLE_DBSTAT_VTAB=1"
|
|
67
67
|
]
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
# sqlite >= 3.54 requires Vista APIs, same as ruby's default target. Placed first so a
|
|
69
|
+
# user-defined _WIN32_WINNT takes precedence.
|
|
70
|
+
windows_cflags = windows? ? ["-D_WIN32_WINNT=0x0600"] : []
|
|
71
|
+
env["CFLAGS"] = [windows_cflags, user_cflags, env["CFLAGS"], more_cflags].flatten.join(" ")
|
|
72
|
+
recipe.configure_options += env.slice(*ENV_ALLOWLIST)
|
|
70
73
|
.map { |key, value| "#{key}=#{value.strip}" }
|
|
71
74
|
end
|
|
72
75
|
|
|
@@ -115,9 +118,10 @@ module Sqlite3
|
|
|
115
118
|
|
|
116
119
|
abort_could_not_find(libname) unless find_library(libname, "sqlite3_libversion_number", "sqlite3.h")
|
|
117
120
|
|
|
118
|
-
#
|
|
121
|
+
# Ruby 3.0+, but not on every implementation:
|
|
119
122
|
# https://github.com/oracle/truffleruby/issues/3408
|
|
120
123
|
have_func("rb_enc_interned_str_cstr")
|
|
124
|
+
have_func("rb_enc_interned_str")
|
|
121
125
|
|
|
122
126
|
# Functions defined in 1.9 but not 1.8
|
|
123
127
|
have_func("rb_proc_arity")
|
|
@@ -139,6 +143,8 @@ module Sqlite3
|
|
|
139
143
|
have_func("sqlite3_prepare_v2")
|
|
140
144
|
have_func("sqlite3_db_name", "sqlite3.h") # v3.39.0
|
|
141
145
|
have_func("sqlite3_error_offset", "sqlite3.h") # v3.38.0
|
|
146
|
+
have_func("sqlite3_serialize", "sqlite3.h") # v3.23.0
|
|
147
|
+
have_func("sqlite3_deserialize", "sqlite3.h") # v3.23.0
|
|
142
148
|
|
|
143
149
|
have_type("sqlite3_int64", "sqlite3.h")
|
|
144
150
|
have_type("sqlite3_uint64", "sqlite3.h")
|
data/ext/sqlite3/statement.c
CHANGED
|
@@ -66,6 +66,8 @@ prepare(VALUE self, VALUE db, VALUE sql)
|
|
|
66
66
|
* variable will keep it from being GCed. */
|
|
67
67
|
ctx->db = db_ctx;
|
|
68
68
|
|
|
69
|
+
timespecclear(&db_ctx->stmt_deadline);
|
|
70
|
+
|
|
69
71
|
#ifdef HAVE_SQLITE3_PREPARE_V2
|
|
70
72
|
status = sqlite3_prepare_v2(
|
|
71
73
|
#else
|
|
@@ -79,7 +81,6 @@ prepare(VALUE self, VALUE db, VALUE sql)
|
|
|
79
81
|
);
|
|
80
82
|
|
|
81
83
|
CHECK_PREPARE(db_ctx->db, status, StringValuePtr(sql));
|
|
82
|
-
timespecclear(&db_ctx->stmt_deadline);
|
|
83
84
|
|
|
84
85
|
return rb_utf8_str_new_cstr(tail);
|
|
85
86
|
}
|
|
@@ -139,6 +140,10 @@ step(VALUE self)
|
|
|
139
140
|
|
|
140
141
|
stmt = ctx->st;
|
|
141
142
|
|
|
143
|
+
if (!sqlite3_stmt_busy(stmt)) {
|
|
144
|
+
timespecclear(&ctx->db->stmt_deadline);
|
|
145
|
+
}
|
|
146
|
+
|
|
142
147
|
value = sqlite3_step(stmt);
|
|
143
148
|
if (rb_errinfo() != Qnil) {
|
|
144
149
|
/* some user defined function was invoked as a callback during step and
|
|
@@ -216,6 +221,21 @@ step(VALUE self)
|
|
|
216
221
|
* Fixnum, it is treated as an index for a positional placeholder.
|
|
217
222
|
* Otherwise it is used as the name of the placeholder to bind to.
|
|
218
223
|
*
|
|
224
|
+
* Type mapping from Ruby to SQLite3:
|
|
225
|
+
* - +nil+ → NULL
|
|
226
|
+
* - Integer → INTEGER (or REAL when outside the signed int64 range)
|
|
227
|
+
* - Float → REAL
|
|
228
|
+
* - SQLite3::Blob → BLOB
|
|
229
|
+
* - String with Encoding::ASCII_8BIT (a.k.a. BINARY) → BLOB
|
|
230
|
+
* - String with Encoding::UTF_16LE or Encoding::UTF_16BE → TEXT (bound as UTF-16)
|
|
231
|
+
* - String (all other encodings) → TEXT (re-encoded to UTF-8 if necessary)
|
|
232
|
+
* - Any other type → raises RuntimeError
|
|
233
|
+
*
|
|
234
|
+
* Note: if you have a string with only ASCII characters but ASCII-8BIT
|
|
235
|
+
* encoding and want it bound as TEXT rather than BLOB, re-encode it first:
|
|
236
|
+
*
|
|
237
|
+
* stmt.bind_param(1, my_binary_str.encode(Encoding::UTF_8))
|
|
238
|
+
*
|
|
219
239
|
* See also #bind_params.
|
|
220
240
|
*/
|
|
221
241
|
static VALUE
|
|
@@ -398,6 +418,33 @@ interned_utf8_cstr(const char *str)
|
|
|
398
418
|
}
|
|
399
419
|
#endif
|
|
400
420
|
|
|
421
|
+
/* Like interned_utf8_cstr, but ASCII-downcases as it copies. */
|
|
422
|
+
static VALUE
|
|
423
|
+
interned_downcased_utf8_cstr(const char *str)
|
|
424
|
+
{
|
|
425
|
+
#if HAVE_RB_ENC_INTERNED_STR
|
|
426
|
+
VALUE tmp, ret;
|
|
427
|
+
char *buf;
|
|
428
|
+
size_t i, len;
|
|
429
|
+
|
|
430
|
+
len = strlen(str);
|
|
431
|
+
buf = ALLOCV_N(char, tmp, len);
|
|
432
|
+
|
|
433
|
+
for (i = 0; i < len; i++) {
|
|
434
|
+
unsigned char c = (unsigned char)str[i];
|
|
435
|
+
buf[i] = (char)((c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
ret = rb_enc_interned_str(buf, (long)len, rb_utf8_encoding());
|
|
439
|
+
ALLOCV_END(tmp);
|
|
440
|
+
return ret;
|
|
441
|
+
#else
|
|
442
|
+
VALUE rb_str = rb_funcall(rb_utf8_str_new_cstr(str), rb_intern("downcase"), 1,
|
|
443
|
+
ID2SYM(rb_intern("ascii")));
|
|
444
|
+
return rb_funcall(rb_str, rb_intern("-@"), 0);
|
|
445
|
+
#endif
|
|
446
|
+
}
|
|
447
|
+
|
|
401
448
|
/* call-seq: stmt.column_name(index)
|
|
402
449
|
*
|
|
403
450
|
* Get the column name at +index+. 0 based.
|
|
@@ -425,7 +472,8 @@ column_name(VALUE self, VALUE index)
|
|
|
425
472
|
|
|
426
473
|
/* call-seq: stmt.column_decltype(index)
|
|
427
474
|
*
|
|
428
|
-
* Get the
|
|
475
|
+
* Get the declared type of the column at +index+, as a UTF-8 string. 0 based.
|
|
476
|
+
* Returns +nil+ if the column has no declared type, such as an expression.
|
|
429
477
|
*/
|
|
430
478
|
static VALUE
|
|
431
479
|
column_decltype(VALUE self, VALUE index)
|
|
@@ -440,7 +488,24 @@ column_decltype(VALUE self, VALUE index)
|
|
|
440
488
|
|
|
441
489
|
name = sqlite3_column_decltype(ctx->st, (int)NUM2INT(index));
|
|
442
490
|
|
|
443
|
-
if (name) { return
|
|
491
|
+
if (name) { return rb_utf8_str_new_cstr(name); }
|
|
492
|
+
return Qnil;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
static VALUE
|
|
496
|
+
column_decltype_downcased(VALUE self, VALUE index)
|
|
497
|
+
{
|
|
498
|
+
sqlite3StmtRubyPtr ctx;
|
|
499
|
+
const char *name;
|
|
500
|
+
|
|
501
|
+
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
|
502
|
+
|
|
503
|
+
REQUIRE_LIVE_DB(ctx);
|
|
504
|
+
REQUIRE_OPEN_STMT(ctx);
|
|
505
|
+
|
|
506
|
+
name = sqlite3_column_decltype(ctx->st, (int)NUM2INT(index));
|
|
507
|
+
|
|
508
|
+
if (name) { return interned_downcased_utf8_cstr(name); }
|
|
444
509
|
return Qnil;
|
|
445
510
|
}
|
|
446
511
|
|
|
@@ -733,6 +798,7 @@ init_sqlite3_statement(void)
|
|
|
733
798
|
rb_define_method(cSqlite3Statement, "memused", memused, 0);
|
|
734
799
|
#endif
|
|
735
800
|
|
|
801
|
+
rb_define_private_method(cSqlite3Statement, "column_decltype_downcased", column_decltype_downcased, 1);
|
|
736
802
|
rb_define_private_method(cSqlite3Statement, "prepare", prepare, 2);
|
|
737
803
|
rb_define_private_method(cSqlite3Statement, "stats_as_hash", stats_as_hash, 0);
|
|
738
804
|
rb_define_private_method(cSqlite3Statement, "stat_for", stat_for, 1);
|
data/ext/sqlite3/timespec.h
CHANGED
|
@@ -15,6 +15,15 @@
|
|
|
15
15
|
(vsp)->tv_nsec += 1000000000L; \
|
|
16
16
|
} \
|
|
17
17
|
} while (0)
|
|
18
|
+
#define timespecadd(tsp, usp, vsp) \
|
|
19
|
+
do { \
|
|
20
|
+
(vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec; \
|
|
21
|
+
(vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec; \
|
|
22
|
+
if ((vsp)->tv_nsec >= 1000000000L) { \
|
|
23
|
+
(vsp)->tv_sec++; \
|
|
24
|
+
(vsp)->tv_nsec -= 1000000000L; \
|
|
25
|
+
} \
|
|
26
|
+
} while (0)
|
|
18
27
|
#define timespecafter(tsp, usp) \
|
|
19
28
|
(((tsp)->tv_sec > (usp)->tv_sec) || \
|
|
20
29
|
((tsp)->tv_sec == (usp)->tv_sec && (tsp)->tv_nsec > (usp)->tv_nsec))
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
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/resultset.rb
CHANGED
|
@@ -43,20 +43,26 @@ module SQLite3
|
|
|
43
43
|
@stmt.step
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
-
#
|
|
47
|
-
#
|
|
46
|
+
# With a block given, iterates over the rows of the result set, passing each
|
|
47
|
+
# to the block. Returns self.
|
|
48
|
+
# With no block given, returns a new Enumerator.
|
|
48
49
|
def each
|
|
50
|
+
return enum_for(__method__) unless block_given?
|
|
49
51
|
while (node = self.next)
|
|
50
52
|
yield node
|
|
51
53
|
end
|
|
54
|
+
self
|
|
52
55
|
end
|
|
53
56
|
|
|
54
|
-
#
|
|
55
|
-
#
|
|
57
|
+
# With a block given, iterates over the rows of the result set, passing each
|
|
58
|
+
# to the block as a hash. Returns self.
|
|
59
|
+
# With no block given, returns a new Enumerator.
|
|
56
60
|
def each_hash
|
|
61
|
+
return enum_for(__method__) unless block_given?
|
|
57
62
|
while (node = next_hash)
|
|
58
63
|
yield node
|
|
59
64
|
end
|
|
65
|
+
self
|
|
60
66
|
end
|
|
61
67
|
|
|
62
68
|
# Closes the statement that spawned this result set.
|
data/lib/sqlite3/statement.rb
CHANGED
|
@@ -42,6 +42,24 @@ module SQLite3
|
|
|
42
42
|
# See Database#execute for a description of the valid placeholder
|
|
43
43
|
# syntaxes.
|
|
44
44
|
#
|
|
45
|
+
# Type mapping from Ruby to SQLite3:
|
|
46
|
+
# - +nil+ → NULL
|
|
47
|
+
# - Integer → INTEGER (or REAL when outside the signed int64 range)
|
|
48
|
+
# - Float → REAL
|
|
49
|
+
# - SQLite3::Blob → BLOB
|
|
50
|
+
# - String with Encoding::ASCII_8BIT
|
|
51
|
+
# (a.k.a. BINARY) → BLOB
|
|
52
|
+
# - String with Encoding::UTF_16LE
|
|
53
|
+
# or Encoding::UTF_16BE → TEXT (bound as UTF-16)
|
|
54
|
+
# - String (all other encodings) → TEXT (re-encoded to UTF-8 if needed)
|
|
55
|
+
# - Any other type → raises RuntimeError
|
|
56
|
+
#
|
|
57
|
+
# Note: a String with Encoding::ASCII_8BIT (BINARY) is always bound as a
|
|
58
|
+
# BLOB, even when its bytes are all valid ASCII. If you want such a string
|
|
59
|
+
# compared or stored as TEXT, re-encode it before binding:
|
|
60
|
+
#
|
|
61
|
+
# stmt.bind_params(my_binary_str.encode(Encoding::UTF_8))
|
|
62
|
+
#
|
|
45
63
|
# Example:
|
|
46
64
|
#
|
|
47
65
|
# stmt = db.prepare( "select * from table where a=? and b=?" )
|
|
@@ -112,9 +130,9 @@ module SQLite3
|
|
|
112
130
|
!done?
|
|
113
131
|
end
|
|
114
132
|
|
|
115
|
-
# Return
|
|
116
|
-
# may execute the statement in order to
|
|
117
|
-
# a (potentially) expensive operation.
|
|
133
|
+
# Return a frozen array of the column names for this statement, as frozen
|
|
134
|
+
# UTF-8 strings. Note that this may execute the statement in order to
|
|
135
|
+
# obtain the metadata; this makes it a (potentially) expensive operation.
|
|
118
136
|
def columns
|
|
119
137
|
get_metadata unless @columns
|
|
120
138
|
@columns
|
|
@@ -128,9 +146,11 @@ module SQLite3
|
|
|
128
146
|
end
|
|
129
147
|
end
|
|
130
148
|
|
|
131
|
-
# Return
|
|
132
|
-
#
|
|
133
|
-
#
|
|
149
|
+
# Return a frozen array of the declared types of the columns in this
|
|
150
|
+
# statement, as frozen UTF-8 strings with ASCII letters downcased. A column
|
|
151
|
+
# with no declared type, such as an expression, has a +nil+ type. Note that
|
|
152
|
+
# this may execute the statement in order to obtain the metadata; this makes
|
|
153
|
+
# it a (potentially) expensive operation.
|
|
134
154
|
def types
|
|
135
155
|
must_be_open!
|
|
136
156
|
get_metadata unless @types
|
|
@@ -180,11 +200,10 @@ module SQLite3
|
|
|
180
200
|
def get_metadata
|
|
181
201
|
@columns = Array.new(column_count) do |column|
|
|
182
202
|
column_name column
|
|
183
|
-
end
|
|
203
|
+
end.freeze
|
|
184
204
|
@types = Array.new(column_count) do |column|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
end
|
|
205
|
+
column_decltype_downcased(column)
|
|
206
|
+
end.freeze
|
|
188
207
|
end
|
|
189
208
|
end
|
|
190
209
|
end
|
data/lib/sqlite3/version.rb
CHANGED