extralite 2.5 → 2.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +34 -13
  4. data/Gemfile +4 -0
  5. data/Gemfile-bundle +1 -1
  6. data/LICENSE +1 -1
  7. data/README.md +1059 -247
  8. data/Rakefile +18 -0
  9. data/TODO.md +0 -7
  10. data/examples/kv_store.rb +49 -0
  11. data/examples/multi_fiber.rb +16 -0
  12. data/examples/on_progress.rb +9 -0
  13. data/examples/pubsub_store_polyphony.rb +194 -0
  14. data/examples/pubsub_store_threads.rb +204 -0
  15. data/ext/extralite/changeset.c +463 -0
  16. data/ext/extralite/common.c +177 -91
  17. data/ext/extralite/database.c +745 -276
  18. data/ext/extralite/extconf-bundle.rb +10 -4
  19. data/ext/extralite/extconf.rb +34 -34
  20. data/ext/extralite/extralite.h +104 -47
  21. data/ext/extralite/extralite_ext.c +6 -0
  22. data/ext/extralite/iterator.c +14 -86
  23. data/ext/extralite/query.c +171 -264
  24. data/extralite-bundle.gemspec +1 -1
  25. data/extralite.gemspec +1 -1
  26. data/gemspec.rb +10 -11
  27. data/lib/extralite/version.rb +1 -1
  28. data/lib/extralite.rb +69 -10
  29. data/lib/sequel/adapters/extralite.rb +1 -1
  30. data/test/helper.rb +9 -1
  31. data/test/perf_argv_transform.rb +74 -0
  32. data/test/perf_ary.rb +14 -12
  33. data/test/perf_hash.rb +17 -15
  34. data/test/perf_hash_prepared.rb +58 -0
  35. data/test/perf_hash_transform.rb +66 -0
  36. data/test/perf_polyphony.rb +74 -0
  37. data/test/test_changeset.rb +161 -0
  38. data/test/test_database.rb +720 -104
  39. data/test/test_extralite.rb +2 -2
  40. data/test/test_iterator.rb +28 -13
  41. data/test/test_query.rb +352 -110
  42. data/test/test_sequel.rb +4 -4
  43. metadata +24 -16
  44. data/Gemfile.lock +0 -37
  45. data/test/perf_prepared.rb +0 -64
@@ -16,12 +16,30 @@ ID ID_call;
16
16
  ID ID_each;
17
17
  ID ID_keys;
18
18
  ID ID_new;
19
+ ID ID_pragma;
19
20
  ID ID_strip;
21
+ ID ID_to_s;
22
+ ID ID_track;
20
23
 
24
+ VALUE SYM_at_least_once;
21
25
  VALUE SYM_gvl_release_threshold;
26
+ VALUE SYM_once;
27
+ VALUE SYM_none;
28
+ VALUE SYM_normal;
29
+ VALUE SYM_pragma;
22
30
  VALUE SYM_read_only;
23
- VALUE SYM_synchronous;
24
- VALUE SYM_wal_journal_mode;
31
+ VALUE SYM_wal;
32
+
33
+ struct progress_handler global_progress_handler = {
34
+ .mode = PROGRESS_NONE,
35
+ .proc = Qnil,
36
+ .period = DEFAULT_PROGRESS_HANDLER_PERIOD,
37
+ .tick = DEFAULT_PROGRESS_HANDLER_TICK,
38
+ .tick_count = 0,
39
+ .call_count = 0
40
+ };
41
+
42
+ #define DB_GVL_MODE(db) Database_prepare_gvl_mode(db)
25
43
 
