extralite 2.6 → 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.
@@ -16,14 +16,28 @@ 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;
20
21
  ID ID_to_s;
21
22
  ID ID_track;
22
23
 
24
+ VALUE SYM_at_least_once;
23
25
  VALUE SYM_gvl_release_threshold;
26
+ VALUE SYM_once;
27
+ VALUE SYM_none;
28
+ VALUE SYM_normal;
29
+ VALUE SYM_pragma;
24
30
  VALUE SYM_read_only;
25
- VALUE SYM_synchronous;
26
- 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
+ };
27
41
 
28
42
  #define DB_GVL_MODE(db) Database_prepare_gvl_mode(db)
29
43
 
@@ -34,13 +48,13 @@ static size_t Database_size(const void *ptr) {
34
48
  static void Database_mark(void *ptr) {
35
49
  Database_t *db = ptr;
36
50
  rb_gc_mark_movable(db->trace_proc);
37
- rb_gc_mark_movable(db->progress_handler_proc);
51
+ rb_gc_mark_movable(db->progress_handler.proc);
38
52
  }
39
53
 
40
54
  static void Database_compact(void *ptr) {
41
55
  Database_t *db = ptr;
42
- db->trace_proc = rb_gc_location(db->trace_proc);
43
- db->progress_handler_proc = rb_gc_location(db->progress_handler_proc);
56
+ db->trace_proc = rb_gc_location(db->trace_proc);
57
+ db->progress_handler.proc = rb_gc_location(db->progress_handler.proc);
44
58
  }
45
59
 
46
60
  static void Database_free(void *ptr) {
@@ -57,7 +71,10 @@ static const rb_data_type_t Database_type = {
57
71
 
58
72
  static VALUE Database_allocate(VALUE klass) {
59
73
  Database_t *db = ALLOC(Database_t);
60
- 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;
61
78
  return TypedData_Wrap_Struct(klass, &Database_type, db);
62
79
  }
63
80
 
@@ -78,12 +95,10 @@ inline sqlite3 *Database_sqlite3_db(VALUE self) {
78
95
  return self_to_database(self)->sqlite3_db;
79
96
  }
80
97
 
81
- /* call-seq:
82
- * Extralite.sqlite3_version -> version
98
+ /* Returns the sqlite3 library version used by Extralite.
83
99
  *
84
- * Returns the sqlite3 version used by Extralite.
100
+ * @return [String] SQLite version
85
101
  */
86
-
87
102
  VALUE Extralite_sqlite3_version(VALUE self) {
88
103
  return rb_str_new_cstr(sqlite3_version);
89
104
  }
@@ -100,40 +115,64 @@ default_flags:
100
115
  return SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
101
116
  }
102
117
 
103
- VALUE Database_execute(int argc, VALUE *argv, VALUE self);
104
-
105
118
  void Database_apply_opts(VALUE self, Database_t *db, VALUE opts) {
106
119
  VALUE value = Qnil;
107
120
 
121
+ // :gvl_release_threshold
108
122
  value = rb_hash_aref(opts, SYM_gvl_release_threshold);
109
123
  if (!NIL_P(value)) db->gvl_release_threshold = NUM2INT(value);
110
124
 
111
- value = rb_hash_aref(opts, SYM_wal_journal_mode);
112
- if (RTEST(value)) {
113
- value = rb_str_new_literal("PRAGMA journal_mode=wal");
114
- Database_execute(1, &value, self);
115
- }
125
+ // :pragma
126
+ value = rb_hash_aref(opts, SYM_pragma);
127
+ if (!NIL_P(value)) rb_funcall(self, ID_pragma, 1, value);
116
128
 
117
- value = rb_hash_aref(opts, SYM_synchronous);
129
+ // :wal
130
+ value = rb_hash_aref(opts, SYM_wal);
118
131
  if (RTEST(value)) {
119
- value = rb_str_new_literal("PRAGMA synchronous=1");
120
- 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));
121
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
+ }
122
153
 
123
- 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;
124
158
  }
125
159
 
126
- /* 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`.
127
169
  *
128
170
  * @overload initialize(path)
129
171
  * @param path [String] file path (or ':memory:' for memory database)
130
172
  * @return [void]
131
- * @overload initialize(path, gvl_release_threshold: , read_only: , synchronous: , wal_journal_mode: )
173
+ * @overload initialize(path, gvl_release_threshold: , on_progress: , read_only: , wal: )
132
174
  * @param path [String] file path (or ':memory:' for memory database)
133
- * @param gvl_release_threshold [Integer] GVL release threshold
134
- * @param read_only [boolean] true for opening the database for reading only
135
- * @param synchronous [boolean] true to set PRAGMA synchronous=1
136
- * @param wal_journal_mode [boolean] true to set PRAGMA journal_mode=wal
175
+ * @param options [Hash] options for opening the database
137
176
  * @return [void]
138
177
  */
