sqlite3 2.0.4-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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e4fda41284b5cbc76faafcb5c91caba134045270b84ffb97b6bde6260d5434cc
4
- data.tar.gz: 19a475ae4302ebc84aee3ef33d9be68c3207897d3e28e6aa0fcf723e67a47bd9
3
+ metadata.gz: 58efd48fc68d6c46767c603de815022f41d35eb2aaafb187b8f18fa71ff4330e
4
+ data.tar.gz: 3b904d8a115523e5a716ca2b1994fc66740181e40affddab7524c5c773a28576
5
5
  SHA512:
6
- metadata.gz: dc80e97cfa3032788ccb5f615143ecd323ba56b729eb0d322f54e4bb19b695871441c1c3e9ca9ad87f37459cfc7969c5c8262a59b8f22b2148bcb1cbf9949d9a
7
- data.tar.gz: ceb1d28498544812084c47eed7684a96d86e4b343148b4ad1bcde5d4fdb670e26576f904ce282b5b7beb3c091f6ee5dff9a93a24b0abaa857f657f5ee4673251
6
+ metadata.gz: 0e059c53db3c06f362ba18d1e8e3d2c588156225b3616adbff17d07f86c67688585d66807c4aa227b174e8dc9e0ea0f6acc23bc67d6a2c3ddec7dcf04aab0401
7
+ data.tar.gz: 2dc22242ff0d87c182c7b90dec3e14f85636d257d3576317142e30ad78b8ce7cc6d1638227d42ae968715d30e1ff17ecdbb07677fb713a2944e5f6fd4a5df415
data/CHANGELOG.md CHANGED
@@ -1,5 +1,35 @@
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
+
3
33
  ## 2.0.4 / 2024-08-13
4
34
 
5
35
  ### Dependencies
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. Memory allocated for a statement handler will be freed in
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 can't free the memory for the statement in the garbage collection function
21
- for the statement handler. The reason is because there exists a race
22
- condition. We cannot guarantee the order in which objects will be garbage
23
- collected. So, it is possible that a connection and a statement are up for
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
- If you ever want to know the names or types of the columns in a result
213
- set, you can do it in several ways.
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
- The first way is to ask the row object itself. Each row will have a
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
- ```ruby
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
- Obviously, this approach requires you to execute a statement that actually
230
- returns data. If you don't know if the statement will return any rows, but
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
- db.query( "select * from table" ) do |result|
237
- p result.columns
238
- p result.types
239
- ...
240
- end
241
- ```
242
-
243
-
244
- Lastly, you can use `Database#prepare` and ask the `Statement` object what
245
- the metadata are:
246
-
247
-
248
- ```ruby
249
- stmt = db.prepare( "select * from table" )
250
- p stmt.columns
251
- p stmt.types
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
- The other way is to use Ara Howard's
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-mingw32` / `x64-mingw-ucrt`
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
@@ -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
- sqlite3RubyPtr c = (sqlite3RubyPtr)ctx;
26
- sqlite3 *db = c->db;
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
- return TypedData_Make_Struct(klass, sqlite3Ruby, &database_type, ctx);
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
- NUM2INT(mode),
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
- /* call-seq: db.close
182
+ /*
183
+ * Close the database and release all associated resources.
123
184
  *
124
- * Closes this database.
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
- db = ctx->db;
134
- CHECK(db, sqlite3_close(ctx->db));
199
+ close_or_discard_db(ctx);
135
200
 
136
- ctx->db = NULL;
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 = block;
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);
@@ -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;
@@ -131,6 +131,8 @@ module Sqlite3
131
131
  end
132
132
 
133
133
  have_func("sqlite3_prepare_v2")
134
+ have_func("sqlite3_db_name", "sqlite3.h") # v3.39.0
135
+
134
136
  have_type("sqlite3_int64", "sqlite3.h")
135
137
  have_type("sqlite3_uint64", "sqlite3.h")
136
138
  end
@@ -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);
@@ -5,6 +5,7 @@
5
5
 
6
6
  struct _sqlite3StmtRuby {
7
7
  sqlite3_stmt *st;
8
+ sqlite3Ruby *db;
8
9
  int done_p;
9
10
  };
10
11
 
Binary file
Binary file
Binary file
@@ -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!
@@ -1,3 +1,3 @@
1
1
  module SQLite3
2
- VERSION = "2.0.4"
2
+ VERSION = "2.1.0"
3
3
  end
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
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-08-13 00:00:00.000000000 Z
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.0'
87
+ version: '3.1'
88
88
  - - "<"
89
89
  - !ruby/object:Gem::Version
90
90
  version: 3.4.dev
Binary file