sqlite3 2.0.3-arm64-darwin → 2.1.0-arm64-darwin
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +37 -0
- data/CONTRIBUTING.md +12 -12
- 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 +98 -17
- data/ext/sqlite3/database.h +6 -0
- data/ext/sqlite3/extconf.rb +2 -0
- data/ext/sqlite3/statement.c +38 -1
- data/ext/sqlite3/statement.h +1 -0
- data/lib/sqlite3/3.1/sqlite3_native.bundle +0 -0
- data/lib/sqlite3/3.2/sqlite3_native.bundle +0 -0
- data/lib/sqlite3/3.3/sqlite3_native.bundle +0 -0
- data/lib/sqlite3/database.rb +3 -1
- data/lib/sqlite3/fork_safety.rb +62 -0
- data/lib/sqlite3/version.rb +1 -1
- metadata +4 -4
- data/lib/sqlite3/3.0/sqlite3_native.bundle +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 58efd48fc68d6c46767c603de815022f41d35eb2aaafb187b8f18fa71ff4330e
|
4
|
+
data.tar.gz: 3b904d8a115523e5a716ca2b1994fc66740181e40affddab7524c5c773a28576
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0e059c53db3c06f362ba18d1e8e3d2c588156225b3616adbff17d07f86c67688585d66807c4aa227b174e8dc9e0ea0f6acc23bc67d6a2c3ddec7dcf04aab0401
|
7
|
+
data.tar.gz: 2dc22242ff0d87c182c7b90dec3e14f85636d257d3576317142e30ad78b8ce7cc6d1638227d42ae968715d30e1ff17ecdbb07677fb713a2944e5f6fd4a5df415
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,42 @@
|
|
1
1
|
# sqlite3-ruby Changelog
|
2
2
|
|
3
|
+
## 2.1.0 / 2024-09-24
|
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. If absolutely necessary (and you know what you're doing), you may suppress the fork safety warnings by calling `SQLite3::ForkSafety.suppress_warnings!`.
|
18
|
+
|
19
|
+
See the README's "Fork Safety" section and `adr/2024-09-fork-safety.md` for more information. [#558, #565, #566] @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
|
+
### Documentation
|
29
|
+
|
30
|
+
- The `FAQ.md` has been updated to fix some inaccuracies. [#562] @rickhull
|
31
|
+
|
32
|
+
|
33
|
+
## 2.0.4 / 2024-08-13
|
34
|
+
|
35
|
+
### Dependencies
|
36
|
+
|
37
|
+
- Vendored sqlite is updated to [v3.46.1](https://sqlite.org/releaselog/3_46_1.html) @flavorjones
|
38
|
+
|
39
|
+
|
3
40
|
## 2.0.3 / 2024-07-29
|
4
41
|
|
5
42
|
### Improved
|
data/CONTRIBUTING.md
CHANGED
@@ -7,24 +7,24 @@ 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.
|
13
18
|
The `@connection` instance variable on the `Statement` handle keeps the database
|
14
|
-
connection alive.
|
15
|
-
two cases:
|
16
|
-
|
17
|
-
1. `#close` is called on the statement
|
18
|
-
2. The `SQLite3::Database` object gets garbage collected
|
19
|
+
connection alive.
|
19
20
|
|
20
|
-
We
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
garbage collection. If the database connection were to be free'd before the
|
25
|
-
statement, then boom. Instead we'll be conservative and free unclosed
|
26
|
-
statements when the connection is terminated.
|
21
|
+
We use `sqlite3_close_v2` in `Database#close` since v2.1.0 which defers _actually_ closing the
|
22
|
+
connection and freeing the underlying memory until all open statments are closed; though the
|
23
|
+
`Database` object will immediately behave as though it's been fully closed. If a Database is not
|
24
|
+
explicitly closed, it will be closed when it is GCed.
|
27
25
|
|
26
|
+
`Statement#close` finalizes the underlying statement. If a Statement is not explicitly closed, it
|
27
|
+
will be closed/finalized when it is GCed.
|
28
28
|
|
29
29
|
|
30
30
|
## Building gems
|
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. If absolutely necessary (and you know what you're doing), you may suppress the fork safety warnings by calling `SQLite3::ForkSafety.suppress_warnings!`.
|
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,64 @@
|
|
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
|
+
ctx->flags |= SQLITE3_RB_DATABASE_DISCARDED;
|
53
|
+
}
|
54
|
+
|
55
|
+
static void
|
56
|
+
close_or_discard_db(sqlite3RubyPtr ctx)
|
57
|
+
{
|
58
|
+
if (ctx->db) {
|
59
|
+
int is_readonly = (ctx->flags & SQLITE3_RB_DATABASE_READONLY);
|
60
|
+
|
61
|
+
if (is_readonly || ctx->owner == getpid()) {
|
62
|
+
// Ordinary close.
|
63
|
+
sqlite3_close_v2(ctx->db);
|
64
|
+
ctx->db = NULL;
|
65
|
+
} else {
|
66
|
+
// This is an open connection carried across a fork(). "Discard" it.
|
67
|
+
discard_db(ctx);
|
68
|
+
}
|
69
|
+
}
|
70
|
+
}
|
71
|
+
|
72
|
+
|
15
73
|
static void
|
16
74
|
database_mark(void *ctx)
|
17
75
|
{
|
@@ -22,11 +80,8 @@ database_mark(void *ctx)
|
|
22
80
|
static void
|
23
81
|
deallocate(void *ctx)
|
24
82
|
{
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
if (db) { sqlite3_close(db); }
|
29
|
-
xfree(c);
|
83
|
+
close_or_discard_db((sqlite3RubyPtr)ctx);
|
84
|
+
xfree(ctx);
|
30
85
|
}
|
31
86
|
|
32
87
|
static size_t
|
@@ -51,7 +106,9 @@ static VALUE
|
|
51
106
|
allocate(VALUE klass)
|
52
107
|
{
|
53
108
|
sqlite3RubyPtr ctx;
|
54
|
-
|
109
|
+
VALUE object = TypedData_Make_Struct(klass, sqlite3Ruby, &database_type, ctx);
|
110
|
+
ctx->owner = getpid();
|
111
|
+
return object;
|
55
112
|
}
|
56
113
|
|
57
114
|
static char *
|
@@ -62,8 +119,6 @@ utf16_string_value_ptr(VALUE str)
|
|
62
119
|
return RSTRING_PTR(str);
|
63
120
|
}
|
64
121
|
|
65
|
-
static VALUE sqlite3_rb_close(VALUE self);
|
66
|
-
|
67
122
|
sqlite3RubyPtr
|
68
123
|
sqlite3_database_unwrap(VALUE database)
|
69
124
|
{
|
@@ -77,6 +132,7 @@ rb_sqlite3_open_v2(VALUE self, VALUE file, VALUE mode, VALUE zvfs)
|
|
77
132
|
{
|
78
133
|
sqlite3RubyPtr ctx;
|
79
134
|
int status;
|
135
|
+
int flags;
|
80
136
|
|
81
137
|
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
82
138
|
|
@@ -89,14 +145,18 @@ rb_sqlite3_open_v2(VALUE self, VALUE file, VALUE mode, VALUE zvfs)
|
|
89
145
|
# endif
|
90
146
|
#endif
|
91
147
|
|
148
|
+
flags = NUM2INT(mode);
|
92
149
|
status = sqlite3_open_v2(
|
93
150
|
StringValuePtr(file),
|
94
151
|
&ctx->db,
|
95
|
-
|
152
|
+
flags,
|
96
153
|
NIL_P(zvfs) ? NULL : StringValuePtr(zvfs)
|
97
154
|
);
|
98
155
|
|
99
|
-
CHECK(ctx->db, status)
|
156
|
+
CHECK(ctx->db, status);
|
157
|
+
if (flags & SQLITE_OPEN_READONLY) {
|
158
|
+
ctx->flags |= SQLITE3_RB_DATABASE_READONLY;
|
159
|
+
}
|
100
160
|
|
101
161
|
return self;
|
102
162
|
}
|
@@ -119,21 +179,38 @@ rb_sqlite3_disable_quirk_mode(VALUE self)
|
|
119
179
|
#endif
|
120
180
|
}
|
121
181
|
|
122
|
-
/*
|
182
|
+
/*
|
183
|
+
* Close the database and release all associated resources.
|
123
184
|
*
|
124
|
-
*
|
185
|
+
* ⚠ Writable connections that are carried across a <tt>fork()</tt> are not completely
|
186
|
+
* closed. {Sqlite does not support forking}[https://www.sqlite.org/howtocorrupt.html],
|
187
|
+
* and fully closing a writable connection that has been carried across a fork may corrupt the
|
188
|
+
* database. Since it is an incomplete close, not all memory resources are freed, but this is safer
|
189
|
+
* than risking data loss.
|
190
|
+
*
|
191
|
+
* See rdoc-ref:adr/2024-09-fork-safety.md for more information on fork safety.
|
125
192
|
*/
|
126
193
|
static VALUE
|
127
194
|
sqlite3_rb_close(VALUE self)
|
128
195
|
{
|
129
196
|
sqlite3RubyPtr ctx;
|
130
|
-
sqlite3 *db;
|
131
197
|
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
132
198
|
|
133
|
-
|
134
|
-
CHECK(db, sqlite3_close(ctx->db));
|
199
|
+
close_or_discard_db(ctx);
|
135
200
|
|
136
|
-
|
201
|
+
rb_iv_set(self, "-aggregators", Qnil);
|
202
|
+
|
203
|
+
return self;
|
204
|
+
}
|
205
|
+
|
206
|
+
/* private method, primarily for testing */
|
207
|
+
static VALUE
|
208
|
+
sqlite3_rb_discard(VALUE self)
|
209
|
+
{
|
210
|
+
sqlite3RubyPtr ctx;
|
211
|
+
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
212
|
+
|
213
|
+
discard_db(ctx);
|
137
214
|
|
138
215
|
rb_iv_set(self, "-aggregators", Qnil);
|
139
216
|
|
@@ -246,7 +323,7 @@ busy_handler(int argc, VALUE *argv, VALUE self)
|
|
246
323
|
rb_scan_args(argc, argv, "01", &block);
|
247
324
|
|
248
325
|
if (NIL_P(block) && rb_block_given_p()) { block = rb_block_proc(); }
|
249
|
-
ctx->busy_handler
|
326
|
+
RB_OBJ_WRITE(self, &ctx->busy_handler, block);
|
250
327
|
|
251
328
|
status = sqlite3_busy_handler(
|
252
329
|
ctx->db,
|
@@ -869,6 +946,9 @@ rb_sqlite3_open16(VALUE self, VALUE file)
|
|
869
946
|
#endif
|
870
947
|
#endif
|
871
948
|
|
949
|
+
// sqlite3_open16 implicitly uses flags (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE)
|
950
|
+
// see https://www.sqlite.org/capi3ref.html#sqlite3_open
|
951
|
+
// so we do not ever set SQLITE3_RB_DATABASE_READONLY in ctx->flags
|
872
952
|
status = sqlite3_open16(utf16_string_value_ptr(file), &ctx->db);
|
873
953
|
|
874
954
|
CHECK(ctx->db, status)
|
@@ -889,6 +969,7 @@ init_sqlite3_database(void)
|
|
889
969
|
rb_define_private_method(cSqlite3Database, "open16", rb_sqlite3_open16, 1);
|
890
970
|
rb_define_method(cSqlite3Database, "collation", collation, 2);
|
891
971
|
rb_define_method(cSqlite3Database, "close", sqlite3_rb_close, 0);
|
972
|
+
rb_define_private_method(cSqlite3Database, "discard", sqlite3_rb_discard, 0);
|
892
973
|
rb_define_method(cSqlite3Database, "closed?", closed_p, 0);
|
893
974
|
rb_define_method(cSqlite3Database, "total_changes", total_changes, 0);
|
894
975
|
rb_define_method(cSqlite3Database, "trace", trace, -1);
|
data/ext/sqlite3/database.h
CHANGED
@@ -3,11 +3,17 @@
|
|
3
3
|
|
4
4
|
#include <sqlite3_ruby.h>
|
5
5
|
|
6
|
+
/* bits in the `flags` field */
|
7
|
+
#define SQLITE3_RB_DATABASE_READONLY 0x01
|
8
|
+
#define SQLITE3_RB_DATABASE_DISCARDED 0x02
|
9
|
+
|
6
10
|
struct _sqlite3Ruby {
|
7
11
|
sqlite3 *db;
|
8
12
|
VALUE busy_handler;
|
9
13
|
int stmt_timeout;
|
10
14
|
struct timespec stmt_deadline;
|
15
|
+
rb_pid_t owner;
|
16
|
+
int flags;
|
11
17
|
};
|
12
18
|
|
13
19
|
typedef struct _sqlite3Ruby sqlite3Ruby;
|
data/ext/sqlite3/extconf.rb
CHANGED
data/ext/sqlite3/statement.c
CHANGED
@@ -1,9 +1,13 @@
|
|
1
1
|
#include <sqlite3_ruby.h>
|
2
2
|
|
3
3
|
#define REQUIRE_OPEN_STMT(_ctxt) \
|
4
|
-
if(!_ctxt->st) \
|
4
|
+
if (!_ctxt->st) \
|
5
5
|
rb_raise(rb_path2class("SQLite3::Exception"), "cannot use a closed statement");
|
6
6
|
|
7
|
+
#define REQUIRE_LIVE_DB(_ctxt) \
|
8
|
+
if (_ctxt->db->flags & SQLITE3_RB_DATABASE_DISCARDED) \
|
9
|
+
rb_raise(rb_path2class("SQLite3::Exception"), "cannot use a statement associated with a discarded database");
|
10
|
+
|
7
11
|
VALUE cSqlite3Statement;
|
8
12
|
|
9
13
|
static void
|
@@ -57,6 +61,11 @@ prepare(VALUE self, VALUE db, VALUE sql)
|
|
57
61
|
|
58
62
|
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
59
63
|
|
64
|
+
/* Dereferencing a pointer to the database struct will be faster than accessing it through the
|
65
|
+
* instance variable @connection. The struct pointer is guaranteed to be live because instance
|
66
|
+
* variable will keep it from being GCed. */
|
67
|
+
ctx->db = db_ctx;
|
68
|
+
|
60
69
|
#ifdef HAVE_SQLITE3_PREPARE_V2
|
61
70
|
status = sqlite3_prepare_v2(
|
62
71
|
#else
|
@@ -121,6 +130,7 @@ step(VALUE self)
|
|
121
130
|
|
122
131
|
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
123
132
|
|
133
|
+
REQUIRE_LIVE_DB(ctx);
|
124
134
|
REQUIRE_OPEN_STMT(ctx);
|
125
135
|
|
126
136
|
if (ctx->done_p) { return Qnil; }
|
@@ -216,6 +226,8 @@ bind_param(VALUE self, VALUE key, VALUE value)
|
|
216
226
|
int index;
|
217
227
|
|
218
228
|
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
229
|
+
|
230
|
+
REQUIRE_LIVE_DB(ctx);
|
219
231
|
REQUIRE_OPEN_STMT(ctx);
|
220
232
|
|
221
233
|
switch (TYPE(key)) {
|
@@ -308,6 +320,8 @@ reset_bang(VALUE self)
|
|
308
320
|
sqlite3StmtRubyPtr ctx;
|
309
321
|
|
310
322
|
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
323
|
+
|
324
|
+
REQUIRE_LIVE_DB(ctx);
|
311
325
|
REQUIRE_OPEN_STMT(ctx);
|
312
326
|
|
313
327
|
sqlite3_reset(ctx->st);
|
@@ -328,6 +342,8 @@ clear_bindings_bang(VALUE self)
|
|
328
342
|
sqlite3StmtRubyPtr ctx;
|
329
343
|
|
330
344
|
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
345
|
+
|
346
|
+
REQUIRE_LIVE_DB(ctx);
|
331
347
|
REQUIRE_OPEN_STMT(ctx);
|
332
348
|
|
333
349
|
sqlite3_clear_bindings(ctx->st);
|
@@ -360,6 +376,8 @@ column_count(VALUE self)
|
|
360
376
|
{
|
361
377
|
sqlite3StmtRubyPtr ctx;
|
362
378
|
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
379
|
+
|
380
|
+
REQUIRE_LIVE_DB(ctx);
|
363
381
|
REQUIRE_OPEN_STMT(ctx);
|
364
382
|
|
365
383
|
return INT2NUM(sqlite3_column_count(ctx->st));
|
@@ -391,6 +409,8 @@ column_name(VALUE self, VALUE index)
|
|
391
409
|
const char *name;
|
392
410
|
|
393
411
|
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
412
|
+
|
413
|
+
REQUIRE_LIVE_DB(ctx);
|
394
414
|
REQUIRE_OPEN_STMT(ctx);
|
395
415
|
|
396
416
|
name = sqlite3_column_name(ctx->st, (int)NUM2INT(index));
|
@@ -414,6 +434,8 @@ column_decltype(VALUE self, VALUE index)
|
|
414
434
|
const char *name;
|
415
435
|
|
416
436
|
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
437
|
+
|
438
|
+
REQUIRE_LIVE_DB(ctx);
|
417
439
|
REQUIRE_OPEN_STMT(ctx);
|
418
440
|
|
419
441
|
name = sqlite3_column_decltype(ctx->st, (int)NUM2INT(index));
|
@@ -431,6 +453,8 @@ bind_parameter_count(VALUE self)
|
|
431
453
|
{
|
432
454
|
sqlite3StmtRubyPtr ctx;
|
433
455
|
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
456
|
+
|
457
|
+
REQUIRE_LIVE_DB(ctx);
|
434
458
|
REQUIRE_OPEN_STMT(ctx);
|
435
459
|
|
436
460
|
return INT2NUM(sqlite3_bind_parameter_count(ctx->st));
|
@@ -538,7 +562,10 @@ stats_as_hash(VALUE self)
|
|
538
562
|
{
|
539
563
|
sqlite3StmtRubyPtr ctx;
|
540
564
|
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
565
|
+
|
566
|
+
REQUIRE_LIVE_DB(ctx);
|
541
567
|
REQUIRE_OPEN_STMT(ctx);
|
568
|
+
|
542
569
|
VALUE arg = rb_hash_new();
|
543
570
|
|
544
571
|
stmt_stat_internal(arg, ctx->st);
|
@@ -554,6 +581,8 @@ stat_for(VALUE self, VALUE key)
|
|
554
581
|
{
|
555
582
|
sqlite3StmtRubyPtr ctx;
|
556
583
|
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
584
|
+
|
585
|
+
REQUIRE_LIVE_DB(ctx);
|
557
586
|
REQUIRE_OPEN_STMT(ctx);
|
558
587
|
|
559
588
|
if (SYMBOL_P(key)) {
|
@@ -574,6 +603,8 @@ memused(VALUE self)
|
|
574
603
|
{
|
575
604
|
sqlite3StmtRubyPtr ctx;
|
576
605
|
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
606
|
+
|
607
|
+
REQUIRE_LIVE_DB(ctx);
|
577
608
|
REQUIRE_OPEN_STMT(ctx);
|
578
609
|
|
579
610
|
return INT2NUM(sqlite3_stmt_status(ctx->st, SQLITE_STMTSTATUS_MEMUSED, 0));
|
@@ -591,6 +622,8 @@ database_name(VALUE self, VALUE index)
|
|
591
622
|
{
|
592
623
|
sqlite3StmtRubyPtr ctx;
|
593
624
|
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
625
|
+
|
626
|
+
REQUIRE_LIVE_DB(ctx);
|
594
627
|
REQUIRE_OPEN_STMT(ctx);
|
595
628
|
|
596
629
|
return SQLITE3_UTF8_STR_NEW2(
|
@@ -608,6 +641,8 @@ get_sql(VALUE self)
|
|
608
641
|
{
|
609
642
|
sqlite3StmtRubyPtr ctx;
|
610
643
|
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
644
|
+
|
645
|
+
REQUIRE_LIVE_DB(ctx);
|
611
646
|
REQUIRE_OPEN_STMT(ctx);
|
612
647
|
|
613
648
|
return rb_obj_freeze(SQLITE3_UTF8_STR_NEW2(sqlite3_sql(ctx->st)));
|
@@ -626,6 +661,8 @@ get_expanded_sql(VALUE self)
|
|
626
661
|
VALUE rb_expanded_sql;
|
627
662
|
|
628
663
|
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
664
|
+
|
665
|
+
REQUIRE_LIVE_DB(ctx);
|
629
666
|
REQUIRE_OPEN_STMT(ctx);
|
630
667
|
|
631
668
|
expanded_sql = sqlite3_expanded_sql(ctx->st);
|
data/ext/sqlite3/statement.h
CHANGED
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,62 @@
|
|
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
|
+
@suppress = false
|
21
|
+
|
22
|
+
class << self
|
23
|
+
def hook!
|
24
|
+
::Process.singleton_class.prepend(CoreExt)
|
25
|
+
end
|
26
|
+
|
27
|
+
def track(database)
|
28
|
+
@mutex.synchronize do
|
29
|
+
@databases << WeakRef.new(database)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def discard
|
34
|
+
warned = @suppress
|
35
|
+
@databases.each do |db|
|
36
|
+
next unless db.weakref_alive?
|
37
|
+
|
38
|
+
unless db.closed? || db.readonly?
|
39
|
+
unless warned
|
40
|
+
# If you are here, you may want to read
|
41
|
+
# https://github.com/sparklemotion/sqlite3-ruby/pull/558
|
42
|
+
warn("Writable sqlite database connection(s) were inherited from a forked process. " \
|
43
|
+
"This is unsafe and the connections are being closed to prevent possible data " \
|
44
|
+
"corruption. Please close writable sqlite database connections before forking.",
|
45
|
+
uplevel: 0)
|
46
|
+
warned = true
|
47
|
+
end
|
48
|
+
db.close
|
49
|
+
end
|
50
|
+
end
|
51
|
+
@databases.clear
|
52
|
+
end
|
53
|
+
|
54
|
+
# Call to suppress the fork-related warnings.
|
55
|
+
def suppress_warnings!
|
56
|
+
@suppress = true
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
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
|
5
5
|
platform: arm64-darwin
|
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-24 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.bundle
|
56
55
|
- lib/sqlite3/3.1/sqlite3_native.bundle
|
57
56
|
- lib/sqlite3/3.2/sqlite3_native.bundle
|
58
57
|
- lib/sqlite3/3.3/sqlite3_native.bundle
|
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,7 +84,7 @@ 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
|
Binary file
|