139
178
  VALUE Database_initialize(int argc, VALUE *argv, VALUE self) {
@@ -147,6 +186,7 @@ VALUE Database_initialize(int argc, VALUE *argv, VALUE self) {
147
186
  int rc = sqlite3_open_v2(StringValueCStr(path), &db->sqlite3_db, flags, NULL);
148
187
  if (rc) {
149
188
  sqlite3_close_v2(db->sqlite3_db);
189
+ db->sqlite3_db = NULL;
150
190
  rb_raise(cError, "%s", sqlite3_errstr(rc));
151
191
  }
152
192
 
@@ -166,11 +206,19 @@ VALUE Database_initialize(int argc, VALUE *argv, VALUE self) {
166
206
  #endif
167
207
 
168
208
  db->trace_proc = Qnil;
169
- db->progress_handler_proc = Qnil;
170
209
  db->gvl_release_threshold = DEFAULT_GVL_RELEASE_THRESHOLD;
171
210
 
172
- 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
+ }
173
220
 
221
+ if (!NIL_P(opts)) Database_apply_opts(self, db, opts);
174
222
  return Qnil;
175
223
  }
176
224
 
@@ -184,10 +232,9 @@ VALUE Database_read_only_p(VALUE self) {
184
232
  return (open == 1) ? Qtrue : Qfalse;
185
233
  }
186
234
 
187
- /* call-seq:
188
- * db.close -> db
189
- *
190
- * Closes the database.
235
+ /* Closes the database.
236
+ *
237
+ * @return [Extralite::Database] database
191
238
  */
192
239
  VALUE Database_close(VALUE self) {
193
240
  int rc;
@@ -198,7 +245,7 @@ VALUE Database_close(VALUE self) {
198
245
  rb_raise(cError, "%s", sqlite3_errmsg(db->sqlite3_db));
199
246
  }
200
247
 
201
- db->sqlite3_db = 0;
248
+ db->sqlite3_db = NULL;
202
249
  return self;
203
250
  }
204
251
 
@@ -218,24 +265,40 @@ inline enum gvl_mode Database_prepare_gvl_mode(Database_t *db) {
218
265
  return db->gvl_release_threshold < 0 ? GVL_HOLD : GVL_RELEASE;
219
266
  }
220
267
 
221
- static inline VALUE Database_perform_query(int argc, VALUE *argv, VALUE self, VALUE (*call)(query_ctx *)) {
268
+ static inline VALUE Database_perform_query(int argc, VALUE *argv, VALUE self, VALUE (*call)(query_ctx *), enum query_mode query_mode) {
222
269
  Database_t *db = self_to_open_database(self);
223
270
  sqlite3_stmt *stmt;
224
- VALUE sql;
225
-
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
+
226
277
  // extract query from args
227
- 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
+
228
286
  sql = rb_funcall(argv[0], ID_strip, 0);
229
287
  if (RSTRING_LEN(sql) == 0) return Qnil;
230
288
 
231
- TRACE_SQL(db, sql);
289
+ Database_issue_query(db, sql);
232
290
  prepare_multi_stmt(DB_GVL_MODE(db), db->sqlite3_db, &stmt, sql);
233
291
  RB_GC_GUARD(sql);
234
292
 
235
293
  bind_all_parameters(stmt, argc - 1, argv + 1);
236
- query_ctx ctx = QUERY_CTX(self, db, stmt, Qnil, QUERY_MODE(QUERY_MULTI_ROW), ALL_ROWS);
237
-
238
- 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;
239
302
  }
240
303
 
241
304
  /* call-seq:
@@ -254,21 +317,46 @@ static inline VALUE Database_perform_query(int argc, VALUE *argv, VALUE self, VA
254
317
  * db.query('select * from foo where x = ?', 42)
255
318
  *
256
319
  * Named placeholders are specified using `:`. The placeholder values are
257
- * specified using a hash, where keys are either strings are symbols. String
258
- * keys can include or omit the `:` prefix. The following are equivalent:
320
+ * specified using keyword arguments:
259
321
  *
260
322
  * db.query('select * from foo where x = :bar', bar: 42)
261
- * db.query('select * from foo where x = :bar', 'bar' => 42)
262
- * 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
263
331
  */
264
- VALUE Database_query_hash(int argc, VALUE *argv, VALUE self) {
265
- 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);
266
334
  }
267
335
 
268
- /* call-seq:
269
- * 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.
270
340
  *
271
- * 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
272
360
  * for each row. Otherwise, an array containing all rows is returned.
273
361
  *
274
362
  * Query parameters to be bound to placeholders in the query can be specified as
@@ -285,82 +373,95 @@ VALUE Database_query_hash(int argc, VALUE *argv, VALUE self) {
285
373
  * db.query_ary('select * from foo where x = :bar', bar: 42)
286
374
  * db.query_ary('select * from foo where x = :bar', 'bar' => 42)
287
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
288
384
  */
289
385
  VALUE Database_query_ary(int argc, VALUE *argv, VALUE self) {
290
- return Database_perform_query(argc, argv, self, safe_query_ary);
386
+ return Database_perform_query(argc, argv, self, safe_query_ary, QUERY_ARY);
291
387
  }
292
388
 
293
- /* call-seq:
294
- * db.query_single_row(sql, *parameters) -> {...}
295
- *
296
- * Runs a query returning a single row as a hash.
389
+ /* Runs a query returning a single row as a hash.
297
390
  *
298
391
  * Query parameters to be bound to placeholders in the query can be specified as
299
392
  * a list of values or as a hash mapping parameter names to values. When
300
393
  * parameters are given as an array, the query should specify parameters using
301
394
  * `?`:
302
395
  *
303
- * db.query_single_row('select * from foo where x = ?', 42)
396
+ * db.query_single('select * from foo where x = ?', 42)
304
397
  *
305
398
  * Named placeholders are specified using `:`. The placeholder values are
306
- * specified using a hash, where keys are either strings are symbols. String
307
- * keys can include or omit the `:` prefix. The following are equivalent:
399
+ * specified using keyword arguments:
308
400
  *
309
- * db.query_single_row('select * from foo where x = :bar', bar: 42)
310
- * db.query_single_row('select * from foo where x = :bar', 'bar' => 42)
311
- * 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
312
410
  */
313
- VALUE Database_query_single_row(int argc, VALUE *argv, VALUE self) {
314
- 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);
315
413
  }
316
414
 
