extralite 2.3 → 2.5

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.
@@ -1,26 +1,42 @@
1
1
  #include <stdio.h>
2
+ #include <stdlib.h>
2
3
  #include "extralite.h"
3
4
 
4
5
  VALUE cDatabase;
6
+ VALUE cBlob;
5
7
  VALUE cError;
6
8
  VALUE cSQLError;
7
9
  VALUE cBusyError;
8
10
  VALUE cInterruptError;
11
+ VALUE cParameterError;
9
12
  VALUE eArgumentError;
10
13
 
11
14
  ID ID_bind;
12
15
  ID ID_call;
16
+ ID ID_each;
13
17
  ID ID_keys;
14
18
  ID ID_new;
15
19
  ID ID_strip;
16
- ID ID_to_s;
17
20
 
21
+ VALUE SYM_gvl_release_threshold;
18
22
  VALUE SYM_read_only;
23
+ VALUE SYM_synchronous;
24
+ VALUE SYM_wal_journal_mode;
19
25
 
20
26
  static size_t Database_size(const void *ptr) {
21
27
  return sizeof(Database_t);
22
28
  }
23
29
 
30
+ static void Database_mark(void *ptr) {
31
+ Database_t *db = ptr;
32
+ rb_gc_mark_movable(db->trace_block);
33
+ }
34
+
35
+ static void Database_compact(void *ptr) {
36
+ Database_t *db = ptr;
37
+ db->trace_block = rb_gc_location(db->trace_block);
38
+ }
39
+
24
40
  static void Database_free(void *ptr) {
25
41
  Database_t *db = ptr;
26
42
  if (db->sqlite3_db) sqlite3_close_v2(db->sqlite3_db);
@@ -29,8 +45,8 @@ static void Database_free(void *ptr) {
29
45
 
30
46
  static const rb_data_type_t Database_type = {
31
47
  "Database",
32
- {0, Database_free, Database_size,},
33
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
48
+ {Database_mark, Database_free, Database_size, Database_compact},
49
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
34
50
  };
35
51
 
36
52
  static VALUE Database_allocate(VALUE klass) {
@@ -48,7 +64,7 @@ inline Database_t *self_to_database(VALUE self) {
48
64
  inline Database_t *self_to_open_database(VALUE self) {
49
65
  Database_t *db = self_to_database(self);
50
66
  if (!(db)->sqlite3_db) rb_raise(cError, "Database is closed");
51
-
67
+
52
68
  return db;
53
69
  }
54
70
 
@@ -78,14 +94,40 @@ default_flags:
78
94
  return SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
79
95
  }
80
96
 
97
+ VALUE Database_execute(int argc, VALUE *argv, VALUE self);
98
+
99
+ void Database_apply_opts(VALUE self, Database_t *db, VALUE opts) {
100
+ VALUE value = Qnil;
101
+
102
+ value = rb_hash_aref(opts, SYM_gvl_release_threshold);
103
+ if (!NIL_P(value)) db->gvl_release_threshold = NUM2INT(value);
104
+
105
+ value = rb_hash_aref(opts, SYM_wal_journal_mode);
106
+ if (RTEST(value)) {
107
+ value = rb_str_new_literal("PRAGMA journal_mode=wal");
108
+ Database_execute(1, &value, self);
109
+ }
110
+
111
+ value = rb_hash_aref(opts, SYM_synchronous);
112
+ if (RTEST(value)) {
113
+ value = rb_str_new_literal("PRAGMA synchronous=1");
114
+ Database_execute(1, &value, self);
115
+ }
116
+
117
+ RB_GC_GUARD(value);
118
+ }
119
+
81
120
  /* Initializes a new SQLite database with the given path and options.
82
121
  *
83
122
  * @overload initialize(path)
84
123
  * @param path [String] file path (or ':memory:' for memory database)
85
124
  * @return [void]
86
- * @overload initialize(path, read_only: false)
125
+ * @overload initialize(path, gvl_release_threshold: , read_only: , synchronous: , wal_journal_mode: )
87
126
  * @param path [String] file path (or ':memory:' for memory database)
127
+ * @param gvl_release_threshold [Integer] GVL release threshold
88
128
  * @param read_only [boolean] true for opening the database for reading only
129
+ * @param synchronous [boolean] true to set PRAGMA synchronous=1
130
+ * @param wal_journal_mode [boolean] true to set PRAGMA journal_mode=wal
89
131
  * @return [void]
90
132
  */
91
133
  VALUE Database_initialize(int argc, VALUE *argv, VALUE self) {
@@ -118,6 +160,9 @@ VALUE Database_initialize(int argc, VALUE *argv, VALUE self) {
118
160
  #endif
119
161
 
120
162
  db->trace_block = Qnil;
163
+ db->gvl_release_threshold = DEFAULT_GVL_RELEASE_THRESHOLD;
164
+
165
+ if (!NIL_P(opts)) Database_apply_opts(self, db, opts);
121
166
 
122
167
  return Qnil;
123
168
  }
@@ -172,14 +217,13 @@ static inline VALUE Database_perform_query(int argc, VALUE *argv, VALUE self, VA
172
217
  sql = rb_funcall(argv[0], ID_strip, 0);
173
218
  if (RSTRING_LEN(sql) == 0) return Qnil;
174
219
 
175
- // prepare query ctx
176
220
  if (db->trace_block != Qnil) rb_funcall(db->trace_block, ID_call, 1, sql);
177
221
  prepare_multi_stmt(db->sqlite3_db, &stmt, sql);
178
222
  RB_GC_GUARD(sql);
179
223
 
180
224
  bind_all_parameters(stmt, argc - 1, argv + 1);
181
- query_ctx ctx = { self, db->sqlite3_db, stmt, Qnil, QUERY_MODE(QUERY_MULTI_ROW), ALL_ROWS };
182
-
225
+ query_ctx ctx = QUERY_CTX(self, db, stmt, Qnil, QUERY_MODE(QUERY_MULTI_ROW), ALL_ROWS);
226
+
183
227
  return rb_ensure(SAFE(call), (VALUE)&ctx, SAFE(cleanup_stmt), (VALUE)&ctx);
184
228
  }
185
229
 
@@ -190,18 +234,18 @@ static inline VALUE Database_perform_query(int argc, VALUE *argv, VALUE self, VA
190
234
  * Runs a query returning rows as hashes (with symbol keys). If a block is
191
235
  * given, it will be called for each row. Otherwise, an array containing all
192
236
  * rows is returned.
193
- *
237
+ *
194
238
  * Query parameters to be bound to placeholders in the query can be specified as
195
239
  * a list of values or as a hash mapping parameter names to values. When
196
240
  * parameters are given as an array, the query should specify parameters using
197
241
  * `?`:
198
- *
242
+ *
199
243
  * db.query('select * from foo where x = ?', 42)
200
244
  *
201
245
  * Named placeholders are specified using `:`. The placeholder values are
202
246
  * specified using a hash, where keys are either strings are symbols. String
203
247
  * keys can include or omit the `:` prefix. The following are equivalent:
204
- *
248
+ *
205
249
  * db.query('select * from foo where x = :bar', bar: 42)
206
250
  * db.query('select * from foo where x = :bar', 'bar' => 42)
207
251
  * db.query('select * from foo where x = :bar', ':bar' => 42)
@@ -334,30 +378,150 @@ VALUE Database_execute(int argc, VALUE *argv, VALUE self) {
334
378
  }
335
379
 
336
380
  /* call-seq:
337
- * db.execute_multi(sql, params_array) -> changes
381
+ * db.batch_execute(sql, params_array) -> changes
382
+ * db.batch_execute(sql, enumerable) -> changes
383
+ * db.batch_execute(sql, callable) -> changes
384
+ *
385
+ * Executes the given query for each list of parameters in the paramter source.
386
+ * If an enumerable is given, it is iterated and each of its values is used as
387
+ * the parameters for running the query. If a callable is given, it is called
388
+ * repeatedly and each of its return values is used as the parameters, until nil
389
+ * is returned.
338
390
  *
339
- * Executes the given query for each list of parameters in params_array. Returns
340
- * the number of changes effected. This method is designed for inserting
341
- * multiple records.
391
+ * Returns the number of changes effected. This method is designed for inserting
392
+ * multiple records or performing other mass operations.
342
393
  *
343
394
  * records = [
344
395
  * [1, 2, 3],
345
396
  * [4, 5, 6]
346
397
  * ]
347
- * db.execute_multi('insert into foo values (?, ?, ?)', records)
398
+ * db.batch_execute('insert into foo values (?, ?, ?)', records)
399
+ *
400
+ * source = [
401
+ * [1, 2, 3],
402
+ * [4, 5, 6]
403
+ * ]
404
+ * db.batch_execute('insert into foo values (?, ?, ?)', -> { records.shift })
348
405
  *
406
+ * @param sql [String] query SQL
407
+ * @param parameters [Array<Array, Hash>, Enumerable, Enumerator, Callable] parameters to run query with
408
+ * @return [Integer] Total number of changes effected
349
409
  */
350
- VALUE Database_execute_multi(VALUE self, VALUE sql, VALUE params_array) {
410
+ VALUE Database_batch_execute(VALUE self, VALUE sql, VALUE parameters) {
351
411
  Database_t *db = self_to_open_database(self);
352
412
  sqlite3_stmt *stmt;
353
413
 
354
414
  if (RSTRING_LEN(sql) == 0) return Qnil;
355
415
 
356
- // prepare query ctx
357
416
  prepare_single_stmt(db->sqlite3_db, &stmt, sql);
358
- query_ctx ctx = { self, db->sqlite3_db, stmt, params_array, QUERY_MODE(QUERY_MULTI_ROW), ALL_ROWS };
417
+ query_ctx ctx = QUERY_CTX(self, db, stmt, parameters, QUERY_MULTI_ROW, ALL_ROWS);
418
+
419
+ return rb_ensure(SAFE(safe_batch_execute), (VALUE)&ctx, SAFE(cleanup_stmt), (VALUE)&ctx);
420
+ }
421
+
422
+ /* call-seq:
423
+ * db.batch_query(sql, params_array) -> rows
424
+ * db.batch_query(sql, enumerable) -> rows
425
+ * db.batch_query(sql, callable) -> rows
426
+ * db.batch_query(sql, params_array) { |rows| ... } -> changes
427
+ * db.batch_query(sql, enumerable) { |rows| ... } -> changes
428
+ * db.batch_query(sql, callable) { |rows| ... } -> changes
429
+ *
430
+ * Executes the given query for each list of parameters in the given paramter
431
+ * source. If a block is given, it is called with the resulting rows for each
432
+ * invocation of the query, and the total number of changes is returned.
433
+ * Otherwise, an array containing the resulting rows for each invocation is
434
+ * returned.
435
+ *
436
+ * records = [
437
+ * [1, 2],
438
+ * [3, 4]
439
+ * ]
440
+ * db.batch_query('insert into foo values (?, ?) returning bar, baz', records)
441
+ * #=> [{ bar: 1, baz: 2 }, { bar: 3, baz: 4}]
442
+ * *
443
+ * @param sql [String] query SQL
444
+ * @param parameters [Array<Array, Hash>, Enumerable, Enumerator, Callable] parameters to run query with
445
+ * @return [Array<Hash>, Integer] Total number of changes effected
446
+ */
447
+ VALUE Database_batch_query(VALUE self, VALUE sql, VALUE parameters) {
448
+ Database_t *db = self_to_open_database(self);
449
+ sqlite3_stmt *stmt;
450
+
451
+ prepare_single_stmt(db->sqlite3_db, &stmt, sql);
452
+ query_ctx ctx = QUERY_CTX(self, db, stmt, parameters, QUERY_MULTI_ROW, ALL_ROWS);
453
+
454
+ return rb_ensure(SAFE(safe_batch_query), (VALUE)&ctx, SAFE(cleanup_stmt), (VALUE)&ctx);
455
+ }
456
+
457
+ /* call-seq:
458
+ * db.batch_query_ary(sql, params_array) -> rows
459
+ * db.batch_query_ary(sql, enumerable) -> rows
460
+ * db.batch_query_ary(sql, callable) -> rows
461
+ * db.batch_query_ary(sql, params_array) { |rows| ... } -> changes
462
+ * db.batch_query_ary(sql, enumerable) { |rows| ... } -> changes
463
+ * db.batch_query_ary(sql, callable) { |rows| ... } -> changes
464
+ *
465
+ * Executes the given query for each list of parameters in the given paramter
466
+ * source. If a block is given, it is called with the resulting rows for each
467
+ * invocation of the query, and the total number of changes is returned.
468
+ * Otherwise, an array containing the resulting rows for each invocation is
469
+ * returned. Rows are represented as arrays.
470
+ *
471
+ * records = [
472
+ * [1, 2],
473
+ * [3, 4]
474
+ * ]
475
+ * db.batch_query_ary('insert into foo values (?, ?) returning bar, baz', records)
476
+ * #=> [[1, 2], [3, 4]]
477
+ * *
478
+ * @param sql [String] query SQL
479
+ * @param parameters [Array<Array, Hash>, Enumerable, Enumerator, Callable] parameters to run query with
480
+ * @return [Array<Array>, Integer] Total number of changes effected
481
+ */
482
+ VALUE Database_batch_query_ary(VALUE self, VALUE sql, VALUE parameters) {
483
+ Database_t *db = self_to_open_database(self);
484
+ sqlite3_stmt *stmt;
485
+
486
+ prepare_single_stmt(db->sqlite3_db, &stmt, sql);
487
+ query_ctx ctx = QUERY_CTX(self, db, stmt, parameters, QUERY_MULTI_ROW, ALL_ROWS);
359
488
 
360
- return rb_ensure(SAFE(safe_execute_multi), (VALUE)&ctx, SAFE(cleanup_stmt), (VALUE)&ctx);
489
+ return rb_ensure(SAFE(safe_batch_query_ary), (VALUE)&ctx, SAFE(cleanup_stmt), (VALUE)&ctx);
490
+ }
491
+
492
+ /* call-seq:
493
+ * db.batch_query_single_column(sql, params_array) -> rows
494
+ * db.batch_query_single_column(sql, enumerable) -> rows
495
+ * db.batch_query_single_column(sql, callable) -> rows
496
+ * db.batch_query_single_column(sql, params_array) { |rows| ... } -> changes
497
+ * db.batch_query_single_column(sql, enumerable) { |rows| ... } -> changes
498
+ * db.batch_query_single_column(sql, callable) { |rows| ... } -> changes
499
+ *
500
+ * Executes the given query for each list of parameters in the given paramter
501
+ * source. If a block is given, it is called with the resulting rows for each
502
+ * invocation of the query, and the total number of changes is returned.
503
+ * Otherwise, an array containing the resulting rows for each invocation is
504
+ * returned. Rows are single values.
505
+ *
506
+ * records = [
507
+ * [1, 2],
508
+ * [3, 4]
509
+ * ]
510
+ * db.batch_query_ary('insert into foo values (?, ?) returning baz', records)
511
+ * #=> [2, 4]
512
+ * *
513
+ * @param sql [String] query SQL
514
+ * @param parameters [Array<Array, Hash>, Enumerable, Enumerator, Callable] parameters to run query with
515
+ * @return [Array<any>, Integer] Total number of changes effected
516
+ */
517
+ VALUE Database_batch_query_single_column(VALUE self, VALUE sql, VALUE parameters) {
518
+ Database_t *db = self_to_open_database(self);
519
+ sqlite3_stmt *stmt;
520
+
521
+ prepare_single_stmt(db->sqlite3_db, &stmt, sql);
522
+ query_ctx ctx = QUERY_CTX(self, db, stmt, parameters, QUERY_MULTI_ROW, ALL_ROWS);
523
+
524
+ return rb_ensure(SAFE(safe_batch_query_single_column), (VALUE)&ctx, SAFE(cleanup_stmt), (VALUE)&ctx);
361
525
  }
362
526
 
363
527
  /* call-seq:
@@ -391,10 +555,10 @@ VALUE Database_changes(VALUE self) {
391
555
  return INT2FIX(sqlite3_changes(db->sqlite3_db));
392
556
  }
393
557
 
394
- /* call-seq:
395
- * db.filename -> string
558
+ /* call-seq: db.filename -> string db.filename(db_name) -> string
396
559
  *
397
- * Returns the database filename.
560
+ * Returns the database filename. If db_name is given, returns the filename for
561
+ * the respective attached database.
398
562
  */
399
563
  VALUE Database_filename(int argc, VALUE *argv, VALUE self) {
400
564
  const char *db_name;
@@ -441,8 +605,10 @@ VALUE Database_load_extension(VALUE self, VALUE path) {
441
605
 
442
606
  /* call-seq:
443
607
  * db.prepare(sql) -> Extralite::Query
608
+ * db.prepare(sql, ...) -> Extralite::Query
444
609
  *
445
- * Creates a prepared statement with the given SQL query.
610
+ * Creates a prepared statement with the given SQL query. If query parameters
611
+ * are given, they are bound to the query.
446
612
  */
447
613
  VALUE Database_prepare(int argc, VALUE *argv, VALUE self) {
448
614
  rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
@@ -480,13 +646,13 @@ typedef struct {
480
646
  #define BACKUP_STEP_MAX_PAGES 16
481
647
  #define BACKUP_SLEEP_MS 100
482
648
 
483
- void *backup_step_without_gvl(void *ptr) {
649
+ void *backup_step_impl(void *ptr) {
484
650
  backup_ctx *ctx = (backup_ctx *)ptr;
485
651
  ctx->rc = sqlite3_backup_step(ctx->backup, BACKUP_STEP_MAX_PAGES);
486
652
  return NULL;
487
653
  }
488
654
 
489
- void *backup_sleep_without_gvl(void *unused) {
655
+ void *backup_sleep_impl(void *unused) {
490
656
  sqlite3_sleep(BACKUP_SLEEP_MS);
491
657
  return NULL;
492
658
  }
@@ -496,7 +662,7 @@ VALUE backup_safe_iterate(VALUE ptr) {
496
662
  int done = 0;
497
663
 
498
664
  while (!done) {
499
- rb_thread_call_without_gvl(backup_step_without_gvl, (void *)ctx, RUBY_UBF_IO, 0);
665
+ gvl_call(GVL_RELEASE, backup_step_impl, (void *)ctx);
500
666
  switch(ctx->rc) {
501
667
  case SQLITE_DONE:
502
668
  if (ctx->block_given) {
@@ -514,7 +680,7 @@ VALUE backup_safe_iterate(VALUE ptr) {
514
680
  continue;
515
681
  case SQLITE_BUSY:
516
682
  case SQLITE_LOCKED:
517
- rb_thread_call_without_gvl(backup_sleep_without_gvl, NULL, RUBY_UBF_IO, 0);
683
+ gvl_call(GVL_RELEASE, backup_sleep_impl, NULL);
518
684
  continue;
519
685
  default:
520
686
  rb_raise(cError, "%s", sqlite3_errstr(ctx->rc));
@@ -689,7 +855,7 @@ VALUE Database_total_changes(VALUE self) {
689
855
  VALUE Database_trace(VALUE self) {
690
856
  Database_t *db = self_to_open_database(self);
691
857
 
692
- db->trace_block = rb_block_given_p() ? rb_block_proc() : Qnil;
858
+ RB_OBJ_WRITE(self, &db->trace_block, rb_block_given_p() ? rb_block_proc() : Qnil);
693
859
  return self;
694
860
  }
695
861
 
@@ -734,11 +900,51 @@ VALUE Database_error_offset(VALUE self) {
734
900
  * @return [String] string representation
735
901
  */
736
902
  VALUE Database_inspect(VALUE self) {
903
+ Database_t *db = self_to_database(self);
737
904
  VALUE cname = rb_class_name(CLASS_OF(self));
738
- VALUE filename = Database_filename(0, NULL, self);
739
- if (RSTRING_LEN(filename) == 0) filename = rb_str_new_literal(":memory:");
740
905
 
741
- return rb_sprintf("#<%"PRIsVALUE":%p %"PRIsVALUE">", cname, (void*)self, filename);
906
+ if (!(db)->sqlite3_db)
907
+ return rb_sprintf("#<%"PRIsVALUE":%p (closed)>", cname, (void*)self);
908
+ else {
909
+ VALUE filename = Database_filename(0, NULL, self);
910
+ if (RSTRING_LEN(filename) == 0) filename = rb_str_new_literal(":memory:");
911
+ return rb_sprintf("#<%"PRIsVALUE":%p %"PRIsVALUE">", cname, (void*)self, filename);
912
+ }
913
+ }
914
+
915
+ /* Returns the database's GVL release threshold.
916
+ *
917
+ * @return [Integer] GVL release threshold
918
+ */
919
+ VALUE Database_gvl_release_threshold_get(VALUE self) {
920
+ Database_t *db = self_to_open_database(self);
921
+ return INT2NUM(db->gvl_release_threshold);
922
+ }
923
+
924
+ /* Sets the database's GVL release threshold. To always hold the GVL while
925
+ * running a query, set the threshold to 0. To release the GVL on each record,
926
+ * set the threshold to 1. Larger values mean the GVL will be released less
927
+ * often, e.g. a value of 10 means the GVL will be released every 10 records
928
+ * iterated. A value of nil sets the threshold to the default value, which is
929
+ * currently 1000.
930
+ *
931
+ * @return [Integer, nil] New GVL release threshold
932
+ */
933
+ VALUE Database_gvl_release_threshold_set(VALUE self, VALUE value) {
934
+ Database_t *db = self_to_open_database(self);
935
+
936
+ switch (TYPE(value)) {
937
+ case T_FIXNUM:
938
+ db->gvl_release_threshold = NUM2INT(value);
939
+ break;
940
+ case T_NIL:
941
+ db->gvl_release_threshold = DEFAULT_GVL_RELEASE_THRESHOLD;
942
+ break;
943
+ default:
944
+ rb_raise(eArgumentError, "Invalid GVL release threshold value (expect integer or nil)");
945
+ }
946
+
947
+ return INT2NUM(db->gvl_release_threshold);
742
948
  }
743
949
 
744
950
  void Init_ExtraliteDatabase(void) {
@@ -763,8 +969,13 @@ void Init_ExtraliteDatabase(void) {
763
969
  #endif
764
970
 
765
971
  rb_define_method(cDatabase, "execute", Database_execute, -1);
766
- rb_define_method(cDatabase, "execute_multi", Database_execute_multi, 2);
972
+ rb_define_method(cDatabase, "batch_execute", Database_batch_execute, 2);
973
+ rb_define_method(cDatabase, "batch_query", Database_batch_query, 2);
974
+ rb_define_method(cDatabase, "batch_query_ary", Database_batch_query_ary, 2);
975
+ rb_define_method(cDatabase, "batch_query_single_column", Database_batch_query_single_column, 2);
767
976
  rb_define_method(cDatabase, "filename", Database_filename, -1);
977
+ rb_define_method(cDatabase, "gvl_release_threshold", Database_gvl_release_threshold_get, 0);
978
+ rb_define_method(cDatabase, "gvl_release_threshold=", Database_gvl_release_threshold_set, 1);
768
979
  rb_define_method(cDatabase, "initialize", Database_initialize, -1);
769
980
  rb_define_method(cDatabase, "inspect", Database_inspect, 0);
770
981
  rb_define_method(cDatabase, "interrupt", Database_interrupt, 0);
@@ -787,26 +998,32 @@ void Init_ExtraliteDatabase(void) {
787
998
  rb_define_method(cDatabase, "load_extension", Database_load_extension, 1);
788
999
  #endif
789
1000
 
1001
+ cBlob = rb_define_class_under(mExtralite, "Blob", rb_cString);
1002
+
790
1003
  cError = rb_define_class_under(mExtralite, "Error", rb_eStandardError);
791
1004
  cSQLError = rb_define_class_under(mExtralite, "SQLError", cError);
792
1005
  cBusyError = rb_define_class_under(mExtralite, "BusyError", cError);
793
1006
  cInterruptError = rb_define_class_under(mExtralite, "InterruptError", cError);
794
- rb_gc_register_mark_object(cError);
795
- rb_gc_register_mark_object(cSQLError);
796
- rb_gc_register_mark_object(cBusyError);
797
- rb_gc_register_mark_object(cInterruptError);
1007
+ cParameterError = rb_define_class_under(mExtralite, "ParameterError", cError);
798
1008
 
799
1009
  eArgumentError = rb_const_get(rb_cObject, rb_intern("ArgumentError"));
800
1010
 
801
1011
  ID_bind = rb_intern("bind");
802
1012
  ID_call = rb_intern("call");
1013
+ ID_each = rb_intern("each");
803
1014
  ID_keys = rb_intern("keys");
804
1015
  ID_new = rb_intern("new");
805
1016
  ID_strip = rb_intern("strip");
806
- ID_to_s = rb_intern("to_s");
807
1017
 
808
- SYM_read_only = ID2SYM(rb_intern("read_only"));
1018
+ SYM_gvl_release_threshold = ID2SYM(rb_intern("gvl_release_threshold"));
1019
+ SYM_read_only = ID2SYM(rb_intern("read_only"));
1020
+ SYM_synchronous = ID2SYM(rb_intern("synchronous"));
1021
+ SYM_wal_journal_mode = ID2SYM(rb_intern("wal_journal_mode"));
1022
+
1023
+ rb_gc_register_mark_object(SYM_gvl_release_threshold);
809
1024
  rb_gc_register_mark_object(SYM_read_only);
1025
+ rb_gc_register_mark_object(SYM_synchronous);
1026
+ rb_gc_register_mark_object(SYM_wal_journal_mode);
810
1027
 
811
1028
  UTF8_ENCODING = rb_utf8_encoding();
812
1029
  }
@@ -23,17 +23,19 @@
23
23
  extern VALUE cDatabase;
24
24
  extern VALUE cQuery;
25
25
  extern VALUE cIterator;
26
+ extern VALUE cBlob;
26
27
 
27
28
  extern VALUE cError;
28
29
  extern VALUE cSQLError;
29
30
  extern VALUE cBusyError;
30
31
  extern VALUE cInterruptError;
32
+ extern VALUE cParameterError;
31
33
 
32
34
  extern ID ID_call;
35
+ extern ID ID_each;
33
36
  extern ID ID_keys;
34
37
  extern ID ID_new;
35
38
  extern ID ID_strip;
36
- extern ID ID_to_s;
37
39
 
38
40
  extern VALUE SYM_hash;
39
41
  extern VALUE SYM_ary;
@@ -42,6 +44,7 @@ extern VALUE SYM_single_column;
42
44
  typedef struct {
43
45
  sqlite3 *sqlite3_db;
44
46
  VALUE trace_block;
47
+ int gvl_release_threshold;
45
48
  } Database_t;
46
49
 
47
50
  typedef struct {
@@ -79,23 +82,29 @@ typedef struct {
79
82
  enum query_mode mode;
80
83
  int max_rows;
81
84
  int eof;
85
+ int gvl_release_threshold;
86
+ int step_count;
82
87
  } query_ctx;
83
88
 
84
- typedef struct {
85
- VALUE dst;
86
- VALUE src;
87
- sqlite3_backup *p;
88
- } backup_t;
89
+ enum gvl_mode {
90
+ GVL_RELEASE,
91
+ GVL_HOLD
92
+ };
89
93
 
90
- #define TUPLE_MAX_EMBEDDED_VALUES 20
91
94
  #define ALL_ROWS -1
92
95
  #define SINGLE_ROW -2
93
96
  #define QUERY_MODE(default) (rb_block_given_p() ? QUERY_YIELD : default)
94
97
  #define MULTI_ROW_P(mode) (mode == QUERY_MULTI_ROW)
98
+ #define QUERY_CTX(self, db, stmt, params, mode, max_rows) \
99
+ { self, db->sqlite3_db, stmt, params, mode, max_rows, 0, db->gvl_release_threshold, 0 }
100
+ #define DEFAULT_GVL_RELEASE_THRESHOLD 1000
95
101
 
96
102
  extern rb_encoding *UTF8_ENCODING;
97
103
 
98
- VALUE safe_execute_multi(query_ctx *ctx);
104
+ VALUE safe_batch_execute(query_ctx *ctx);
105
+ VALUE safe_batch_query(query_ctx *ctx);
106
+ VALUE safe_batch_query_ary(query_ctx *ctx);
107
+ VALUE safe_batch_query_single_column(query_ctx *ctx);
99
108
  VALUE safe_query_ary(query_ctx *ctx);
100
109
  VALUE safe_query_changes(query_ctx *ctx);
101
110
  VALUE safe_query_columns(query_ctx *ctx);
@@ -126,4 +135,6 @@ VALUE cleanup_stmt(query_ctx *ctx);
126
135
  sqlite3 *Database_sqlite3_db(VALUE self);
127
136
  Database_t *self_to_database(VALUE self);
128
137
 
129
- #endif /* EXTRALITE_H */
138
+ void *gvl_call(enum gvl_mode mode, void *(*fn)(void *), void *data);
139
+
140
+ #endif /* EXTRALITE_H */
@@ -1,8 +1,12 @@
1
+ #include "ruby.h"
2
+
1
3
  void Init_ExtraliteDatabase();
2
4
  void Init_ExtraliteQuery();
3
5
  void Init_ExtraliteIterator();
4
6
 
5
7
  void Init_extralite_ext(void) {
8
+ rb_ext_ractor_safe(true);
9
+
6
10
  Init_ExtraliteDatabase();
7
11
  Init_ExtraliteQuery();
8
12
  Init_ExtraliteIterator();
@@ -52,7 +52,7 @@ static inline enum iterator_mode symbol_to_mode(VALUE sym) {
52
52
  * iteration mode is one of: `:hash`, `:ary`, or `:single_column`. An iterator
53
53
  * is normally returned from one of the methods `Query#each`/`Query#each_hash`,
54
54
  * `Query#each_ary` or `Query#each_single_column` when called without a block:
55
- *
55
+ *
56
56
  * iterator = query.each
57
57
  * ...
58
58
  *
@@ -115,12 +115,12 @@ inline next_method mode_to_next_method(enum iterator_mode mode) {
115
115
 
116
116
  /* Returns the next 1 or more rows from the associated query's result set
117
117
  * according to the iteration mode, i.e. as a hash, an array or a single value.
118
- *
118
+ *
119
119
  * If no row count is given, a single row is returned. If a row count is given,
120
120
  * an array containing up to the `row_count` rows is returned. If `row_count` is
121
121
  * -1, all rows are returned. If the end of the result set has been reached,
122
122
  * `nil` is returned.
123
- *
123
+ *
124
124
  * If a block is given, rows are passed to the block and self is returned.
125
125
  *
126
126
  * @overload next()
@@ -152,7 +152,7 @@ inline to_a_method mode_to_to_a_method(enum iterator_mode mode) {
152
152
 
153
153
  /* Returns all rows from the associated query's result set according to the
154
154
  * iteration mode, i.e. as a hash, an array or a single value.
155
- *
155
+ *
156
156
  * @return [Array] array of query result set rows
157
157
  */
158
158
  VALUE Iterator_to_a(VALUE self) {
@@ -180,7 +180,7 @@ inline VALUE mode_to_symbol(Iterator_t *iterator) {
180
180
  VALUE Iterator_inspect(VALUE self) {
181
181
  VALUE cname = rb_class_name(CLASS_OF(self));
182
182
  VALUE sym = mode_to_symbol(self_to_iterator(self));
183
-
183
+
184
184
  return rb_sprintf("#<%"PRIsVALUE":%p %"PRIsVALUE">", cname, (void*)self, sym);
185
185
  }
186
186