extralite 2.6 → 2.7.1

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,76 @@ VALUE Database_load_extension(VALUE self, VALUE path) {
614
710
  }
615
711
  #endif
616
712
 
617
- /* call-seq:
618
- * db.prepare(sql) -> Extralite::Query
619
- * db.prepare(sql, ...) -> Extralite::Query
620
- *
621
- * Creates a prepared statement with the given SQL query. If query parameters
622
- * are given, they are bound to the query.
623
- */
624
- VALUE Database_prepare(int argc, VALUE *argv, VALUE self) {
713
+ static inline VALUE Database_prepare(int argc, VALUE *argv, VALUE self, VALUE mode) {
625
714
  rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
626
- VALUE query = rb_funcall(cQuery, ID_new, 2, self, argv[0]);
715
+
716
+ VALUE args[] = { self, argv[0], mode};
717
+ VALUE query = rb_funcall_passing_block(cQuery, ID_new, 3, args);
627
718
  if (argc > 1) rb_funcallv(query, ID_bind, argc - 1, argv + 1);
628
719
  RB_GC_GUARD(query);
629
720
  return query;
630
721
  }
631
722
 
632
723
  /* call-seq:
633
- * db.interrupt -> db
724
+ * db.prepare(sql) -> query
725
+ * db.prepare(sql, *params) -> query
726
+ * db.prepare(sql, *params) { ... } -> query
634
727
  *
635
- * Interrupts a long running query. This method is to be called from a different
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
+ * @param *params [Array<any>] parameters to bind
734
+ * @return [Extralite::Query] prepared query
735
+ */
736
+ VALUE Database_prepare_hash(int argc, VALUE *argv, VALUE self) {
737
+ return Database_prepare(argc, argv, self, SYM_hash);
738
+ }
739
+
740
+ /* call-seq:
741
+ * db.prepare_argv(sql) -> Extralite::Query
742
+ * db.prepare_argv(sql, *params) -> Extralite::Query
743
+ * db.prepare_argv(sql, *params) { ... } -> Extralite::Query
744
+ *
745
+ * Creates a prepared query with the given SQL query in argv mode. If query
746
+ * parameters are given, they are bound to the query. If a block is given, it is
747
+ * used as a transform proc.
748
+ *
749
+ * @param sql [String] SQL statement
750
+ * @param *params [Array<any>] parameters to bind
751
+ * @return [Extralite::Query] prepared query
752
+ */
753
+ VALUE Database_prepare_argv(int argc, VALUE *argv, VALUE self) {
754
+ return Database_prepare(argc, argv, self, SYM_argv);
755
+ }
756
+
757
+ /* call-seq:
758
+ * db.prepare_ary(sql) -> Extralite::Query
759
+ * db.prepare_ary(sql, *params) -> Extralite::Query
760
+ * db.prepare_ary(sql, *params) { ... } -> Extralite::Query
761
+ *
762
+ * Creates a prepared query with the given SQL query in ary mode. If query
763
+ * parameters are given, they are bound to the query. If a block is given, it is
764
+ * used as a transform proc.
765
+ *
766
+ * @param sql [String] SQL statement
767
+ * @param *params [Array<any>] parameters to bind
768
+ * @return [Extralite::Query] prepared query
769
+ */
770
+ VALUE Database_prepare_ary(int argc, VALUE *argv, VALUE self) {
771
+ return Database_prepare(argc, argv, self, SYM_ary);
772
+ }
773
+
774
+ /* Interrupts a long running query. This method is to be called from a different
636
775
  * thread than the one running the query. Upon calling `#interrupt` the running
637
776
  * query will stop and raise an `Extralite::InterruptError` exception.
638
777
  *
639
778
  * 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).
779
+ * For more information, consult the [sqlite3 API
780
+ * docs](https://sqlite.org/c3ref/interrupt.html).
781
+ *
782
+ * @return [Extralite::Database] database
641
783
  */
642
784
  VALUE Database_interrupt(VALUE self) {
643
785
  Database_t *db = self_to_open_database(self);
@@ -712,15 +854,24 @@ VALUE backup_cleanup(VALUE ptr) {
712
854
  }
713
855
 
714
856
  /* call-seq:
715
- * db.backup(dest) -> db
716
- * db.backup(dest) { |remaining, total| } -> db
857
+ * db.backup(dest, src_db_name = 'main', dst_db_name = 'main') { |remaining, total| ... } -> db
717
858
  *
859
+ *
718
860
  * Creates a backup of the database to the given destination, which can be
719
861
  * either a filename or a database instance. In order to monitor the backup
720
862
  * progress you can pass a block that will be called periodically by the backup
721
863
  * method with two arguments: the remaining page count, and the total page
722
864
  * count, which can be used to display the progress to the user or to collect
723
865
  * statistics.
866
+ *
867
+ * db_src.backup(db_dest) do |remaining, total|
868
+ * puts "Backing up #{remaining}/#{total}"
869
+ * end
870
+ *
871
+ * @param dest [String, Extralite::Database] backup destination
872
+ * @param src_db_name [String] source database name (default: "main")
873
+ * @param dst_db_name [String] Destination database name (default: "main")
874
+ * @return [Extralite::Database] source database
724
875
  */
725
876
  VALUE Database_backup(int argc, VALUE *argv, VALUE self) {
726
877
  VALUE dst;
@@ -766,12 +917,17 @@ VALUE Database_backup(int argc, VALUE *argv, VALUE self) {
766
917
  return self;
767
918
  }
768
919
 
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
920
+ /* Returns runtime status values for the given op as an array containing the
773
921
  * current value and the high water mark value. To reset the high water mark,
774
922
  * pass true as reset.
923
+ *
924
+ * @overload runtime_status(op)
925
+ * @param op [Integer] op
926
+ * @return [Array<Integer>] array containing the value and high water mark
927
+ * @overload runtime_status(op, reset)
928
+ * @param op [Integer] op
929
+ * @param reset [Integer, bool] reset flag
930
+ * @return [Array<Integer>] array containing the value and high water mark
775
931
  */
776
932
  VALUE Extralite_runtime_status(int argc, VALUE* argv, VALUE self) {
777
933
  VALUE op, reset;
@@ -785,12 +941,17 @@ VALUE Extralite_runtime_status(int argc, VALUE* argv, VALUE self) {
785
941
  return rb_ary_new3(2, LONG2FIX(cur), LONG2FIX(hwm));
786
942
  }
787
943
 
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
944
+ /* Returns database status values for the given op as an array containing the
792
945
  * current value and the high water mark value. To reset the high water mark,
793
946
  * pass true as reset.
947
+ *
948
+ * @overload status(op)
949
+ * @param op [Integer] op
950
+ * @return [Array<Integer>] array containing the value and high water mark
951
+ * @overload status(op, reset)
952
+ * @param op [Integer] op
953
+ * @param reset [Integer, bool] reset flag
954
+ * @return [Array<Integer>] array containing the value and high water mark
794
955
  */
795
956
  VALUE Database_status(int argc, VALUE *argv, VALUE self) {
796
957
  VALUE op, reset;
@@ -806,12 +967,16 @@ VALUE Database_status(int argc, VALUE *argv, VALUE self) {
806
967
  return rb_ary_new3(2, INT2NUM(cur), INT2NUM(hwm));
807
968
  }
808
969
 
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,
970
+ /* Returns the current limit for the given category. If a new value is given,
814
971
  * sets the limit to the new value and returns the previous value.
972
+ *
973
+ * @overload limit(category)
974
+ * @param category [Integer] category
975
+ * @return [Integer] limit value
976
+ * @overload limit(category, new_value)
977
+ * @param category [Integer] category
978
+ * @param new_value [Integer] new value
979
+ * @return [Integer] old value
815
980
  */
816
981
  VALUE Database_limit(int argc, VALUE *argv, VALUE self) {
817
982
  VALUE category, new_value;
@@ -827,12 +992,18 @@ VALUE Database_limit(int argc, VALUE *argv, VALUE self) {
827
992
  return INT2NUM(value);
828
993
  }
829
994
 
830
- /* call-seq:
831
- * db.busy_timeout=(sec) -> db
832
- * db.busy_timeout=nil -> db
995
+ /* Sets the busy timeout for the database, in seconds or fractions thereof. To
996
+ * disable the busy timeout, set it to 0 or nil. When the busy timeout is set to
997
+ * a value larger than zero, running a query when the database is locked will
998
+ * cause the program to wait for the database to become available. If the
999
+ * database is still locked when the timeout period has elapsed, the query will
1000
+ * fail with a `Extralite::BusyError` exception.
1001
+ *
1002
+ * Setting the busy timeout allows other threads to run while waiting for the
1003
+ * database to become available. See also `#on_progress`.
833
1004
  *
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.
1005
+ * @param sec [Number, nil] timeout value
1006
+ * @return [Extralite::Database] database
836
1007
  */
837
1008
  VALUE Database_busy_timeout_set(VALUE self, VALUE sec) {
838
1009
  Database_t *db = self_to_open_database(self);
@@ -844,10 +1015,9 @@ VALUE Database_busy_timeout_set(VALUE self, VALUE sec) {
844
1015
  return self;
845
1016
  }
846
1017
 
847
- /* call-seq:
848
- * db.total_changes -> value
849
- *
850
- * Returns the total number of changes made to the database since opening it.
1018
+ /* Returns the total number of changes made to the database since opening it.
1019
+ *
1020
+ * @return [Integer] total changes
851
1021
  */
852
1022
  VALUE Database_total_changes(VALUE self) {
853
1023
  Database_t *db = self_to_open_database(self);
@@ -856,12 +1026,10 @@ VALUE Database_total_changes(VALUE self) {
856
1026
  return INT2NUM(value);
857
1027
  }
858
1028
 
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.
1029
+ /* Installs or removes a block that will be invoked for every SQL statement
1030
+ * executed. To stop tracing, call `#trace` without a block.
1031
+ *
1032
+ * @return [Extralite::Database] database
865
1033
  */
866
1034
  VALUE Database_trace(VALUE self) {
867
1035
  Database_t *db = self_to_open_database(self);
@@ -871,18 +1039,21 @@ VALUE Database_trace(VALUE self) {
871
1039
  }
872
1040
 
873
1041
  #ifdef EXTRALITE_ENABLE_CHANGESET
874
- /* Tracks changes to the database and returns a changeset. The changeset can
1042
+ /* call-seq:
1043
+ * db.track_changes(*tables) { ... } -> changeset
1044
+ *
1045
+ * Tracks changes to the database and returns a changeset. The changeset can
875
1046
  * then be used to store the changes to a file, apply them to another database,
876
1047
  * or undo the changes. The given table names specify which tables should be
877
1048
  * tracked for changes. Passing a value of nil causes all tables to be tracked.
878
1049
  *
879
- * changeset = db.track_changes(:foo, :bar) do
880
- * perform_a_bunch_of_queries
881
- * end
1050
+ * changeset = db.track_changes(:foo, :bar) do
1051
+ * perform_a_bunch_of_queries
1052
+ * end
882
1053
  *
883
- * File.open('my.changes', 'w+') { |f| f << changeset.to_blob }
1054
+ * File.open('my.changes', 'w+') { |f| f << changeset.to_blob }
884
1055
  *
885
- * @param table [String, Symbol] table to track
1056
+ * @param *tables [Array<String, Symbol>] table(s) to track
886
1057
  * @return [Extralite::Changeset] changeset
887
1058
  */
888
1059
  VALUE Database_track_changes(int argc, VALUE *argv, VALUE self) {
@@ -899,101 +1070,239 @@ VALUE Database_track_changes(int argc, VALUE *argv, VALUE self) {
899
1070
  }
900
1071
  #endif
901
1072
 
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;
1073
+ void Database_reset_progress_handler(VALUE self, Database_t *db) {
1074
+ db->progress_handler.mode = PROGRESS_NONE;
1075
+ RB_OBJ_WRITE(self, &db->progress_handler.proc, Qnil);
1076
+ sqlite3_progress_handler(db->sqlite3_db, 0, NULL, NULL);
1077
+ sqlite3_busy_handler(db->sqlite3_db, NULL, NULL);
906
1078
  }
907
1079
 
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;
1080
+ static inline enum progress_handler_mode symbol_to_progress_mode(VALUE mode) {
1081
+ if (mode == SYM_at_least_once) return PROGRESS_AT_LEAST_ONCE;
1082
+ if (mode == SYM_once) return PROGRESS_ONCE;
1083
+ if (mode == SYM_normal) return PROGRESS_NORMAL;
1084
+ if (mode == SYM_none) return PROGRESS_NONE;
1085
+ rb_raise(eArgumentError, "Invalid progress handler mode");
912
1086
  }
913
1087
 
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);
1088
+ inline void Database_issue_query(Database_t *db, VALUE sql) {
1089
+ if (db->trace_proc != Qnil) rb_funcall(db->trace_proc, ID_call, 1, sql);
1090
+ switch (db->progress_handler.mode) {
1091
+ case PROGRESS_AT_LEAST_ONCE:
1092
+ case PROGRESS_ONCE:
1093
+ rb_funcall(db->progress_handler.proc, ID_call, 0);
1094
+ default:
1095
+ ; // do nothing
1096
+
1097
+ }
918
1098
  }
919
1099
 
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
1100
+ struct progress_handler parse_progress_handler_opts(VALUE opts) {
1101
+ static ID kw_ids[3];
1102
+ VALUE kw_args[3];
1103
+ struct progress_handler prog = {
1104
+ .mode = rb_block_given_p() ? PROGRESS_NORMAL : PROGRESS_NONE,
1105
+ .proc = rb_block_given_p() ? rb_block_proc() : Qnil,
1106
+ .period = DEFAULT_PROGRESS_HANDLER_PERIOD,
1107
+ .tick = DEFAULT_PROGRESS_HANDLER_TICK
1108
+ };
1109
+
1110
+ if (!NIL_P(opts)) {
1111
+ if (!kw_ids[0]) {
1112
+ CONST_ID(kw_ids[0], "period");
1113
+ CONST_ID(kw_ids[1], "tick");
1114
+ CONST_ID(kw_ids[2], "mode");
1115
+ }
1116
+
1117
+ rb_get_kwargs(opts, kw_ids, 0, 3, kw_args);
1118
+ if (kw_args[0] != Qundef) { prog.period = NUM2INT(kw_args[0]); }
1119
+ if (kw_args[1] != Qundef) { prog.tick = NUM2INT(kw_args[1]); }
1120
+ if (kw_args[2] != Qundef) { prog.mode = symbol_to_progress_mode(kw_args[2]); }
1121
+ if (prog.tick > prog.period) prog.tick = prog.period;
1122
+ }
1123
+ if (NIL_P(prog.proc) || (prog.period <= 0)) prog.mode = PROGRESS_NONE;
1124
+ if (prog.mode == PROGRESS_NONE) prog.proc = Qnil;
1125
+
1126
+ return prog;
1127
+ }
1128
+
1129
+ /* Installs or removes a progress handler that will be executed periodically
925
1130
  * while a query is running. This method can be used to support switching
926
1131
  * between fibers and threads or implementing timeouts for running queries.
927
1132
  *
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.
1133
+ * The `period` parameter specifies the approximate number of SQLite
1134
+ * virtual machine instructions that are evaluated between successive
1135
+ * invocations of the progress handler. A period of less than 1 removes the
1136
+ * progress handler. The default period value is 1000.
1137
+ *
1138
+ * The optional `tick` parameter specifies the granularity of how often the
1139
+ * progress handler is called. The default tick value is 10, which means that
1140
+ * Extralite's underlying progress callback will be called every 10 SQLite VM
1141
+ * instructions. The given progress proc, however, will be only called every
1142
+ * `period` (cumulative) VM instructions. This allows the progress handler to
1143
+ * work correctly also when running simple queries that don't include many
1144
+ * VM instructions. If the `tick` value is greater than the period value it is
1145
+ * automatically capped to the period value.
1146
+ *
1147
+ * The `mode` parameter controls the progress handler mode, which is one of the
1148
+ * following:
1149
+ *
1150
+ * - `:normal` (default): the progress handler proc is invoked on query
1151
+ * progress.
1152
+ * - `:once`: the progress handler proc is invoked only once, when preparing the
1153
+ * query.
1154
+ * - `:at_least_once`: the progress handler proc is invoked when prearing the
1155
+ * query, and on query progress.
931
1156
  *
932
1157
  * The progress handler is called also when the database is busy. This lets the
933
1158
  * application perform work while waiting for the database to become unlocked,
934
1159
  * or implement a timeout. Note that setting the database's busy_timeout _after_
935
1160
  * setting a progress handler may lead to undefined behaviour in a concurrent
936
- * application.
1161
+ * application. When busy, the progress handler proc is passed `true` as the
1162
+ * first argument.
937
1163
  *
938
1164
  * When the progress handler is set, the gvl release threshold value is set to
939
1165
  * -1, which means that the GVL will not be released at all when preparing or
940
1166
  * running queries. It is the application's responsibility to let other threads
941
1167
  * or fibers run by calling e.g. Thread.pass:
942
- *
943
- * db.on_progress(1000) do
1168
+ *
1169
+ * db.on_progress do
944
1170
  * do_something_interesting
945
1171
  * Thread.pass # let other threads run
946
1172
  * end
947
- *
948
- * Note that the progress handler is set globally for the database and that
1173
+ *
1174
+ * Note that the progress handler is set globally for the database and that
949
1175
  * 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
1176
+ * running or at what time they were started. This means that you'll need to
1177
+ * wrap the stock #query_xxx and #execute methods with your own code that
952
1178
  * calculates timeouts, for example:
953
- *
1179
+ *
954
1180
  * def setup_progress_handler
955
- * @db.on_progress(1000) do
1181
+ * @db.on_progress do
956
1182
  * raise TimeoutError if Time.now - @t0 >= @timeout
957
1183
  * Thread.pass
958
1184
  * end
959
1185
  * end
960
- *
1186
+ *
961
1187
  * def query(sql, *)
962
1188
  * @t0 = Time.now
963
1189
  * @db.query(sql, *)
964
1190
  * end
965
- *
1191
+ *
966
1192
  * If the gvl release threshold is set to a value equal to or larger than 0
967
1193
  * after setting the progress handler, the progress handler will be reset.
968
1194
  *
969
1195
  * @param period [Integer] progress handler period
970
- * @returns [Extralite::Database] database
1196
+ * @param [Hash] opts progress options
1197
+ * @option opts [Integer] :period period value (`1000` by default)
1198
+ * @option opts [Integer] :tick tick value (`10` by default)
1199
+ * @option opts [Symbol] :mode progress handler mode (`:normal` by default)
1200
+ * @return [Extralite::Database] database
971
1201
  */
972
- VALUE Database_on_progress(VALUE self, VALUE period) {
1202
+ VALUE Database_on_progress(int argc, VALUE *argv, VALUE self) {
973
1203
  Database_t *db = self_to_open_database(self);
974
- int period_int = NUM2INT(period);
1204
+ VALUE opts;
1205
+ struct progress_handler prog;
975
1206
 
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;
1207
+ rb_scan_args(argc, argv, "00:", &opts);
1208
+ prog = parse_progress_handler_opts(opts);
979
1209
 
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);
1210
+ if (prog.mode == PROGRESS_NONE) {
1211
+ Database_reset_progress_handler(self, db);
985
1212
  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);
1213
+ return self;
988
1214
  }
989
1215
 
1216
+ db->gvl_release_threshold = -1;
1217
+ db->progress_handler.mode = prog.mode;
1218
+ RB_OBJ_WRITE(self, &db->progress_handler.proc, prog.proc);
1219
+ db->progress_handler.period = prog.period;
1220
+ db->progress_handler.tick = prog.tick;
1221
+ db->progress_handler.tick_count = 0;
1222
+ db->progress_handler.call_count = 0;
1223
+
1224
+ // The PROGRESS_ONCE mode works by invoking the progress handler proc exactly
1225
+ // once, before iterating over the result set, so in that mode we don't
1226
+ // actually need to set the progress handler at the sqlite level.
1227
+ if (prog.mode != PROGRESS_ONCE)
1228
+ sqlite3_progress_handler(db->sqlite3_db, prog.tick, &Database_progress_handler, db);
1229
+ if (prog.mode != PROGRESS_NONE)
1230
+ sqlite3_busy_handler(db->sqlite3_db, &Database_busy_handler, db);
1231
+
990
1232
  return self;
991
1233
  }
992
1234
 
993
1235
  /* call-seq:
994
- * db.errcode -> errcode
1236
+ * Extralite.on_progress(**opts) { ... }
1237
+ *
1238
+ * Installs or removes a global progress handler that will be executed
1239
+ * periodically while a query is running. This method can be used to support
1240
+ * switching between fibers and threads or implementing timeouts for running
1241
+ * queries.
1242
+ *
1243
+ * This method sets the progress handler settings and behaviour for all
1244
+ * subsequently created `Database` instances. Calling this method will have no
1245
+ * effect on already existing `Database` instances
1246
+ *
1247
+ * The `period` parameter specifies the approximate number of SQLite
1248
+ * virtual machine instructions that are evaluated between successive
1249
+ * invocations of the progress handler. A period of less than 1 removes the
1250
+ * progress handler. The default period value is 1000.
1251
+ *
1252
+ * The optional `tick` parameter specifies the granularity of how often the
1253
+ * progress handler is called. The default tick value is 10, which means that
1254
+ * Extralite's underlying progress callback will be called every 10 SQLite VM
1255
+ * instructions. The given progress proc, however, will be only called every
1256
+ * `period` (cumulative) VM instructions. This allows the progress handler to
1257
+ * work correctly also when running simple queries that don't include many
1258
+ * VM instructions. If the `tick` value is greater than the period value it is
1259
+ * automatically capped to the period value.
1260
+ *
1261
+ * The `mode` parameter controls the progress handler mode, which is one of the
1262
+ * following:
1263
+ *
1264
+ * - `:normal` (default): the progress handler proc is invoked on query
1265
+ * progress.
1266
+ * - `:once`: the progress handler proc is invoked only once, when preparing the
1267
+ * query.
1268
+ * - `:at_least_once`: the progress handler proc is invoked when prearing the
1269
+ * query, and on query progress.
1270
+ *
1271
+ * The progress handler is called also when the database is busy. This lets the
1272
+ * application perform work while waiting for the database to become unlocked,
1273
+ * or implement a timeout. Note that setting the database's busy_timeout _after_
1274
+ * setting a progress handler may lead to undefined behaviour in a concurrent
1275
+ * application. When busy, the progress handler proc is passed `true` as the
1276
+ * first argument.
1277
+ *
1278
+ * When the progress handler is set, the gvl release threshold value is set to
1279
+ * -1, which means that the GVL will not be released at all when preparing or
1280
+ * running queries. It is the application's responsibility to let other threads
1281
+ * or fibers run by calling e.g. Thread.pass:
1282
+ *
1283
+ * Extralite.on_progress do
1284
+ * do_something_interesting
1285
+ * Thread.pass # let other threads run
1286
+ * end
995
1287
  *
996
- * Returns the last error code for the database.
1288
+ * @param period [Integer] progress handler period
1289
+ * @param [Hash] opts progress options
1290
+ * @option opts [Integer] :period period value (`1000` by default)
1291
+ * @option opts [Integer] :tick tick value (`10` by default)
1292
+ * @option opts [Symbol] :mode progress handler mode (`:normal` by default)
1293
+ * @return [Extralite::Database] database
1294
+ */
1295
+ VALUE Extralite_on_progress(int argc, VALUE *argv, VALUE self) {
1296
+ VALUE opts;
1297
+
1298
+ rb_scan_args(argc, argv, "00:", &opts);
1299
+ global_progress_handler = parse_progress_handler_opts(opts);
1300
+ return self;
1301
+ }
1302
+
1303
+ /* Returns the last error code for the database.
1304
+ *
1305
+ * @return [Integer] last error code
997
1306
  */
998
1307
  VALUE Database_errcode(VALUE self) {
999
1308
  Database_t *db = self_to_open_database(self);
@@ -1001,10 +1310,9 @@ VALUE Database_errcode(VALUE self) {
1001
1310
  return INT2NUM(sqlite3_errcode(db->sqlite3_db));
1002
1311
  }
1003
1312
 
1004
- /* call-seq:
1005
- * db.errmsg -> errmsg
1006
- *
1007
- * Returns the last error message for the database.
1313
+ /* Returns the last error message for the database.
1314
+ *
1315
+ * @return [String] last error message
1008
1316
  */
1009
1317
  VALUE Database_errmsg(VALUE self) {
1010
1318
  Database_t *db = self_to_open_database(self);
@@ -1013,10 +1321,10 @@ VALUE Database_errmsg(VALUE self) {
1013
1321
  }
1014
1322
 
1015
1323
  #ifdef HAVE_SQLITE3_ERROR_OFFSET
1016
- /* call-seq:
1017
- * db.error_offset -> ofs
1018
- *
1019
- * Returns the offset for the last error
1324
+ /* Returns the offset for the last error. This is useful for indicating where in
1325
+ * the SQL string an error was encountered.
1326
+ *
1327
+ * @return [Integer] offset in the last submitted SQL string
1020
1328
  */
1021
1329
  VALUE Database_error_offset(VALUE self) {
1022
1330
  Database_t *db = self_to_open_database(self);
@@ -1052,14 +1360,24 @@ VALUE Database_gvl_release_threshold_get(VALUE self) {
1052
1360
  return INT2NUM(db->gvl_release_threshold);
1053
1361
  }
1054
1362
 
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
1363
+ /* Sets the database's GVL release threshold. The release policy changes
1364
+ * according to the given value:
1365
+ *
1366
+ * - Less than 0: the GVL is never released while running queries. This is the
1367
+ * policy used when a progress handler is set. For more information see
1368
+ * `#on_progress`.
1369
+ * - 0: The GVL is released while preparing queries, but held when iterating
1370
+ * through records.
1371
+ * - Greater than 0: the GVL is released while preparing queries, and released
1372
+ * periodically while iterating through records, according to the given
1373
+ * period. A value of 1 will release the GVL on every iterated record. A value
1374
+ * of 100 will release the GVL once for every 100 records.
1375
+ *
1376
+ * A value of nil sets the threshold to the default value, which is
1060
1377
  * currently 1000.
1061
1378
  *
1062
- * @return [Integer, nil] New GVL release threshold
1379
+ * @param threshold [Integer, nil] GVL release threshold
1380
+ * @return [Integer] GVL release threshold
1063
1381
  */
1064
1382
  VALUE Database_gvl_release_threshold_set(VALUE self, VALUE value) {
1065
1383
  Database_t *db = self_to_open_database(self);
@@ -1071,7 +1389,7 @@ VALUE Database_gvl_release_threshold_set(VALUE self, VALUE value) {
1071
1389
  if (value_int < -1)
1072
1390
  rb_raise(eArgumentError, "Invalid GVL release threshold value (expect integer >= -1)");
1073
1391
 
1074
- if (value_int > -1 && !NIL_P(db->progress_handler_proc))
1392
+ if (value_int > -1 && db->progress_handler.mode != PROGRESS_NONE)
1075
1393
  Database_reset_progress_handler(self, db);
1076
1394
  db->gvl_release_threshold = value_int;
1077
1395
  break;
@@ -1090,87 +1408,104 @@ void Init_ExtraliteDatabase(void) {
1090
1408
  VALUE mExtralite = rb_define_module("Extralite");
1091
1409
  rb_define_singleton_method(mExtralite, "runtime_status", Extralite_runtime_status, -1);
1092
1410
  rb_define_singleton_method(mExtralite, "sqlite3_version", Extralite_sqlite3_version, 0);
1411
+ rb_define_singleton_method(mExtralite, "on_progress", Extralite_on_progress, -1);
1093
1412
 
1094
1413
  cDatabase = rb_define_class_under(mExtralite, "Database", rb_cObject);
1095
1414
  rb_define_alloc_func(cDatabase, Database_allocate);
1096
1415
 
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);
1416
+ rb_define_method(cDatabase, "backup", Database_backup, -1);
1417
+ rb_define_method(cDatabase, "batch_execute", Database_batch_execute, 2);
1418
+ rb_define_method(cDatabase, "batch_query", Database_batch_query, 2);
1419
+ rb_define_method(cDatabase, "batch_query_ary", Database_batch_query_ary, 2);
1420
+ rb_define_method(cDatabase, "batch_query_argv", Database_batch_query_argv, 2);
1421
+ rb_define_method(cDatabase, "batch_query_hash", Database_batch_query, 2);
1422
+ rb_define_method(cDatabase, "busy_timeout=", Database_busy_timeout_set, 1);
1423
+ rb_define_method(cDatabase, "changes", Database_changes, 0);
1424
+ rb_define_method(cDatabase, "close", Database_close, 0);
1425
+ rb_define_method(cDatabase, "closed?", Database_closed_p, 0);
1426
+ rb_define_method(cDatabase, "columns", Database_columns, 1);
1427
+ rb_define_method(cDatabase, "errcode", Database_errcode, 0);
1428
+ rb_define_method(cDatabase, "errmsg", Database_errmsg, 0);
1105
1429
 
1106
1430
  #ifdef HAVE_SQLITE3_ERROR_OFFSET
1107
- rb_define_method(cDatabase, "error_offset", Database_error_offset, 0);
1431
+ rb_define_method(cDatabase, "error_offset", Database_error_offset, 0);
1108
1432
  #endif
1109
1433
 
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);
1434
+ rb_define_method(cDatabase, "execute", Database_execute, -1);
1435
+ rb_define_method(cDatabase, "filename", Database_filename, -1);
1436
+ rb_define_method(cDatabase, "gvl_release_threshold", Database_gvl_release_threshold_get, 0);
1117
1437
  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);
1438
+ rb_define_method(cDatabase, "initialize", Database_initialize, -1);
1439
+ rb_define_method(cDatabase, "inspect", Database_inspect, 0);
1440
+ rb_define_method(cDatabase, "interrupt", Database_interrupt, 0);
1441
+ rb_define_method(cDatabase, "last_insert_rowid", Database_last_insert_rowid, 0);
1442
+ rb_define_method(cDatabase, "limit", Database_limit, -1);
1443
+
1444
+ #ifdef HAVE_SQLITE3_LOAD_EXTENSION
1445
+ rb_define_method(cDatabase, "load_extension", Database_load_extension, 1);
1446
+ #endif
1447
+
1448
+ rb_define_method(cDatabase, "on_progress", Database_on_progress, -1);
1449
+ rb_define_method(cDatabase, "prepare", Database_prepare_hash, -1);
1450
+ rb_define_method(cDatabase, "prepare_argv", Database_prepare_argv, -1);
1451
+ rb_define_method(cDatabase, "prepare_ary", Database_prepare_ary, -1);
1452
+ rb_define_method(cDatabase, "prepare_hash", Database_prepare_hash, -1);
1453
+ rb_define_method(cDatabase, "query", Database_query, -1);
1454
+ rb_define_method(cDatabase, "query_argv", Database_query_argv, -1);
1455
+ rb_define_method(cDatabase, "query_ary", Database_query_ary, -1);
1456
+ rb_define_method(cDatabase, "query_hash", Database_query, -1);
1457
+ rb_define_method(cDatabase, "query_single", Database_query_single, -1);
1458
+ rb_define_method(cDatabase, "query_single_ary", Database_query_single_ary, -1);
1459
+ rb_define_method(cDatabase, "query_single_argv", Database_query_single_argv, -1);
1460
+ rb_define_method(cDatabase, "query_single_hash", Database_query_single, -1);
1461
+ rb_define_method(cDatabase, "read_only?", Database_read_only_p, 0);
1462
+ rb_define_method(cDatabase, "status", Database_status, -1);
1463
+ rb_define_method(cDatabase, "total_changes", Database_total_changes, 0);
1464
+ rb_define_method(cDatabase, "trace", Database_trace, 0);
1135
1465
 
1136
1466
  #ifdef EXTRALITE_ENABLE_CHANGESET
1137
- rb_define_method(cDatabase, "track_changes", Database_track_changes, -1);
1467
+ rb_define_method(cDatabase, "track_changes", Database_track_changes, -1);
1138
1468
  #endif
1139
1469
 
1140
- rb_define_method(cDatabase, "transaction_active?", Database_transaction_active_p, 0);
1141
-
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);
1470
+ rb_define_method(cDatabase, "transaction_active?", Database_transaction_active_p, 0);
1147
1471
 
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);
1472
+ cBlob = rb_define_class_under(mExtralite, "Blob", rb_cString);
1473
+ cError = rb_define_class_under(mExtralite, "Error", rb_eStandardError);
1474
+ cSQLError = rb_define_class_under(mExtralite, "SQLError", cError);
1475
+ cBusyError = rb_define_class_under(mExtralite, "BusyError", cError);
1151
1476
  cInterruptError = rb_define_class_under(mExtralite, "InterruptError", cError);
1152
1477
  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
-
1478
+ eArgumentError = rb_const_get(rb_cObject, rb_intern("ArgumentError"));
1479
+
1480
+ ID_bind = rb_intern("bind");
1481
+ ID_call = rb_intern("call");
1482
+ ID_each = rb_intern("each");
1483
+ ID_keys = rb_intern("keys");
1484
+ ID_new = rb_intern("new");
1485
+ ID_pragma = rb_intern("pragma");
1486
+ ID_strip = rb_intern("strip");
1487
+ ID_to_s = rb_intern("to_s");
1488
+ ID_track = rb_intern("track");
1489
+
1490
+ SYM_at_least_once = ID2SYM(rb_intern("at_least_once"));
1165
1491
  SYM_gvl_release_threshold = ID2SYM(rb_intern("gvl_release_threshold"));
1492
+ SYM_once = ID2SYM(rb_intern("once"));
1493
+ SYM_none = ID2SYM(rb_intern("none"));
1494
+ SYM_normal = ID2SYM(rb_intern("normal"));
1495
+ SYM_pragma = ID2SYM(rb_intern("pragma"));
1166
1496
  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"));
1497
+ SYM_wal = ID2SYM(rb_intern("wal"));
1169
1498
 
1499
+ rb_gc_register_mark_object(SYM_at_least_once);
1170
1500
  rb_gc_register_mark_object(SYM_gvl_release_threshold);
1501
+ rb_gc_register_mark_object(SYM_once);
1502
+ rb_gc_register_mark_object(SYM_none);
1503
+ rb_gc_register_mark_object(SYM_normal);
1504
+ rb_gc_register_mark_object(SYM_pragma);
1171
1505
  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);
1506
+ rb_gc_register_mark_object(SYM_wal);
1507
+
1508
+ rb_gc_register_mark_object(global_progress_handler.proc);
1174
1509
 
1175
1510
  UTF8_ENCODING = rb_utf8_encoding();
1176
1511
  }