317
- /* call-seq:
318
- * db.query_single_column(sql, *parameters, &block) -> [...]
319
- *
320
- * Runs a query returning single column values. If a block is given, it will be called
321
- * 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.
322
416
  *
323
417
  * Query parameters to be bound to placeholders in the query can be specified as
324
418
  * a list of values or as a hash mapping parameter names to values. When
325
419
  * parameters are given as an array, the query should specify parameters using
326
420
  * `?`:
327
421
  *
328
- * db.query_single_column('select x from foo where x = ?', 42)
422
+ * db.query_single_argv('select * from foo where x = ?', 42)
329
423
  *
330
424
  * Named placeholders are specified using `:`. The placeholder values are
331
- * specified using a hash, where keys are either strings are symbols. String
332
- * keys can include or omit the `:` prefix. The following are equivalent:
425
+ * specified using keyword arguments:
333
426
  *
334
- * db.query_single_column('select x from foo where x = :bar', bar: 42)
335
- * db.query_single_column('select x from foo where x = :bar', 'bar' => 42)
336
- * db.query_single_column('select x from foo where x = :bar', ':bar' => 42)
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
337
436
  */
338
- VALUE Database_query_single_column(int argc, VALUE *argv, VALUE self) {
339
- 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);
340
439
  }
341
440
 
342
- /* call-seq:
343
- * db.query_single_value(sql, *parameters) -> value
344
- *
345
- * Runs a query returning a single value from the first row.
441
+ /* Runs a query returning a single row as an array.
346
442
  *
347
443
  * Query parameters to be bound to placeholders in the query can be specified as
348
444
  * a list of values or as a hash mapping parameter names to values. When
349
445
  * parameters are given as an array, the query should specify parameters using
350
446
  * `?`:
351
447
  *
352
- * db.query_single_value('select x from foo where x = ?', 42)
448
+ * db.query_single_ary('select * from foo where x = ?', 42)
353
449
  *
354
450
  * Named placeholders are specified using `:`. The placeholder values are
355
- * specified using a hash, where keys are either strings are symbols. String
356
- * keys can include or omit the `:` prefix. The following are equivalent:
451
+ * specified using keyword arguments:
357
452
  *
358
- * db.query_single_value('select x from foo where x = :bar', bar: 42)
359
- * db.query_single_value('select x from foo where x = :bar', 'bar' => 42)
360
- * db.query_single_value('select x from foo where x = :bar', ':bar' => 42)
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
361
462
  */
362
- VALUE Database_query_single_value(int argc, VALUE *argv, VALUE self) {
363
- 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);
364
465
  }
365
466
 
366
467
  /* call-seq:
@@ -377,21 +478,16 @@ VALUE Database_query_single_value(int argc, VALUE *argv, VALUE self) {
377
478
  * db.execute('update foo set x = ? where y = ?', 42, 43)
378
479
  *
379
480
  * Named placeholders are specified using `:`. The placeholder values are
380
- * specified using a hash, where keys are either strings are symbols. String
381
- * keys can include or omit the `:` prefix. The following are equivalent:
481
+ * specified using keyword arguments:
382
482
  *
383
483
  * db.execute('update foo set x = :bar', bar: 42)
384
- * db.execute('update foo set x = :bar', 'bar' => 42)
385
- * db.execute('update foo set x = :bar', ':bar' => 42)
386
484
  */
387
485
  VALUE Database_execute(int argc, VALUE *argv, VALUE self) {
388
- return Database_perform_query(argc, argv, self, safe_query_changes);
486
+ return Database_perform_query(argc, argv, self, safe_query_changes, QUERY_HASH);
389
487
  }
390
488
 
