extralite-bundle 2.4 → 2.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/test-bundle.yml +30 -0
- data/.github/workflows/test.yml +2 -12
- data/CHANGELOG.md +39 -10
- data/Gemfile.lock +1 -1
- data/README.md +45 -11
- data/TODO.md +0 -3
- data/ext/extralite/common.c +222 -15
- data/ext/extralite/database.c +185 -16
- data/ext/extralite/extralite.h +5 -1
- data/ext/extralite/extralite_ext.c +4 -0
- data/ext/extralite/query.c +213 -12
- data/ext/sqlite3/sqlite3.c +5420 -2501
- data/ext/sqlite3/sqlite3.h +73 -18
- data/gemspec.rb +1 -1
- data/lib/extralite/version.rb +1 -1
- data/lib/extralite.rb +14 -6
- data/test/helper.rb +1 -0
- data/test/issue-54.rb +21 -0
- data/test/issue-59.rb +70 -0
- data/test/test_database.rb +471 -12
- data/test/test_query.rb +362 -2
- metadata +6 -3
data/ext/extralite/database.c
CHANGED
@@ -13,11 +13,15 @@ VALUE eArgumentError;
|
|
13
13
|
|
14
14
|
ID ID_bind;
|
15
15
|
ID ID_call;
|
16
|
+
ID ID_each;
|
16
17
|
ID ID_keys;
|
17
18
|
ID ID_new;
|
18
19
|
ID ID_strip;
|
19
20
|
|
21
|
+
VALUE SYM_gvl_release_threshold;
|
20
22
|
VALUE SYM_read_only;
|
23
|
+
VALUE SYM_synchronous;
|
24
|
+
VALUE SYM_wal_journal_mode;
|
21
25
|
|
22
26
|
static size_t Database_size(const void *ptr) {
|
23
27
|
return sizeof(Database_t);
|
@@ -25,7 +29,12 @@ static size_t Database_size(const void *ptr) {
|
|
25
29
|
|
26
30
|
static void Database_mark(void *ptr) {
|
27
31
|
Database_t *db = ptr;
|
28
|
-
|
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);
|
29
38
|
}
|
30
39
|
|
31
40
|
static void Database_free(void *ptr) {
|
@@ -36,7 +45,7 @@ static void Database_free(void *ptr) {
|
|
36
45
|
|
37
46
|
static const rb_data_type_t Database_type = {
|
38
47
|
"Database",
|
39
|
-
{Database_mark, Database_free, Database_size,},
|
48
|
+
{Database_mark, Database_free, Database_size, Database_compact},
|
40
49
|
0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
|
41
50
|
};
|
42
51
|
|
@@ -85,14 +94,40 @@ default_flags:
|
|
85
94
|
return SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
|
86
95
|
}
|
87
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
|
+
|
88
120
|
/* Initializes a new SQLite database with the given path and options.
|
89
121
|
*
|
90
122
|
* @overload initialize(path)
|
91
123
|
* @param path [String] file path (or ':memory:' for memory database)
|
92
124
|
* @return [void]
|
93
|
-
* @overload initialize(path, read_only:
|
125
|
+
* @overload initialize(path, gvl_release_threshold: , read_only: , synchronous: , wal_journal_mode: )
|
94
126
|
* @param path [String] file path (or ':memory:' for memory database)
|
127
|
+
* @param gvl_release_threshold [Integer] GVL release threshold
|
95
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
|
96
131
|
* @return [void]
|
97
132
|
*/
|
98
133
|
VALUE Database_initialize(int argc, VALUE *argv, VALUE self) {
|
@@ -127,6 +162,8 @@ VALUE Database_initialize(int argc, VALUE *argv, VALUE self) {
|
|
127
162
|
db->trace_block = Qnil;
|
128
163
|
db->gvl_release_threshold = DEFAULT_GVL_RELEASE_THRESHOLD;
|
129
164
|
|
165
|
+
if (!NIL_P(opts)) Database_apply_opts(self, db, opts);
|
166
|
+
|
130
167
|
return Qnil;
|
131
168
|
}
|
132
169
|
|
@@ -180,7 +217,6 @@ static inline VALUE Database_perform_query(int argc, VALUE *argv, VALUE self, VA
|
|
180
217
|
sql = rb_funcall(argv[0], ID_strip, 0);
|
181
218
|
if (RSTRING_LEN(sql) == 0) return Qnil;
|
182
219
|
|
183
|
-
// prepare query ctx
|
184
220
|
if (db->trace_block != Qnil) rb_funcall(db->trace_block, ID_call, 1, sql);
|
185
221
|
prepare_multi_stmt(db->sqlite3_db, &stmt, sql);
|
186
222
|
RB_GC_GUARD(sql);
|
@@ -342,30 +378,150 @@ VALUE Database_execute(int argc, VALUE *argv, VALUE self) {
|
|
342
378
|
}
|
343
379
|
|
344
380
|
/* call-seq:
|
345
|
-
* db.
|
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.
|
346
390
|
*
|
347
|
-
*
|
348
|
-
*
|
349
|
-
* multiple records.
|
391
|
+
* Returns the number of changes effected. This method is designed for inserting
|
392
|
+
* multiple records or performing other mass operations.
|
350
393
|
*
|
351
394
|
* records = [
|
352
395
|
* [1, 2, 3],
|
353
396
|
* [4, 5, 6]
|
354
397
|
* ]
|
355
|
-
* db.
|
398
|
+
* db.batch_execute('insert into foo values (?, ?, ?)', records)
|
356
399
|
*
|
400
|
+
* source = [
|
401
|
+
* [1, 2, 3],
|
402
|
+
* [4, 5, 6]
|
403
|
+
* ]
|
404
|
+
* db.batch_execute('insert into foo values (?, ?, ?)', -> { records.shift })
|
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
|
357
409
|
*/
|
358
|
-
VALUE
|
410
|
+
VALUE Database_batch_execute(VALUE self, VALUE sql, VALUE parameters) {
|
359
411
|
Database_t *db = self_to_open_database(self);
|
360
412
|
sqlite3_stmt *stmt;
|
361
413
|
|
362
414
|
if (RSTRING_LEN(sql) == 0) return Qnil;
|
363
415
|
|
364
|
-
// prepare query ctx
|
365
416
|
prepare_single_stmt(db->sqlite3_db, &stmt, sql);
|
366
|
-
query_ctx ctx = QUERY_CTX(self, db, stmt,
|
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);
|
367
488
|
|
368
|
-
return rb_ensure(SAFE(
|
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);
|
369
525
|
}
|
370
526
|
|
371
527
|
/* call-seq:
|
@@ -449,8 +605,10 @@ VALUE Database_load_extension(VALUE self, VALUE path) {
|
|
449
605
|
|
450
606
|
/* call-seq:
|
451
607
|
* db.prepare(sql) -> Extralite::Query
|
608
|
+
* db.prepare(sql, ...) -> Extralite::Query
|
452
609
|
*
|
453
|
-
* 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.
|
454
612
|
*/
|
455
613
|
VALUE Database_prepare(int argc, VALUE *argv, VALUE self) {
|
456
614
|
rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
|
@@ -811,7 +969,10 @@ void Init_ExtraliteDatabase(void) {
|
|
811
969
|
#endif
|
812
970
|
|
813
971
|
rb_define_method(cDatabase, "execute", Database_execute, -1);
|
814
|
-
rb_define_method(cDatabase, "
|
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);
|
815
976
|
rb_define_method(cDatabase, "filename", Database_filename, -1);
|
816
977
|
rb_define_method(cDatabase, "gvl_release_threshold", Database_gvl_release_threshold_get, 0);
|
817
978
|
rb_define_method(cDatabase, "gvl_release_threshold=", Database_gvl_release_threshold_set, 1);
|
@@ -849,12 +1010,20 @@ void Init_ExtraliteDatabase(void) {
|
|
849
1010
|
|
850
1011
|
ID_bind = rb_intern("bind");
|
851
1012
|
ID_call = rb_intern("call");
|
1013
|
+
ID_each = rb_intern("each");
|
852
1014
|
ID_keys = rb_intern("keys");
|
853
1015
|
ID_new = rb_intern("new");
|
854
1016
|
ID_strip = rb_intern("strip");
|
855
1017
|
|
856
|
-
|
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);
|
857
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);
|
858
1027
|
|
859
1028
|
UTF8_ENCODING = rb_utf8_encoding();
|
860
1029
|
}
|
data/ext/extralite/extralite.h
CHANGED
@@ -32,6 +32,7 @@ extern VALUE cInterruptError;
|
|
32
32
|
extern VALUE cParameterError;
|
33
33
|
|
34
34
|
extern ID ID_call;
|
35
|
+
extern ID ID_each;
|
35
36
|
extern ID ID_keys;
|
36
37
|
extern ID ID_new;
|
37
38
|
extern ID ID_strip;
|
@@ -100,7 +101,10 @@ enum gvl_mode {
|
|
100
101
|
|
101
102
|
extern rb_encoding *UTF8_ENCODING;
|
102
103
|
|
103
|
-
VALUE
|
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);
|
104
108
|
VALUE safe_query_ary(query_ctx *ctx);
|
105
109
|
VALUE safe_query_changes(query_ctx *ctx);
|
106
110
|
VALUE safe_query_columns(query_ctx *ctx);
|
@@ -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();
|
data/ext/extralite/query.c
CHANGED
@@ -20,8 +20,14 @@ static size_t Query_size(const void *ptr) {
|
|
20
20
|
|
21
21
|
static void Query_mark(void *ptr) {
|
22
22
|
Query_t *query = ptr;
|
23
|
-
|
24
|
-
|
23
|
+
rb_gc_mark_movable(query->db);
|
24
|
+
rb_gc_mark_movable(query->sql);
|
25
|
+
}
|
26
|
+
|
27
|
+
static void Query_compact(void *ptr) {
|
28
|
+
Query_t *query = ptr;
|
29
|
+
query->db = rb_gc_location(query->db);
|
30
|
+
query->sql = rb_gc_location(query->sql);
|
25
31
|
}
|
26
32
|
|
27
33
|
static void Query_free(void *ptr) {
|
@@ -32,7 +38,7 @@ static void Query_free(void *ptr) {
|
|
32
38
|
|
33
39
|
static const rb_data_type_t Query_type = {
|
34
40
|
"Query",
|
35
|
-
{Query_mark, Query_free, Query_size,},
|
41
|
+
{Query_mark, Query_free, Query_size, Query_compact},
|
36
42
|
0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
|
37
43
|
};
|
38
44
|
|
@@ -367,22 +373,66 @@ VALUE Query_execute(int argc, VALUE *argv, VALUE self) {
|
|
367
373
|
return Query_perform_next(self, ALL_ROWS, safe_query_changes);
|
368
374
|
}
|
369
375
|
|
370
|
-
/*
|
371
|
-
*
|
372
|
-
*
|
373
|
-
*
|
376
|
+
/* call-seq:
|
377
|
+
* query << [...] -> query
|
378
|
+
* query << { ... } -> query
|
379
|
+
*
|
380
|
+
* Runs the with the given parameters, returning the total changes effected.
|
381
|
+
* This method should be used for data- or schema-manipulation queries.
|
382
|
+
*
|
383
|
+
* Query parameters to be bound to placeholders in the query can be specified as
|
384
|
+
* a list of values or as a hash mapping parameter names to values. When
|
385
|
+
* parameters are given as an array, the query should specify parameters using
|
386
|
+
* `?`:
|
387
|
+
*
|
388
|
+
* query = db.prepare('update foo set x = ? where y = ?')
|
389
|
+
* query << [42, 43]
|
390
|
+
*
|
391
|
+
* Named placeholders are specified using `:`. The placeholder values are
|
392
|
+
* specified using a hash, where keys are either strings are symbols. String
|
393
|
+
* keys can include or omit the `:` prefix. The following are equivalent:
|
394
|
+
*
|
395
|
+
* query = db.prepare('update foo set x = :bar')
|
396
|
+
* query << { bar: 42 }
|
397
|
+
* query << { 'bar' => 42 }
|
398
|
+
* query << { ':bar' => 42 }
|
399
|
+
*/
|
400
|
+
VALUE Query_execute_chevrons(VALUE self, VALUE params) {
|
401
|
+
Query_execute(1, ¶ms, self);
|
402
|
+
return self;
|
403
|
+
}
|
404
|
+
|
405
|
+
/* call-seq:
|
406
|
+
* query.batch_execute(params_array) -> changes
|
407
|
+
* query.batch_execute(enumerable) -> changes
|
408
|
+
* query.batch_execute(callable) -> changes
|
409
|
+
*
|
410
|
+
* Executes the query for each set of parameters in the paramter source. If an
|
411
|
+
* enumerable is given, it is iterated and each of its values is used as the
|
412
|
+
* parameters for running the query. If a callable is given, it is called
|
413
|
+
* repeatedly and each of its return values is used as the parameters, until nil
|
414
|
+
* is returned.
|
415
|
+
*
|
416
|
+
* Returns the number of changes effected. This method is designed for inserting
|
417
|
+
* multiple records.
|
374
418
|
*
|
375
419
|
* query = db.prepare('insert into foo values (?, ?, ?)')
|
376
420
|
* records = [
|
377
421
|
* [1, 2, 3],
|
378
422
|
* [4, 5, 6]
|
379
423
|
* ]
|
380
|
-
* query.
|
424
|
+
* query.batch_execute(records)
|
425
|
+
*
|
426
|
+
* source = [
|
427
|
+
* [1, 2, 3],
|
428
|
+
* [4, 5, 6]
|
429
|
+
* ]
|
430
|
+
* query.batch_execute { records.shift }
|
381
431
|
*
|
382
|
-
* @param parameters [Array<Array, Hash
|
432
|
+
* @param parameters [Array<Array, Hash>, Enumerable, Enumerator, Callable] array of parameters to run query with
|
383
433
|
* @return [Integer] number of changes effected
|
384
434
|
*/
|
385
|
-
VALUE
|
435
|
+
VALUE Query_batch_execute(VALUE self, VALUE parameters) {
|
386
436
|
Query_t *query = self_to_query(self);
|
387
437
|
if (query->closed) rb_raise(cError, "Query is closed");
|
388
438
|
|
@@ -397,7 +447,139 @@ VALUE Query_execute_multi(VALUE self, VALUE parameters) {
|
|
397
447
|
QUERY_MODE(QUERY_MULTI_ROW),
|
398
448
|
ALL_ROWS
|
399
449
|
);
|
400
|
-
return
|
450
|
+
return safe_batch_execute(&ctx);
|
451
|
+
}
|
452
|
+
|
453
|
+
/* call-seq:
|
454
|
+
* query.batch_query(sql, params_array) -> rows
|
455
|
+
* query.batch_query(sql, enumerable) -> rows
|
456
|
+
* query.batch_query(sql, callable) -> rows
|
457
|
+
* query.batch_query(sql, params_array) { |rows| ... } -> changes
|
458
|
+
* query.batch_query(sql, enumerable) { |rows| ... } -> changes
|
459
|
+
* query.batch_query(sql, callable) { |rows| ... } -> changes
|
460
|
+
*
|
461
|
+
* Executes the prepared query for each list of parameters in the given paramter
|
462
|
+
* source. If a block is given, it is called with the resulting rows for each
|
463
|
+
* invocation of the query, and the total number of changes is returned.
|
464
|
+
* Otherwise, an array containing the resulting rows for each invocation is
|
465
|
+
* returned.
|
466
|
+
*
|
467
|
+
* q = db.prepare('insert into foo values (?, ?) returning bar, baz')
|
468
|
+
* records = [
|
469
|
+
* [1, 2],
|
470
|
+
* [3, 4]
|
471
|
+
* ]
|
472
|
+
* q.batch_query(records)
|
473
|
+
* #=> [{ bar: 1, baz: 2 }, { bar: 3, baz: 4}]
|
474
|
+
* *
|
475
|
+
* @param sql [String] query SQL
|
476
|
+
* @param parameters [Array<Array, Hash>, Enumerable, Enumerator, Callable] parameters to run query with
|
477
|
+
* @return [Array<Hash>, Integer] Total number of changes effected
|
478
|
+
*/
|
479
|
+
VALUE Query_batch_query(VALUE self, VALUE parameters) {
|
480
|
+
Query_t *query = self_to_query(self);
|
481
|
+
if (query->closed) rb_raise(cError, "Query is closed");
|
482
|
+
|
483
|
+
if (!query->stmt)
|
484
|
+
prepare_single_stmt(query->sqlite3_db, &query->stmt, query->sql);
|
485
|
+
|
486
|
+
query_ctx ctx = QUERY_CTX(
|
487
|
+
self,
|
488
|
+
query->db_struct,
|
489
|
+
query->stmt,
|
490
|
+
parameters,
|
491
|
+
QUERY_MODE(QUERY_MULTI_ROW),
|
492
|
+
ALL_ROWS
|
493
|
+
);
|
494
|
+
return safe_batch_query(&ctx);
|
495
|
+
}
|
496
|
+
|
497
|
+
/* call-seq:
|
498
|
+
* query.batch_query_ary(sql, params_array) -> rows
|
499
|
+
* query.batch_query_ary(sql, enumerable) -> rows
|
500
|
+
* query.batch_query_ary(sql, callable) -> rows
|
501
|
+
* query.batch_query_ary(sql, params_array) { |rows| ... } -> changes
|
502
|
+
* query.batch_query_ary(sql, enumerable) { |rows| ... } -> changes
|
503
|
+
* query.batch_query_ary(sql, callable) { |rows| ... } -> changes
|
504
|
+
*
|
505
|
+
* Executes the prepared query for each list of parameters in the given paramter
|
506
|
+
* source. If a block is given, it is called with the resulting rows for each
|
507
|
+
* invocation of the query, and the total number of changes is returned.
|
508
|
+
* Otherwise, an array containing the resulting rows for each invocation is
|
509
|
+
* returned. Rows are represented as arrays.
|
510
|
+
*
|
511
|
+
* q = db.prepare('insert into foo values (?, ?) returning bar, baz')
|
512
|
+
* records = [
|
513
|
+
* [1, 2],
|
514
|
+
* [3, 4]
|
515
|
+
* ]
|
516
|
+
* q.batch_query_ary(records)
|
517
|
+
* #=> [{ bar: 1, baz: 2 }, { bar: 3, baz: 4}]
|
518
|
+
* *
|
519
|
+
* @param sql [String] query SQL
|
520
|
+
* @param parameters [Array<Array, Hash>, Enumerable, Enumerator, Callable] parameters to run query with
|
521
|
+
* @return [Array<Hash>, Integer] Total number of changes effected
|
522
|
+
*/
|
523
|
+
VALUE Query_batch_query_ary(VALUE self, VALUE parameters) {
|
524
|
+
Query_t *query = self_to_query(self);
|
525
|
+
if (query->closed) rb_raise(cError, "Query is closed");
|
526
|
+
|
527
|
+
if (!query->stmt)
|
528
|
+
prepare_single_stmt(query->sqlite3_db, &query->stmt, query->sql);
|
529
|
+
|
530
|
+
query_ctx ctx = QUERY_CTX(
|
531
|
+
self,
|
532
|
+
query->db_struct,
|
533
|
+
query->stmt,
|
534
|
+
parameters,
|
535
|
+
QUERY_MODE(QUERY_MULTI_ROW),
|
536
|
+
ALL_ROWS
|
537
|
+
);
|
538
|
+
return safe_batch_query_ary(&ctx);
|
539
|
+
}
|
540
|
+
|
541
|
+
/* call-seq:
|
542
|
+
* query.batch_query_single_column(sql, params_array) -> rows
|
543
|
+
* query.batch_query_single_column(sql, enumerable) -> rows
|
544
|
+
* query.batch_query_single_column(sql, callable) -> rows
|
545
|
+
* query.batch_query_single_column(sql, params_array) { |rows| ... } -> changes
|
546
|
+
* query.batch_query_single_column(sql, enumerable) { |rows| ... } -> changes
|
547
|
+
* query.batch_query_single_column(sql, callable) { |rows| ... } -> changes
|
548
|
+
*
|
549
|
+
* Executes the prepared query for each list of parameters in the given paramter
|
550
|
+
* source. If a block is given, it is called with the resulting rows for each
|
551
|
+
* invocation of the query, and the total number of changes is returned.
|
552
|
+
* Otherwise, an array containing the resulting rows for each invocation is
|
553
|
+
* returned. Rows are represented as single values.
|
554
|
+
*
|
555
|
+
* q = db.prepare('insert into foo values (?, ?) returning bar, baz')
|
556
|
+
* records = [
|
557
|
+
* [1, 2],
|
558
|
+
* [3, 4]
|
559
|
+
* ]
|
560
|
+
* q.batch_query_single_column(records)
|
561
|
+
* #=> [{ bar: 1, baz: 2 }, { bar: 3, baz: 4}]
|
562
|
+
* *
|
563
|
+
* @param sql [String] query SQL
|
564
|
+
* @param parameters [Array<Array, Hash>, Enumerable, Enumerator, Callable] parameters to run query with
|
565
|
+
* @return [Array<Hash>, Integer] Total number of changes effected
|
566
|
+
*/
|
567
|
+
VALUE Query_batch_query_single_column(VALUE self, VALUE parameters) {
|
568
|
+
Query_t *query = self_to_query(self);
|
569
|
+
if (query->closed) rb_raise(cError, "Query is closed");
|
570
|
+
|
571
|
+
if (!query->stmt)
|
572
|
+
prepare_single_stmt(query->sqlite3_db, &query->stmt, query->sql);
|
573
|
+
|
574
|
+
query_ctx ctx = QUERY_CTX(
|
575
|
+
self,
|
576
|
+
query->db_struct,
|
577
|
+
query->stmt,
|
578
|
+
parameters,
|
579
|
+
QUERY_MODE(QUERY_MULTI_ROW),
|
580
|
+
ALL_ROWS
|
581
|
+
);
|
582
|
+
return safe_batch_query_single_column(&ctx);
|
401
583
|
}
|
402
584
|
|
403
585
|
/* Returns the database associated with the query.
|
@@ -431,6 +613,19 @@ VALUE Query_columns(VALUE self) {
|
|
431
613
|
return Query_perform_next(self, ALL_ROWS, safe_query_columns);
|
432
614
|
}
|
433
615
|
|
616
|
+
/* call-seq:
|
617
|
+
* query.clone -> copy
|
618
|
+
* query.dup -> copy
|
619
|
+
*
|
620
|
+
* Returns a new query instance for the same SQL as the original query.
|
621
|
+
*
|
622
|
+
* @return [Extralite::Query] copy of query
|
623
|
+
*/
|
624
|
+
VALUE Query_clone(VALUE self) {
|
625
|
+
Query_t *query = self_to_query(self);
|
626
|
+
return rb_funcall(cQuery, ID_new, 2, query->db, query->sql);
|
627
|
+
}
|
628
|
+
|
434
629
|
/* Closes the query. Attempting to run a closed query will raise an error.
|
435
630
|
*
|
436
631
|
* @return [Extralite::Query] self
|
@@ -509,8 +704,10 @@ void Init_ExtraliteQuery(void) {
|
|
509
704
|
rb_define_method(cQuery, "close", Query_close, 0);
|
510
705
|
rb_define_method(cQuery, "closed?", Query_closed_p, 0);
|
511
706
|
rb_define_method(cQuery, "columns", Query_columns, 0);
|
707
|
+
rb_define_method(cQuery, "clone", Query_clone, 0);
|
512
708
|
rb_define_method(cQuery, "database", Query_database, 0);
|
513
709
|
rb_define_method(cQuery, "db", Query_database, 0);
|
710
|
+
rb_define_method(cQuery, "dup", Query_clone, 0);
|
514
711
|
|
515
712
|
rb_define_method(cQuery, "each", Query_each_hash, 0);
|
516
713
|
rb_define_method(cQuery, "each_ary", Query_each_ary, 0);
|
@@ -519,7 +716,11 @@ void Init_ExtraliteQuery(void) {
|
|
519
716
|
|
520
717
|
rb_define_method(cQuery, "eof?", Query_eof_p, 0);
|
521
718
|
rb_define_method(cQuery, "execute", Query_execute, -1);
|
522
|
-
rb_define_method(cQuery, "
|
719
|
+
rb_define_method(cQuery, "<<", Query_execute_chevrons, 1);
|
720
|
+
rb_define_method(cQuery, "batch_execute", Query_batch_execute, 1);
|
721
|
+
rb_define_method(cQuery, "batch_query", Query_batch_query, 1);
|
722
|
+
rb_define_method(cQuery, "batch_query_ary", Query_batch_query_ary, 1);
|
723
|
+
rb_define_method(cQuery, "batch_query_single_column", Query_batch_query_single_column, 1);
|
523
724
|
rb_define_method(cQuery, "initialize", Query_initialize, 2);
|
524
725
|
rb_define_method(cQuery, "inspect", Query_inspect, 0);
|
525
726
|
|