extralite-bundle 2.4 → 2.6

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,9 +1,19 @@
1
+ #include "ruby.h"
2
+
1
3
  void Init_ExtraliteDatabase();
2
4
  void Init_ExtraliteQuery();
3
5
  void Init_ExtraliteIterator();
6
+ #ifdef EXTRALITE_ENABLE_CHANGESET
7
+ void Init_ExtraliteChangeset();
8
+ #endif
4
9
 
5
10
  void Init_extralite_ext(void) {
11
+ rb_ext_ractor_safe(true);
12
+
6
13
  Init_ExtraliteDatabase();
7
14
  Init_ExtraliteQuery();
8
15
  Init_ExtraliteIterator();
16
+ #ifdef EXTRALITE_ENABLE_CHANGESET
17
+ Init_ExtraliteChangeset();
18
+ #endif
9
19
  }
@@ -19,13 +19,18 @@ static size_t Iterator_size(const void *ptr) {
19
19
 
20
20
  static void Iterator_mark(void *ptr) {
21
21
  Iterator_t *iterator = ptr;
22
- rb_gc_mark(iterator->query);
22
+ rb_gc_mark_movable(iterator->query);
23
+ }
24
+
25
+ static void Iterator_compact(void *ptr) {
26
+ Iterator_t *iterator = ptr;
27
+ iterator->query = rb_gc_location(iterator->query);
23
28
  }
24
29
 
25
30
  static const rb_data_type_t Iterator_type = {
26
31
  "Iterator",
27
- {Iterator_mark, free, Iterator_size,},
28
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
32
+ {Iterator_mark, free, Iterator_size, Iterator_compact},
33
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
29
34
  };
30
35
 
31
36
  static VALUE Iterator_allocate(VALUE klass) {
@@ -14,14 +14,22 @@ VALUE cQuery;
14
14
  ID ID_inspect;
15
15
  ID ID_slice;
16
16
 
17
+ #define DB_GVL_MODE(query) Database_prepare_gvl_mode(query->db_struct)
18
+
17
19
  static size_t Query_size(const void *ptr) {
18
20
  return sizeof(Query_t);
19
21
  }
20
22
 
21
23
  static void Query_mark(void *ptr) {
22
24
  Query_t *query = ptr;
23
- rb_gc_mark(query->db);
24
- rb_gc_mark(query->sql);
25
+ rb_gc_mark_movable(query->db);
26
+ rb_gc_mark_movable(query->sql);
27
+ }
28
+
29
+ static void Query_compact(void *ptr) {
30
+ Query_t *query = ptr;
31
+ query->db = rb_gc_location(query->db);
32
+ query->sql = rb_gc_location(query->sql);
25
33
  }
26
34
 
27
35
  static void Query_free(void *ptr) {
@@ -32,7 +40,7 @@ static void Query_free(void *ptr) {
32
40
 
33
41
  static const rb_data_type_t Query_type = {
34
42
  "Query",
35
- {Query_mark, Query_free, Query_size,},
43
+ {Query_mark, Query_free, Query_size, Query_compact},
36
44
  0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
37
45
  };
38
46
 
@@ -82,19 +90,17 @@ VALUE Query_initialize(VALUE self, VALUE db, VALUE sql) {
82
90
 
83
91
  static inline void query_reset(Query_t *query) {
84
92
  if (!query->stmt)
85
- prepare_single_stmt(query->sqlite3_db, &query->stmt, query->sql);
86
- if (query->db_struct->trace_block != Qnil)
87
- rb_funcall(query->db_struct->trace_block, ID_call, 1, query->sql);
93
+ prepare_single_stmt(DB_GVL_MODE(query), query->sqlite3_db, &query->stmt, query->sql);
94
+ TRACE_SQL(query->db_struct, query->sql);
88
95
  sqlite3_reset(query->stmt);
89
96
  query->eof = 0;
90
97
  }
91
98
 
92
99
  static inline void query_reset_and_bind(Query_t *query, int argc, VALUE * argv) {
93
100
  if (!query->stmt)
94
- prepare_single_stmt(query->sqlite3_db, &query->stmt, query->sql);
101
+ prepare_single_stmt(DB_GVL_MODE(query), query->sqlite3_db, &query->stmt, query->sql);
95
102
 
96
- if (query->db_struct->trace_block != Qnil)
97
- rb_funcall(query->db_struct->trace_block, ID_call, 1, query->sql);
103
+ TRACE_SQL(query->db_struct, query->sql);
98
104
 
99
105
  sqlite3_reset(query->stmt);
100
106
  query->eof = 0;
@@ -122,8 +128,7 @@ VALUE Query_reset(VALUE self) {
122
128
  if (query->closed) rb_raise(cError, "Query is closed");
123
129
 
124
130
  query_reset(query);
125
- if (query->db_struct->trace_block != Qnil)
126
- rb_funcall(query->db_struct->trace_block, ID_call, 1, query->sql);
131
+ TRACE_SQL(query->db_struct, query->sql);
127
132
 
128
133
  return self;
129
134
  }
@@ -367,27 +372,115 @@ VALUE Query_execute(int argc, VALUE *argv, VALUE self) {
367
372
  return Query_perform_next(self, ALL_ROWS, safe_query_changes);
368
373
  }
369
374
 
370
- /* Executes the query for each set of parameters in the given array. Parameters
371
- * can be specified as either an array (for unnamed parameters) or a hash (for
372
- * named parameters). Returns the number of changes effected. This method is
373
- * designed for inserting multiple records.
375
+ /* call-seq:
376
+ * query << [...] -> query
377
+ * query << { ... } -> query
378
+ *
379
+ * Runs the with the given parameters, returning the total changes effected.
380
+ * This method should be used for data- or schema-manipulation queries.
381
+ *
382
+ * Query parameters to be bound to placeholders in the query can be specified as
383
+ * a list of values or as a hash mapping parameter names to values. When
384
+ * parameters are given as an array, the query should specify parameters using
385
+ * `?`:
386
+ *
387
+ * query = db.prepare('update foo set x = ? where y = ?')
388
+ * query << [42, 43]
389
+ *
390
+ * Named placeholders are specified using `:`. The placeholder values are
391
+ * specified using a hash, where keys are either strings are symbols. String
392
+ * keys can include or omit the `:` prefix. The following are equivalent:
393
+ *
394
+ * query = db.prepare('update foo set x = :bar')
395
+ * query << { bar: 42 }
396
+ * query << { 'bar' => 42 }
397
+ * query << { ':bar' => 42 }
398
+ */
399
+ VALUE Query_execute_chevrons(VALUE self, VALUE params) {
400
+ Query_execute(1, &params, self);
401
+ return self;
402
+ }
403
+
404
+ /* call-seq:
405
+ * query.batch_execute(params_array) -> changes
406
+ * query.batch_execute(enumerable) -> changes
407
+ * query.batch_execute(callable) -> changes
408
+ *
409
+ * Executes the query for each set of parameters in the paramter source. If an
410
+ * enumerable is given, it is iterated and each of its values is used as the
411
+ * parameters for running the query. If a callable is given, it is called
412
+ * repeatedly and each of its return values is used as the parameters, until nil
413
+ * is returned.
414
+ *
415
+ * Returns the number of changes effected. This method is designed for inserting
416
+ * multiple records.
374
417
  *
375
418
  * query = db.prepare('insert into foo values (?, ?, ?)')
376
419
  * records = [
377
420
  * [1, 2, 3],
378
421
  * [4, 5, 6]
379
422
  * ]
380
- * query.execute_multi(records)
423
+ * query.batch_execute(records)
424
+ *
425
+ * source = [
426
+ * [1, 2, 3],
427
+ * [4, 5, 6]
428
+ * ]
429
+ * query.batch_execute { records.shift }
381
430
  *
382
- * @param parameters [Array<Array, Hash>] array of parameters to run query with
431
+ * @param parameters [Array<Array, Hash>, Enumerable, Enumerator, Callable] array of parameters to run query with
383
432
  * @return [Integer] number of changes effected
384
433
  */
385
- VALUE Query_execute_multi(VALUE self, VALUE parameters) {
434
+ VALUE Query_batch_execute(VALUE self, VALUE parameters) {
435
+ Query_t *query = self_to_query(self);
436
+ if (query->closed) rb_raise(cError, "Query is closed");
437
+
438
+ if (!query->stmt)
439
+ prepare_single_stmt(DB_GVL_MODE(query), query->sqlite3_db, &query->stmt, query->sql);
440
+
441
+ query_ctx ctx = QUERY_CTX(
442
+ self,
443
+ query->db_struct,
444
+ query->stmt,
445
+ parameters,
446
+ QUERY_MODE(QUERY_MULTI_ROW),
447
+ ALL_ROWS
448
+ );
449
+ return safe_batch_execute(&ctx);
450
+ }
451
+
452
+ /* call-seq:
453
+ * query.batch_query(sql, params_array) -> rows
454
+ * query.batch_query(sql, enumerable) -> rows
455
+ * query.batch_query(sql, callable) -> rows
456
+ * query.batch_query(sql, params_array) { |rows| ... } -> changes
457
+ * query.batch_query(sql, enumerable) { |rows| ... } -> changes
458
+ * query.batch_query(sql, callable) { |rows| ... } -> changes
459
+ *
460
+ * Executes the prepared query for each list of parameters in the given paramter
461
+ * source. If a block is given, it is called with the resulting rows for each
462
+ * invocation of the query, and the total number of changes is returned.
463
+ * Otherwise, an array containing the resulting rows for each invocation is
464
+ * returned.
465
+ *
466
+ * q = db.prepare('insert into foo values (?, ?) returning bar, baz')
467
+ * records = [
468
+ * [1, 2],
469
+ * [3, 4]
470
+ * ]
471
+ * q.batch_query(records)
472
+ * #=> [{ bar: 1, baz: 2 }, { bar: 3, baz: 4}]
473
+ * *
474
+ * @param sql [String] query SQL
475
+ * @param parameters [Array<Array, Hash>, Enumerable, Enumerator, Callable] parameters to run query with
476
+ * @return [Array<Hash>, Integer] Total number of changes effected
477
+ */
478
+ VALUE Query_batch_query(VALUE self, VALUE parameters) {
386
479
  Query_t *query = self_to_query(self);
387
480
  if (query->closed) rb_raise(cError, "Query is closed");
388
481
 
389
482
  if (!query->stmt)
390
- prepare_single_stmt(query->sqlite3_db, &query->stmt, query->sql);
483
+ prepare_single_stmt(DB_GVL_MODE(query), query->sqlite3_db, &query->stmt, query->sql);
391
484
 
392
485
  query_ctx ctx = QUERY_CTX(
393
486
  self,
@@ -397,7 +490,95 @@ VALUE Query_execute_multi(VALUE self, VALUE parameters) {
397
490
  QUERY_MODE(QUERY_MULTI_ROW),
398
491
  ALL_ROWS
399
492
  );
400
- return safe_execute_multi(&ctx);
493
+ return safe_batch_query(&ctx);
494
+ }
495
+
496
+ /* call-seq:
497
+ * query.batch_query_ary(sql, params_array) -> rows
498
+ * query.batch_query_ary(sql, enumerable) -> rows
499
+ * query.batch_query_ary(sql, callable) -> rows
500
+ * query.batch_query_ary(sql, params_array) { |rows| ... } -> changes
501
+ * query.batch_query_ary(sql, enumerable) { |rows| ... } -> changes
502
+ * query.batch_query_ary(sql, callable) { |rows| ... } -> changes
503
+ *
504
+ * Executes the prepared query for each list of parameters in the given paramter
505
+ * source. If a block is given, it is called with the resulting rows for each
506
+ * invocation of the query, and the total number of changes is returned.
507
+ * Otherwise, an array containing the resulting rows for each invocation is
508
+ * returned. Rows are represented as arrays.
509
+ *
510
+ * q = db.prepare('insert into foo values (?, ?) returning bar, baz')
511
+ * records = [
512
+ * [1, 2],
513
+ * [3, 4]
514
+ * ]
515
+ * q.batch_query_ary(records)
516
+ * #=> [{ bar: 1, baz: 2 }, { bar: 3, baz: 4}]
517
+ * *
518
+ * @param sql [String] query SQL
519
+ * @param parameters [Array<Array, Hash>, Enumerable, Enumerator, Callable] parameters to run query with
520
+ * @return [Array<Hash>, Integer] Total number of changes effected
521
+ */
522
+ VALUE Query_batch_query_ary(VALUE self, VALUE parameters) {
523
+ Query_t *query = self_to_query(self);
524
+ if (query->closed) rb_raise(cError, "Query is closed");
525
+
526
+ if (!query->stmt)
527
+ prepare_single_stmt(DB_GVL_MODE(query), query->sqlite3_db, &query->stmt, query->sql);
528
+
529
+ query_ctx ctx = QUERY_CTX(
530
+ self,
531
+ query->db_struct,
532
+ query->stmt,
533
+ parameters,
534
+ QUERY_MODE(QUERY_MULTI_ROW),
535
+ ALL_ROWS
536
+ );
537
+ return safe_batch_query_ary(&ctx);
538
+ }
539
+
540
+ /* call-seq:
541
+ * query.batch_query_single_column(sql, params_array) -> rows
542
+ * query.batch_query_single_column(sql, enumerable) -> rows
543
+ * query.batch_query_single_column(sql, callable) -> rows
544
+ * query.batch_query_single_column(sql, params_array) { |rows| ... } -> changes
545
+ * query.batch_query_single_column(sql, enumerable) { |rows| ... } -> changes
546
+ * query.batch_query_single_column(sql, callable) { |rows| ... } -> changes
547
+ *
548
+ * Executes the prepared query for each list of parameters in the given paramter
549
+ * source. If a block is given, it is called with the resulting rows for each
550
+ * invocation of the query, and the total number of changes is returned.
551
+ * Otherwise, an array containing the resulting rows for each invocation is
552
+ * returned. Rows are represented as single values.
553
+ *
554
+ * q = db.prepare('insert into foo values (?, ?) returning bar, baz')
555
+ * records = [
556
+ * [1, 2],
557
+ * [3, 4]
558
+ * ]
559
+ * q.batch_query_single_column(records)
560
+ * #=> [{ bar: 1, baz: 2 }, { bar: 3, baz: 4}]
561
+ * *
562
+ * @param sql [String] query SQL
563
+ * @param parameters [Array<Array, Hash>, Enumerable, Enumerator, Callable] parameters to run query with
564
+ * @return [Array<Hash>, Integer] Total number of changes effected
565
+ */
566
+ VALUE Query_batch_query_single_column(VALUE self, VALUE parameters) {
567
+ Query_t *query = self_to_query(self);
568
+ if (query->closed) rb_raise(cError, "Query is closed");
569
+
570
+ if (!query->stmt)
571
+ prepare_single_stmt(DB_GVL_MODE(query), query->sqlite3_db, &query->stmt, query->sql);
572
+
573
+ query_ctx ctx = QUERY_CTX(
574
+ self,
575
+ query->db_struct,
576
+ query->stmt,
577
+ parameters,
578
+ QUERY_MODE(QUERY_MULTI_ROW),
579
+ ALL_ROWS
580
+ );
581
+ return safe_batch_query_single_column(&ctx);
401
582
  }
402
583
 
403
584
  /* Returns the database associated with the query.
@@ -431,6 +612,19 @@ VALUE Query_columns(VALUE self) {
431
612
  return Query_perform_next(self, ALL_ROWS, safe_query_columns);
432
613
  }
433
614
 
615
+ /* call-seq:
616
+ * query.clone -> copy
617
+ * query.dup -> copy
618
+ *
619
+ * Returns a new query instance for the same SQL as the original query.
620
+ *
621
+ * @return [Extralite::Query] copy of query
622
+ */
623
+ VALUE Query_clone(VALUE self) {
624
+ Query_t *query = self_to_query(self);
625
+ return rb_funcall(cQuery, ID_new, 2, query->db, query->sql);
626
+ }
627
+
434
628
  /* Closes the query. Attempting to run a closed query will raise an error.
435
629
  *
436
630
  * @return [Extralite::Query] self
@@ -475,7 +669,7 @@ VALUE Query_status(int argc, VALUE* argv, VALUE self) {
475
669
  if (query->closed) rb_raise(cError, "Query is closed");
476
670
 
477
671
  if (!query->stmt)
478
- prepare_single_stmt(query->sqlite3_db, &query->stmt, query->sql);
672
+ prepare_single_stmt(DB_GVL_MODE(query), query->sqlite3_db, &query->stmt, query->sql);
479
673
 
480
674
  int value = sqlite3_stmt_status(query->stmt, NUM2INT(op), RTEST(reset) ? 1 : 0);
481
675
  return INT2NUM(value);
@@ -509,8 +703,10 @@ void Init_ExtraliteQuery(void) {
509
703
  rb_define_method(cQuery, "close", Query_close, 0);
510
704
  rb_define_method(cQuery, "closed?", Query_closed_p, 0);
511
705
  rb_define_method(cQuery, "columns", Query_columns, 0);
706
+ rb_define_method(cQuery, "clone", Query_clone, 0);
512
707
  rb_define_method(cQuery, "database", Query_database, 0);
513
708
  rb_define_method(cQuery, "db", Query_database, 0);
709
+ rb_define_method(cQuery, "dup", Query_clone, 0);
514
710
 
515
711
  rb_define_method(cQuery, "each", Query_each_hash, 0);
516
712
  rb_define_method(cQuery, "each_ary", Query_each_ary, 0);
@@ -519,7 +715,11 @@ void Init_ExtraliteQuery(void) {
519
715
 
520
716
  rb_define_method(cQuery, "eof?", Query_eof_p, 0);
521
717
  rb_define_method(cQuery, "execute", Query_execute, -1);
522
- rb_define_method(cQuery, "execute_multi", Query_execute_multi, 1);
718
+ rb_define_method(cQuery, "<<", Query_execute_chevrons, 1);
719
+ rb_define_method(cQuery, "batch_execute", Query_batch_execute, 1);
720
+ rb_define_method(cQuery, "batch_query", Query_batch_query, 1);
721
+ rb_define_method(cQuery, "batch_query_ary", Query_batch_query_ary, 1);
722
+ rb_define_method(cQuery, "batch_query_single_column", Query_batch_query_single_column, 1);
523
723
  rb_define_method(cQuery, "initialize", Query_initialize, 2);
524
724
  rb_define_method(cQuery, "inspect", Query_inspect, 0);
525
725