extralite-bundle 3.0.1 → 3.1.1
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 +13 -0
- data/README.md +20 -1
- data/Rakefile +2 -2
- data/TODO.md +2 -2
- data/examples/pubsub_store_polyphony.rb +1 -1
- data/ext/extralite/common.c +181 -81
- data/ext/extralite/database.c +102 -31
- 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/helper.rb +10 -0
- data/test/test_database.rb +274 -15
- data/test/test_query.rb +10 -9
- data/test/test_sequel.rb +4 -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,19 +60,39 @@ 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
|
}
|
|
70
74
|
|
|
75
|
+
static int stmt_cache_iter(VALUE key, VALUE value, VALUE _param) {
|
|
76
|
+
sqlite3_stmt *stmt = (sqlite3_stmt *)NUM2ULONG(value);
|
|
77
|
+
sqlite3_finalize(stmt);
|
|
78
|
+
return ST_CONTINUE;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
static void stmt_cache_clear(sqlite3 *db, VALUE stmt_cache) {
|
|
82
|
+
if (stmt_cache == Qnil) return;
|
|
83
|
+
if (rb_hash_size_num(stmt_cache) == 0) return;
|
|
84
|
+
|
|
85
|
+
rb_hash_foreach(stmt_cache, stmt_cache_iter, Qnil);
|
|
86
|
+
rb_hash_clear(stmt_cache);
|
|
87
|
+
RB_GC_GUARD(stmt_cache);
|
|
88
|
+
}
|
|
89
|
+
|
|
71
90
|
static void Database_free(void *ptr) {
|
|
72
91
|
Database_t *db = ptr;
|
|
73
|
-
if (db->sqlite3_db)
|
|
92
|
+
if (db->sqlite3_db) {
|
|
93
|
+
stmt_cache_clear(db->sqlite3_db, db->stmt_cache);
|
|
94
|
+
sqlite3_close_v2(db->sqlite3_db);
|
|
95
|
+
}
|
|
74
96
|
free(ptr);
|
|
75
97
|
}
|
|
76
98
|
|
|
@@ -83,6 +105,7 @@ static const rb_data_type_t Database_type = {
|
|
|
83
105
|
static VALUE Database_allocate(VALUE klass) {
|
|
84
106
|
Database_t *db = ALLOC(Database_t);
|
|
85
107
|
db->sqlite3_db = NULL;
|
|
108
|
+
db->stmt_cache = Qnil;
|
|
86
109
|
db->trace_proc = Qnil;
|
|
87
110
|
db->progress_handler.proc = Qnil;
|
|
88
111
|
db->progress_handler.mode = PROGRESS_NONE;
|
|
@@ -127,12 +150,17 @@ default_flags:
|
|
|
127
150
|
}
|
|
128
151
|
|
|
129
152
|
void Database_apply_opts(VALUE self, Database_t *db, VALUE opts) {
|
|
153
|
+
db->flags = DB_F_STMT_CACHE;
|
|
130
154
|
if (NIL_P(opts)) goto modern_pragmas;
|
|
131
155
|
|
|
132
156
|
// :gvl_release_threshold
|
|
133
157
|
VALUE value = rb_hash_aref(opts, SYM_gvl_release_threshold);
|
|
134
158
|
if (!NIL_P(value)) db->gvl_release_threshold = NUM2INT(value);
|
|
135
159
|
|
|
160
|
+
value = rb_hash_aref(opts, SYM_stmt_cache);
|
|
161
|
+
if (value == Qfalse)
|
|
162
|
+
db->flags &= !DB_F_STMT_CACHE;
|
|
163
|
+
|
|
136
164
|
value = rb_hash_aref(opts, SYM_legacy);
|
|
137
165
|
if (RTEST(value)) return;
|
|
138
166
|
|
|
@@ -175,9 +203,9 @@ int Database_busy_handler(void *ptr, int v) {
|
|
|
175
203
|
* `#gvl_release_threshold=`).
|
|
176
204
|
* - `:read_only` (`true`/`false`): opens the database in read-only mode if true.
|
|
177
205
|
* - `:legacy` (`true`/`false`): By default the database is set up for
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
206
|
+
* concurrent access with [WAL journaling mode](https://www.sqlite.org/wal.html).
|
|
207
|
+
* To prevent Extralite from setting up WAL journaling, set this option to true.
|
|
208
|
+
* - `:stmt_cache`: true by default, set to false to disable stmt caching
|
|
181
209
|
*
|
|
182
210
|
* @overload initialize(path)
|
|
183
211
|
* @param path [String] file path (or ':memory:' for memory database)
|
|
@@ -215,6 +243,7 @@ VALUE Database_initialize(int argc, VALUE *argv, VALUE self) {
|
|
|
215
243
|
sqlite3_db_config(db->sqlite3_db ,SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 1, NULL);
|
|
216
244
|
#endif
|
|
217
245
|
|
|
246
|
+
db->stmt_cache = rb_hash_new();
|
|
218
247
|
db->trace_proc = Qnil;
|
|
219
248
|
db->gvl_release_threshold = DEFAULT_GVL_RELEASE_THRESHOLD;
|
|
220
249
|
|
|
@@ -250,6 +279,7 @@ VALUE Database_close(VALUE self) {
|
|
|
250
279
|
int rc;
|
|
251
280
|
Database_t *db = self_to_database(self);
|
|
252
281
|
|
|
282
|
+
stmt_cache_clear(db->sqlite3_db, db->stmt_cache);
|
|
253
283
|
rc = sqlite3_close_v2(db->sqlite3_db);
|
|
254
284
|
if (rc) {
|
|
255
285
|
rb_raise(cError, "%s", sqlite3_errmsg(db->sqlite3_db));
|
|
@@ -277,12 +307,13 @@ inline enum gvl_mode Database_prepare_gvl_mode(Database_t *db) {
|
|
|
277
307
|
|
|
278
308
|
static inline VALUE Database_perform_query(int argc, VALUE *argv, VALUE self, VALUE (*call)(query_ctx *), enum query_mode query_mode) {
|
|
279
309
|
Database_t *db = self_to_open_database(self);
|
|
280
|
-
sqlite3_stmt *stmt;
|
|
310
|
+
sqlite3_stmt *stmt = NULL;
|
|
281
311
|
VALUE sql = Qnil;
|
|
282
312
|
VALUE transform = Qnil;
|
|
283
313
|
// transform mode is set and the first parameter is not a string, so we expect
|
|
284
314
|
// a transform.,
|
|
285
315
|
int got_transform = (TYPE(argv[0]) != T_STRING);
|
|
316
|
+
int execute_mode = query_mode == QUERY_VOID;
|
|
286
317
|
|
|
287
318
|
// extract query from args
|
|
288
319
|
rb_check_arity(argc, got_transform ? 2 : 1, UNLIMITED_ARGUMENTS);
|
|
@@ -293,26 +324,38 @@ static inline VALUE Database_perform_query(int argc, VALUE *argv, VALUE self, VA
|
|
|
293
324
|
argv++;
|
|
294
325
|
}
|
|
295
326
|
|
|
296
|
-
sql =
|
|
297
|
-
if (RSTRING_LEN(sql) == 0) return Qnil;
|
|
298
|
-
// sql = argv[0];
|
|
299
|
-
|
|
300
|
-
prepare_multi_stmt(DB_GVL_MODE(db), db->sqlite3_db, &stmt, sql);
|
|
301
|
-
RB_GC_GUARD(sql);
|
|
327
|
+
sql = argv[0];
|
|
302
328
|
|
|
303
|
-
|
|
329
|
+
stmt_ctx stmt_ctx;
|
|
330
|
+
make_stmt_ctx(&stmt_ctx, db, &stmt, sql, argc - 1, argv + 1);
|
|
304
331
|
|
|
305
|
-
bind_all_parameters(stmt, argc - 1, argv + 1);
|
|
306
332
|
Database_pre_query_hook(db, stmt, sql, argc - 1, argv + 1);
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
333
|
+
if (execute_mode) {
|
|
334
|
+
exec_multi_stmt(&stmt_ctx);
|
|
335
|
+
|
|
336
|
+
query_ctx ctx = QUERY_CTX(
|
|
337
|
+
self, sql, db, NULL, Qnil, transform,
|
|
338
|
+
query_mode, ROW_YIELD_OR_MODE(ROW_MULTI), ALL_ROWS
|
|
339
|
+
);
|
|
340
|
+
ctx.flags = stmt_ctx.flags;
|
|
341
|
+
ctx.total_changes = stmt_ctx.total_changes;
|
|
342
|
+
VALUE result = (VALUE)call(&ctx);
|
|
343
|
+
return result;
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
prep_single_stmt(&stmt_ctx);
|
|
347
|
+
query_ctx ctx = QUERY_CTX(
|
|
348
|
+
self, sql, db, stmt, Qnil, transform,
|
|
349
|
+
query_mode, ROW_YIELD_OR_MODE(ROW_MULTI), ALL_ROWS
|
|
350
|
+
);
|
|
351
|
+
ctx.flags = stmt_ctx.flags;
|
|
352
|
+
if (!stmt) return Qnil;
|
|
353
|
+
|
|
354
|
+
bind_all_parameters(stmt, argc - 1, argv + 1);
|
|
355
|
+
VALUE result = rb_ensure(SAFE(call), (VALUE)&ctx, SAFE(cleanup_stmt), (VALUE)&ctx);
|
|
356
|
+
RB_GC_GUARD(result);
|
|
357
|
+
return result;
|
|
358
|
+
}
|
|
316
359
|
}
|
|
317
360
|
|
|
318
361
|
/* call-seq:
|
|
@@ -490,7 +533,9 @@ VALUE Database_query_single_array(int argc, VALUE *argv, VALUE self) {
|
|
|
490
533
|
* db.execute(sql, *parameters) -> changes
|
|
491
534
|
*
|
|
492
535
|
* Runs a query returning the total changes effected. This method should be used
|
|
493
|
-
* for data- or schema-manipulation queries.
|
|
536
|
+
* for data- or schema-manipulation queries. If the given SQL string contains
|
|
537
|
+
* multiple statements, they will be executed in the order given. If multiple
|
|
538
|
+
* statements are given along with query parameters, an error is raised.
|
|
494
539
|
*
|
|
495
540
|
* Query parameters to be bound to placeholders in the query can be specified as
|
|
496
541
|
* a list of values or as a hash mapping parameter names to values. When
|
|
@@ -506,10 +551,10 @@ VALUE Database_query_single_array(int argc, VALUE *argv, VALUE self) {
|
|
|
506
551
|
*
|
|
507
552
|
* @param sql [String] query SQL
|
|
508
553
|
* @param parameters [Array, Hash] parameters to run query with
|
|
509
|
-
* @return [Integer
|
|
554
|
+
* @return [Integer] Total number of changes effected
|
|
510
555
|
*/
|
|
511
556
|
VALUE Database_execute(int argc, VALUE *argv, VALUE self) {
|
|
512
|
-
return Database_perform_query(argc, argv, self,
|
|
557
|
+
return Database_perform_query(argc, argv, self, safe_total_changes, QUERY_VOID);
|
|
513
558
|
}
|
|
514
559
|
|
|
515
560
|
/* call-seq:
|
|
@@ -546,11 +591,15 @@ VALUE Database_batch_execute(VALUE self, VALUE sql, VALUE parameters) {
|
|
|
546
591
|
|
|
547
592
|
if (RSTRING_LEN(sql) == 0) return Qnil;
|
|
548
593
|
|
|
549
|
-
|
|
594
|
+
stmt_ctx stmt_ctx;
|
|
595
|
+
make_stmt_ctx(&stmt_ctx, db, &stmt, sql, 1, NULL);
|
|
596
|
+
prep_single_stmt(&stmt_ctx);
|
|
597
|
+
// prepare_xsingle_stmt(DB_GVL_MODE(db), db->stmt_cache, db->sqlite3_db, &stmt, sql, 0, NULL);
|
|
550
598
|
query_ctx ctx = QUERY_CTX(
|
|
551
599
|
self, sql, db, stmt, parameters,
|
|
552
600
|
Qnil, QUERY_HASH, ROW_MULTI, ALL_ROWS
|
|
553
601
|
);
|
|
602
|
+
ctx.flags = stmt_ctx.flags;
|
|
554
603
|
|
|
555
604
|
return rb_ensure(SAFE(safe_batch_execute), (VALUE)&ctx, SAFE(cleanup_stmt), (VALUE)&ctx);
|
|
556
605
|
}
|
|
@@ -582,7 +631,10 @@ VALUE Database_batch_query(VALUE self, VALUE sql, VALUE parameters) {
|
|
|
582
631
|
Database_t *db = self_to_open_database(self);
|
|
583
632
|
sqlite3_stmt *stmt;
|
|
584
633
|
|
|
585
|
-
|
|
634
|
+
stmt_ctx stmt_ctx;
|
|
635
|
+
make_stmt_ctx(&stmt_ctx, db, &stmt, sql, RARRAY_LEN(parameters), NULL);
|
|
636
|
+
prep_single_stmt(&stmt_ctx);
|
|
637
|
+
// prepare_xsingle_stmt(DB_GVL_MODE(db), db->stmt_cache, db->sqlite3_db, &stmt, sql, 0, NULL);
|
|
586
638
|
query_ctx ctx = QUERY_CTX(
|
|
587
639
|
self, sql, db, stmt, parameters,
|
|
588
640
|
Qnil, QUERY_HASH, ROW_MULTI, ALL_ROWS
|
|
@@ -616,7 +668,10 @@ VALUE Database_batch_query_array(VALUE self, VALUE sql, VALUE parameters) {
|
|
|
616
668
|
Database_t *db = self_to_open_database(self);
|
|
617
669
|
sqlite3_stmt *stmt;
|
|
618
670
|
|
|
619
|
-
|
|
671
|
+
stmt_ctx stmt_ctx;
|
|
672
|
+
make_stmt_ctx(&stmt_ctx, db, &stmt, sql, RARRAY_LEN(parameters), NULL);
|
|
673
|
+
prep_single_stmt(&stmt_ctx);
|
|
674
|
+
// prepare_xsingle_stmt(DB_GVL_MODE(db), db->stmt_cache, db->sqlite3_db, &stmt, sql, 0, NULL);
|
|
620
675
|
query_ctx ctx = QUERY_CTX(
|
|
621
676
|
self, sql, db, stmt, parameters,
|
|
622
677
|
Qnil, QUERY_ARRAY, ROW_MULTI, ALL_ROWS
|
|
@@ -650,7 +705,10 @@ VALUE Database_batch_query_splat(VALUE self, VALUE sql, VALUE parameters) {
|
|
|
650
705
|
Database_t *db = self_to_open_database(self);
|
|
651
706
|
sqlite3_stmt *stmt;
|
|
652
707
|
|
|
653
|
-
|
|
708
|
+
stmt_ctx stmt_ctx;
|
|
709
|
+
make_stmt_ctx(&stmt_ctx, db, &stmt, sql, RARRAY_LEN(parameters), NULL);
|
|
710
|
+
prep_single_stmt(&stmt_ctx);
|
|
711
|
+
// prepare_xsingle_stmt(DB_GVL_MODE(db), db->stmt_cache, db->sqlite3_db, &stmt, sql, 0, NULL);
|
|
654
712
|
query_ctx ctx = QUERY_CTX(
|
|
655
713
|
self, sql, db, stmt, parameters,
|
|
656
714
|
Qnil, QUERY_SPLAT, ROW_MULTI, ALL_ROWS
|
|
@@ -1383,7 +1441,6 @@ VALUE Extralite_on_progress(int argc, VALUE *argv, VALUE self) {
|
|
|
1383
1441
|
*/
|
|
1384
1442
|
VALUE Database_errcode(VALUE self) {
|
|
1385
1443
|
Database_t *db = self_to_open_database(self);
|
|
1386
|
-
|
|
1387
1444
|
return INT2NUM(sqlite3_errcode(db->sqlite3_db));
|
|
1388
1445
|
}
|
|
1389
1446
|
|
|
@@ -1393,10 +1450,19 @@ VALUE Database_errcode(VALUE self) {
|
|
|
1393
1450
|
*/
|
|
1394
1451
|
VALUE Database_errmsg(VALUE self) {
|
|
1395
1452
|
Database_t *db = self_to_open_database(self);
|
|
1396
|
-
|
|
1397
1453
|
return rb_str_new2(sqlite3_errmsg(db->sqlite3_db));
|
|
1398
1454
|
}
|
|
1399
1455
|
|
|
1456
|
+
/* Returns the stmt cache for the database. If the database was setup with
|
|
1457
|
+
* stmt_cache set to false, returns nil.
|
|
1458
|
+
*
|
|
1459
|
+
* @return [Hash, nil] stmt cache or nil if disabled
|
|
1460
|
+
*/
|
|
1461
|
+
VALUE Database_stmt_cache(VALUE self) {
|
|
1462
|
+
Database_t *db = self_to_open_database(self);
|
|
1463
|
+
return (db->flags & DB_F_STMT_CACHE) ? db->stmt_cache : Qnil;
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1400
1466
|
#ifdef HAVE_SQLITE3_ERROR_OFFSET
|
|
1401
1467
|
/* Returns the offset for the last error. This is useful for indicating where in
|
|
1402
1468
|
* the SQL string an error was encountered.
|
|
@@ -1581,6 +1647,7 @@ void Init_ExtraliteDatabase(void) {
|
|
|
1581
1647
|
rb_define_method(cDatabase, "columns", Database_columns, 1);
|
|
1582
1648
|
rb_define_method(cDatabase, "errcode", Database_errcode, 0);
|
|
1583
1649
|
rb_define_method(cDatabase, "errmsg", Database_errmsg, 0);
|
|
1650
|
+
rb_define_method(cDatabase, "stmt_cache", Database_stmt_cache, 0);
|
|
1584
1651
|
|
|
1585
1652
|
#ifdef HAVE_SQLITE3_ERROR_OFFSET
|
|
1586
1653
|
rb_define_method(cDatabase, "error_offset", Database_error_offset, 0);
|
|
@@ -1644,6 +1711,8 @@ void Init_ExtraliteDatabase(void) {
|
|
|
1644
1711
|
ID_to_s = rb_intern_const("to_s");
|
|
1645
1712
|
ID_track = rb_intern_const("track");
|
|
1646
1713
|
|
|
1714
|
+
ID_inspect = rb_intern_const("inspect");
|
|
1715
|
+
|
|
1647
1716
|
SYM_at_least_once = ID2SYM(rb_intern_const("at_least_once"));
|
|
1648
1717
|
SYM_full = ID2SYM(rb_intern_const("full"));
|
|
1649
1718
|
SYM_gvl_release_threshold = ID2SYM(rb_intern_const("gvl_release_threshold"));
|
|
@@ -1654,6 +1723,7 @@ void Init_ExtraliteDatabase(void) {
|
|
|
1654
1723
|
SYM_passive = ID2SYM(rb_intern_const("passive"));
|
|
1655
1724
|
SYM_read_only = ID2SYM(rb_intern_const("read_only"));
|
|
1656
1725
|
SYM_restart = ID2SYM(rb_intern_const("restart"));
|
|
1726
|
+
SYM_stmt_cache = ID2SYM(rb_intern_const("stmt_cache"));
|
|
1657
1727
|
SYM_truncate = ID2SYM(rb_intern_const("truncate"));
|
|
1658
1728
|
SYM_wal = ID2SYM(rb_intern_const("wal"));
|
|
1659
1729
|
|
|
@@ -1667,6 +1737,7 @@ void Init_ExtraliteDatabase(void) {
|
|
|
1667
1737
|
rb_gc_register_mark_object(SYM_passive);
|
|
1668
1738
|
rb_gc_register_mark_object(SYM_read_only);
|
|
1669
1739
|
rb_gc_register_mark_object(SYM_restart);
|
|
1740
|
+
rb_gc_register_mark_object(SYM_stmt_cache);
|
|
1670
1741
|
rb_gc_register_mark_object(SYM_truncate);
|
|
1671
1742
|
rb_gc_register_mark_object(SYM_wal);
|
|
1672
1743
|
|
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"));
|