391
489
  /* call-seq:
392
- * db.batch_execute(sql, params_array) -> changes
393
- * db.batch_execute(sql, enumerable) -> changes
394
- * db.batch_execute(sql, callable) -> changes
490
+ * db.batch_execute(sql, params_source) -> changes
395
491
  *
396
492
  * Executes the given query for each list of parameters in the paramter source.
397
493
  * If an enumerable is given, it is iterated and each of its values is used as
@@ -425,18 +521,17 @@ VALUE Database_batch_execute(VALUE self, VALUE sql, VALUE parameters) {
425
521
  if (RSTRING_LEN(sql) == 0) return Qnil;
426
522
 
427
523
  prepare_single_stmt(DB_GVL_MODE(db), db->sqlite3_db, &stmt, sql);
428
- query_ctx ctx = QUERY_CTX(self, db, stmt, parameters, QUERY_MULTI_ROW, ALL_ROWS);
524
+ query_ctx ctx = QUERY_CTX(
525
+ self, sql, db, stmt, parameters,
526
+ Qnil, QUERY_HASH, ROW_MULTI, ALL_ROWS
527
+ );
429
528
 
430
529
  return rb_ensure(SAFE(safe_batch_execute), (VALUE)&ctx, SAFE(cleanup_stmt), (VALUE)&ctx);
431
530
  }
432
531
 
433
532
  /* call-seq:
434
- * db.batch_query(sql, params_array) -> rows
435
- * db.batch_query(sql, enumerable) -> rows
436
- * db.batch_query(sql, callable) -> rows
437
- * db.batch_query(sql, params_array) { |rows| ... } -> changes
438
- * db.batch_query(sql, enumerable) { |rows| ... } -> changes
439
- * db.batch_query(sql, callable) { |rows| ... } -> changes
533
+ * db.batch_query(sql, params_source) -> rows
534
+ * db.batch_query(sql, params_source) { |rows| ... } -> changes
440
535
  *
441
536
  * Executes the given query for each list of parameters in the given paramter
442
537
  * source. If a block is given, it is called with the resulting rows for each
@@ -460,18 +555,17 @@ VALUE Database_batch_query(VALUE self, VALUE sql, VALUE parameters) {
460
555
  sqlite3_stmt *stmt;
461
556
 
462
557
  prepare_single_stmt(DB_GVL_MODE(db), db->sqlite3_db, &stmt, sql);
463
- query_ctx ctx = QUERY_CTX(self, db, stmt, parameters, QUERY_MULTI_ROW, ALL_ROWS);
558
+ query_ctx ctx = QUERY_CTX(
559
+ self, sql, db, stmt, parameters,
560
+ Qnil, QUERY_HASH, ROW_MULTI, ALL_ROWS
561
+ );
464
562
 
465
563
  return rb_ensure(SAFE(safe_batch_query), (VALUE)&ctx, SAFE(cleanup_stmt), (VALUE)&ctx);
466
564
  }
467
565
 
468
566
  /* call-seq:
469
- * db.batch_query_ary(sql, params_array) -> rows
470
- * db.batch_query_ary(sql, enumerable) -> rows
471
- * db.batch_query_ary(sql, callable) -> rows
472
- * db.batch_query_ary(sql, params_array) { |rows| ... } -> changes
473
- * db.batch_query_ary(sql, enumerable) { |rows| ... } -> changes
474
- * 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
475
569
  *
476
570
  * Executes the given query for each list of parameters in the given paramter
477
571
  * source. If a block is given, it is called with the resulting rows for each
@@ -495,18 +589,17 @@ VALUE Database_batch_query_ary(VALUE self, VALUE sql, VALUE parameters) {
495
589
  sqlite3_stmt *stmt;
496
590
 
497
591
  prepare_single_stmt(DB_GVL_MODE(db), db->sqlite3_db, &stmt, sql);
498
- query_ctx ctx = QUERY_CTX(self, db, stmt, parameters, QUERY_MULTI_ROW, ALL_ROWS);
592
+ query_ctx ctx = QUERY_CTX(
593
+ self, sql, db, stmt, parameters,
594
+ Qnil, QUERY_ARY, ROW_MULTI, ALL_ROWS
595
+ );
499
596
 
500
597
  return rb_ensure(SAFE(safe_batch_query_ary), (VALUE)&ctx, SAFE(cleanup_stmt), (VALUE)&ctx);
501
598
  }
502
599
 
503
600
  /* call-seq:
504
- * db.batch_query_single_column(sql, params_array) -> rows
505
- * db.batch_query_single_column(sql, enumerable) -> rows
506
- * db.batch_query_single_column(sql, callable) -> rows
507
- * db.batch_query_single_column(sql, params_array) { |rows| ... } -> changes
508
- * db.batch_query_single_column(sql, enumerable) { |rows| ... } -> changes
509
- * 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
510
603
  *
511
604
  * Executes the given query for each list of parameters in the given paramter
512
605
  * source. If a block is given, it is called with the resulting rows for each
@@ -518,36 +611,37 @@ VALUE Database_batch_query_ary(VALUE self, VALUE sql, VALUE parameters) {
518
611
  * [1, 2],
519
612
  * [3, 4]
520
613
  * ]
521
- * db.batch_query_ary('insert into foo values (?, ?) returning baz', records)
614
+ * db.batch_query_argv('insert into foo values (?, ?) returning baz', records)
522
615
  * #=> [2, 4]
523
616
  * *
524
617
  * @param sql [String] query SQL
525
618
  * @param parameters [Array<Array, Hash>, Enumerable, Enumerator, Callable] parameters to run query with
526
619
  * @return [Array<any>, Integer] Total number of changes effected
527
620
  */
528
- VALUE Database_batch_query_single_column(VALUE self, VALUE sql, VALUE parameters) {
621
+ VALUE Database_batch_query_argv(VALUE self, VALUE sql, VALUE parameters) {
529
622
  Database_t *db = self_to_open_database(self);
530
623
  sqlite3_stmt *stmt;
531
624
 
532
625
  prepare_single_stmt(DB_GVL_MODE(db), db->sqlite3_db, &stmt, sql);
533
- query_ctx ctx = QUERY_CTX(self, db, stmt, parameters, QUERY_MULTI_ROW, ALL_ROWS);
626
+ query_ctx ctx = QUERY_CTX(
627
+ self, sql, db, stmt, parameters,
628
+ Qnil, QUERY_ARGV, ROW_MULTI, ALL_ROWS
629
+ );
534
630
 
535
- 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);
536
632
  }
537
633
 
538
- /* call-seq:
539
- * db.columns(sql) -> columns
540
- *
541
- * 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
542
637
  */
543
638
  VALUE Database_columns(VALUE self, VALUE sql) {
544
- return Database_perform_query(1, &sql, self, safe_query_columns);
639
+ return Database_perform_query(1, &sql, self, safe_query_columns, QUERY_HASH);
545
640
  }
546
641
 
547
- /* call-seq:
548
- * db.last_insert_rowid -> int
549
- *
550
- * Returns the rowid of the last inserted row.
642
+ /* Returns the rowid of the last inserted row.
643
+ *
644
+ * @return [Integer] last rowid
551
645
  */
