extralite 2.6 → 2.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
  }