extralite-bundle 2.5 → 2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
  }