extralite-bundle 2.3 → 2.5

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