sqlite3 2.0.4-x86_64-darwin → 2.1.0.rc1-x86_64-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: 88a950658b47c43af434dbb7559fc7c3bb8c76f184e9124aac4a060c1b30b636
4
- data.tar.gz: 875d436187636b847836c686e527fb8cb995427e34e6a4be2043eabee2e30f49
3
+ metadata.gz: ec86b458d7af7bd57340d42f71081b6998cc79b37f44953bccb0beef8e7ae446
4
+ data.tar.gz: 6a51f55660e5f8c62a12f37a4abf59f2727accaf62bc7319648c54aa42579904
5
5
  SHA512:
6
- metadata.gz: 0dc1c7ab55125b90052194df2a37f52d4b34ad81d247d58bd9ebad108beaa6ce41a57f2cdf9ec75be12817ffe8c88cf965a079c1dabe43cad0a79400af8e25fc
7
- data.tar.gz: 3cb14e5b72ada3807fec97b74fcdede11fe5d98ea03285ef4f7b1110b5e43ae923b7ad88e51558a66861af954d1cdd5448f3efe7a8bc9846bb32d8e9f33fd365
6
+ metadata.gz: ccebcd097c3752747bdbb948cbfc45625b8bd31981ca166131ccbc93d19c1f4e4b98af6245f0758c1733893ccc6691ffdd09a1b2e4439e8b5539daab6956af70
7
+ data.tar.gz: c4bbfdd2fab16ddf56de6da1ea2aeb4595b7c2a529cd71234bd74c7093024be08dfa7e0f5fce6f6dd05ae8e8a7dce669df02dc46e64daa2ab6a40656b31bb6cc
data/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
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
+
3
28
  ## 2.0.4 / 2024-08-13
4
29
 
5
30
  ### Dependencies
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
- 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.
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,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
- sqlite3RubyPtr c = (sqlite3RubyPtr)ctx;
26
- sqlite3 *db = c->db;
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
- return TypedData_Make_Struct(klass, sqlite3Ruby, &database_type, ctx);
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
- NUM2INT(mode),
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
- /* call-seq: db.close
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
- * Closes this database.
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
- db = ctx->db;
134
- CHECK(db, sqlite3_close(ctx->db));
196
+ close_or_discard_db(ctx);
135
197
 
136
- ctx->db = NULL;
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 = block;
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);
@@ -8,6 +8,8 @@ struct _sqlite3Ruby {
8
8
  VALUE busy_handler;
9
9
  int stmt_timeout;
10
10
  struct timespec stmt_deadline;
11
+ rb_pid_t owner;
12
+ int flags;
11
13
  };
12
14
 
13
15
  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
@@ -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
@@ -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!
@@ -1,3 +1,3 @@
1
1
  module SQLite3
2
- VERSION = "2.0.4"
2
+ VERSION = "2.1.0.rc1"
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.rc1
5
5
  platform: x86_64-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-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.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,15 +84,15 @@ 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
91
91
  required_rubygems_version: !ruby/object:Gem::Requirement
92
92
  requirements:
93
- - - ">="
93
+ - - ">"
94
94
  - !ruby/object:Gem::Version
95
- version: '0'
95
+ version: 1.3.1
96
96
  requirements: []
97
97
  rubygems_version: 3.3.26
98
98
  signing_key:
Binary file