sqlite3 2.0.3-x86_64-linux-gnu → 2.1.0.rc1-x86_64-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 +32 -0
- data/CONTRIBUTING.md +5 -0
- data/FAQ.md +43 -34
- data/INSTALLATION.md +1 -3
- data/README.md +17 -0
- data/dependencies.yml +8 -8
- data/ext/sqlite3/database.c +96 -17
- data/ext/sqlite3/database.h +2 -0
- data/ext/sqlite3/extconf.rb +2 -0
- data/ext/sqlite3/statement.c +42 -0
- data/lib/sqlite3/3.1/sqlite3_native.so +0 -0
- data/lib/sqlite3/3.2/sqlite3_native.so +0 -0
- data/lib/sqlite3/3.3/sqlite3_native.so +0 -0
- data/lib/sqlite3/database.rb +3 -1
- data/lib/sqlite3/fork_safety.rb +56 -0
- data/lib/sqlite3/version.rb +1 -1
- metadata +7 -4
- data/lib/sqlite3/3.0/sqlite3_native.so +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 354e3943741a819d8bc285bd26573e6df25154ace5a39e6edf2220e24f7ce5e0
|
4
|
+
data.tar.gz: dc33f1e99524a65d3ab5c387bb20fbf550723937f307ee4352aa49411d669be9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4092492e92fc72e59474081e2e11749f364329b84833acad749ee7ad9e17797419a0377f31f68e15aa5c3d7a298ae33fc0e648f821b8bc46957ef9ebb1246cfd
|
7
|
+
data.tar.gz: 8e0434d82a80b566826d939c106536226017eb30bb339199b6ac797a6d7596220cad557e3dc086e206780edd394a53614322927558aa29986089aebf23aa408f
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,37 @@
|
|
1
1
|
# sqlite3-ruby Changelog
|
2
2
|
|
3
|
+
## prerelease 2.1.0.rc1 / 2024-09-18
|
4
|
+
|
5
|
+
### Ruby
|
6
|
+
|
7
|
+
- This release drops support for Ruby 3.0. [#563] @flavorjones
|
8
|
+
|
9
|
+
|
10
|
+
### Fork safety improvements
|
11
|
+
|
12
|
+
Sqlite itself is [not fork-safe](https://www.sqlite.org/howtocorrupt.html#_carrying_an_open_database_connection_across_a_fork_). Specifically, writing in a child process to a database connection that was created in the parent process may corrupt the database file. To mitigate this risk, sqlite3-ruby has implemented the following changes:
|
13
|
+
|
14
|
+
- All open writable database connections carried across a `fork()` will immediately be closed in the child process to mitigate the risk of corrupting the database file.
|
15
|
+
- These connections will be incompletely closed ("discarded") which will result in a one-time memory leak in the child process.
|
16
|
+
|
17
|
+
If it's at all possible, we strongly recommend that you close writable database connections in the parent before forking.
|
18
|
+
|
19
|
+
See the README "Fork Safety" section and `adr/2024-09-fork-safety.md` for more information. [#558] @flavorjones
|
20
|
+
|
21
|
+
|
22
|
+
### Improved
|
23
|
+
|
24
|
+
- Use `sqlite3_close_v2` to close databases in a deferred manner if there are unclosed prepared statements. Previously closing a database while statements were open resulted in a `BusyException`. See https://www.sqlite.org/c3ref/close.html for more context. [#557] @flavorjones
|
25
|
+
- When setting a Database `busy_handler`, fire the write barrier to prevent potential crashes during the GC mark phase. [#556] @jhawthorn
|
26
|
+
|
27
|
+
|
28
|
+
## 2.0.4 / 2024-08-13
|
29
|
+
|
30
|
+
### Dependencies
|
31
|
+
|
32
|
+
- Vendored sqlite is updated to [v3.46.1](https://sqlite.org/releaselog/3_46_1.html) @flavorjones
|
33
|
+
|
34
|
+
|
3
35
|
## 2.0.3 / 2024-07-29
|
4
36
|
|
5
37
|
### Improved
|
data/CONTRIBUTING.md
CHANGED
@@ -7,6 +7,11 @@ This doc is a short introduction on how to modify and maintain the sqlite3-ruby
|
|
7
7
|
|
8
8
|
## Architecture notes
|
9
9
|
|
10
|
+
### Decision record
|
11
|
+
|
12
|
+
As of 2024-09, we're starting to keep some architecture decisions in the subdirectory `/adr`, so
|
13
|
+
please look there for additional information.
|
14
|
+
|
10
15
|
### Garbage collection
|
11
16
|
|
12
17
|
All statements keep pointers back to their respective database connections.
|
data/FAQ.md
CHANGED
@@ -207,48 +207,46 @@ Or do a `Database#prepare` to get the `Statement`, and then use either
|
|
207
207
|
stmt.bind_params( "value", "name" => "bob" )
|
208
208
|
```
|
209
209
|
|
210
|
-
## How do I discover metadata about a query?
|
210
|
+
## How do I discover metadata about a query result?
|
211
211
|
|
212
|
-
|
213
|
-
|
212
|
+
IMPORTANT: `Database#execute` returns an Array of Array of Strings
|
213
|
+
which will have no metadata about the query or the result, such
|
214
|
+
as column names.
|
214
215
|
|
215
216
|
|
216
|
-
|
217
|
-
property "fields" that returns an array of the column names. The row
|
218
|
-
will also have a property "types" that returns an array of the column
|
219
|
-
types:
|
217
|
+
There are 2 main sources of query metadata:
|
220
218
|
|
221
|
-
|
222
|
-
|
223
|
-
rows = db.execute( "select * from table" )
|
224
|
-
p rows[0].fields
|
225
|
-
p rows[0].types
|
226
|
-
```
|
219
|
+
* `Statement`
|
220
|
+
* `ResultSet`
|
227
221
|
|
228
222
|
|
229
|
-
|
230
|
-
|
231
|
-
you still need the metadata, you can use `Database#query` and ask the
|
232
|
-
`ResultSet` object itself:
|
223
|
+
You can get a `Statement` via `Database#prepare`, and you can get
|
224
|
+
a `ResultSet` via `Statement#execute` or `Database#query`.
|
233
225
|
|
234
226
|
|
235
227
|
```ruby
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
228
|
+
sql = 'select * from table'
|
229
|
+
|
230
|
+
# No metadata
|
231
|
+
rows = db.execute(sql)
|
232
|
+
rows.class # => Array, no metadata
|
233
|
+
rows.first.class # => Array, no metadata
|
234
|
+
rows.first.first.class #=> String, no metadata
|
235
|
+
|
236
|
+
# Statement has metadata
|
237
|
+
stmt = db.prepare(sql)
|
238
|
+
stmt.columns # => [ ... ]
|
239
|
+
stmt.types # => [ ... ]
|
240
|
+
|
241
|
+
# ResultSet has metadata
|
242
|
+
results = stmt.execute
|
243
|
+
results.columns # => [ ... ]
|
244
|
+
results.types # => [ ... ]
|
245
|
+
|
246
|
+
# ResultSet has metadata
|
247
|
+
results = db.query(sql)
|
248
|
+
results.columns # => [ ... ]
|
249
|
+
results.types # => [ ... ]
|
252
250
|
```
|
253
251
|
|
254
252
|
## I'd like the rows to be indexible by column name.
|
@@ -273,7 +271,18 @@ is unavailable on the row, although the "types" property remains.)
|
|
273
271
|
```
|
274
272
|
|
275
273
|
|
276
|
-
|
274
|
+
A more granular way to do this is via `ResultSet#next_hash` or
|
275
|
+
`ResultSet#each_hash`.
|
276
|
+
|
277
|
+
|
278
|
+
```ruby
|
279
|
+
results = db.query( "select * from table" )
|
280
|
+
row = results.next_hash
|
281
|
+
p row['column1']
|
282
|
+
```
|
283
|
+
|
284
|
+
|
285
|
+
Another way is to use Ara Howard's
|
277
286
|
[`ArrayFields`](http://rubyforge.org/projects/arrayfields)
|
278
287
|
module. Just `require "arrayfields"`, and all of your rows will be indexable
|
279
288
|
by column name, even though they are still arrays!
|
data/INSTALLATION.md
CHANGED
@@ -14,15 +14,13 @@ In v2.0.0 and later, native (precompiled) gems are available for recent Ruby ver
|
|
14
14
|
- `arm-linux-gnu` (requires: glibc >= 2.29)
|
15
15
|
- `arm-linux-musl`
|
16
16
|
- `arm64-darwin`
|
17
|
-
- `x64-
|
17
|
+
- `x64-mingw-ucrt`
|
18
18
|
- `x86-linux-gnu` (requires: glibc >= 2.17)
|
19
19
|
- `x86-linux-musl`
|
20
20
|
- `x86_64-darwin`
|
21
21
|
- `x86_64-linux-gnu` (requires: glibc >= 2.17)
|
22
22
|
- `x86_64-linux-musl`
|
23
23
|
|
24
|
-
⚠ Ruby 3.0 linux users must use Rubygems >= 3.3.22 in order to use these gems.
|
25
|
-
|
26
24
|
⚠ Musl linux users should update to Bundler >= 2.5.6 to avoid https://github.com/rubygems/rubygems/issues/7432
|
27
25
|
|
28
26
|
If you are using one of these Ruby versions on one of these platforms, the native gem is the recommended way to install sqlite3-ruby.
|
data/README.md
CHANGED
@@ -148,6 +148,23 @@ It is generally recommended that if applications want to share a database among
|
|
148
148
|
threads, they _only_ share the database instance object. Other objects are
|
149
149
|
fine to share, but may require manual locking for thread safety.
|
150
150
|
|
151
|
+
|
152
|
+
## Fork Safety
|
153
|
+
|
154
|
+
[Sqlite is not fork
|
155
|
+
safe](https://www.sqlite.org/howtocorrupt.html#_carrying_an_open_database_connection_across_a_fork_)
|
156
|
+
and instructs users to not carry an open writable database connection across a `fork()`. Using an inherited
|
157
|
+
connection in the child may corrupt your database, leak memory, or cause other undefined behavior.
|
158
|
+
|
159
|
+
To help protect users of this gem from accidental corruption due to this lack of fork safety, the gem will immediately close any open writable databases in the child after a fork. Discarding writable
|
160
|
+
connections in the child will incur a small one-time memory leak per connection, but that's
|
161
|
+
preferable to potentially corrupting your database.
|
162
|
+
|
163
|
+
Whenever possible, close writable connections in the parent before forking.
|
164
|
+
|
165
|
+
See [./adr/2024-09-fork-safety.md](./adr/2024-09-fork-safety.md) for more information and context.
|
166
|
+
|
167
|
+
|
151
168
|
## Support
|
152
169
|
|
153
170
|
### Installation or database extensions
|
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
|
+
# 923f68143dcd9fc0c38778dee253fd6540a91f578173a04ca5adff885d8a8fbb
|
4
4
|
#
|
5
|
-
# $ sha3sum -a 256 ports/archives/sqlite-autoconf-
|
6
|
-
#
|
5
|
+
# $ sha3sum -a 256 ports/archives/sqlite-autoconf-3460100.tar.gz
|
6
|
+
# 923f68143dcd9fc0c38778dee253fd6540a91f578173a04ca5adff885d8a8fbb ports/archives/sqlite-autoconf-3460100.tar.gz
|
7
7
|
#
|
8
|
-
# $ sha256sum ports/archives/sqlite-autoconf-
|
9
|
-
#
|
10
|
-
version: "3.46.
|
8
|
+
# $ sha256sum ports/archives/sqlite-autoconf-3460100.tar.gz
|
9
|
+
# 67d3fe6d268e6eaddcae3727fce58fcc8e9c53869bdd07a0c61e38ddf2965071 ports/archives/sqlite-autoconf-3460100.tar.gz
|
10
|
+
version: "3.46.1"
|
11
11
|
files:
|
12
|
-
- url: "https://sqlite.org/2024/sqlite-autoconf-
|
13
|
-
sha256: "
|
12
|
+
- url: "https://sqlite.org/2024/sqlite-autoconf-3460100.tar.gz"
|
13
|
+
sha256: "67d3fe6d268e6eaddcae3727fce58fcc8e9c53869bdd07a0c61e38ddf2965071"
|
data/ext/sqlite3/database.c
CHANGED
@@ -12,6 +12,63 @@
|
|
12
12
|
|
13
13
|
VALUE cSqlite3Database;
|
14
14
|
|
15
|
+
/* See adr/2024-09-fork-safety.md */
|
16
|
+
static void
|
17
|
+
discard_db(sqlite3RubyPtr ctx)
|
18
|
+
{
|
19
|
+
sqlite3_file *sfile;
|
20
|
+
int status;
|
21
|
+
|
22
|
+
// release as much heap memory as possible by deallocating non-essential memory
|
23
|
+
// allocations held by the database library. Memory used to cache database pages to
|
24
|
+
// improve performance is an example of non-essential memory.
|
25
|
+
// on my development machine, this reduces the lost memory from 152k to 69k.
|
26
|
+
sqlite3_db_release_memory(ctx->db);
|
27
|
+
|
28
|
+
// release file descriptors
|
29
|
+
#ifdef HAVE_SQLITE3_DB_NAME
|
30
|
+
const char *db_name;
|
31
|
+
int j_db = 0;
|
32
|
+
while ((db_name = sqlite3_db_name(ctx->db, j_db)) != NULL) {
|
33
|
+
status = sqlite3_file_control(ctx->db, db_name, SQLITE_FCNTL_FILE_POINTER, &sfile);
|
34
|
+
if (status == 0 && sfile->pMethods != NULL) {
|
35
|
+
sfile->pMethods->xClose(sfile);
|
36
|
+
}
|
37
|
+
j_db++;
|
38
|
+
}
|
39
|
+
#else
|
40
|
+
status = sqlite3_file_control(ctx->db, NULL, SQLITE_FCNTL_FILE_POINTER, &sfile);
|
41
|
+
if (status == 0 && sfile->pMethods != NULL) {
|
42
|
+
sfile->pMethods->xClose(sfile);
|
43
|
+
}
|
44
|
+
#endif
|
45
|
+
|
46
|
+
status = sqlite3_file_control(ctx->db, NULL, SQLITE_FCNTL_JOURNAL_POINTER, &sfile);
|
47
|
+
if (status == 0 && sfile->pMethods != NULL) {
|
48
|
+
sfile->pMethods->xClose(sfile);
|
49
|
+
}
|
50
|
+
|
51
|
+
ctx->db = NULL;
|
52
|
+
}
|
53
|
+
|
54
|
+
static void
|
55
|
+
close_or_discard_db(sqlite3RubyPtr ctx)
|
56
|
+
{
|
57
|
+
if (ctx->db) {
|
58
|
+
int isReadonly = (ctx->flags & SQLITE_OPEN_READONLY);
|
59
|
+
|
60
|
+
if (isReadonly || ctx->owner == getpid()) {
|
61
|
+
// Ordinary close.
|
62
|
+
sqlite3_close_v2(ctx->db);
|
63
|
+
ctx->db = NULL;
|
64
|
+
} else {
|
65
|
+
// This is an open connection carried across a fork(). "Discard" it.
|
66
|
+
discard_db(ctx);
|
67
|
+
}
|
68
|
+
}
|
69
|
+
}
|
70
|
+
|
71
|
+
|
15
72
|
static void
|
16
73
|
database_mark(void *ctx)
|
17
74
|
{
|
@@ -22,11 +79,8 @@ database_mark(void *ctx)
|
|
22
79
|
static void
|
23
80
|
deallocate(void *ctx)
|
24
81
|
{
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
if (db) { sqlite3_close(db); }
|
29
|
-
xfree(c);
|
82
|
+
close_or_discard_db((sqlite3RubyPtr)ctx);
|
83
|
+
xfree(ctx);
|
30
84
|
}
|
31
85
|
|
32
86
|
static size_t
|
@@ -51,7 +105,9 @@ static VALUE
|
|
51
105
|
allocate(VALUE klass)
|
52
106
|
{
|
53
107
|
sqlite3RubyPtr ctx;
|
54
|
-
|
108
|
+
VALUE object = TypedData_Make_Struct(klass, sqlite3Ruby, &database_type, ctx);
|
109
|
+
ctx->owner = getpid();
|
110
|
+
return object;
|
55
111
|
}
|
56
112
|
|
57
113
|
static char *
|
@@ -62,8 +118,6 @@ utf16_string_value_ptr(VALUE str)
|
|
62
118
|
return RSTRING_PTR(str);
|
63
119
|
}
|
64
120
|
|
65
|
-
static VALUE sqlite3_rb_close(VALUE self);
|
66
|
-
|
67
121
|
sqlite3RubyPtr
|
68
122
|
sqlite3_database_unwrap(VALUE database)
|
69
123
|
{
|
@@ -77,6 +131,7 @@ rb_sqlite3_open_v2(VALUE self, VALUE file, VALUE mode, VALUE zvfs)
|
|
77
131
|
{
|
78
132
|
sqlite3RubyPtr ctx;
|
79
133
|
int status;
|
134
|
+
int flags;
|
80
135
|
|
81
136
|
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
82
137
|
|
@@ -89,14 +144,16 @@ rb_sqlite3_open_v2(VALUE self, VALUE file, VALUE mode, VALUE zvfs)
|
|
89
144
|
# endif
|
90
145
|
#endif
|
91
146
|
|
147
|
+
flags = NUM2INT(mode);
|
92
148
|
status = sqlite3_open_v2(
|
93
149
|
StringValuePtr(file),
|
94
150
|
&ctx->db,
|
95
|
-
|
151
|
+
flags,
|
96
152
|
NIL_P(zvfs) ? NULL : StringValuePtr(zvfs)
|
97
153
|
);
|
98
154
|
|
99
|
-
CHECK(ctx->db, status)
|
155
|
+
CHECK(ctx->db, status);
|
156
|
+
ctx->flags = flags;
|
100
157
|
|
101
158
|
return self;
|
102
159
|
}
|
@@ -119,21 +176,38 @@ rb_sqlite3_disable_quirk_mode(VALUE self)
|
|
119
176
|
#endif
|
120
177
|
}
|
121
178
|
|
122
|
-
/*
|
179
|
+
/*
|
180
|
+
* Close the database and release all associated resources.
|
181
|
+
*
|
182
|
+
* ⚠ Writable connections that are carried across a <tt>fork()</tt> are not completely
|
183
|
+
* closed. {Sqlite does not support forking}[https://www.sqlite.org/howtocorrupt.html],
|
184
|
+
* and fully closing a writable connection that has been carried across a fork may corrupt the
|
185
|
+
* database. Since it is an incomplete close, not all memory resources are freed, but this is safer
|
186
|
+
* than risking data loss.
|
123
187
|
*
|
124
|
-
*
|
188
|
+
* See rdoc-ref:adr/2024-09-fork-safety.md for more information on fork safety.
|
125
189
|
*/
|
126
190
|
static VALUE
|
127
191
|
sqlite3_rb_close(VALUE self)
|
128
192
|
{
|
129
193
|
sqlite3RubyPtr ctx;
|
130
|
-
sqlite3 *db;
|
131
194
|
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
132
195
|
|
133
|
-
|
134
|
-
CHECK(db, sqlite3_close(ctx->db));
|
196
|
+
close_or_discard_db(ctx);
|
135
197
|
|
136
|
-
|
198
|
+
rb_iv_set(self, "-aggregators", Qnil);
|
199
|
+
|
200
|
+
return self;
|
201
|
+
}
|
202
|
+
|
203
|
+
/* private method, primarily for testing */
|
204
|
+
static VALUE
|
205
|
+
sqlite3_rb_discard(VALUE self)
|
206
|
+
{
|
207
|
+
sqlite3RubyPtr ctx;
|
208
|
+
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
209
|
+
|
210
|
+
discard_db(ctx);
|
137
211
|
|
138
212
|
rb_iv_set(self, "-aggregators", Qnil);
|
139
213
|
|
@@ -246,7 +320,7 @@ busy_handler(int argc, VALUE *argv, VALUE self)
|
|
246
320
|
rb_scan_args(argc, argv, "01", &block);
|
247
321
|
|
248
322
|
if (NIL_P(block) && rb_block_given_p()) { block = rb_block_proc(); }
|
249
|
-
ctx->busy_handler
|
323
|
+
RB_OBJ_WRITE(self, &ctx->busy_handler, block);
|
250
324
|
|
251
325
|
status = sqlite3_busy_handler(
|
252
326
|
ctx->db,
|
@@ -871,6 +945,10 @@ rb_sqlite3_open16(VALUE self, VALUE file)
|
|
871
945
|
|
872
946
|
status = sqlite3_open16(utf16_string_value_ptr(file), &ctx->db);
|
873
947
|
|
948
|
+
// these are the perm flags used implicitly by sqlite3_open16,
|
949
|
+
// see https://www.sqlite.org/capi3ref.html#sqlite3_open
|
950
|
+
ctx->flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
|
951
|
+
|
874
952
|
CHECK(ctx->db, status)
|
875
953
|
|
876
954
|
return INT2NUM(status);
|
@@ -889,6 +967,7 @@ init_sqlite3_database(void)
|
|
889
967
|
rb_define_private_method(cSqlite3Database, "open16", rb_sqlite3_open16, 1);
|
890
968
|
rb_define_method(cSqlite3Database, "collation", collation, 2);
|
891
969
|
rb_define_method(cSqlite3Database, "close", sqlite3_rb_close, 0);
|
970
|
+
rb_define_private_method(cSqlite3Database, "discard", sqlite3_rb_discard, 0);
|
892
971
|
rb_define_method(cSqlite3Database, "closed?", closed_p, 0);
|
893
972
|
rb_define_method(cSqlite3Database, "total_changes", total_changes, 0);
|
894
973
|
rb_define_method(cSqlite3Database, "trace", trace, -1);
|
data/ext/sqlite3/database.h
CHANGED
data/ext/sqlite3/extconf.rb
CHANGED
data/ext/sqlite3/statement.c
CHANGED
@@ -4,6 +4,20 @@
|
|
4
4
|
if(!_ctxt->st) \
|
5
5
|
rb_raise(rb_path2class("SQLite3::Exception"), "cannot use a closed statement");
|
6
6
|
|
7
|
+
static void
|
8
|
+
require_open_db(VALUE stmt_rb)
|
9
|
+
{
|
10
|
+
VALUE closed_p = rb_funcall(
|
11
|
+
rb_iv_get(stmt_rb, "@connection"),
|
12
|
+
rb_intern("closed?"), 0);
|
13
|
+
|
14
|
+
if (RTEST(closed_p)) {
|
15
|
+
rb_raise(rb_path2class("SQLite3::Exception"),
|
16
|
+
"cannot use a statement associated with a closed database");
|
17
|
+
}
|
18
|
+
}
|
19
|
+
|
20
|
+
|
7
21
|
VALUE cSqlite3Statement;
|
8
22
|
|
9
23
|
static void
|
@@ -121,6 +135,7 @@ step(VALUE self)
|
|
121
135
|
|
122
136
|
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
123
137
|
|
138
|
+
require_open_db(self);
|
124
139
|
REQUIRE_OPEN_STMT(ctx);
|
125
140
|
|
126
141
|
if (ctx->done_p) { return Qnil; }
|
@@ -216,6 +231,8 @@ bind_param(VALUE self, VALUE key, VALUE value)
|
|
216
231
|
int index;
|
217
232
|
|
218
233
|
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
234
|
+
|
235
|
+
require_open_db(self);
|
219
236
|
REQUIRE_OPEN_STMT(ctx);
|
220
237
|
|
221
238
|
switch (TYPE(key)) {
|
@@ -308,6 +325,8 @@ reset_bang(VALUE self)
|
|
308
325
|
sqlite3StmtRubyPtr ctx;
|
309
326
|
|
310
327
|
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
328
|
+
|
329
|
+
require_open_db(self);
|
311
330
|
REQUIRE_OPEN_STMT(ctx);
|
312
331
|
|
313
332
|
sqlite3_reset(ctx->st);
|
@@ -328,6 +347,8 @@ clear_bindings_bang(VALUE self)
|
|
328
347
|
sqlite3StmtRubyPtr ctx;
|
329
348
|
|
330
349
|
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
350
|
+
|
351
|
+
require_open_db(self);
|
331
352
|
REQUIRE_OPEN_STMT(ctx);
|
332
353
|
|
333
354
|
sqlite3_clear_bindings(ctx->st);
|
@@ -360,6 +381,8 @@ column_count(VALUE self)
|
|
360
381
|
{
|
361
382
|
sqlite3StmtRubyPtr ctx;
|
362
383
|
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
384
|
+
|
385
|
+
require_open_db(self);
|
363
386
|
REQUIRE_OPEN_STMT(ctx);
|
364
387
|
|
365
388
|
return INT2NUM(sqlite3_column_count(ctx->st));
|
@@ -391,6 +414,8 @@ column_name(VALUE self, VALUE index)
|
|
391
414
|
const char *name;
|
392
415
|
|
393
416
|
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
417
|
+
|
418
|
+
require_open_db(self);
|
394
419
|
REQUIRE_OPEN_STMT(ctx);
|
395
420
|
|
396
421
|
name = sqlite3_column_name(ctx->st, (int)NUM2INT(index));
|
@@ -414,6 +439,8 @@ column_decltype(VALUE self, VALUE index)
|
|
414
439
|
const char *name;
|
415
440
|
|
416
441
|
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
442
|
+
|
443
|
+
require_open_db(self);
|
417
444
|
REQUIRE_OPEN_STMT(ctx);
|
418
445
|
|
419
446
|
name = sqlite3_column_decltype(ctx->st, (int)NUM2INT(index));
|
@@ -431,6 +458,8 @@ bind_parameter_count(VALUE self)
|
|
431
458
|
{
|
432
459
|
sqlite3StmtRubyPtr ctx;
|
433
460
|
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
461
|
+
|
462
|
+
require_open_db(self);
|
434
463
|
REQUIRE_OPEN_STMT(ctx);
|
435
464
|
|
436
465
|
return INT2NUM(sqlite3_bind_parameter_count(ctx->st));
|
@@ -538,7 +567,10 @@ stats_as_hash(VALUE self)
|
|
538
567
|
{
|
539
568
|
sqlite3StmtRubyPtr ctx;
|
540
569
|
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
570
|
+
|
571
|
+
require_open_db(self);
|
541
572
|
REQUIRE_OPEN_STMT(ctx);
|
573
|
+
|
542
574
|
VALUE arg = rb_hash_new();
|
543
575
|
|
544
576
|
stmt_stat_internal(arg, ctx->st);
|
@@ -554,6 +586,8 @@ stat_for(VALUE self, VALUE key)
|
|
554
586
|
{
|
555
587
|
sqlite3StmtRubyPtr ctx;
|
556
588
|
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
589
|
+
|
590
|
+
require_open_db(self);
|
557
591
|
REQUIRE_OPEN_STMT(ctx);
|
558
592
|
|
559
593
|
if (SYMBOL_P(key)) {
|
@@ -574,6 +608,8 @@ memused(VALUE self)
|
|
574
608
|
{
|
575
609
|
sqlite3StmtRubyPtr ctx;
|
576
610
|
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
611
|
+
|
612
|
+
require_open_db(self);
|
577
613
|
REQUIRE_OPEN_STMT(ctx);
|
578
614
|
|
579
615
|
return INT2NUM(sqlite3_stmt_status(ctx->st, SQLITE_STMTSTATUS_MEMUSED, 0));
|
@@ -591,6 +627,8 @@ database_name(VALUE self, VALUE index)
|
|
591
627
|
{
|
592
628
|
sqlite3StmtRubyPtr ctx;
|
593
629
|
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
630
|
+
|
631
|
+
require_open_db(self);
|
594
632
|
REQUIRE_OPEN_STMT(ctx);
|
595
633
|
|
596
634
|
return SQLITE3_UTF8_STR_NEW2(
|
@@ -608,6 +646,8 @@ get_sql(VALUE self)
|
|
608
646
|
{
|
609
647
|
sqlite3StmtRubyPtr ctx;
|
610
648
|
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
649
|
+
|
650
|
+
require_open_db(self);
|
611
651
|
REQUIRE_OPEN_STMT(ctx);
|
612
652
|
|
613
653
|
return rb_obj_freeze(SQLITE3_UTF8_STR_NEW2(sqlite3_sql(ctx->st)));
|
@@ -626,6 +666,8 @@ get_expanded_sql(VALUE self)
|
|
626
666
|
VALUE rb_expanded_sql;
|
627
667
|
|
628
668
|
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
669
|
+
|
670
|
+
require_open_db(self);
|
629
671
|
REQUIRE_OPEN_STMT(ctx);
|
630
672
|
|
631
673
|
expanded_sql = sqlite3_expanded_sql(ctx->st);
|
Binary file
|
Binary file
|
Binary file
|
data/lib/sqlite3/database.rb
CHANGED
@@ -5,6 +5,7 @@ require "sqlite3/errors"
|
|
5
5
|
require "sqlite3/pragmas"
|
6
6
|
require "sqlite3/statement"
|
7
7
|
require "sqlite3/value"
|
8
|
+
require "sqlite3/fork_safety"
|
8
9
|
|
9
10
|
module SQLite3
|
10
11
|
# The Database class encapsulates a single connection to a SQLite3 database.
|
@@ -127,7 +128,6 @@ module SQLite3
|
|
127
128
|
|
128
129
|
@tracefunc = nil
|
129
130
|
@authorizer = nil
|
130
|
-
@busy_handler = nil
|
131
131
|
@progress_handler = nil
|
132
132
|
@collations = {}
|
133
133
|
@functions = {}
|
@@ -135,6 +135,8 @@ module SQLite3
|
|
135
135
|
@readonly = mode & Constants::Open::READONLY != 0
|
136
136
|
@default_transaction_mode = options[:default_transaction_mode] || :deferred
|
137
137
|
|
138
|
+
ForkSafety.track(self)
|
139
|
+
|
138
140
|
if block_given?
|
139
141
|
begin
|
140
142
|
yield self
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "weakref"
|
4
|
+
|
5
|
+
# based on Rails's active_support/fork_tracker.rb
|
6
|
+
module SQLite3
|
7
|
+
module ForkSafety
|
8
|
+
module CoreExt
|
9
|
+
def _fork
|
10
|
+
pid = super
|
11
|
+
if pid == 0
|
12
|
+
ForkSafety.discard
|
13
|
+
end
|
14
|
+
pid
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
@databases = []
|
19
|
+
@mutex = Mutex.new
|
20
|
+
|
21
|
+
class << self
|
22
|
+
def hook!
|
23
|
+
::Process.singleton_class.prepend(CoreExt)
|
24
|
+
end
|
25
|
+
|
26
|
+
def track(database)
|
27
|
+
@mutex.synchronize do
|
28
|
+
@databases << WeakRef.new(database)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def discard
|
33
|
+
warned = false
|
34
|
+
@databases.each do |db|
|
35
|
+
next unless db.weakref_alive?
|
36
|
+
|
37
|
+
unless db.closed? || db.readonly?
|
38
|
+
unless warned
|
39
|
+
# If you are here, you may want to read
|
40
|
+
# https://github.com/sparklemotion/sqlite3-ruby/pull/558
|
41
|
+
warn("Writable sqlite database connection(s) were inherited from a forked process. " \
|
42
|
+
"This is unsafe and the connections are being closed to prevent possible data " \
|
43
|
+
"corruption. Please close writable sqlite database connections before forking.",
|
44
|
+
uplevel: 0)
|
45
|
+
warned = true
|
46
|
+
end
|
47
|
+
db.close
|
48
|
+
end
|
49
|
+
end
|
50
|
+
@databases.clear
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
SQLite3::ForkSafety.hook!
|
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.0.
|
4
|
+
version: 2.1.0.rc1
|
5
5
|
platform: x86_64-linux-gnu
|
6
6
|
authors:
|
7
7
|
- Jamis Buck
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2024-
|
14
|
+
date: 2024-09-18 00:00:00.000000000 Z
|
15
15
|
dependencies: []
|
16
16
|
description: |
|
17
17
|
Ruby library to interface with the SQLite3 database engine (http://www.sqlite.org). Precompiled
|
@@ -52,13 +52,13 @@ files:
|
|
52
52
|
- ext/sqlite3/statement.h
|
53
53
|
- ext/sqlite3/timespec.h
|
54
54
|
- lib/sqlite3.rb
|
55
|
-
- lib/sqlite3/3.0/sqlite3_native.so
|
56
55
|
- lib/sqlite3/3.1/sqlite3_native.so
|
57
56
|
- lib/sqlite3/3.2/sqlite3_native.so
|
58
57
|
- lib/sqlite3/3.3/sqlite3_native.so
|
59
58
|
- lib/sqlite3/constants.rb
|
60
59
|
- lib/sqlite3/database.rb
|
61
60
|
- lib/sqlite3/errors.rb
|
61
|
+
- lib/sqlite3/fork_safety.rb
|
62
62
|
- lib/sqlite3/pragmas.rb
|
63
63
|
- lib/sqlite3/resultset.rb
|
64
64
|
- lib/sqlite3/statement.rb
|
@@ -84,12 +84,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
84
84
|
requirements:
|
85
85
|
- - ">="
|
86
86
|
- !ruby/object:Gem::Version
|
87
|
-
version: '3.
|
87
|
+
version: '3.1'
|
88
88
|
- - "<"
|
89
89
|
- !ruby/object:Gem::Version
|
90
90
|
version: 3.4.dev
|
91
91
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
92
|
requirements:
|
93
|
+
- - ">"
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: 1.3.1
|
93
96
|
- - ">="
|
94
97
|
- !ruby/object:Gem::Version
|
95
98
|
version: 3.3.22
|
Binary file
|