552
646
  VALUE Database_last_insert_rowid(VALUE self) {
553
647
  Database_t *db = self_to_open_database(self);
@@ -555,10 +649,9 @@ VALUE Database_last_insert_rowid(VALUE self) {
555
649
  return INT2FIX(sqlite3_last_insert_rowid(db->sqlite3_db));
556
650
  }
557
651
 
558
- /* call-seq:
559
- * db.changes -> int
560
- *
561
- * 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
562
655
  */
563
656
  VALUE Database_changes(VALUE self) {
564
657
  Database_t *db = self_to_open_database(self);
@@ -566,10 +659,14 @@ VALUE Database_changes(VALUE self) {
566
659
  return INT2FIX(sqlite3_changes(db->sqlite3_db));
567
660
  }
568
661
 
569
- /* call-seq: db.filename -> string db.filename(db_name) -> string
570
- *
571
- * 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
572
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
573
670
  */
574
671
  VALUE Database_filename(int argc, VALUE *argv, VALUE self) {
575
672
  const char *db_name;
@@ -582,10 +679,9 @@ VALUE Database_filename(int argc, VALUE *argv, VALUE self) {
582
679
  return filename ? rb_str_new_cstr(filename) : Qnil;
583
680
  }
584
681
 
585
- /* call-seq:
586
- * db.transaction_active? -> bool
587
- *
588
- * 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
589
685
  */
590
686
  VALUE Database_transaction_active_p(VALUE self) {
591
687
  Database_t *db = self_to_open_database(self);
@@ -594,10 +690,10 @@ VALUE Database_transaction_active_p(VALUE self) {
594
690
  }
595
691
 
596
692
  #ifdef HAVE_SQLITE3_LOAD_EXTENSION
597
- /* call-seq:
598
- * db.load_extension(path) -> db
599
- *
600
- * 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
601
697
  */
602
698
  VALUE Database_load_extension(VALUE self, VALUE path) {
603
699
  Database_t *db = self_to_open_database(self);
@@ -614,30 +710,73 @@ VALUE Database_load_extension(VALUE self, VALUE path) {
614
710
  }
615
711
  #endif
616
712
 
713
+ static inline VALUE Database_prepare(int argc, VALUE *argv, VALUE self, VALUE mode) {
714
+ rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
715
+
716
+ VALUE args[] = { self, argv[0], mode};
717
+ VALUE query = rb_funcall_passing_block(cQuery, ID_new, 3, args);
718
+ if (argc > 1) rb_funcallv(query, ID_bind, argc - 1, argv + 1);
719
+ RB_GC_GUARD(query);
720
+ return query;
721
+ }
722
+
617
723
  /* call-seq:
618
724
  * db.prepare(sql) -> Extralite::Query
619
725
  * db.prepare(sql, ...) -> Extralite::Query
726
+ * db.prepare(sql, ...) { ... } -> Extralite::Query
620
727
  *
621
- * Creates a prepared statement with the given SQL query. If query parameters
622
- * are given, they are bound to the query.
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
623
734
  */
624
- VALUE Database_prepare(int argc, VALUE *argv, VALUE self) {
625
- rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
626
- VALUE query = rb_funcall(cQuery, ID_new, 2, self, argv[0]);
627
- if (argc > 1) rb_funcallv(query, ID_bind, argc - 1, argv + 1);
628
- RB_GC_GUARD(query);
629
- return query;
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);
630
753
  }
631
754
 
632
755
  /* call-seq:
633
- * db.interrupt -> db
756
+ * db.prepare_ary(sql) -> Extralite::Query
757
+ * db.prepare_ary(sql, ...) -> Extralite::Query
758
+ * db.prepare_ary(sql, ...) { ... } -> Extralite::Query
634
759
  *
635
- * Interrupts a long running query. This method is to be called from a different
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
636
772
  * thread than the one running the query. Upon calling `#interrupt` the running
637
773
  * query will stop and raise an `Extralite::InterruptError` exception.
638
774
  *
639
775
  * It is not safe to call `#interrupt` on a database that is about to be closed.
640
- * 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
641
780
  */
642
781
  VALUE Database_interrupt(VALUE self) {
643
782
  Database_t *db = self_to_open_database(self);
@@ -711,16 +850,19 @@ VALUE backup_cleanup(VALUE ptr) {
711
850
  return Qnil;
712
851
  }
713
852
 
714
- /* call-seq:
715
- * db.backup(dest) -> db
716
- * db.backup(dest) { |remaining, total| } -> db
717
- *
718
- * 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
719
854
  * either a filename or a database instance. In order to monitor the backup
720
855
  * progress you can pass a block that will be called periodically by the backup
721
856
  * method with two arguments: the remaining page count, and the total page
722
857
  * count, which can be used to display the progress to the user or to collect
723
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
724
866
  */
725
867
  VALUE Database_backup(int argc, VALUE *argv, VALUE self) {
726
868
  VALUE dst;
@@ -766,12 +908,17 @@ VALUE Database_backup(int argc, VALUE *argv, VALUE self) {
766
908
  return self;
767
909
  }
768
910
 
769
- /* call-seq:
770
- * Extralite.runtime_status(op[, reset]) -> [value, highwatermark]
771
- *
772
- * 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
773
912
  * current value and the high water mark value. To reset the high water mark,
774
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
775
922
  */
776
923
  VALUE Extralite_runtime_status(int argc, VALUE* argv, VALUE self) {
777
924
  VALUE op, reset;
@@ -785,12 +932,17 @@ VALUE Extralite_runtime_status(int argc, VALUE* argv, VALUE self) {
785
932
  return rb_ary_new3(2, LONG2FIX(cur), LONG2FIX(hwm));
786
933
  }
787
934
 
788
- /* call-seq:
789
- * db.status(op[, reset]) -> [value, highwatermark]
790
- *
791
- * 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
792
936
  * current value and the high water mark value. To reset the high water mark,
793
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
794
946
  */
795
947
  VALUE Database_status(int argc, VALUE *argv, VALUE self) {
796
948
  VALUE op, reset;
@@ -806,12 +958,16 @@ VALUE Database_status(int argc, VALUE *argv, VALUE self) {
806
958
  return rb_ary_new3(2, INT2NUM(cur), INT2NUM(hwm));
807
959
  }
808
960
 
809
- /* call-seq:
810
- * db.limit(category) -> value
811
- * db.limit(category, new_value) -> prev_value
812
- *
813
- * 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,
814
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
815
971
  */
816
972
  VALUE Database_limit(int argc, VALUE *argv, VALUE self) {
817
973
  VALUE category, new_value;
@@ -827,12 +983,18 @@ VALUE Database_limit(int argc, VALUE *argv, VALUE self) {
827
983
  return INT2NUM(value);
828
984
  }
829
985
 
830
- /* call-seq:
831
- * db.busy_timeout=(sec) -> db
832
- * db.busy_timeout=nil -> db
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`.
833
995
  *
834
- * Sets the busy timeout for the database, in seconds or fractions thereof. To
835
- * disable the busy timeout, set it to 0 or nil.
996
+ * @param sec [Number, nil] timeout value
997
+ * @return [Extralite::Database] database
836
998
  */
837
999
  VALUE Database_busy_timeout_set(VALUE self, VALUE sec) {
838
1000
  Database_t *db = self_to_open_database(self);
@@ -844,10 +1006,9 @@ VALUE Database_busy_timeout_set(VALUE self, VALUE sec) {
844
1006
  return self;
845
1007
  }
846
1008
 
847
- /* call-seq:
848
- * db.total_changes -> value
849
- *
850
- * 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
851
1012
  */
852
1013
  VALUE Database_total_changes(VALUE self) {
853
1014
  Database_t *db = self_to_open_database(self);
@@ -856,12 +1017,10 @@ VALUE Database_total_changes(VALUE self) {
856
1017
  return INT2NUM(value);
857
1018
  }
858
1019
 
859
- /* call-seq:
860
- * db.trace { |sql| } -> db
861
- * db.trace -> db
862
- *
863
- * Installs or removes a block that will be invoked for every SQL statement
864
- * 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
865
1024
  */
866
1025
  VALUE Database_trace(VALUE self) {
867
1026
  Database_t *db = self_to_open_database(self);
@@ -876,11 +1035,11 @@ VALUE Database_trace(VALUE self) {
876
1035
  * or undo the changes. The given table names specify which tables should be
877
1036
  * tracked for changes. Passing a value of nil causes all tables to be tracked.
878
1037
  *
879
- * changeset = db.track_changes(:foo, :bar) do
880
- * perform_a_bunch_of_queries
881
- * end
1038
+ * changeset = db.track_changes(:foo, :bar) do
1039
+ * perform_a_bunch_of_queries
1040
+ * end
882
1041
  *
883
- * File.open('my.changes', 'w+') { |f| f << changeset.to_blob }
1042
+ * File.open('my.changes', 'w+') { |f| f << changeset.to_blob }
884
1043
  *
885
1044
  * @param table [String, Symbol] table to track
886
1045
  * @return [Extralite::Changeset] changeset
@@ -899,101 +1058,236 @@ VALUE Database_track_changes(int argc, VALUE *argv, VALUE self) {
899
1058
  }
900
1059
  #endif
901
1060
 
902
- int Database_progress_handler(void *ptr) {
903
- Database_t *db = (Database_t *)ptr;
904
- rb_funcall(db->progress_handler_proc, ID_call, 0);
905
- return 0;
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);
906
1066
  }
907
1067
 
908
- int Database_busy_handler(void *ptr, int v) {
909
- Database_t *db = (Database_t *)ptr;
910
- rb_funcall(db->progress_handler_proc, ID_call, 0);
911
- return 1;
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");
912
1074
  }
913
1075
 
914
- void Database_reset_progress_handler(VALUE self, Database_t *db) {
915
- RB_OBJ_WRITE(self, &db->progress_handler_proc, Qnil);
916
- sqlite3_progress_handler(db->sqlite3_db, 0, NULL, NULL);
917
- sqlite3_busy_handler(db->sqlite3_db, NULL, NULL);
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
+ }
918
1086
  }
919
1087
 
920
- /* call-seq:
921
- * db.on_progress(period) { } -> db
922
- * db.on_progress(0) -> db
923
- *
924
- * Installs or removes a progress handler that will be executed periodically
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
925
1118
  * while a query is running. This method can be used to support switching
926
1119
  * between fibers and threads or implementing timeouts for running queries.
927
1120
  *
928
- * The given period parameter specifies the approximate number of SQLite virtual
929
- * machine instructions that are evaluated between successive invocations of the
930
- * progress handler. A period of less than 1 removes the progress handler.
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.
931
1144
  *
932
1145
  * The progress handler is called also when the database is busy. This lets the
933
1146
  * application perform work while waiting for the database to become unlocked,
934
1147
  * or implement a timeout. Note that setting the database's busy_timeout _after_
935
1148
  * setting a progress handler may lead to undefined behaviour in a concurrent
936
- * application.
1149
+ * application. When busy, the progress handler proc is passed `true` as the
1150
+ * first argument.
937
1151
  *
938
1152
  * When the progress handler is set, the gvl release threshold value is set to
939
1153
  * -1, which means that the GVL will not be released at all when preparing or
940
1154
  * running queries. It is the application's responsibility to let other threads
941
1155
  * or fibers run by calling e.g. Thread.pass:
942
- *
943
- * db.on_progress(1000) do
1156
+ *
1157
+ * db.on_progress do
944
1158
  * do_something_interesting
945
1159
  * Thread.pass # let other threads run
946
1160
  * end
947
- *
948
- * Note that the progress handler is set globally for the database and that
1161
+ *
1162
+ * Note that the progress handler is set globally for the database and that
949
1163
  * Extralite does provide any hooks for telling which queries are currently
950
- * running or at what time they were started. This means that you'll need
951
- * to wrap the stock #query_xxx and #execute methods with your own code that
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
952
1166
  * calculates timeouts, for example:
953
- *
1167
+ *
954
1168
  * def setup_progress_handler
955
- * @db.on_progress(1000) do
1169
+ * @db.on_progress do
956
1170
  * raise TimeoutError if Time.now - @t0 >= @timeout
957
1171
  * Thread.pass
958
1172
  * end
959
1173
  * end
960
- *
1174
+ *
961
1175
  * def query(sql, *)
962
1176
  * @t0 = Time.now
963
1177
  * @db.query(sql, *)
964
1178
  * end
965
- *
1179
+ *
966
1180
  * If the gvl release threshold is set to a value equal to or larger than 0
967
1181
  * after setting the progress handler, the progress handler will be reset.
968
1182
  *
969
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)
970
1188
  * @returns [Extralite::Database] database
971
1189
  */
972
- VALUE Database_on_progress(VALUE self, VALUE period) {
1190
+ VALUE Database_on_progress(int argc, VALUE *argv, VALUE self) {
973
1191
  Database_t *db = self_to_open_database(self);
974
- int period_int = NUM2INT(period);
1192
+ VALUE opts;
1193
+ struct progress_handler prog;
975
1194
 
976
- if (period_int > 0 && rb_block_given_p()) {
977
- RB_OBJ_WRITE(self, &db->progress_handler_proc, rb_block_proc());
978
- db->gvl_release_threshold = -1;
1195
+ rb_scan_args(argc, argv, "00:", &opts);
1196
+ prog = parse_progress_handler_opts(opts);
979
1197
 
980
- sqlite3_progress_handler(db->sqlite3_db, period_int, &Database_progress_handler, db);
981
- sqlite3_busy_handler(db->sqlite3_db, &Database_busy_handler, db);
982
- }
983
- else {
984
- RB_OBJ_WRITE(self, &db->progress_handler_proc, Qnil);
1198
+ if (prog.mode == PROGRESS_NONE) {
1199
+ Database_reset_progress_handler(self, db);
985
1200
  db->gvl_release_threshold = DEFAULT_GVL_RELEASE_THRESHOLD;
986
- sqlite3_progress_handler(db->sqlite3_db, 0, NULL, NULL);
987
- sqlite3_busy_handler(db->sqlite3_db, NULL, NULL);
1201
+ return self;
988
1202
  }
989
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
+
990
1220
  return self;
991
1221
  }
992
1222
 
993
- /* call-seq:
994
- * db.errcode -> errcode
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
995
1272
  *
996
- * Returns the last error code for the database.
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
997
1291
  */
998
1292
  VALUE Database_errcode(VALUE self) {
999
1293
  Database_t *db = self_to_open_database(self);
@@ -1001,10 +1295,9 @@ VALUE Database_errcode(VALUE self) {
1001
1295
  return INT2NUM(sqlite3_errcode(db->sqlite3_db));
1002
1296
  }
1003
1297
 
1004
- /* call-seq:
1005
- * db.errmsg -> errmsg
1006
- *
1007
- * Returns the last error message for the database.
1298
+ /* Returns the last error message for the database.
1299
+ *
1300
+ * @return [String] last error message
1008
1301
  */
1009
1302
  VALUE Database_errmsg(VALUE self) {
1010
1303
  Database_t *db = self_to_open_database(self);
@@ -1013,10 +1306,10 @@ VALUE Database_errmsg(VALUE self) {
1013
1306
  }
1014
1307
 
1015
1308
  #ifdef HAVE_SQLITE3_ERROR_OFFSET
1016
- /* call-seq:
1017
- * db.error_offset -> ofs
1018
- *
1019
- * 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
1020
1313
  */
1021
1314
  VALUE Database_error_offset(VALUE self) {
1022
1315
  Database_t *db = self_to_open_database(self);
@@ -1052,14 +1345,24 @@ VALUE Database_gvl_release_threshold_get(VALUE self) {
1052
1345
  return INT2NUM(db->gvl_release_threshold);
1053
1346
  }
1054
1347
 
1055
- /* Sets the database's GVL release threshold. To always hold the GVL while
1056
- * running a query, set the threshold to 0. To release the GVL on each record,
1057
- * set the threshold to 1. Larger values mean the GVL will be released less
1058
- * often, e.g. a value of 10 means the GVL will be released every 10 records
1059
- * 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
1060
1362
  * currently 1000.
1061
1363
  *
1062
- * @return [Integer, nil] New GVL release threshold
1364
+ * @param [Integer, nil] GVL release threshold
1365
+ * @return [Integer] GVL release threshold
1063
1366
  */
1064
1367
  VALUE Database_gvl_release_threshold_set(VALUE self, VALUE value) {
1065
1368
  Database_t *db = self_to_open_database(self);
@@ -1071,7 +1374,7 @@ VALUE Database_gvl_release_threshold_set(VALUE self, VALUE value) {
1071
1374
  if (value_int < -1)
1072
1375
  rb_raise(eArgumentError, "Invalid GVL release threshold value (expect integer >= -1)");
1073
1376
 
1074
- if (value_int > -1 && !NIL_P(db->progress_handler_proc))
1377
+ if (value_int > -1 && db->progress_handler.mode != PROGRESS_NONE)
1075
1378
  Database_reset_progress_handler(self, db);
1076
1379
  db->gvl_release_threshold = value_int;
1077
1380
  break;
@@ -1090,87 +1393,106 @@ void Init_ExtraliteDatabase(void) {
1090
1393
  VALUE mExtralite = rb_define_module("Extralite");
1091
1394
  rb_define_singleton_method(mExtralite, "runtime_status", Extralite_runtime_status, -1);
1092
1395
  rb_define_singleton_method(mExtralite, "sqlite3_version", Extralite_sqlite3_version, 0);
1396
+ rb_define_singleton_method(mExtralite, "on_progress", Extralite_on_progress, -1);
1093
1397
 
1094
1398
  cDatabase = rb_define_class_under(mExtralite, "Database", rb_cObject);
1095
1399
  rb_define_alloc_func(cDatabase, Database_allocate);
1096
1400
 
1097
- rb_define_method(cDatabase, "backup", Database_backup, -1);
1098
- rb_define_method(cDatabase, "busy_timeout=", Database_busy_timeout_set, 1);
1099
- rb_define_method(cDatabase, "changes", Database_changes, 0);
1100
- rb_define_method(cDatabase, "close", Database_close, 0);
1101
- rb_define_method(cDatabase, "closed?", Database_closed_p, 0);
1102
- rb_define_method(cDatabase, "columns", Database_columns, 1);
1103
- rb_define_method(cDatabase, "errcode", Database_errcode, 0);
1104
- 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);
1105
1416
 
1106
1417
  #ifdef HAVE_SQLITE3_ERROR_OFFSET
1107
- rb_define_method(cDatabase, "error_offset", Database_error_offset, 0);
1418
+ DEF("error_offset", Database_error_offset, 0);
1108
1419
  #endif
1109
1420
 
1110
- rb_define_method(cDatabase, "execute", Database_execute, -1);
1111
- rb_define_method(cDatabase, "batch_execute", Database_batch_execute, 2);
1112
- rb_define_method(cDatabase, "batch_query", Database_batch_query, 2);
1113
- rb_define_method(cDatabase, "batch_query_ary", Database_batch_query_ary, 2);
1114
- rb_define_method(cDatabase, "batch_query_single_column", Database_batch_query_single_column, 2);
1115
- rb_define_method(cDatabase, "filename", Database_filename, -1);
1116
- rb_define_method(cDatabase, "gvl_release_threshold", Database_gvl_release_threshold_get, 0);
1117
- rb_define_method(cDatabase, "gvl_release_threshold=", Database_gvl_release_threshold_set, 1);
1118
- rb_define_method(cDatabase, "initialize", Database_initialize, -1);
1119
- rb_define_method(cDatabase, "inspect", Database_inspect, 0);
1120
- rb_define_method(cDatabase, "interrupt", Database_interrupt, 0);
1121
- rb_define_method(cDatabase, "last_insert_rowid", Database_last_insert_rowid, 0);
1122
- rb_define_method(cDatabase, "limit", Database_limit, -1);
1123
- rb_define_method(cDatabase, "on_progress", Database_on_progress, 1);
1124
- rb_define_method(cDatabase, "prepare", Database_prepare, -1);
1125
- rb_define_method(cDatabase, "query", Database_query_hash, -1);
1126
- rb_define_method(cDatabase, "query_ary", Database_query_ary, -1);
1127
- rb_define_method(cDatabase, "query_hash", Database_query_hash, -1);
1128
- rb_define_method(cDatabase, "query_single_column", Database_query_single_column, -1);
1129
- rb_define_method(cDatabase, "query_single_row", Database_query_single_row, -1);
1130
- rb_define_method(cDatabase, "query_single_value", Database_query_single_value, -1);
1131
- rb_define_method(cDatabase, "read_only?", Database_read_only_p, 0);
1132
- rb_define_method(cDatabase, "status", Database_status, -1);
1133
- rb_define_method(cDatabase, "total_changes", Database_total_changes, 0);
1134
- rb_define_method(cDatabase, "trace", Database_trace, 0);
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
1434
+
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);
1135
1452
 
1136
1453
  #ifdef EXTRALITE_ENABLE_CHANGESET
1137
- rb_define_method(cDatabase, "track_changes", Database_track_changes, -1);
1454
+ DEF("track_changes", Database_track_changes, -1);
1138
1455
  #endif
1139
1456
 
1140
- rb_define_method(cDatabase, "transaction_active?", Database_transaction_active_p, 0);
1457
+ DEF("transaction_active?", Database_transaction_active_p, 0);
1141
1458
 
1142
- #ifdef HAVE_SQLITE3_LOAD_EXTENSION
1143
- rb_define_method(cDatabase, "load_extension", Database_load_extension, 1);
1144
- #endif
1145
-
1146
- cBlob = rb_define_class_under(mExtralite, "Blob", rb_cString);
1147
-
1148
- cError = rb_define_class_under(mExtralite, "Error", rb_eStandardError);
1149
- cSQLError = rb_define_class_under(mExtralite, "SQLError", cError);
1150
- 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);
1151
1463
  cInterruptError = rb_define_class_under(mExtralite, "InterruptError", cError);
1152
1464
  cParameterError = rb_define_class_under(mExtralite, "ParameterError", cError);
1153
-
1154
- eArgumentError = rb_const_get(rb_cObject, rb_intern("ArgumentError"));
1155
-
1156
- ID_bind = rb_intern("bind");
1157
- ID_call = rb_intern("call");
1158
- ID_each = rb_intern("each");
1159
- ID_keys = rb_intern("keys");
1160
- ID_new = rb_intern("new");
1161
- ID_strip = rb_intern("strip");
1162
- ID_to_s = rb_intern("to_s");
1163
- ID_track = rb_intern("track");
1164
-
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"));
1165
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"));
1166
1483
  SYM_read_only = ID2SYM(rb_intern("read_only"));
1167
- SYM_synchronous = ID2SYM(rb_intern("synchronous"));
1168
- SYM_wal_journal_mode = ID2SYM(rb_intern("wal_journal_mode"));
1484
+ SYM_wal = ID2SYM(rb_intern("wal"));
1169
1485
 
1486
+ rb_gc_register_mark_object(SYM_at_least_once);
1170
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);
1171
1492
  rb_gc_register_mark_object(SYM_read_only);
1172
- rb_gc_register_mark_object(SYM_synchronous);
1173
- 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);
1174
1496
 
1175
1497
  UTF8_ENCODING = rb_utf8_encoding();
1176
1498
  }