extralite-bundle 3.0.1 → 3.1.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/README.md +10 -1
- data/Rakefile +2 -2
- data/TODO.md +2 -2
- data/examples/pubsub_store_polyphony.rb +1 -1
- data/ext/extralite/common.c +180 -79
- data/ext/extralite/database.c +81 -30
- data/ext/extralite/extralite.h +39 -17
- data/ext/extralite/iterator.c +1 -1
- data/ext/extralite/query.c +11 -11
- data/ext/sqlite3/sqlite3.c +88 -52
- data/ext/sqlite3/sqlite3.h +5 -5
- data/lib/extralite/version.rb +3 -2
- data/lib/extralite.rb +2 -1
- data/lib/sequel/adapters/extralite.rb +22 -22
- data/test/test_database.rb +239 -15
- data/test/test_query.rb +10 -9
- data/test/test_sequel.rb +3 -3
- data/test/test_trace.rb +3 -3
- metadata +1 -1
data/ext/extralite/database.c
CHANGED
|
@@ -19,6 +19,7 @@ VALUE cParameterError;
|
|
|
19
19
|
VALUE eArgumentError;
|
|
20
20
|
|
|
21
21
|
ID ID_bind;
|
|
22
|
+
ID ID_inspect;
|
|
22
23
|
ID ID_call;
|
|
23
24
|
ID ID_each;
|
|
24
25
|
ID ID_keys;
|
|
@@ -38,6 +39,7 @@ VALUE SYM_normal;
|
|
|
38
39
|
VALUE SYM_passive;
|
|
39
40
|
VALUE SYM_read_only;
|
|
40
41
|
VALUE SYM_restart;
|
|
42
|
+
VALUE SYM_stmt_cache;
|
|
41
43
|
VALUE SYM_truncate;
|
|
42
44
|
VALUE SYM_wal;
|
|
43
45
|
|
|
@@ -58,12 +60,14 @@ static size_t Database_size(const void *ptr) {
|
|
|
58
60
|
|
|
59
61
|
static void Database_mark(void *ptr) {
|
|
60
62
|
Database_t *db = ptr;
|
|
63
|
+
rb_gc_mark_movable(db->stmt_cache);
|
|
61
64
|
rb_gc_mark_movable(db->trace_proc);
|
|
62
65
|
rb_gc_mark_movable(db->progress_handler.proc);
|
|
63
66
|
}
|
|
64
67
|
|
|
65
68
|
static void Database_compact(void *ptr) {
|
|
66
69
|
Database_t *db = ptr;
|
|
70
|
+
db->stmt_cache = rb_gc_location(db->stmt_cache);
|
|
67
71
|
db->trace_proc = rb_gc_location(db->trace_proc);
|
|
68
72
|
db->progress_handler.proc = rb_gc_location(db->progress_handler.proc);
|
|
69
73
|
}
|
|
@@ -83,6 +87,7 @@ static const rb_data_type_t Database_type = {
|
|
|
83
87
|
static VALUE Database_allocate(VALUE klass) {
|
|
84
88
|
Database_t *db = ALLOC(Database_t);
|
|
85
89
|
db->sqlite3_db = NULL;
|
|
90
|
+
db->stmt_cache = Qnil;
|
|
86
91
|
db->trace_proc = Qnil;
|
|
87
92
|
db->progress_handler.proc = Qnil;
|
|
88
93
|
db->progress_handler.mode = PROGRESS_NONE;
|
|
@@ -127,12 +132,17 @@ default_flags:
|
|
|
127
132
|
}
|
|
128
133
|
|
|
129
134
|
void Database_apply_opts(VALUE self, Database_t *db, VALUE opts) {
|
|
135
|
+
db->flags = DB_F_STMT_CACHE;
|
|
130
136
|
if (NIL_P(opts)) goto modern_pragmas;
|
|
131
137
|
|
|
132
138
|
// :gvl_release_threshold
|
|
133
139
|
VALUE value = rb_hash_aref(opts, SYM_gvl_release_threshold);
|
|
134
140
|
if (!NIL_P(value)) db->gvl_release_threshold = NUM2INT(value);
|
|
135
141
|
|
|
142
|
+
value = rb_hash_aref(opts, SYM_stmt_cache);
|
|
143
|
+
if (value == Qfalse)
|
|
144
|
+
db->flags &= !DB_F_STMT_CACHE;
|
|
145
|
+
|
|
136
146
|
value = rb_hash_aref(opts, SYM_legacy);
|
|
137
147
|
if (RTEST(value)) return;
|
|
138
148
|
|
|
@@ -175,9 +185,9 @@ int Database_busy_handler(void *ptr, int v) {
|
|
|
175
185
|
* `#gvl_release_threshold=`).
|
|
176
186
|
* - `:read_only` (`true`/`false`): opens the database in read-only mode if true.
|
|
177
187
|
* - `:legacy` (`true`/`false`): By default the database is set up for
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
188
|
+
* concurrent access with [WAL journaling mode](https://www.sqlite.org/wal.html).
|
|
189
|
+
* To prevent Extralite from setting up WAL journaling, set this option to true.
|
|
190
|
+
* - `:stmt_cache`: true by default, set to false to disable stmt caching
|
|
181
191
|
*
|
|
182
192
|
* @overload initialize(path)
|
|
183
193
|
* @param path [String] file path (or ':memory:' for memory database)
|
|
@@ -215,6 +225,7 @@ VALUE Database_initialize(int argc, VALUE *argv, VALUE self) {
|
|
|
215
225
|
sqlite3_db_config(db->sqlite3_db ,SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 1, NULL);
|
|
216
226
|
#endif
|
|
217
227
|
|
|
228
|
+
db->stmt_cache = rb_hash_new();
|
|
218
229
|
db->trace_proc = Qnil;
|
|
219
230
|
db->gvl_release_threshold = DEFAULT_GVL_RELEASE_THRESHOLD;
|
|
220
231
|
|
|
@@ -277,12 +288,13 @@ inline enum gvl_mode Database_prepare_gvl_mode(Database_t *db) {
|
|
|
277
288
|
|
|
278
289
|
static inline VALUE Database_perform_query(int argc, VALUE *argv, VALUE self, VALUE (*call)(query_ctx *), enum query_mode query_mode) {
|
|
279
290
|
Database_t *db = self_to_open_database(self);
|
|
280
|
-
sqlite3_stmt *stmt;
|
|
291
|
+
sqlite3_stmt *stmt = NULL;
|
|
281
292
|
VALUE sql = Qnil;
|
|
282
293
|
VALUE transform = Qnil;
|
|
283
294
|
// transform mode is set and the first parameter is not a string, so we expect
|
|
284
295
|
// a transform.,
|
|
285
296
|
int got_transform = (TYPE(argv[0]) != T_STRING);
|
|
297
|
+
int execute_mode = query_mode == QUERY_VOID;
|
|
286
298
|
|
|
287
299
|
// extract query from args
|
|
288
300
|
rb_check_arity(argc, got_transform ? 2 : 1, UNLIMITED_ARGUMENTS);
|
|
@@ -293,26 +305,38 @@ static inline VALUE Database_perform_query(int argc, VALUE *argv, VALUE self, VA
|
|
|
293
305
|
argv++;
|
|
294
306
|
}
|
|
295
307
|
|
|
296
|
-
sql =
|
|
297
|
-
if (RSTRING_LEN(sql) == 0) return Qnil;
|
|
298
|
-
// sql = argv[0];
|
|
308
|
+
sql = argv[0];
|
|
299
309
|
|
|
300
|
-
|
|
301
|
-
|
|
310
|
+
stmt_ctx stmt_ctx;
|
|
311
|
+
make_stmt_ctx(&stmt_ctx, db, &stmt, sql, argc - 1, argv + 1);
|
|
302
312
|
|
|
303
|
-
if (stmt == NULL) return Qnil;
|
|
304
|
-
|
|
305
|
-
bind_all_parameters(stmt, argc - 1, argv + 1);
|
|
306
313
|
Database_pre_query_hook(db, stmt, sql, argc - 1, argv + 1);
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
314
|
+
if (execute_mode) {
|
|
315
|
+
exec_multi_stmt(&stmt_ctx);
|
|
316
|
+
|
|
317
|
+
query_ctx ctx = QUERY_CTX(
|
|
318
|
+
self, sql, db, NULL, Qnil, transform,
|
|
319
|
+
query_mode, ROW_YIELD_OR_MODE(ROW_MULTI), ALL_ROWS
|
|
320
|
+
);
|
|
321
|
+
ctx.flags = stmt_ctx.flags;
|
|
322
|
+
ctx.total_changes = stmt_ctx.total_changes;
|
|
323
|
+
VALUE result = (VALUE)call(&ctx);
|
|
324
|
+
return result;
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
prep_single_stmt(&stmt_ctx);
|
|
328
|
+
query_ctx ctx = QUERY_CTX(
|
|
329
|
+
self, sql, db, stmt, Qnil, transform,
|
|
330
|
+
query_mode, ROW_YIELD_OR_MODE(ROW_MULTI), ALL_ROWS
|
|
331
|
+
);
|
|
332
|
+
ctx.flags = stmt_ctx.flags;
|
|
333
|
+
if (!stmt) return Qnil;
|
|
334
|
+
|
|
335
|
+
bind_all_parameters(stmt, argc - 1, argv + 1);
|
|
336
|
+
VALUE result = rb_ensure(SAFE(call), (VALUE)&ctx, SAFE(cleanup_stmt), (VALUE)&ctx);
|
|
337
|
+
RB_GC_GUARD(result);
|
|
338
|
+
return result;
|
|
339
|
+
}
|
|
316
340
|
}
|
|
317
341
|
|
|
318
342
|
/* call-seq:
|
|
@@ -490,7 +514,9 @@ VALUE Database_query_single_array(int argc, VALUE *argv, VALUE self) {
|
|
|
490
514
|
* db.execute(sql, *parameters) -> changes
|
|
491
515
|
*
|
|
492
516
|
* Runs a query returning the total changes effected. This method should be used
|
|
493
|
-
* for data- or schema-manipulation queries.
|
|
517
|
+
* for data- or schema-manipulation queries. If the given SQL string contains
|
|
518
|
+
* multiple statements, they will be executed in the order given. If multiple
|
|
519
|
+
* statements are given along with query parameters, an error is raised.
|
|
494
520
|
*
|
|
495
521
|
* Query parameters to be bound to placeholders in the query can be specified as
|
|
496
522
|
* a list of values or as a hash mapping parameter names to values. When
|
|
@@ -506,10 +532,10 @@ VALUE Database_query_single_array(int argc, VALUE *argv, VALUE self) {
|
|
|
506
532
|
*
|
|
507
533
|
* @param sql [String] query SQL
|
|
508
534
|
* @param parameters [Array, Hash] parameters to run query with
|
|
509
|
-
* @return [Integer
|
|
535
|
+
* @return [Integer] Total number of changes effected
|
|
510
536
|
*/
|
|
511
537
|
VALUE Database_execute(int argc, VALUE *argv, VALUE self) {
|
|
512
|
-
return Database_perform_query(argc, argv, self,
|
|
538
|
+
return Database_perform_query(argc, argv, self, safe_total_changes, QUERY_VOID);
|
|
513
539
|
}
|
|
514
540
|
|
|
515
541
|
/* call-seq:
|
|
@@ -546,7 +572,10 @@ VALUE Database_batch_execute(VALUE self, VALUE sql, VALUE parameters) {
|
|
|
546
572
|
|
|
547
573
|
if (RSTRING_LEN(sql) == 0) return Qnil;
|
|
548
574
|
|
|
549
|
-
|
|
575
|
+
stmt_ctx stmt_ctx;
|
|
576
|
+
make_stmt_ctx(&stmt_ctx, db, &stmt, sql, RARRAY_LEN(parameters), NULL);
|
|
577
|
+
prep_single_stmt(&stmt_ctx);
|
|
578
|
+
// prepare_xsingle_stmt(DB_GVL_MODE(db), db->stmt_cache, db->sqlite3_db, &stmt, sql, 0, NULL);
|
|
550
579
|
query_ctx ctx = QUERY_CTX(
|
|
551
580
|
self, sql, db, stmt, parameters,
|
|
552
581
|
Qnil, QUERY_HASH, ROW_MULTI, ALL_ROWS
|
|
@@ -582,7 +611,10 @@ VALUE Database_batch_query(VALUE self, VALUE sql, VALUE parameters) {
|
|
|
582
611
|
Database_t *db = self_to_open_database(self);
|
|
583
612
|
sqlite3_stmt *stmt;
|
|
584
613
|
|
|
585
|
-
|
|
614
|
+
stmt_ctx stmt_ctx;
|
|
615
|
+
make_stmt_ctx(&stmt_ctx, db, &stmt, sql, RARRAY_LEN(parameters), NULL);
|
|
616
|
+
prep_single_stmt(&stmt_ctx);
|
|
617
|
+
// prepare_xsingle_stmt(DB_GVL_MODE(db), db->stmt_cache, db->sqlite3_db, &stmt, sql, 0, NULL);
|
|
586
618
|
query_ctx ctx = QUERY_CTX(
|
|
587
619
|
self, sql, db, stmt, parameters,
|
|
588
620
|
Qnil, QUERY_HASH, ROW_MULTI, ALL_ROWS
|
|
@@ -616,7 +648,10 @@ VALUE Database_batch_query_array(VALUE self, VALUE sql, VALUE parameters) {
|
|
|
616
648
|
Database_t *db = self_to_open_database(self);
|
|
617
649
|
sqlite3_stmt *stmt;
|
|
618
650
|
|
|
619
|
-
|
|
651
|
+
stmt_ctx stmt_ctx;
|
|
652
|
+
make_stmt_ctx(&stmt_ctx, db, &stmt, sql, RARRAY_LEN(parameters), NULL);
|
|
653
|
+
prep_single_stmt(&stmt_ctx);
|
|
654
|
+
// prepare_xsingle_stmt(DB_GVL_MODE(db), db->stmt_cache, db->sqlite3_db, &stmt, sql, 0, NULL);
|
|
620
655
|
query_ctx ctx = QUERY_CTX(
|
|
621
656
|
self, sql, db, stmt, parameters,
|
|
622
657
|
Qnil, QUERY_ARRAY, ROW_MULTI, ALL_ROWS
|
|
@@ -650,7 +685,10 @@ VALUE Database_batch_query_splat(VALUE self, VALUE sql, VALUE parameters) {
|
|
|
650
685
|
Database_t *db = self_to_open_database(self);
|
|
651
686
|
sqlite3_stmt *stmt;
|
|
652
687
|
|
|
653
|
-
|
|
688
|
+
stmt_ctx stmt_ctx;
|
|
689
|
+
make_stmt_ctx(&stmt_ctx, db, &stmt, sql, RARRAY_LEN(parameters), NULL);
|
|
690
|
+
prep_single_stmt(&stmt_ctx);
|
|
691
|
+
// prepare_xsingle_stmt(DB_GVL_MODE(db), db->stmt_cache, db->sqlite3_db, &stmt, sql, 0, NULL);
|
|
654
692
|
query_ctx ctx = QUERY_CTX(
|
|
655
693
|
self, sql, db, stmt, parameters,
|
|
656
694
|
Qnil, QUERY_SPLAT, ROW_MULTI, ALL_ROWS
|
|
@@ -1383,7 +1421,6 @@ VALUE Extralite_on_progress(int argc, VALUE *argv, VALUE self) {
|
|
|
1383
1421
|
*/
|
|
1384
1422
|
VALUE Database_errcode(VALUE self) {
|
|
1385
1423
|
Database_t *db = self_to_open_database(self);
|
|
1386
|
-
|
|
1387
1424
|
return INT2NUM(sqlite3_errcode(db->sqlite3_db));
|
|
1388
1425
|
}
|
|
1389
1426
|
|
|
@@ -1393,10 +1430,19 @@ VALUE Database_errcode(VALUE self) {
|
|
|
1393
1430
|
*/
|
|
1394
1431
|
VALUE Database_errmsg(VALUE self) {
|
|
1395
1432
|
Database_t *db = self_to_open_database(self);
|
|
1396
|
-
|
|
1397
1433
|
return rb_str_new2(sqlite3_errmsg(db->sqlite3_db));
|
|
1398
1434
|
}
|
|
1399
1435
|
|
|
1436
|
+
/* Returns the stmt cache for the database. If the database was setup with
|
|
1437
|
+
* stmt_cache set to false, returns nil.
|
|
1438
|
+
*
|
|
1439
|
+
* @return [Hash, nil] stmt cache or nil if disabled
|
|
1440
|
+
*/
|
|
1441
|
+
VALUE Database_stmt_cache(VALUE self) {
|
|
1442
|
+
Database_t *db = self_to_open_database(self);
|
|
1443
|
+
return (db->flags & DB_F_STMT_CACHE) ? db->stmt_cache : Qnil;
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1400
1446
|
#ifdef HAVE_SQLITE3_ERROR_OFFSET
|
|
1401
1447
|
/* Returns the offset for the last error. This is useful for indicating where in
|
|
1402
1448
|
* the SQL string an error was encountered.
|
|
@@ -1581,6 +1627,7 @@ void Init_ExtraliteDatabase(void) {
|
|
|
1581
1627
|
rb_define_method(cDatabase, "columns", Database_columns, 1);
|
|
1582
1628
|
rb_define_method(cDatabase, "errcode", Database_errcode, 0);
|
|
1583
1629
|
rb_define_method(cDatabase, "errmsg", Database_errmsg, 0);
|
|
1630
|
+
rb_define_method(cDatabase, "stmt_cache", Database_stmt_cache, 0);
|
|
1584
1631
|
|
|
1585
1632
|
#ifdef HAVE_SQLITE3_ERROR_OFFSET
|
|
1586
1633
|
rb_define_method(cDatabase, "error_offset", Database_error_offset, 0);
|
|
@@ -1644,6 +1691,8 @@ void Init_ExtraliteDatabase(void) {
|
|
|
1644
1691
|
ID_to_s = rb_intern_const("to_s");
|
|
1645
1692
|
ID_track = rb_intern_const("track");
|
|
1646
1693
|
|
|
1694
|
+
ID_inspect = rb_intern_const("inspect");
|
|
1695
|
+
|
|
1647
1696
|
SYM_at_least_once = ID2SYM(rb_intern_const("at_least_once"));
|
|
1648
1697
|
SYM_full = ID2SYM(rb_intern_const("full"));
|
|
1649
1698
|
SYM_gvl_release_threshold = ID2SYM(rb_intern_const("gvl_release_threshold"));
|
|
@@ -1654,6 +1703,7 @@ void Init_ExtraliteDatabase(void) {
|
|
|
1654
1703
|
SYM_passive = ID2SYM(rb_intern_const("passive"));
|
|
1655
1704
|
SYM_read_only = ID2SYM(rb_intern_const("read_only"));
|
|
1656
1705
|
SYM_restart = ID2SYM(rb_intern_const("restart"));
|
|
1706
|
+
SYM_stmt_cache = ID2SYM(rb_intern_const("stmt_cache"));
|
|
1657
1707
|
SYM_truncate = ID2SYM(rb_intern_const("truncate"));
|
|
1658
1708
|
SYM_wal = ID2SYM(rb_intern_const("wal"));
|
|
1659
1709
|
|
|
@@ -1667,6 +1717,7 @@ void Init_ExtraliteDatabase(void) {
|
|
|
1667
1717
|
rb_gc_register_mark_object(SYM_passive);
|
|
1668
1718
|
rb_gc_register_mark_object(SYM_read_only);
|
|
1669
1719
|
rb_gc_register_mark_object(SYM_restart);
|
|
1720
|
+
rb_gc_register_mark_object(SYM_stmt_cache);
|
|
1670
1721
|
rb_gc_register_mark_object(SYM_truncate);
|
|
1671
1722
|
rb_gc_register_mark_object(SYM_wal);
|
|
1672
1723
|
|
data/ext/extralite/extralite.h
CHANGED
|
@@ -31,6 +31,8 @@ extern VALUE cChangeset;
|
|
|
31
31
|
extern VALUE cBlob;
|
|
32
32
|
extern VALUE cTransform;
|
|
33
33
|
|
|
34
|
+
extern ID ID_inspect;
|
|
35
|
+
|
|
34
36
|
extern VALUE mJSON;
|
|
35
37
|
|
|
36
38
|
extern VALUE cError;
|
|
@@ -68,9 +70,13 @@ struct progress_handler {
|
|
|
68
70
|
int call_count;
|
|
69
71
|
};
|
|
70
72
|
|
|
73
|
+
#define DB_F_STMT_CACHE (1L << 0) // cache stmts
|
|
74
|
+
|
|
71
75
|
typedef struct {
|
|
72
76
|
sqlite3 *sqlite3_db;
|
|
77
|
+
VALUE stmt_cache;
|
|
73
78
|
VALUE trace_proc;
|
|
79
|
+
int flags;
|
|
74
80
|
int gvl_release_threshold;
|
|
75
81
|
struct progress_handler progress_handler;
|
|
76
82
|
} Database_t;
|
|
@@ -78,7 +84,8 @@ typedef struct {
|
|
|
78
84
|
enum query_mode {
|
|
79
85
|
QUERY_HASH,
|
|
80
86
|
QUERY_SPLAT,
|
|
81
|
-
QUERY_ARRAY
|
|
87
|
+
QUERY_ARRAY,
|
|
88
|
+
QUERY_VOID
|
|
82
89
|
};
|
|
83
90
|
|
|
84
91
|
typedef struct {
|
|
@@ -108,7 +115,6 @@ typedef struct {
|
|
|
108
115
|
} Changeset_t;
|
|
109
116
|
#endif
|
|
110
117
|
|
|
111
|
-
// #define TRANSFORM_F_CONTAINER (1 << 0) // node is a container
|
|
112
118
|
#define TRANSFORM_F_ARRAY (1 << 0) // node is an array container
|
|
113
119
|
#define TRANSFORM_F_IDENTITY (1 << 1) // node is an identity column
|
|
114
120
|
#define TRANSFORM_F_NAME (1 << 2) // node has a name VALUE
|
|
@@ -159,6 +165,7 @@ typedef struct {
|
|
|
159
165
|
Database_t *db;
|
|
160
166
|
sqlite3 *sqlite3_db;
|
|
161
167
|
sqlite3_stmt *stmt;
|
|
168
|
+
unsigned int flags;
|
|
162
169
|
|
|
163
170
|
int gvl_release_threshold;
|
|
164
171
|
enum query_mode query_mode;
|
|
@@ -167,6 +174,7 @@ typedef struct {
|
|
|
167
174
|
|
|
168
175
|
int eof;
|
|
169
176
|
int step_count;
|
|
177
|
+
int total_changes;
|
|
170
178
|
} query_ctx;
|
|
171
179
|
|
|
172
180
|
enum gvl_mode {
|
|
@@ -174,24 +182,36 @@ enum gvl_mode {
|
|
|
174
182
|
GVL_HOLD
|
|
175
183
|
};
|
|
176
184
|
|
|
185
|
+
#define STMT_CTX_F_USE_CACHE (1L << 0)
|
|
186
|
+
#define STMT_CTX_F_CACHE_HIT (1L << 1)
|
|
187
|
+
|
|
188
|
+
typedef struct {
|
|
189
|
+
VALUE stmt_cache;
|
|
190
|
+
VALUE sql;
|
|
191
|
+
|
|
192
|
+
sqlite3 *db;
|
|
193
|
+
sqlite3_stmt **stmtptr;
|
|
194
|
+
|
|
195
|
+
const char *str;
|
|
196
|
+
size_t len;
|
|
197
|
+
|
|
198
|
+
enum gvl_mode gvl_mode;
|
|
199
|
+
unsigned int flags;
|
|
200
|
+
int rc;
|
|
201
|
+
int total_changes;
|
|
202
|
+
int argc;
|
|
203
|
+
VALUE *argv;
|
|
204
|
+
} stmt_ctx;
|
|
205
|
+
|
|
177
206
|
#define ALL_ROWS -1
|
|
178
207
|
#define SINGLE_ROW -2
|
|
179
208
|
#define ROW_YIELD_OR_MODE(default) (rb_block_given_p() ? ROW_YIELD : default)
|
|
180
209
|
#define ROW_MULTI_P(mode) (mode == ROW_MULTI)
|
|
181
210
|
#define QUERY_CTX(self, sql, db, stmt, params, transform, query_mode, row_mode, max_rows) { \
|
|
182
|
-
self, \
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
db, \
|
|
187
|
-
db->sqlite3_db, \
|
|
188
|
-
stmt, \
|
|
189
|
-
db->gvl_release_threshold, \
|
|
190
|
-
query_mode, \
|
|
191
|
-
row_mode, \
|
|
192
|
-
max_rows, \
|
|
193
|
-
0, \
|
|
194
|
-
0 \
|
|
211
|
+
self, sql, params, transform, \
|
|
212
|
+
db, db->sqlite3_db, stmt, 0, \
|
|
213
|
+
db->gvl_release_threshold, query_mode, row_mode, max_rows, \
|
|
214
|
+
0, 0, 0 \
|
|
195
215
|
}
|
|
196
216
|
|
|
197
217
|
#define DEFAULT_GVL_RELEASE_THRESHOLD 1000
|
|
@@ -209,6 +229,7 @@ VALUE safe_batch_query_array(query_ctx *ctx);
|
|
|
209
229
|
VALUE safe_query_splat(query_ctx *ctx);
|
|
210
230
|
VALUE safe_query_array(query_ctx *ctx);
|
|
211
231
|
VALUE safe_query_changes(query_ctx *ctx);
|
|
232
|
+
VALUE safe_total_changes(query_ctx *ctx);
|
|
212
233
|
VALUE safe_query_columns(query_ctx *ctx);
|
|
213
234
|
VALUE safe_query_hash(query_ctx *ctx);
|
|
214
235
|
VALUE safe_query_transform(query_ctx *ctx);
|
|
@@ -222,8 +243,9 @@ VALUE Query_next(int argc, VALUE *argv, VALUE self);
|
|
|
222
243
|
VALUE Query_to_a(VALUE self);
|
|
223
244
|
VALUE Query_transform_set(VALUE self, VALUE transform);
|
|
224
245
|
|
|
225
|
-
void
|
|
226
|
-
void
|
|
246
|
+
void make_stmt_ctx(stmt_ctx *ctx, Database_t *db, sqlite3_stmt **stmt, VALUE sql, int argc, VALUE *argv);
|
|
247
|
+
void prep_single_stmt(stmt_ctx *ctx);
|
|
248
|
+
int exec_multi_stmt(stmt_ctx *ctx);
|
|
227
249
|
void bind_all_parameters(sqlite3_stmt *stmt, int argc, VALUE *argv);
|
|
228
250
|
void bind_all_parameters_from_object(sqlite3_stmt *stmt, VALUE obj);
|
|
229
251
|
int stmt_iterate(query_ctx *ctx);
|
data/ext/extralite/iterator.c
CHANGED
|
@@ -25,7 +25,7 @@ static void Iterator_compact(void *ptr) {
|
|
|
25
25
|
|
|
26
26
|
static const rb_data_type_t Iterator_type = {
|
|
27
27
|
"Iterator",
|
|
28
|
-
{Iterator_mark,
|
|
28
|
+
{Iterator_mark, xfree, Iterator_size, Iterator_compact},
|
|
29
29
|
0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
|
|
30
30
|
};
|
|
31
31
|
|
data/ext/extralite/query.c
CHANGED
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
|
|
12
12
|
VALUE cQuery;
|
|
13
13
|
|
|
14
|
-
ID ID_inspect;
|
|
15
14
|
ID ID_slice;
|
|
16
15
|
|
|
17
16
|
VALUE SYM_hash;
|
|
@@ -132,9 +131,15 @@ VALUE Query_initialize(VALUE self, VALUE db, VALUE sql, VALUE mode) {
|
|
|
132
131
|
return Qnil;
|
|
133
132
|
}
|
|
134
133
|
|
|
134
|
+
static inline void prep_query(Query_t *query) {
|
|
135
|
+
stmt_ctx stmt_ctx;
|
|
136
|
+
make_stmt_ctx(&stmt_ctx, query->db_struct, &(query->stmt), query->sql, 0, NULL);
|
|
137
|
+
prep_single_stmt(&stmt_ctx);
|
|
138
|
+
}
|
|
139
|
+
|
|
135
140
|
static inline void query_reset(Query_t *query) {
|
|
136
141
|
if (!query->stmt)
|
|
137
|
-
|
|
142
|
+
prep_query(query);
|
|
138
143
|
else
|
|
139
144
|
sqlite3_reset(query->stmt);
|
|
140
145
|
|
|
@@ -146,7 +151,7 @@ static inline void query_reset(Query_t *query) {
|
|
|
146
151
|
|
|
147
152
|
static inline void query_bind(Query_t *query, int argc, VALUE * argv) {
|
|
148
153
|
if (!query->stmt)
|
|
149
|
-
|
|
154
|
+
prep_query(query);
|
|
150
155
|
else
|
|
151
156
|
// we call sqlite3_reset because that's what the SQLite API expects before
|
|
152
157
|
// changing the binding. See note at bottom of
|
|
@@ -406,8 +411,7 @@ VALUE Query_batch_execute(VALUE self, VALUE parameters) {
|
|
|
406
411
|
Query_t *query = self_to_query_verify(self);
|
|
407
412
|
if (query->closed) rb_raise(cError, "Query is closed");
|
|
408
413
|
|
|
409
|
-
if (!query->stmt)
|
|
410
|
-
prepare_single_stmt(DB_GVL_MODE(query), query->sqlite3_db, &query->stmt, query->sql);
|
|
414
|
+
if (!query->stmt) prep_query(query);
|
|
411
415
|
|
|
412
416
|
query_ctx ctx = QUERY_CTX(
|
|
413
417
|
self,
|
|
@@ -455,8 +459,7 @@ VALUE Query_batch_query(VALUE self, VALUE parameters) {
|
|
|
455
459
|
Query_t *query = self_to_query_verify(self);
|
|
456
460
|
if (query->closed) rb_raise(cError, "Query is closed");
|
|
457
461
|
|
|
458
|
-
if (!query->stmt)
|
|
459
|
-
prepare_single_stmt(DB_GVL_MODE(query), query->sqlite3_db, &query->stmt, query->sql);
|
|
462
|
+
if (!query->stmt) prep_query(query);
|
|
460
463
|
|
|
461
464
|
query_ctx ctx = QUERY_CTX(
|
|
462
465
|
self,
|
|
@@ -562,9 +565,7 @@ VALUE Query_status(int argc, VALUE* argv, VALUE self) {
|
|
|
562
565
|
rb_scan_args(argc, argv, "11", &op, &reset);
|
|
563
566
|
|
|
564
567
|
Query_t *query = self_to_query_verify(self);
|
|
565
|
-
|
|
566
|
-
if (!query->stmt)
|
|
567
|
-
prepare_single_stmt(DB_GVL_MODE(query), query->sqlite3_db, &query->stmt, query->sql);
|
|
568
|
+
if (!query->stmt) prep_query(query);
|
|
568
569
|
|
|
569
570
|
int value = sqlite3_stmt_status(query->stmt, NUM2INT(op), RTEST(reset) ? 1 : 0);
|
|
570
571
|
return INT2NUM(value);
|
|
@@ -685,7 +686,6 @@ void Init_ExtraliteQuery(void) {
|
|
|
685
686
|
rb_define_method(cQuery, "transform", Query_transform_get, 0);
|
|
686
687
|
rb_define_method(cQuery, "transform=", Query_transform_set, 1);
|
|
687
688
|
|
|
688
|
-
ID_inspect = rb_intern_const("inspect");
|
|
689
689
|
ID_slice = rb_intern_const("slice");
|
|
690
690
|
|
|
691
691
|
SYM_hash = ID2SYM(rb_intern_const("hash"));
|