26
44
  static size_t Database_size(const void *ptr) {
27
45
  return sizeof(Database_t);
@@ -29,12 +47,14 @@ static size_t Database_size(const void *ptr) {
29
47
 
30
48
  static void Database_mark(void *ptr) {
31
49
  Database_t *db = ptr;
32
- rb_gc_mark_movable(db->trace_block);
50
+ rb_gc_mark_movable(db->trace_proc);
51
+ rb_gc_mark_movable(db->progress_handler.proc);
33
52
  }
34
53
 
35
54
  static void Database_compact(void *ptr) {
36
55
  Database_t *db = ptr;
37
- db->trace_block = rb_gc_location(db->trace_block);
56
+ db->trace_proc = rb_gc_location(db->trace_proc);
57
+ db->progress_handler.proc = rb_gc_location(db->progress_handler.proc);
38
58
  }
39
59
 
40
60
  static void Database_free(void *ptr) {
@@ -51,7 +71,10 @@ static const rb_data_type_t Database_type = {
51
71
 
52
72
  static VALUE Database_allocate(VALUE klass) {
53
73
  Database_t *db = ALLOC(Database_t);
54
- db->sqlite3_db = 0;
74
+ db->sqlite3_db = NULL;
75
+ db->trace_proc = Qnil;
76
+ db->progress_handler.proc = Qnil;
77
+ db->progress_handler.mode = PROGRESS_NONE;
55
78
  return TypedData_Wrap_Struct(klass, &Database_type, db);
56
79
  }
57
80
 
@@ -72,12 +95,10 @@ inline sqlite3 *Database_sqlite3_db(VALUE self) {
72
95
  return self_to_database(self)->sqlite3_db;
73
96
  }
74
97
 
75
- /* call-seq:
76
- * Extralite.sqlite3_version -> version
98
+ /* Returns the sqlite3 library version used by Extralite.
77
99
  *
78
- * Returns the sqlite3 version used by Extralite.
100
+ * @return [String] SQLite version
79
101
  */
80
-
81
102
  VALUE Extralite_sqlite3_version(VALUE self) {
82
103
  return rb_str_new_cstr(sqlite3_version);
83
104
  }
@@ -94,40 +115,64 @@ default_flags:
94
115
  return SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
95
116
  }
96
117
 
97
- VALUE Database_execute(int argc, VALUE *argv, VALUE self);
98
-
99
118
  void Database_apply_opts(VALUE self, Database_t *db, VALUE opts) {
100
119
  VALUE value = Qnil;
101
120
 
121
+ // :gvl_release_threshold
102
122
  value = rb_hash_aref(opts, SYM_gvl_release_threshold);
103
123
  if (!NIL_P(value)) db->gvl_release_threshold = NUM2INT(value);
104
124
 
105
- value = rb_hash_aref(opts, SYM_wal_journal_mode);
106
- if (RTEST(value)) {
107
- value = rb_str_new_literal("PRAGMA journal_mode=wal");
108
- Database_execute(1, &value, self);
109
- }
125
+ // :pragma
126
+ value = rb_hash_aref(opts, SYM_pragma);
127
+ if (!NIL_P(value)) rb_funcall(self, ID_pragma, 1, value);
110
128
 
111
- value = rb_hash_aref(opts, SYM_synchronous);
129
+ // :wal
130
+ value = rb_hash_aref(opts, SYM_wal);
112
131
  if (RTEST(value)) {
113
- value = rb_str_new_literal("PRAGMA synchronous=1");
114
- Database_execute(1, &value, self);
132
+ int rc = sqlite3_exec(db->sqlite3_db, "PRAGMA journal_mode=wal", NULL, NULL, NULL);
133
+ if (rc != SQLITE_OK)
134
+ rb_raise(cError, "Failed to set WAL journaling mode: %s", sqlite3_errstr(rc));
135
+ rc = sqlite3_exec(db->sqlite3_db, "PRAGMA synchronous=1", NULL, NULL, NULL);
136
+ if (rc != SQLITE_OK)
137
+ rb_raise(cError, "Failed to set synchronous mode: %s", sqlite3_errstr(rc));
115
138
  }
139
+ }
140
+
141
+ int Database_progress_handler(void *ptr) {
142
+ Database_t *db = (Database_t *)ptr;
143
+ db->progress_handler.tick_count += db->progress_handler.tick;
144
+ if (db->progress_handler.tick_count < db->progress_handler.period)
145
+ goto done;
146
+
147
+ db->progress_handler.tick_count -= db->progress_handler.period;
148
+ db->progress_handler.call_count += 1;
149
+ rb_funcall(db->progress_handler.proc, ID_call, 0);
150
+ done:
151
+ return 0;
152
+ }
116
153
 
117
- RB_GC_GUARD(value);
154
+ int Database_busy_handler(void *ptr, int v) {
155
+ Database_t *db = (Database_t *)ptr;
156
+ rb_funcall(db->progress_handler.proc, ID_call, 1, Qtrue);
157
+ return 1;
118
158
  }
119
159
 
120
- /* Initializes a new SQLite database with the given path and options.
160
+ /* Initializes a new SQLite database with the given path and options:
161
+ *
162
+ * - `:gvl_release_threshold` (`Integer`): sets the GVL release threshold (see
163
+ * `#gvl_release_threshold=`).
164
+ * - `:pragma` (`Hash`): one or more pragmas to set upon opening the database.
165
+ * - `:read_only` (`true`/`false`): opens the database in read-only mode if true.
166
+ * - `:wal` (`true`/`false`): sets up the database for [WAL journaling
167
+ * mode](https://www.sqlite.org/wal.html) by setting `PRAGMA journal_mode=wal`
168
+ * and `PRAGMA synchronous=1`.
121
169
  *
122
170
  * @overload initialize(path)
123
171
  * @param path [String] file path (or ':memory:' for memory database)
124
172
  * @return [void]
125
- * @overload initialize(path, gvl_release_threshold: , read_only: , synchronous: , wal_journal_mode: )
173
+ * @overload initialize(path, gvl_release_threshold: , on_progress: , read_only: , wal: )
126
174
  * @param path [String] file path (or ':memory:' for memory database)
127
- * @param gvl_release_threshold [Integer] GVL release threshold
128
- * @param read_only [boolean] true for opening the database for reading only
129
- * @param synchronous [boolean] true to set PRAGMA synchronous=1
130
- * @param wal_journal_mode [boolean] true to set PRAGMA journal_mode=wal
175
+ * @param options [Hash] options for opening the database
131
176
  * @return [void]
132
177
  */
133
178
  VALUE Database_initialize(int argc, VALUE *argv, VALUE self) {
@@ -141,6 +186,7 @@ VALUE Database_initialize(int argc, VALUE *argv, VALUE self) {
141
186
  int rc = sqlite3_open_v2(StringValueCStr(path), &db->sqlite3_db, flags, NULL);
142
187
  if (rc) {
143
188
  sqlite3_close_v2(db->sqlite3_db);
189
+ db->sqlite3_db = NULL;
144
190
  rb_raise(cError, "%s", sqlite3_errstr(rc));
145
191
  }
146
192
 
@@ -159,11 +205,20 @@ VALUE Database_initialize(int argc, VALUE *argv, VALUE self) {
159
205
  }
160
206
  #endif
161
207
 
162
- db->trace_block = Qnil;
208
+ db->trace_proc = Qnil;
163
209
  db->gvl_release_threshold = DEFAULT_GVL_RELEASE_THRESHOLD;
164
210
 
165
- if (!NIL_P(opts)) Database_apply_opts(self, db, opts);
211
+ db->progress_handler = global_progress_handler;
212
+ db->progress_handler.tick_count = 0;
213
+ db->progress_handler.call_count = 0;
214
+ if (db->progress_handler.mode != PROGRESS_NONE) {
215
+ db->gvl_release_threshold = -1;
216
+ if (db->progress_handler.mode != PROGRESS_ONCE)
217
+ sqlite3_progress_handler(db->sqlite3_db, db->progress_handler.tick, &Database_progress_handler, db);
218
+ sqlite3_busy_handler(db->sqlite3_db, &Database_busy_handler, db);
219
+ }
166
220
 
221
+ if (!NIL_P(opts)) Database_apply_opts(self, db, opts);
167
222
  return Qnil;
168
223
  }
169
224
 
@@ -177,10 +232,9 @@ VALUE Database_read_only_p(VALUE self) {
177
232
  return (open == 1) ? Qtrue : Qfalse;
178
233
  }
179
234
 
180
- /* call-seq:
181
- * db.close -> db
182
- *
183
- * Closes the database.
235
+ /* Closes the database.
236
+ *
237
+ * @return [Extralite::Database] database
184
238
  */
185
239
  VALUE Database_close(VALUE self) {
186
240
  int rc;
@@ -191,7 +245,7 @@ VALUE Database_close(VALUE self) {
191
245
  rb_raise(cError, "%s", sqlite3_errmsg(db->sqlite3_db));
192
246
  }
193
247
 
194
- db->sqlite3_db = 0;
248
+ db->sqlite3_db = NULL;
195
249
  return self;
196
250
  }
197
251
 
@@ -207,24 +261,44 @@ VALUE Database_closed_p(VALUE self) {
207
261
  return db->sqlite3_db ? Qfalse : Qtrue;
208
262
  }
209
263
 
210
- static inline VALUE Database_perform_query(int argc, VALUE *argv, VALUE self, VALUE (*call)(query_ctx *)) {
264
+ inline enum gvl_mode Database_prepare_gvl_mode(Database_t *db) {
265
+ return db->gvl_release_threshold < 0 ? GVL_HOLD : GVL_RELEASE;
266
+ }
267
+
268
+ static inline VALUE Database_perform_query(int argc, VALUE *argv, VALUE self, VALUE (*call)(query_ctx *), enum query_mode query_mode) {
211
269
  Database_t *db = self_to_open_database(self);
212
270
  sqlite3_stmt *stmt;
213
- VALUE sql;
214
-
271
+ VALUE sql = Qnil;
272
+ VALUE transform = Qnil;
273
+ // transform mode is set and the first parameter is not a string, so we expect
274
+ // a transform.
275
+ int got_transform = (TYPE(argv[0]) != T_STRING);
276
+
215
277
  // extract query from args
216
- rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
278
+ rb_check_arity(argc, got_transform ? 2 : 1, UNLIMITED_ARGUMENTS);
279
+
280
+ if (got_transform) {
281
+ transform = argv[0];
282
+ argc--;
283
+ argv++;
284
+ }
285
+
217
286
  sql = rb_funcall(argv[0], ID_strip, 0);
218
287
  if (RSTRING_LEN(sql) == 0) return Qnil;
219
288
 
220
- if (db->trace_block != Qnil) rb_funcall(db->trace_block, ID_call, 1, sql);
221
- prepare_multi_stmt(db->sqlite3_db, &stmt, sql);
289
+ Database_issue_query(db, sql);
290
+ prepare_multi_stmt(DB_GVL_MODE(db), db->sqlite3_db, &stmt, sql);
222
291
  RB_GC_GUARD(sql);
223
292
 
224
293
  bind_all_parameters(stmt, argc - 1, argv + 1);
225
- query_ctx ctx = QUERY_CTX(self, db, stmt, Qnil, QUERY_MODE(QUERY_MULTI_ROW), ALL_ROWS);
226
-
227
- return rb_ensure(SAFE(call), (VALUE)&ctx, SAFE(cleanup_stmt), (VALUE)&ctx);
294
+ query_ctx ctx = QUERY_CTX(
295
+ self, sql, db, stmt, Qnil, transform,
296
+ query_mode, ROW_YIELD_OR_MODE(ROW_MULTI), ALL_ROWS
297
+ );
298
+
299
+ VALUE result = rb_ensure(SAFE(call), (VALUE)&ctx, SAFE(cleanup_stmt), (VALUE)&ctx);
300
+ RB_GC_GUARD(result);
301
+ return result;
228
302
  }
229
303
 
230
304
  /* call-seq:
@@ -243,21 +317,46 @@ static inline VALUE Database_perform_query(int argc, VALUE *argv, VALUE self, VA
243
317
  * db.query('select * from foo where x = ?', 42)
244
318
  *
245
319
  * Named placeholders are specified using `:`. The placeholder values are
246
- * specified using a hash, where keys are either strings are symbols. String
247
- * keys can include or omit the `:` prefix. The following are equivalent:
320
+ * specified using keyword arguments:
248
321
  *
249
322
  * db.query('select * from foo where x = :bar', bar: 42)
250
- * db.query('select * from foo where x = :bar', 'bar' => 42)
251
- * db.query('select * from foo where x = :bar', ':bar' => 42)
323
+ *
324
+ * @overload query(sql, ...)
325
+ * @param sql [String] SQL statement
326
+ * @return [Array<Hash>, Integer] rows or total changes
327
+ * @overload query(transform, sql, ...)
328
+ * @param transform [Proc] transform proc
329
+ * @param sql [String] SQL statement
330
+ * @return [Array<Hash>, Integer] rows or total changes
252
331
  */
253
- VALUE Database_query_hash(int argc, VALUE *argv, VALUE self) {
254
- return Database_perform_query(argc, argv, self, safe_query_hash);
332
+ VALUE Database_query(int argc, VALUE *argv, VALUE self) {
333
+ return Database_perform_query(argc, argv, self, safe_query_hash, QUERY_HASH);
255
334
  }
256
335
 
257
- /* call-seq:
258
- * db.query_ary(sql, *parameters, &block) -> [...]
336
+ /* Runs a query and transforms rows through the given transform poc. Each row is
337
+ * provided to the transform proc as a list of values. If a block is given, it
338
+ * will be called for each row. Otherwise, an array containing all rows is
339
+ * returned.
259
340
  *
260
- * Runs a query returning rows as arrays. If a block is given, it will be called
341
+ * If a transform block is given, it is called for each row, with the row values
342
+ * splatted:
343
+ *
344
+ * transform = ->(a, b, c) { a * 100 + b * 10 + c }
345
+ * db.query_argv(transform, 'select a, b, c from foo where c = ?', 42)
346
+ *
347
+ * @overload query_argv(sql, ...)
348
+ * @param sql [String] SQL statement
349
+ * @return [Array<Array, any>, Integer] rows or total changes
350
+ * @overload query_argv(transform, sql, ...)
351
+ * @param transform [Proc] transform proc
352
+ * @param sql [String] SQL statement
353
+ * @return [Array<Array, any>, Integer] rows or total changes
354
+ */
355
+ VALUE Database_query_argv(int argc, VALUE *argv, VALUE self) {
356
+ return Database_perform_query(argc, argv, self, safe_query_argv, QUERY_ARGV);
357
+ }
358
+
359
+ /* Runs a query returning rows as arrays. If a block is given, it will be called
261
360
  * for each row. Otherwise, an array containing all rows is returned.
262
361
  *
263
362
  * Query parameters to be bound to placeholders in the query can be specified as
@@ -274,82 +373,95 @@ VALUE Database_query_hash(int argc, VALUE *argv, VALUE self) {
274
373
  * db.query_ary('select * from foo where x = :bar', bar: 42)
275
374
  * db.query_ary('select * from foo where x = :bar', 'bar' => 42)
276
375
  * db.query_ary('select * from foo where x = :bar', ':bar' => 42)
376
+ *
377
+ * @overload query_ary(sql, ...)
378
+ * @param sql [String] SQL statement
379
+ * @return [Array<Array>, Integer] rows or total changes
380
+ * @overload query_ary(transform, sql, ...)
381
+ * @param transform [Proc] transform proc
382
+ * @param sql [String] SQL statement
383
+ * @return [Array<Array>, Integer] rows or total changes
277
384
  */
278
385
  VALUE Database_query_ary(int argc, VALUE *argv, VALUE self) {
279
- return Database_perform_query(argc, argv, self, safe_query_ary);
386
+ return Database_perform_query(argc, argv, self, safe_query_ary, QUERY_ARY);
280
387
  }
281
388
 
282
- /* call-seq:
283
- * db.query_single_row(sql, *parameters) -> {...}
284
- *
285
- * Runs a query returning a single row as a hash.
389
+ /* Runs a query returning a single row as a hash.
286
390
  *
287
391
  * Query parameters to be bound to placeholders in the query can be specified as
288
392
  * a list of values or as a hash mapping parameter names to values. When
289
393
  * parameters are given as an array, the query should specify parameters using
290
394
  * `?`:
291
395
  *
292
- * db.query_single_row('select * from foo where x = ?', 42)
396
+ * db.query_single('select * from foo where x = ?', 42)
293
397
  *
294
398
  * Named placeholders are specified using `:`. The placeholder values are
295
- * specified using a hash, where keys are either strings are symbols. String
296
- * keys can include or omit the `:` prefix. The following are equivalent:
399
+ * specified using keyword arguments:
297
400
  *
298
- * db.query_single_row('select * from foo where x = :bar', bar: 42)
299
- * db.query_single_row('select * from foo where x = :bar', 'bar' => 42)
300
- * db.query_single_row('select * from foo where x = :bar', ':bar' => 42)
401
+ * db.query_single('select * from foo where x = :bar', bar: 42)
402
+ *
403
+ * @overload query_single(sql, ...) -> row
404
+ * @param sql [String] SQL statement
405
+ * @return [Array, any] row
406
+ * @overload query_single(transform, sql, ...) -> row
407
+ * @param transform [Proc] transform proc
408
+ * @param sql [String] SQL statement
409
+ * @return [Array, any] row
301
410
  */
302
- VALUE Database_query_single_row(int argc, VALUE *argv, VALUE self) {
303
- return Database_perform_query(argc, argv, self, safe_query_single_row);
411
+ VALUE Database_query_single(int argc, VALUE *argv, VALUE self) {
412
+ return Database_perform_query(argc, argv, self, safe_query_single_row_hash, QUERY_HASH);
304
413
  }
305
414
 
306
- /* call-seq:
307
- * db.query_single_column(sql, *parameters, &block) -> [...]
308
- *
309
- * Runs a query returning single column values. If a block is given, it will be called
310
- * for each value. Otherwise, an array containing all values is returned.
415
+ /* Runs a query returning a single row as an array or a single value.
311
416
  *
312
417
  * Query parameters to be bound to placeholders in the query can be specified as
313
418
  * a list of values or as a hash mapping parameter names to values. When
314
419
  * parameters are given as an array, the query should specify parameters using
315
420
  * `?`:
316
421
  *
317
- * db.query_single_column('select x from foo where x = ?', 42)
422
+ * db.query_single_argv('select * from foo where x = ?', 42)
318
423
  *
319
424
  * Named placeholders are specified using `:`. The placeholder values are
320
- * specified using a hash, where keys are either strings are symbols. String
321
- * keys can include or omit the `:` prefix. The following are equivalent:
322
- *
323
- * db.query_single_column('select x from foo where x = :bar', bar: 42)
324
- * db.query_single_column('select x from foo where x = :bar', 'bar' => 42)
325
- * db.query_single_column('select x from foo where x = :bar', ':bar' => 42)
425
+ * specified using keyword arguments:
426
+ *
427
+ * db.query_single_argv('select * from foo where x = :bar', bar: 42)
428
+ *
429
+ * @overload query_single_argv(sql, ...) -> row
430
+ * @param sql [String] SQL statement
431
+ * @return [Array, any] row
432
+ * @overload query_single_argv(transform, sql, ...) -> row
433
+ * @param transform [Proc] transform proc
434
+ * @param sql [String] SQL statement
435
+ * @return [Array, any] row
326
436
  */
327
- VALUE Database_query_single_column(int argc, VALUE *argv, VALUE self) {
328
- return Database_perform_query(argc, argv, self, safe_query_single_column);
437
+ VALUE Database_query_single_argv(int argc, VALUE *argv, VALUE self) {
438
+ return Database_perform_query(argc, argv, self, safe_query_single_row_argv, QUERY_ARGV);
329
439
  }
330
440
 
331
- /* call-seq:
332
- * db.query_single_value(sql, *parameters) -> value
333
- *
334
- * Runs a query returning a single value from the first row.
441
+ /* Runs a query returning a single row as an array.
335
442
  *
336
443
  * Query parameters to be bound to placeholders in the query can be specified as
337
444
  * a list of values or as a hash mapping parameter names to values. When
338
445
  * parameters are given as an array, the query should specify parameters using
339
446
  * `?`:
340
447
  *
341
- * db.query_single_value('select x from foo where x = ?', 42)
448
+ * db.query_single_ary('select * from foo where x = ?', 42)
342
449
  *
343
450
  * Named placeholders are specified using `:`. The placeholder values are
344
- * specified using a hash, where keys are either strings are symbols. String
345
- * keys can include or omit the `:` prefix. The following are equivalent:
346
- *
347
- * db.query_single_value('select x from foo where x = :bar', bar: 42)
348
- * db.query_single_value('select x from foo where x = :bar', 'bar' => 42)
349
- * db.query_single_value('select x from foo where x = :bar', ':bar' => 42)
451
+ * specified using keyword arguments:
452
+ *
453
+ * db.query_single_ary('select * from foo where x = :bar', bar: 42)
454
+ *
455
+ * @overload query_single_ary(sql, ...) -> row
456
+ * @param sql [String] SQL statement
457
+ * @return [Array, any] row
458
+ * @overload query_single_ary(transform, sql, ...) -> row
459
+ * @param transform [Proc] transform proc
460
+ * @param sql [String] SQL statement
461
+ * @return [Array, any] row
350
462
  */
351
- VALUE Database_query_single_value(int argc, VALUE *argv, VALUE self) {
352
- return Database_perform_query(argc, argv, self, safe_query_single_value);
463
+ VALUE Database_query_single_ary(int argc, VALUE *argv, VALUE self) {
464
+ return Database_perform_query(argc, argv, self, safe_query_single_row_ary, QUERY_ARY);
353
465
  }
354
466
 
355
467
  /* call-seq:
@@ -366,21 +478,16 @@ VALUE Database_query_single_value(int argc, VALUE *argv, VALUE self) {
366
478
  * db.execute('update foo set x = ? where y = ?', 42, 43)
367
479
  *
368
480
  * Named placeholders are specified using `:`. The placeholder values are
369
- * specified using a hash, where keys are either strings are symbols. String
370
- * keys can include or omit the `:` prefix. The following are equivalent:
481
+ * specified using keyword arguments:
371
482
  *
372
483
  * db.execute('update foo set x = :bar', bar: 42)
373
- * db.execute('update foo set x = :bar', 'bar' => 42)
374
- * db.execute('update foo set x = :bar', ':bar' => 42)
375
484
  */
376
485
  VALUE Database_execute(int argc, VALUE *argv, VALUE self) {
377
- return Database_perform_query(argc, argv, self, safe_query_changes);
486
+ return Database_perform_query(argc, argv, self, safe_query_changes, QUERY_HASH);
378
487
  }
379
488
 
380
489
  /* call-seq:
381
- * db.batch_execute(sql, params_array) -> changes
382
- * db.batch_execute(sql, enumerable) -> changes
383
- * db.batch_execute(sql, callable) -> changes
490
+ * db.batch_execute(sql, params_source) -> changes
384
491
  *
385
492
  * Executes the given query for each list of parameters in the paramter source.
386
493
  * If an enumerable is given, it is iterated and each of its values is used as
@@ -413,19 +520,18 @@ VALUE Database_batch_execute(VALUE self, VALUE sql, VALUE parameters) {
413
520
 
414
521
  if (RSTRING_LEN(sql) == 0) return Qnil;
415
522
 
416
- prepare_single_stmt(db->sqlite3_db, &stmt, sql);
417
- query_ctx ctx = QUERY_CTX(self, db, stmt, parameters, QUERY_MULTI_ROW, ALL_ROWS);
523
+ prepare_single_stmt(DB_GVL_MODE(db), db->sqlite3_db, &stmt, sql);
524
+ query_ctx ctx = QUERY_CTX(
525
+ self, sql, db, stmt, parameters,
526
+ Qnil, QUERY_HASH, ROW_MULTI, ALL_ROWS
527
+ );
418
528
 
419
529
  return rb_ensure(SAFE(safe_batch_execute), (VALUE)&ctx, SAFE(cleanup_stmt), (VALUE)&ctx);
420
530
  }
421
531
 
422
532
  /* call-seq:
423
- * db.batch_query(sql, params_array) -> rows
424
- * db.batch_query(sql, enumerable) -> rows
425
- * db.batch_query(sql, callable) -> rows
426
- * db.batch_query(sql, params_array) { |rows| ... } -> changes
427
- * db.batch_query(sql, enumerable) { |rows| ... } -> changes
428
- * db.batch_query(sql, callable) { |rows| ... } -> changes
533
+ * db.batch_query(sql, params_source) -> rows
534
+ * db.batch_query(sql, params_source) { |rows| ... } -> changes
429
535
  *
430
536
  * Executes the given query for each list of parameters in the given paramter
431
537
  * source. If a block is given, it is called with the resulting rows for each
@@ -448,19 +554,18 @@ VALUE Database_batch_query(VALUE self, VALUE sql, VALUE parameters) {
448
554
  Database_t *db = self_to_open_database(self);
449
555
  sqlite3_stmt *stmt;
450
556
 
451
- prepare_single_stmt(db->sqlite3_db, &stmt, sql);
452
- query_ctx ctx = QUERY_CTX(self, db, stmt, parameters, QUERY_MULTI_ROW, ALL_ROWS);
557
+ prepare_single_stmt(DB_GVL_MODE(db), db->sqlite3_db, &stmt, sql);
558
+ query_ctx ctx = QUERY_CTX(
559
+ self, sql, db, stmt, parameters,
560
+ Qnil, QUERY_HASH, ROW_MULTI, ALL_ROWS
561
+ );
453
562
 
454
563
  return rb_ensure(SAFE(safe_batch_query), (VALUE)&ctx, SAFE(cleanup_stmt), (VALUE)&ctx);
455
564
  }
456
565
 
457
566
  /* call-seq:
458
- * db.batch_query_ary(sql, params_array) -> rows
459
- * db.batch_query_ary(sql, enumerable) -> rows
460
- * db.batch_query_ary(sql, callable) -> rows
461
- * db.batch_query_ary(sql, params_array) { |rows| ... } -> changes
462
- * db.batch_query_ary(sql, enumerable) { |rows| ... } -> changes
463
- * db.batch_query_ary(sql, callable) { |rows| ... } -> changes
567
+ * db.batch_query_ary(sql, params_source) -> rows
568
+ * db.batch_query_ary(sql, params_source) { |rows| ... } -> changes
464
569
  *
465
570
  * Executes the given query for each list of parameters in the given paramter
466
571
  * source. If a block is given, it is called with the resulting rows for each
@@ -483,19 +588,18 @@ VALUE Database_batch_query_ary(VALUE self, VALUE sql, VALUE parameters) {
483
588
  Database_t *db = self_to_open_database(self);
484
589
  sqlite3_stmt *stmt;
485
590
 
486
- prepare_single_stmt(db->sqlite3_db, &stmt, sql);
487
- query_ctx ctx = QUERY_CTX(self, db, stmt, parameters, QUERY_MULTI_ROW, ALL_ROWS);
591
+ prepare_single_stmt(DB_GVL_MODE(db), db->sqlite3_db, &stmt, sql);
592
+ query_ctx ctx = QUERY_CTX(
593
+ self, sql, db, stmt, parameters,
594
+ Qnil, QUERY_ARY, ROW_MULTI, ALL_ROWS
595
+ );
488
596
 
489
597
  return rb_ensure(SAFE(safe_batch_query_ary), (VALUE)&ctx, SAFE(cleanup_stmt), (VALUE)&ctx);
490
598
  }
491
599
 
492
600
  /* call-seq:
493
- * db.batch_query_single_column(sql, params_array) -> rows
494
- * db.batch_query_single_column(sql, enumerable) -> rows
495
- * db.batch_query_single_column(sql, callable) -> rows
496
- * db.batch_query_single_column(sql, params_array) { |rows| ... } -> changes
497
- * db.batch_query_single_column(sql, enumerable) { |rows| ... } -> changes
498
- * db.batch_query_single_column(sql, callable) { |rows| ... } -> changes
601
+ * db.batch_query_argv(sql, params_source) -> rows
602
+ * db.batch_query_argv(sql, params_source) { |rows| ... } -> changes
499
603
  *
500
604
  * Executes the given query for each list of parameters in the given paramter
501
605
  * source. If a block is given, it is called with the resulting rows for each
@@ -507,36 +611,37 @@ VALUE Database_batch_query_ary(VALUE self, VALUE sql, VALUE parameters) {
507
611
  * [1, 2],
508
612
  * [3, 4]
509
613
  * ]
510
- * db.batch_query_ary('insert into foo values (?, ?) returning baz', records)
614
+ * db.batch_query_argv('insert into foo values (?, ?) returning baz', records)
511
615
  * #=> [2, 4]
512
616
  * *
513
617
  * @param sql [String] query SQL
514
618
  * @param parameters [Array<Array, Hash>, Enumerable, Enumerator, Callable] parameters to run query with
515
619
  * @return [Array<any>, Integer] Total number of changes effected
516
620
  */
517
- VALUE Database_batch_query_single_column(VALUE self, VALUE sql, VALUE parameters) {
621
+ VALUE Database_batch_query_argv(VALUE self, VALUE sql, VALUE parameters) {
518
622
  Database_t *db = self_to_open_database(self);
519
623
  sqlite3_stmt *stmt;
520
624
 
521
- prepare_single_stmt(db->sqlite3_db, &stmt, sql);
522
- query_ctx ctx = QUERY_CTX(self, db, stmt, parameters, QUERY_MULTI_ROW, ALL_ROWS);
625
+ prepare_single_stmt(DB_GVL_MODE(db), db->sqlite3_db, &stmt, sql);
626
+ query_ctx ctx = QUERY_CTX(
627
+ self, sql, db, stmt, parameters,
628
+ Qnil, QUERY_ARGV, ROW_MULTI, ALL_ROWS
629
+ );
523
630
 
524
- return rb_ensure(SAFE(safe_batch_query_single_column), (VALUE)&ctx, SAFE(cleanup_stmt), (VALUE)&ctx);
631
+ return rb_ensure(SAFE(safe_batch_query_argv), (VALUE)&ctx, SAFE(cleanup_stmt), (VALUE)&ctx);
525
632
  }
526
633
 
527
- /* call-seq:
528
- * db.columns(sql) -> columns
529
- *
530
- * Returns the column names for the given query, without running it.
634
+ /* Returns the column names for the given query, without running it.
635
+ *
636
+ * @return [Array<String>] column names
531
637
  */
532
638
  VALUE Database_columns(VALUE self, VALUE sql) {
533
- return Database_perform_query(1, &sql, self, safe_query_columns);
639
+ return Database_perform_query(1, &sql, self, safe_query_columns, QUERY_HASH);
534
640
  }
535
641
 
536
- /* call-seq:
537
- * db.last_insert_rowid -> int
538
- *
539
- * Returns the rowid of the last inserted row.
642
+ /* Returns the rowid of the last inserted row.
643
+ *
644
+ * @return [Integer] last rowid
540
645
  */
541
646
  VALUE Database_last_insert_rowid(VALUE self) {
542
647
  Database_t *db = self_to_open_database(self);
@@ -544,10 +649,9 @@ VALUE Database_last_insert_rowid(VALUE self) {
544
649
  return INT2FIX(sqlite3_last_insert_rowid(db->sqlite3_db));
545
650
  }
546
651
 
547
- /* call-seq:
548
- * db.changes -> int
549
- *
550
- * Returns the number of changes made to the database by the last operation.
652
+ /* Returns the number of changes made to the database by the last operation.
653
+ *
654
+ * @return [Integer] number of changes
551
655
  */
552
656
  VALUE Database_changes(VALUE self) {
553
657
  Database_t *db = self_to_open_database(self);
@@ -555,10 +659,14 @@ VALUE Database_changes(VALUE self) {
555
659
  return INT2FIX(sqlite3_changes(db->sqlite3_db));
556
660
  }
557
661
 
558
- /* call-seq: db.filename -> string db.filename(db_name) -> string
559
- *
560
- * Returns the database filename. If db_name is given, returns the filename for
662
+ /* Returns the database filename. If db_name is given, returns the filename for
561
663
  * the respective attached database.
664
+ *
665
+ * @overload filename()
666
+ * @return [String] database filename
667
+ * @overload filename(db_name)
668
+ * @param db_name [String] attached database name
669
+ * @return [String] database filename
562
670
  */
563
671
  VALUE Database_filename(int argc, VALUE *argv, VALUE self) {
564
672
  const char *db_name;
@@ -571,10 +679,9 @@ VALUE Database_filename(int argc, VALUE *argv, VALUE self) {
571
679
  return filename ? rb_str_new_cstr(filename) : Qnil;
572
680
  }
573
681
 
574
- /* call-seq:
575
- * db.transaction_active? -> bool
576
- *
577
- * Returns true if a transaction is currently in progress.
682
+ /* Returns true if a transaction is currently in progress.
683
+ *
684
+ * @return [bool] is transaction in progress
578
685
  */
579
686
  VALUE Database_transaction_active_p(VALUE self) {
580
687
  Database_t *db = self_to_open_database(self);
@@ -583,10 +690,10 @@ VALUE Database_transaction_active_p(VALUE self) {
583
690
  }
584
691
 
585
692
  #ifdef HAVE_SQLITE3_LOAD_EXTENSION
586
- /* call-seq:
587
- * db.load_extension(path) -> db
588
- *
589
- * Loads an extension with the given path.
693
+ /* Loads an extension with the given path.
694
+ *
695
+ * @param path [String] extension file path
696
+ * @return [Extralite::Database] database
590
697
  */
591
698
  VALUE Database_load_extension(VALUE self, VALUE path) {
592
699
  Database_t *db = self_to_open_database(self);
@@ -603,30 +710,73 @@ VALUE Database_load_extension(VALUE self, VALUE path) {
603
710
  }
604
711
  #endif
605
712
 
606
- /* call-seq:
607
- * db.prepare(sql) -> Extralite::Query
608
- * db.prepare(sql, ...) -> Extralite::Query
609
- *
610
- * Creates a prepared statement with the given SQL query. If query parameters
611
- * are given, they are bound to the query.
612
- */
613
- VALUE Database_prepare(int argc, VALUE *argv, VALUE self) {
713
+ static inline VALUE Database_prepare(int argc, VALUE *argv, VALUE self, VALUE mode) {
614
714
  rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
615
- VALUE query = rb_funcall(cQuery, ID_new, 2, self, argv[0]);
715
+
716
+ VALUE args[] = { self, argv[0], mode};
717
+ VALUE query = rb_funcall_passing_block(cQuery, ID_new, 3, args);
616
718
  if (argc > 1) rb_funcallv(query, ID_bind, argc - 1, argv + 1);
617
719
  RB_GC_GUARD(query);
618
720
  return query;
619
721
  }
620
722
 
621
723
  /* call-seq:
622
- * db.interrupt -> db
623
- *
624
- * Interrupts a long running query. This method is to be called from a different
724
+ * db.prepare(sql) -> Extralite::Query
725
+ * db.prepare(sql, ...) -> Extralite::Query
726
+ * db.prepare(sql, ...) { ... } -> Extralite::Query
727
+ *
728
+ * Creates a prepared query with the given SQL query in hash mode. If query
729
+ * parameters are given, they are bound to the query. If a block is given, it is
730
+ * used as a transform proc.
731
+ *
732
+ * @param sql [String] SQL statement
733
+ * @return [Extralite::Query] prepared query
734
+ */
735
+ VALUE Database_prepare_hash(int argc, VALUE *argv, VALUE self) {
736
+ return Database_prepare(argc, argv, self, SYM_hash);
737
+ }
738
+
739
+ /* call-seq:
740
+ * db.prepare_argv(sql) -> Extralite::Query
741
+ * db.prepare_argv(sql, ...) -> Extralite::Query
742
+ * db.prepare_argv(sql, ...) { ... } -> Extralite::Query
743
+ *
744
+ * Creates a prepared query with the given SQL query in argv mode. If query
745
+ * parameters are given, they are bound to the query. If a block is given, it is
746
+ * used as a transform proc.
747
+ *
748
+ * @param sql [String] SQL statement
749
+ * @return [Extralite::Query] prepared query
750
+ */
751
+ VALUE Database_prepare_argv(int argc, VALUE *argv, VALUE self) {
752
+ return Database_prepare(argc, argv, self, SYM_argv);
753
+ }
754
+
755
+ /* call-seq:
756
+ * db.prepare_ary(sql) -> Extralite::Query
757
+ * db.prepare_ary(sql, ...) -> Extralite::Query
758
+ * db.prepare_ary(sql, ...) { ... } -> Extralite::Query
759
+ *
760
+ * Creates a prepared query with the given SQL query in ary mode. If query
761
+ * parameters are given, they are bound to the query. If a block is given, it is
762
+ * used as a transform proc.
763
+ *
764
+ * @param sql [String] SQL statement
765
+ * @return [Extralite::Query] prepared query
766
+ */
767
+ VALUE Database_prepare_ary(int argc, VALUE *argv, VALUE self) {
768
+ return Database_prepare(argc, argv, self, SYM_ary);
769
+ }
770
+
771
+ /* Interrupts a long running query. This method is to be called from a different
625
772
  * thread than the one running the query. Upon calling `#interrupt` the running
626
773
  * query will stop and raise an `Extralite::InterruptError` exception.
627
774
  *
628
775
  * It is not safe to call `#interrupt` on a database that is about to be closed.
629
- * For more information, consult the [sqlite3 API docs](https://sqlite.org/c3ref/interrupt.html).
776
+ * For more information, consult the [sqlite3 API
777
+ * docs](https://sqlite.org/c3ref/interrupt.html).
778
+ *
779
+ * @return [Extralite::Database] database
630
780
  */
631
781
  VALUE Database_interrupt(VALUE self) {
632
782
  Database_t *db = self_to_open_database(self);
@@ -700,16 +850,19 @@ VALUE backup_cleanup(VALUE ptr) {
700
850
  return Qnil;
701
851
  }
702
852
 
703
- /* call-seq:
704
- * db.backup(dest) -> db
705
- * db.backup(dest) { |remaining, total| } -> db
706
- *
707
- * Creates a backup of the database to the given destination, which can be
853
+ /* Creates a backup of the database to the given destination, which can be
708
854
  * either a filename or a database instance. In order to monitor the backup
709
855
  * progress you can pass a block that will be called periodically by the backup
710
856
  * method with two arguments: the remaining page count, and the total page
711
857
  * count, which can be used to display the progress to the user or to collect
712
858
  * statistics.
859
+ *
860
+ * db_src.backup(db_dest) do |remaining, total|
861
+ * puts "Backing up #{remaining}/#{total}"
862
+ * end
863
+ *
864
+ * @param dest [String, Extralite::Database] backup destination
865
+ * @return [Extralite::Database] source database
713
866
  */
714
867
  VALUE Database_backup(int argc, VALUE *argv, VALUE self) {
715
868
  VALUE dst;
@@ -755,12 +908,17 @@ VALUE Database_backup(int argc, VALUE *argv, VALUE self) {
755
908
  return self;
756
909
  }
757
910
 
758
- /* call-seq:
759
- * Extralite.runtime_status(op[, reset]) -> [value, highwatermark]
760
- *
761
- * Returns runtime status values for the given op as an array containing the
911
+ /* Returns runtime status values for the given op as an array containing the
762
912
  * current value and the high water mark value. To reset the high water mark,
763
913
  * pass true as reset.
914
+ *
915
+ * @overload runtime_status(op)
916
+ * @param op [Integer] op
917
+ * @return [Array<Integer>] array containing the value and high water mark
918
+ * @overload runtime_status(op, reset)
919
+ * @param op [Integer] op
920
+ * @param reset [Integer, bool] reset flag
921
+ * @return [Array<Integer>] array containing the value and high water mark
764
922
  */
765
923
  VALUE Extralite_runtime_status(int argc, VALUE* argv, VALUE self) {
766
924
  VALUE op, reset;
@@ -774,12 +932,17 @@ VALUE Extralite_runtime_status(int argc, VALUE* argv, VALUE self) {
774
932
  return rb_ary_new3(2, LONG2FIX(cur), LONG2FIX(hwm));
775
933
  }
776
934
 
777
- /* call-seq:
778
- * db.status(op[, reset]) -> [value, highwatermark]
779
- *
780
- * Returns database status values for the given op as an array containing the
935
+ /* Returns database status values for the given op as an array containing the
781
936
  * current value and the high water mark value. To reset the high water mark,
782
937
  * pass true as reset.
938
+ *
939
+ * @overload status(op)
940
+ * @param op [Integer] op
941
+ * @return [Array<Integer>] array containing the value and high water mark
942
+ * @overload status(op, reset)
943
+ * @param op [Integer] op
944
+ * @param reset [Integer, bool] reset flag
945
+ * @return [Array<Integer>] array containing the value and high water mark
783
946
  */
784
947
  VALUE Database_status(int argc, VALUE *argv, VALUE self) {
785
948
  VALUE op, reset;
@@ -795,12 +958,16 @@ VALUE Database_status(int argc, VALUE *argv, VALUE self) {
795
958
  return rb_ary_new3(2, INT2NUM(cur), INT2NUM(hwm));
796
959
  }
797
960
 
798
- /* call-seq:
799
- * db.limit(category) -> value
800
- * db.limit(category, new_value) -> prev_value
801
- *
802
- * Returns the current limit for the given category. If a new value is given,
961
+ /* Returns the current limit for the given category. If a new value is given,
803
962
  * sets the limit to the new value and returns the previous value.
963
+ *
964
+ * @overload limit(category)
965
+ * @param category [Integer] category
966
+ * @return [Integer] limit value
967
+ * @overload limit(category, new_value)
968
+ * @param category [Integer] category
969
+ * @param new_value [Integer] new value
970
+ * @return [Integer] old value
804
971
  */
805
972
  VALUE Database_limit(int argc, VALUE *argv, VALUE self) {
806
973
  VALUE category, new_value;
@@ -816,12 +983,18 @@ VALUE Database_limit(int argc, VALUE *argv, VALUE self) {
816
983
  return INT2NUM(value);
817
984
  }
818
985
 
819
- /* call-seq:
820
- * db.busy_timeout=(sec) -> db
821
- * db.busy_timeout=nil -> db
822
- *
823
- * Sets the busy timeout for the database, in seconds or fractions thereof. To
824
- * disable the busy timeout, set it to 0 or nil.
986
+ /* Sets the busy timeout for the database, in seconds or fractions thereof. To
987
+ * disable the busy timeout, set it to 0 or nil. When the busy timeout is set to
988
+ * a value larger than zero, running a query when the database is locked will
989
+ * cause the program to wait for the database to become available. If the
990
+ * database is still locked when the timeout period has elapsed, the query will
991
+ * fail with a `Extralite::BusyError` exception.
992
+ *
993
+ * Setting the busy timeout allows other threads to run while waiting for the
994
+ * database to become available. See also `#on_progress`.
995
+ *
996
+ * @param sec [Number, nil] timeout value
997
+ * @return [Extralite::Database] database
825
998
  */
826
999
  VALUE Database_busy_timeout_set(VALUE self, VALUE sec) {
827
1000
  Database_t *db = self_to_open_database(self);
@@ -833,10 +1006,9 @@ VALUE Database_busy_timeout_set(VALUE self, VALUE sec) {
833
1006
  return self;
834
1007
  }
835
1008
 
836
- /* call-seq:
837
- * db.total_changes -> value
838
- *
839
- * Returns the total number of changes made to the database since opening it.
1009
+ /* Returns the total number of changes made to the database since opening it.
1010
+ *
1011
+ * @return [Integer] total changes
840
1012
  */
841
1013
  VALUE Database_total_changes(VALUE self) {
842
1014
  Database_t *db = self_to_open_database(self);
@@ -845,24 +1017,277 @@ VALUE Database_total_changes(VALUE self) {
845
1017
  return INT2NUM(value);
846
1018
  }
847
1019
 
848
- /* call-seq:
849
- * db.trace { |sql| } -> db
850
- * db.trace -> db
851
- *
852
- * Installs or removes a block that will be invoked for every SQL statement
853
- * executed.
1020
+ /* Installs or removes a block that will be invoked for every SQL statement
1021
+ * executed. To stop tracing, call `#trace` without a block.
1022
+ *
1023
+ * @return [Extralite::Database] database
854
1024
  */
855
1025
  VALUE Database_trace(VALUE self) {
856
1026
  Database_t *db = self_to_open_database(self);
857
1027
 
858
- RB_OBJ_WRITE(self, &db->trace_block, rb_block_given_p() ? rb_block_proc() : Qnil);
1028
+ RB_OBJ_WRITE(self, &db->trace_proc, rb_block_given_p() ? rb_block_proc() : Qnil);
859
1029
  return self;
860
1030
  }
861
1031
 
862
- /* call-seq:
863
- * db.errcode -> errcode
864
- *
865
- * Returns the last error code for the database.
1032
+ #ifdef EXTRALITE_ENABLE_CHANGESET
1033
+ /* Tracks changes to the database and returns a changeset. The changeset can
1034
+ * then be used to store the changes to a file, apply them to another database,
1035
+ * or undo the changes. The given table names specify which tables should be
1036
+ * tracked for changes. Passing a value of nil causes all tables to be tracked.
1037
+ *
1038
+ * changeset = db.track_changes(:foo, :bar) do
1039
+ * perform_a_bunch_of_queries
1040
+ * end
1041
+ *
1042
+ * File.open('my.changes', 'w+') { |f| f << changeset.to_blob }
1043
+ *
1044
+ * @param table [String, Symbol] table to track
1045
+ * @return [Extralite::Changeset] changeset
1046
+ */
1047
+ VALUE Database_track_changes(int argc, VALUE *argv, VALUE self) {
1048
+ self_to_open_database(self);
1049
+
1050
+ VALUE changeset = rb_funcall(cChangeset, ID_new, 0);
1051
+ VALUE tables = rb_ary_new_from_values(argc, argv);
1052
+
1053
+ rb_funcall(changeset, ID_track, 2, self, tables);
1054
+
1055
+ RB_GC_GUARD(changeset);
1056
+ RB_GC_GUARD(tables);
1057
+ return changeset;
1058
+ }
1059
+ #endif
1060
+
1061
+ void Database_reset_progress_handler(VALUE self, Database_t *db) {
1062
+ db->progress_handler.mode = PROGRESS_NONE;
1063
+ RB_OBJ_WRITE(self, &db->progress_handler.proc, Qnil);
1064
+ sqlite3_progress_handler(db->sqlite3_db, 0, NULL, NULL);
1065
+ sqlite3_busy_handler(db->sqlite3_db, NULL, NULL);
1066
+ }
1067
+
1068
+ static inline enum progress_handler_mode symbol_to_progress_mode(VALUE mode) {
1069
+ if (mode == SYM_at_least_once) return PROGRESS_AT_LEAST_ONCE;
1070
+ if (mode == SYM_once) return PROGRESS_ONCE;
1071
+ if (mode == SYM_normal) return PROGRESS_NORMAL;
1072
+ if (mode == SYM_none) return PROGRESS_NONE;
1073
+ rb_raise(eArgumentError, "Invalid progress handler mode");
1074
+ }
1075
+
1076
+ inline void Database_issue_query(Database_t *db, VALUE sql) {
1077
+ if (db->trace_proc != Qnil) rb_funcall(db->trace_proc, ID_call, 1, sql);
1078
+ switch (db->progress_handler.mode) {
1079
+ case PROGRESS_AT_LEAST_ONCE:
1080
+ case PROGRESS_ONCE:
1081
+ rb_funcall(db->progress_handler.proc, ID_call, 0);
1082
+ default:
1083
+ ; // do nothing
1084
+
1085
+ }
1086
+ }
1087
+
1088
+ struct progress_handler parse_progress_handler_opts(VALUE opts) {
1089
+ static ID kw_ids[3];
1090
+ VALUE kw_args[3];
1091
+ struct progress_handler prog = {
1092
+ .mode = rb_block_given_p() ? PROGRESS_NORMAL : PROGRESS_NONE,
1093
+ .proc = rb_block_given_p() ? rb_block_proc() : Qnil,
1094
+ .period = DEFAULT_PROGRESS_HANDLER_PERIOD,
1095
+ .tick = DEFAULT_PROGRESS_HANDLER_TICK
1096
+ };
1097
+
1098
+ if (!NIL_P(opts)) {
1099
+ if (!kw_ids[0]) {
1100
+ CONST_ID(kw_ids[0], "period");
1101
+ CONST_ID(kw_ids[1], "tick");
1102
+ CONST_ID(kw_ids[2], "mode");
1103
+ }
1104
+
1105
+ rb_get_kwargs(opts, kw_ids, 0, 3, kw_args);
1106
+ if (kw_args[0] != Qundef) { prog.period = NUM2INT(kw_args[0]); }
1107
+ if (kw_args[1] != Qundef) { prog.tick = NUM2INT(kw_args[1]); }
1108
+ if (kw_args[2] != Qundef) { prog.mode = symbol_to_progress_mode(kw_args[2]); }
1109
+ if (prog.tick > prog.period) prog.tick = prog.period;
1110
+ }
1111
+ if (NIL_P(prog.proc) || (prog.period <= 0)) prog.mode = PROGRESS_NONE;
1112
+ if (prog.mode == PROGRESS_NONE) prog.proc = Qnil;
1113
+
1114
+ return prog;
1115
+ }
1116
+
1117
+ /* Installs or removes a progress handler that will be executed periodically
1118
+ * while a query is running. This method can be used to support switching
1119
+ * between fibers and threads or implementing timeouts for running queries.
1120
+ *
1121
+ * The `period` parameter specifies the approximate number of SQLite
1122
+ * virtual machine instructions that are evaluated between successive
1123
+ * invocations of the progress handler. A period of less than 1 removes the
1124
+ * progress handler. The default period value is 1000.
1125
+ *
1126
+ * The optional `tick` parameter specifies the granularity of how often the
1127
+ * progress handler is called. The default tick value is 10, which means that
1128
+ * Extralite's underlying progress callback will be called every 10 SQLite VM
1129
+ * instructions. The given progress proc, however, will be only called every
1130
+ * `period` (cumulative) VM instructions. This allows the progress handler to
1131
+ * work correctly also when running simple queries that don't include many
1132
+ * VM instructions. If the `tick` value is greater than the period value it is
1133
+ * automatically capped to the period value.
1134
+ *
1135
+ * The `mode` parameter controls the progress handler mode, which is one of the
1136
+ * following:
1137
+ *
1138
+ * - `:normal` (default): the progress handler proc is invoked on query
1139
+ * progress.
1140
+ * - `:once`: the progress handler proc is invoked only once, when preparing the
1141
+ * query.
1142
+ * - `:at_least_once`: the progress handler proc is invoked when prearing the
1143
+ * query, and on query progress.
1144
+ *
1145
+ * The progress handler is called also when the database is busy. This lets the
1146
+ * application perform work while waiting for the database to become unlocked,
1147
+ * or implement a timeout. Note that setting the database's busy_timeout _after_
1148
+ * setting a progress handler may lead to undefined behaviour in a concurrent
1149
+ * application. When busy, the progress handler proc is passed `true` as the
1150
+ * first argument.
1151
+ *
1152
+ * When the progress handler is set, the gvl release threshold value is set to
1153
+ * -1, which means that the GVL will not be released at all when preparing or
1154
+ * running queries. It is the application's responsibility to let other threads
1155
+ * or fibers run by calling e.g. Thread.pass:
1156
+ *
1157
+ * db.on_progress do
1158
+ * do_something_interesting
1159
+ * Thread.pass # let other threads run
1160
+ * end
1161
+ *
1162
+ * Note that the progress handler is set globally for the database and that
1163
+ * Extralite does provide any hooks for telling which queries are currently
1164
+ * running or at what time they were started. This means that you'll need to
1165
+ * wrap the stock #query_xxx and #execute methods with your own code that
1166
+ * calculates timeouts, for example:
1167
+ *
1168
+ * def setup_progress_handler
1169
+ * @db.on_progress do
1170
+ * raise TimeoutError if Time.now - @t0 >= @timeout
1171
+ * Thread.pass
1172
+ * end
1173
+ * end
1174
+ *
1175
+ * def query(sql, *)
1176
+ * @t0 = Time.now
1177
+ * @db.query(sql, *)
1178
+ * end
1179
+ *
1180
+ * If the gvl release threshold is set to a value equal to or larger than 0
1181
+ * after setting the progress handler, the progress handler will be reset.
1182
+ *
1183
+ * @param period [Integer] progress handler period
1184
+ * @param [Hash] opts progress options
1185
+ * @option opts [Integer] :period period value (`1000` by default)
1186
+ * @option opts [Integer] :tick tick value (`10` by default)
1187
+ * @option opts [Symbol] :mode progress handler mode (`:normal` by default)
1188
+ * @returns [Extralite::Database] database
1189
+ */
1190
+ VALUE Database_on_progress(int argc, VALUE *argv, VALUE self) {
1191
+ Database_t *db = self_to_open_database(self);
1192
+ VALUE opts;
1193
+ struct progress_handler prog;
1194
+
1195
+ rb_scan_args(argc, argv, "00:", &opts);
1196
+ prog = parse_progress_handler_opts(opts);
1197
+
1198
+ if (prog.mode == PROGRESS_NONE) {
1199
+ Database_reset_progress_handler(self, db);
1200
+ db->gvl_release_threshold = DEFAULT_GVL_RELEASE_THRESHOLD;
1201
+ return self;
1202
+ }
1203
+
1204
+ db->gvl_release_threshold = -1;
1205
+ db->progress_handler.mode = prog.mode;
1206
+ RB_OBJ_WRITE(self, &db->progress_handler.proc, prog.proc);
1207
+ db->progress_handler.period = prog.period;
1208
+ db->progress_handler.tick = prog.tick;
1209
+ db->progress_handler.tick_count = 0;
1210
+ db->progress_handler.call_count = 0;
1211
+
1212
+ // The PROGRESS_ONCE mode works by invoking the progress handler proc exactly
1213
+ // once, before iterating over the result set, so in that mode we don't
1214
+ // actually need to set the progress handler at the sqlite level.
1215
+ if (prog.mode != PROGRESS_ONCE)
1216
+ sqlite3_progress_handler(db->sqlite3_db, prog.tick, &Database_progress_handler, db);
1217
+ if (prog.mode != PROGRESS_NONE)
1218
+ sqlite3_busy_handler(db->sqlite3_db, &Database_busy_handler, db);
1219
+
1220
+ return self;
1221
+ }
1222
+
1223
+ /* Installs or removes a global progress handler that will be executed
1224
+ * periodically while a query is running. This method can be used to support
1225
+ * switching between fibers and threads or implementing timeouts for running
1226
+ * queries.
1227
+ *
1228
+ * This method sets the progress handler settings and behaviour for all
1229
+ * subsequently created `Database` instances. Calling this method will have no
1230
+ * effect on already existing `Database` instances
1231
+ *
1232
+ * The `period` parameter specifies the approximate number of SQLite
1233
+ * virtual machine instructions that are evaluated between successive
1234
+ * invocations of the progress handler. A period of less than 1 removes the
1235
+ * progress handler. The default period value is 1000.
1236
+ *
1237
+ * The optional `tick` parameter specifies the granularity of how often the
1238
+ * progress handler is called. The default tick value is 10, which means that
1239
+ * Extralite's underlying progress callback will be called every 10 SQLite VM
1240
+ * instructions. The given progress proc, however, will be only called every
1241
+ * `period` (cumulative) VM instructions. This allows the progress handler to
1242
+ * work correctly also when running simple queries that don't include many
1243
+ * VM instructions. If the `tick` value is greater than the period value it is
1244
+ * automatically capped to the period value.
1245
+ *
1246
+ * The `mode` parameter controls the progress handler mode, which is one of the
1247
+ * following:
1248
+ *
1249
+ * - `:normal` (default): the progress handler proc is invoked on query
1250
+ * progress.
1251
+ * - `:once`: the progress handler proc is invoked only once, when preparing the
1252
+ * query.
1253
+ * - `:at_least_once`: the progress handler proc is invoked when prearing the
1254
+ * query, and on query progress.
1255
+ *
1256
+ * The progress handler is called also when the database is busy. This lets the
1257
+ * application perform work while waiting for the database to become unlocked,
1258
+ * or implement a timeout. Note that setting the database's busy_timeout _after_
1259
+ * setting a progress handler may lead to undefined behaviour in a concurrent
1260
+ * application. When busy, the progress handler proc is passed `true` as the
1261
+ * first argument.
1262
+ *
1263
+ * When the progress handler is set, the gvl release threshold value is set to
1264
+ * -1, which means that the GVL will not be released at all when preparing or
1265
+ * running queries. It is the application's responsibility to let other threads
1266
+ * or fibers run by calling e.g. Thread.pass:
1267
+ *
1268
+ * Extralite.on_progress do
1269
+ * do_something_interesting
1270
+ * Thread.pass # let other threads run
1271
+ * end
1272
+ *
1273
+ * @param period [Integer] progress handler period
1274
+ * @param [Hash] opts progress options
1275
+ * @option opts [Integer] :period period value (`1000` by default)
1276
+ * @option opts [Integer] :tick tick value (`10` by default)
1277
+ * @option opts [Symbol] :mode progress handler mode (`:normal` by default)
1278
+ * @returns [Extralite::Database] database
1279
+ */
1280
+ VALUE Extralite_on_progress(int argc, VALUE *argv, VALUE self) {
1281
+ VALUE opts;
1282
+
1283
+ rb_scan_args(argc, argv, "00:", &opts);
1284
+ global_progress_handler = parse_progress_handler_opts(opts);
1285
+ return self;
1286
+ }
1287
+
1288
+ /* Returns the last error code for the database.
1289
+ *
1290
+ * @return [Integer] last error code
866
1291
  */
867
1292
  VALUE Database_errcode(VALUE self) {
868
1293
  Database_t *db = self_to_open_database(self);
@@ -870,10 +1295,9 @@ VALUE Database_errcode(VALUE self) {
870
1295
  return INT2NUM(sqlite3_errcode(db->sqlite3_db));
871
1296
  }
872
1297
 
873
- /* call-seq:
874
- * db.errmsg -> errmsg
875
- *
876
- * Returns the last error message for the database.
1298
+ /* Returns the last error message for the database.
1299
+ *
1300
+ * @return [String] last error message
877
1301
  */
878
1302
  VALUE Database_errmsg(VALUE self) {
879
1303
  Database_t *db = self_to_open_database(self);
@@ -882,10 +1306,10 @@ VALUE Database_errmsg(VALUE self) {
882
1306
  }
883
1307
 
884
1308
  #ifdef HAVE_SQLITE3_ERROR_OFFSET
885
- /* call-seq:
886
- * db.error_offset -> ofs
887
- *
888
- * Returns the offset for the last error
1309
+ /* Returns the offset for the last error. This is useful for indicating where in
1310
+ * the SQL string an error was encountered.
1311
+ *
1312
+ * @return [Integer] offset in the last submitted SQL string
889
1313
  */
890
1314
  VALUE Database_error_offset(VALUE self) {
891
1315
  Database_t *db = self_to_open_database(self);
@@ -921,22 +1345,40 @@ VALUE Database_gvl_release_threshold_get(VALUE self) {
921
1345
  return INT2NUM(db->gvl_release_threshold);
922
1346
  }
923
1347
 
924
- /* Sets the database's GVL release threshold. To always hold the GVL while
925
- * running a query, set the threshold to 0. To release the GVL on each record,
926
- * set the threshold to 1. Larger values mean the GVL will be released less
927
- * often, e.g. a value of 10 means the GVL will be released every 10 records
928
- * iterated. A value of nil sets the threshold to the default value, which is
1348
+ /* Sets the database's GVL release threshold. The release policy changes
1349
+ * according to the given value:
1350
+ *
1351
+ * - Less than 0: the GVL is never released while running queries. This is the
1352
+ * policy used when a progress handler is set. For more information see
1353
+ * `#on_progress`.
1354
+ * - 0: The GVL is released while preparing queries, but held when iterating
1355
+ * through records.
1356
+ * - Greater than 0: the GVL is released while preparing queries, and released
1357
+ * periodically while iterating through records, according to the given
1358
+ * period. A value of 1 will release the GVL on every iterated record. A value
1359
+ * of 100 will release the GVL once for every 100 records.
1360
+ *
1361
+ * A value of nil sets the threshold to the default value, which is
929
1362
  * currently 1000.
930
1363
  *
931
- * @return [Integer, nil] New GVL release threshold
1364
+ * @param [Integer, nil] GVL release threshold
1365
+ * @return [Integer] GVL release threshold
932
1366
  */
933
1367
  VALUE Database_gvl_release_threshold_set(VALUE self, VALUE value) {
934
1368
  Database_t *db = self_to_open_database(self);
935
1369
 
936
1370
  switch (TYPE(value)) {
937
1371
  case T_FIXNUM:
938
- db->gvl_release_threshold = NUM2INT(value);
939
- break;
1372
+ {
1373
+ int value_int = NUM2INT(value);
1374
+ if (value_int < -1)
1375
+ rb_raise(eArgumentError, "Invalid GVL release threshold value (expect integer >= -1)");
1376
+
1377
+ if (value_int > -1 && db->progress_handler.mode != PROGRESS_NONE)
1378
+ Database_reset_progress_handler(self, db);
1379
+ db->gvl_release_threshold = value_int;
1380
+ break;
1381
+ }
940
1382
  case T_NIL:
941
1383
  db->gvl_release_threshold = DEFAULT_GVL_RELEASE_THRESHOLD;
942
1384
  break;
@@ -951,79 +1393,106 @@ void Init_ExtraliteDatabase(void) {
951
1393
  VALUE mExtralite = rb_define_module("Extralite");
952
1394
  rb_define_singleton_method(mExtralite, "runtime_status", Extralite_runtime_status, -1);
953
1395
  rb_define_singleton_method(mExtralite, "sqlite3_version", Extralite_sqlite3_version, 0);
1396
+ rb_define_singleton_method(mExtralite, "on_progress", Extralite_on_progress, -1);
954
1397
 
955
1398
  cDatabase = rb_define_class_under(mExtralite, "Database", rb_cObject);
956
1399
  rb_define_alloc_func(cDatabase, Database_allocate);
957
1400
 
958
- rb_define_method(cDatabase, "backup", Database_backup, -1);
959
- rb_define_method(cDatabase, "busy_timeout=", Database_busy_timeout_set, 1);
960
- rb_define_method(cDatabase, "changes", Database_changes, 0);
961
- rb_define_method(cDatabase, "close", Database_close, 0);
962
- rb_define_method(cDatabase, "closed?", Database_closed_p, 0);
963
- rb_define_method(cDatabase, "columns", Database_columns, 1);
964
- rb_define_method(cDatabase, "errcode", Database_errcode, 0);
965
- rb_define_method(cDatabase, "errmsg", Database_errmsg, 0);
1401
+ #define DEF(s, f, a) rb_define_method(cDatabase, s, f, a)
1402
+
1403
+ DEF("backup", Database_backup, -1);
1404
+ DEF("batch_execute", Database_batch_execute, 2);
1405
+ DEF("batch_query", Database_batch_query, 2);
1406
+ DEF("batch_query_ary", Database_batch_query_ary, 2);
1407
+ DEF("batch_query_argv", Database_batch_query_argv, 2);
1408
+ DEF("batch_query_hash", Database_batch_query, 2);
1409
+ DEF("busy_timeout=", Database_busy_timeout_set, 1);
1410
+ DEF("changes", Database_changes, 0);
1411
+ DEF("close", Database_close, 0);
1412
+ DEF("closed?", Database_closed_p, 0);
1413
+ DEF("columns", Database_columns, 1);
1414
+ DEF("errcode", Database_errcode, 0);
1415
+ DEF("errmsg", Database_errmsg, 0);
966
1416
 
967
1417
  #ifdef HAVE_SQLITE3_ERROR_OFFSET
968
- rb_define_method(cDatabase, "error_offset", Database_error_offset, 0);
1418
+ DEF("error_offset", Database_error_offset, 0);
969
1419
  #endif
970
1420
 
971
- rb_define_method(cDatabase, "execute", Database_execute, -1);
972
- rb_define_method(cDatabase, "batch_execute", Database_batch_execute, 2);
973
- rb_define_method(cDatabase, "batch_query", Database_batch_query, 2);
974
- rb_define_method(cDatabase, "batch_query_ary", Database_batch_query_ary, 2);
975
- rb_define_method(cDatabase, "batch_query_single_column", Database_batch_query_single_column, 2);
976
- rb_define_method(cDatabase, "filename", Database_filename, -1);
977
- rb_define_method(cDatabase, "gvl_release_threshold", Database_gvl_release_threshold_get, 0);
978
- rb_define_method(cDatabase, "gvl_release_threshold=", Database_gvl_release_threshold_set, 1);
979
- rb_define_method(cDatabase, "initialize", Database_initialize, -1);
980
- rb_define_method(cDatabase, "inspect", Database_inspect, 0);
981
- rb_define_method(cDatabase, "interrupt", Database_interrupt, 0);
982
- rb_define_method(cDatabase, "last_insert_rowid", Database_last_insert_rowid, 0);
983
- rb_define_method(cDatabase, "limit", Database_limit, -1);
984
- rb_define_method(cDatabase, "prepare", Database_prepare, -1);
985
- rb_define_method(cDatabase, "query", Database_query_hash, -1);
986
- rb_define_method(cDatabase, "query_ary", Database_query_ary, -1);
987
- rb_define_method(cDatabase, "query_hash", Database_query_hash, -1);
988
- rb_define_method(cDatabase, "query_single_column", Database_query_single_column, -1);
989
- rb_define_method(cDatabase, "query_single_row", Database_query_single_row, -1);
990
- rb_define_method(cDatabase, "query_single_value", Database_query_single_value, -1);
991
- rb_define_method(cDatabase, "read_only?", Database_read_only_p, 0);
992
- rb_define_method(cDatabase, "status", Database_status, -1);
993
- rb_define_method(cDatabase, "total_changes", Database_total_changes, 0);
994
- rb_define_method(cDatabase, "trace", Database_trace, 0);
995
- rb_define_method(cDatabase, "transaction_active?", Database_transaction_active_p, 0);
996
-
997
- #ifdef HAVE_SQLITE3_LOAD_EXTENSION
998
- rb_define_method(cDatabase, "load_extension", Database_load_extension, 1);
999
- #endif
1421
+ DEF("execute", Database_execute, -1);
1422
+ DEF("filename", Database_filename, -1);
1423
+ DEF("gvl_release_threshold", Database_gvl_release_threshold_get, 0);
1424
+ DEF("gvl_release_threshold=", Database_gvl_release_threshold_set, 1);
1425
+ DEF("initialize", Database_initialize, -1);
1426
+ DEF("inspect", Database_inspect, 0);
1427
+ DEF("interrupt", Database_interrupt, 0);
1428
+ DEF("last_insert_rowid", Database_last_insert_rowid, 0);
1429
+ DEF("limit", Database_limit, -1);
1430
+
1431
+ #ifdef HAVE_SQLITE3_LOAD_EXTENSION
1432
+ DEF("load_extension", Database_load_extension, 1);
1433
+ #endif
1000
1434
 
1001
- cBlob = rb_define_class_under(mExtralite, "Blob", rb_cString);
1435
+ DEF("on_progress", Database_on_progress, -1);
1436
+ DEF("prepare", Database_prepare_hash, -1);
1437
+ DEF("prepare_argv", Database_prepare_argv, -1);
1438
+ DEF("prepare_ary", Database_prepare_ary, -1);
1439
+ DEF("prepare_hash", Database_prepare_hash, -1);
1440
+ DEF("query", Database_query, -1);
1441
+ DEF("query_argv", Database_query_argv, -1);
1442
+ DEF("query_ary", Database_query_ary, -1);
1443
+ DEF("query_hash", Database_query, -1);
1444
+ DEF("query_single", Database_query_single, -1);
1445
+ DEF("query_single_ary", Database_query_single_ary, -1);
1446
+ DEF("query_single_argv", Database_query_single_argv, -1);
1447
+ DEF("query_single_hash", Database_query_single, -1);
1448
+ DEF("read_only?", Database_read_only_p, 0);
1449
+ DEF("status", Database_status, -1);
1450
+ DEF("total_changes", Database_total_changes, 0);
1451
+ DEF("trace", Database_trace, 0);
1452
+
1453
+ #ifdef EXTRALITE_ENABLE_CHANGESET
1454
+ DEF("track_changes", Database_track_changes, -1);
1455
+ #endif
1456
+
1457
+ DEF("transaction_active?", Database_transaction_active_p, 0);
1002
1458
 
1003
- cError = rb_define_class_under(mExtralite, "Error", rb_eStandardError);
1004
- cSQLError = rb_define_class_under(mExtralite, "SQLError", cError);
1005
- cBusyError = rb_define_class_under(mExtralite, "BusyError", cError);
1459
+ cBlob = rb_define_class_under(mExtralite, "Blob", rb_cString);
1460
+ cError = rb_define_class_under(mExtralite, "Error", rb_eStandardError);
1461
+ cSQLError = rb_define_class_under(mExtralite, "SQLError", cError);
1462
+ cBusyError = rb_define_class_under(mExtralite, "BusyError", cError);
1006
1463
  cInterruptError = rb_define_class_under(mExtralite, "InterruptError", cError);
1007
1464
  cParameterError = rb_define_class_under(mExtralite, "ParameterError", cError);
1008
-
1009
- eArgumentError = rb_const_get(rb_cObject, rb_intern("ArgumentError"));
1010
-
1011
- ID_bind = rb_intern("bind");
1012
- ID_call = rb_intern("call");
1013
- ID_each = rb_intern("each");
1014
- ID_keys = rb_intern("keys");
1015
- ID_new = rb_intern("new");
1016
- ID_strip = rb_intern("strip");
1017
-
1465
+ eArgumentError = rb_const_get(rb_cObject, rb_intern("ArgumentError"));
1466
+
1467
+ ID_bind = rb_intern("bind");
1468
+ ID_call = rb_intern("call");
1469
+ ID_each = rb_intern("each");
1470
+ ID_keys = rb_intern("keys");
1471
+ ID_new = rb_intern("new");
1472
+ ID_pragma = rb_intern("pragma");
1473
+ ID_strip = rb_intern("strip");
1474
+ ID_to_s = rb_intern("to_s");
1475
+ ID_track = rb_intern("track");
1476
+
1477
+ SYM_at_least_once = ID2SYM(rb_intern("at_least_once"));
1018
1478
  SYM_gvl_release_threshold = ID2SYM(rb_intern("gvl_release_threshold"));
1479
+ SYM_once = ID2SYM(rb_intern("once"));
1480
+ SYM_none = ID2SYM(rb_intern("none"));
1481
+ SYM_normal = ID2SYM(rb_intern("normal"));
1482
+ SYM_pragma = ID2SYM(rb_intern("pragma"));
1019
1483
  SYM_read_only = ID2SYM(rb_intern("read_only"));
1020
- SYM_synchronous = ID2SYM(rb_intern("synchronous"));
1021
- SYM_wal_journal_mode = ID2SYM(rb_intern("wal_journal_mode"));
1484
+ SYM_wal = ID2SYM(rb_intern("wal"));
1022
1485
 
1486
+ rb_gc_register_mark_object(SYM_at_least_once);
1023
1487
  rb_gc_register_mark_object(SYM_gvl_release_threshold);
1488
+ rb_gc_register_mark_object(SYM_once);
1489
+ rb_gc_register_mark_object(SYM_none);
1490
+ rb_gc_register_mark_object(SYM_normal);
1491
+ rb_gc_register_mark_object(SYM_pragma);
1024
1492
  rb_gc_register_mark_object(SYM_read_only);
1025
- rb_gc_register_mark_object(SYM_synchronous);
1026
- rb_gc_register_mark_object(SYM_wal_journal_mode);
1493
+ rb_gc_register_mark_object(SYM_wal);
1494
+
1495
+ rb_gc_register_mark_object(global_progress_handler.proc);
1027
1496
 
1028
1497
  UTF8_ENCODING = rb_utf8_encoding();
1029
1498
  }