extralite-bundle 2.4 → 2.6

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