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
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7fffde36ad772833bc816f85c26c6472d1c9e6c8e4ba91092734efeb24e2541a
|
|
4
|
+
data.tar.gz: c4200d06ffece67d1241297567b37b3ddb6bc882d72dd6b4ba402a2611f4a1a1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 97fa8bbe57df8c8f679703764a7b6a185c235cf53c40738bf46f06bea82385fe1a7deed5d2990cb9837a1e1aaf858a5a4a88e5baccbb6117526dce8c3591fd32
|
|
7
|
+
data.tar.gz: 07716c4ba47c25b2ee796ce287e7a87e051176cdbd79badb906ea86fe45fd7526b3cd0180f077cfae3ff6311a7e182b351c36d8696c10feb8ac50714f8dd1dd0
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
# 3.1.1 2026-09-26
|
|
2
|
+
|
|
3
|
+
- Fix clearing of stmt cache after `DB#close` or after GC (#87)
|
|
4
|
+
|
|
5
|
+
# 3.1.0 2026-09-22
|
|
6
|
+
|
|
7
|
+
- Update bundled SQLite to 3.53.4
|
|
8
|
+
- Add `stmt_cache` option to `Database#initialize`
|
|
9
|
+
- Implement automatic stmt caching for parametric queries
|
|
10
|
+
- Change handling of multi stmt query and execute invocations.
|
|
11
|
+
- For #execute, accept multiple statements but raise error if parameters are given
|
|
12
|
+
- For #query, raise error on multiple statements
|
|
13
|
+
|
|
1
14
|
## 3.0.1 2026-07-21
|
|
2
15
|
|
|
3
16
|
- Fix passing nil transform to `DB#prepare`
|
data/README.md
CHANGED
|
@@ -32,7 +32,7 @@ databases.
|
|
|
32
32
|
Extralite comes in two flavors: the `extralite` gem which uses the
|
|
33
33
|
system-installed sqlite3 library, and the `extralite-bundle` gem which bundles
|
|
34
34
|
the latest version of SQLite
|
|
35
|
-
([3.53.
|
|
35
|
+
([3.53.4](https://sqlite.org/releaselog/3_53_4.html)), offering access to the
|
|
36
36
|
latest features and enhancements.
|
|
37
37
|
|
|
38
38
|
## Features
|
|
@@ -49,6 +49,7 @@ latest features and enhancements.
|
|
|
49
49
|
allowing iterating through single records or batches of records.
|
|
50
50
|
- [Prepared queries](#prepared-queries).
|
|
51
51
|
- [Parameter binding](#parameter-binding).
|
|
52
|
+
- [Automatic query caching](#automatic-query-caching) for parametric queries.
|
|
52
53
|
- [Batch execution](#batch-execution-of-queries) of queries.
|
|
53
54
|
- [transactions and savepoints](#transactions-and-savepoints).
|
|
54
55
|
- Advanced features: load [SQLite extensions](#loading-extensions), create
|
|
@@ -69,6 +70,7 @@ latest features and enhancements.
|
|
|
69
70
|
- [Data Types](#data-types)
|
|
70
71
|
- [Prepared Queries](#prepared-queries)
|
|
71
72
|
- [Batch Execution of Queries](#batch-execution-of-queries)
|
|
73
|
+
- [Automatic query caching](#automatic-query-caching)
|
|
72
74
|
- [Transactions and Savepoints](#transactions-and-savepoints)
|
|
73
75
|
- [Database Information](#database-information)
|
|
74
76
|
- [Error Handling](#error-handling)
|
|
@@ -361,6 +363,16 @@ To use the transform, pass it along with the SQL string to `Database#query`:
|
|
|
361
363
|
db.qurey(transform, sql) #=> [...]
|
|
362
364
|
```
|
|
363
365
|
|
|
366
|
+
Structured transforms support the following column types:
|
|
367
|
+
|
|
368
|
+
- `integer`
|
|
369
|
+
- `float`
|
|
370
|
+
- `text`
|
|
371
|
+
- `bool` - represented as an integer value of 0/1.
|
|
372
|
+
- `json` - represented as a string (text).
|
|
373
|
+
- A custom proc/lambda - e.g. `->(v) { v.split(',') }`.
|
|
374
|
+
- `auto` - uses the native database data type.
|
|
375
|
+
|
|
364
376
|
Transforms can also be used with [prepared
|
|
365
377
|
queries](#transforms-in-prepared-queries).
|
|
366
378
|
|
|
@@ -653,6 +665,13 @@ query.batch_query([[42, 3], [43, 6]])
|
|
|
653
665
|
#=> [{ x: 42, y: 2, z: 3 }, { x: 43, y: 5, z: 6 }]
|
|
654
666
|
```
|
|
655
667
|
|
|
668
|
+
## Automatic query caching
|
|
669
|
+
|
|
670
|
+
Extralite implements automatic caching of parametric queries. This means that
|
|
671
|
+
any time you run one of the `execute` or `query_xxx` methods with parameters,
|
|
672
|
+
Extralite will only prepare the query once, and the underlying `sqlite_stmt`
|
|
673
|
+
object will be cached for reuse.
|
|
674
|
+
|
|
656
675
|
## Transactions and Savepoints
|
|
657
676
|
|
|
658
677
|
All reads and writes to SQLite databases occur within a
|
data/Rakefile
CHANGED
|
@@ -28,7 +28,7 @@ end
|
|
|
28
28
|
task :release do
|
|
29
29
|
require_relative './lib/extralite/version'
|
|
30
30
|
version = Extralite::VERSION
|
|
31
|
-
|
|
31
|
+
|
|
32
32
|
puts 'Building extralite...'
|
|
33
33
|
`gem build extralite.gemspec`
|
|
34
34
|
|
|
@@ -66,4 +66,4 @@ begin
|
|
|
66
66
|
end
|
|
67
67
|
rescue LoadError => e
|
|
68
68
|
warn("NOTE: ruby_memcheck is not available in this environment: #{e}")
|
|
69
|
-
end
|
|
69
|
+
end
|
data/TODO.md
CHANGED
|
@@ -28,7 +28,7 @@ end
|
|
|
28
28
|
]
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
-
|
|
31
|
+
- Use single value instead of hash
|
|
32
32
|
|
|
33
33
|
```ruby
|
|
34
34
|
Extralite::Transform.new do
|
|
@@ -38,7 +38,7 @@ Extralite::Transform.new do
|
|
|
38
38
|
content: text,
|
|
39
39
|
_tag_id: skip,
|
|
40
40
|
tags: [
|
|
41
|
-
name: text
|
|
41
|
+
{ name: text.unbox }
|
|
42
42
|
]
|
|
43
43
|
}
|
|
44
44
|
end
|
data/ext/extralite/common.c
CHANGED
|
@@ -157,11 +157,6 @@ static inline void column_names_set(struct column_names *names, int idx, VALUE v
|
|
|
157
157
|
rb_ary_push(names->array, value);
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
-
static inline VALUE column_names_get(struct column_names *names, int idx) {
|
|
161
|
-
return (names->count <= MAX_EMBEDDED_COLUMN_NAMES) ?
|
|
162
|
-
names->names[idx] : RARRAY_AREF(names->array, idx);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
160
|
static inline struct column_names get_column_names(sqlite3_stmt *stmt, int column_count) {
|
|
166
161
|
struct column_names names;
|
|
167
162
|
column_names_setup(&names, column_count);
|
|
@@ -213,108 +208,205 @@ static inline void row_to_splat_values(sqlite3_stmt *stmt, int column_count, VAL
|
|
|
213
208
|
}
|
|
214
209
|
}
|
|
215
210
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
sqlite3_stmt
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
}
|
|
211
|
+
static inline void lookup_cache_entry(stmt_ctx *ctx) {
|
|
212
|
+
VALUE cached = rb_hash_aref(ctx->stmt_cache, ctx->sql);
|
|
213
|
+
*(ctx->stmtptr) = NIL_P(cached) ? NULL : (sqlite3_stmt *)NUM2ULONG(cached);
|
|
214
|
+
if (*(ctx->stmtptr)) {
|
|
215
|
+
sqlite3_reset(*(ctx->stmtptr));
|
|
216
|
+
ctx->flags |= STMT_CTX_F_CACHE_HIT;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
static inline void finalize_stmt_ctx(stmt_ctx *ctx) {
|
|
221
|
+
if (!*(ctx->stmtptr)) return;
|
|
222
|
+
|
|
223
|
+
if (!(ctx->flags & STMT_CTX_F_USE_CACHE)) {
|
|
224
|
+
sqlite3_finalize(*(ctx->stmtptr));
|
|
225
|
+
*(ctx->stmtptr) = NULL;
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (!(ctx->flags & STMT_CTX_F_CACHE_HIT))
|
|
230
|
+
rb_hash_aset(ctx->stmt_cache, ctx->sql, ULONG2NUM((uint64_t)*(ctx->stmtptr)));
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
void make_stmt_ctx(
|
|
234
|
+
stmt_ctx *ctx, Database_t *db, sqlite3_stmt **stmt, VALUE sql, int argc, VALUE *argv
|
|
235
|
+
) {
|
|
236
|
+
ctx->stmt_cache = db->stmt_cache;
|
|
237
|
+
ctx->sql = sql;
|
|
238
|
+
|
|
239
|
+
ctx->db = db->sqlite3_db;
|
|
240
|
+
ctx->stmtptr = stmt;
|
|
241
|
+
|
|
242
|
+
int use_cache = (argc > 0) && (db->flags & DB_F_STMT_CACHE);
|
|
243
|
+
ctx->flags = use_cache ? STMT_CTX_F_USE_CACHE : 0;
|
|
244
|
+
if (use_cache) {
|
|
245
|
+
lookup_cache_entry(ctx);
|
|
246
|
+
if (ctx->flags & STMT_CTX_F_CACHE_HIT)
|
|
247
|
+
sqlite3_clear_bindings(*(ctx->stmtptr));
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (!use_cache || !(*(ctx->stmtptr))) {
|
|
251
|
+
ctx->str = RSTRING_PTR(sql);
|
|
252
|
+
ctx->len = RSTRING_LEN(sql);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
ctx->gvl_mode = db->gvl_release_threshold < 0 ? GVL_HOLD : GVL_RELEASE;
|
|
256
|
+
ctx->rc = 0;
|
|
257
|
+
ctx->total_changes = 0;
|
|
258
|
+
ctx->argc = argc;
|
|
259
|
+
ctx->argv = argv;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
static inline int exec_stmt_iterate(sqlite3_stmt *stmt) {
|
|
263
|
+
while (true) {
|
|
264
|
+
int rc = sqlite3_step(stmt);
|
|
265
|
+
switch (rc) {
|
|
266
|
+
case SQLITE_ROW: continue;
|
|
267
|
+
case SQLITE_DONE: return 0;
|
|
268
|
+
default: return rc;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
static inline void finalize_stmt(sqlite3_stmt **stmt) {
|
|
274
|
+
if (*stmt) {
|
|
275
|
+
sqlite3_finalize(*stmt);
|
|
276
|
+
*stmt = NULL;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
static inline void *exec_bind_parameters(void *ptr) {
|
|
281
|
+
stmt_ctx *ctx = (stmt_ctx *)ptr;
|
|
282
|
+
bind_all_parameters(*(ctx->stmtptr), ctx->argc, ctx->argv);
|
|
283
|
+
return NULL;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
void *exec_multi_stmt_impl(void *ptr) {
|
|
287
|
+
stmt_ctx *ctx = (stmt_ctx *)ptr;
|
|
288
|
+
|
|
289
|
+
if (ctx->flags & STMT_CTX_F_CACHE_HIT) {
|
|
290
|
+
rb_thread_call_with_gvl(exec_bind_parameters, ctx);
|
|
291
|
+
ctx->rc = exec_stmt_iterate(*(ctx->stmtptr));
|
|
292
|
+
if (ctx->rc == SQLITE_OK)
|
|
293
|
+
ctx->total_changes += sqlite3_changes(ctx->db);
|
|
294
|
+
else
|
|
295
|
+
ctx->total_changes = 0;
|
|
296
|
+
finalize_stmt_ctx(ctx);
|
|
297
|
+
return NULL;
|
|
298
|
+
}
|
|
223
299
|
|
|
224
|
-
void *prepare_multi_stmt_impl(void *ptr) {
|
|
225
|
-
prepare_stmt_ctx *ctx = (prepare_stmt_ctx *)ptr;
|
|
226
300
|
const char *rest = NULL;
|
|
227
301
|
const char *str = ctx->str;
|
|
228
302
|
const char *end = ctx->str + ctx->len;
|
|
303
|
+
sqlite3_stmt *next_stmt = NULL;
|
|
304
|
+
ctx->total_changes = 0;
|
|
229
305
|
while (1) {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
306
|
+
if (next_stmt) {
|
|
307
|
+
*(ctx->stmtptr) = next_stmt;
|
|
308
|
+
next_stmt = NULL;
|
|
309
|
+
ctx->rc = SQLITE_OK;
|
|
310
|
+
}
|
|
311
|
+
else
|
|
312
|
+
ctx->rc = sqlite3_prepare_v2(ctx->db, str, end - str, ctx->stmtptr, &rest);
|
|
313
|
+
|
|
314
|
+
if ((ctx->rc != SQLITE_OK) || !(*(ctx->stmtptr))) goto done;
|
|
315
|
+
if (ctx->argc) {
|
|
316
|
+
// parameters were provided - check if str contains multiple statements
|
|
317
|
+
if (rest != end) {
|
|
318
|
+
int res = sqlite3_prepare_v2(ctx->db, rest, end-rest, &next_stmt, NULL);
|
|
319
|
+
if (next_stmt) res = SQLITE_MISUSE;
|
|
320
|
+
if (res != SQLITE_OK) {
|
|
321
|
+
ctx->flags &= ~STMT_CTX_F_USE_CACHE;
|
|
322
|
+
ctx->rc = res;
|
|
323
|
+
goto done;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
rb_thread_call_with_gvl(exec_bind_parameters, ctx);
|
|
235
327
|
}
|
|
236
328
|
|
|
237
|
-
|
|
329
|
+
ctx->rc = exec_stmt_iterate(*(ctx->stmtptr));
|
|
330
|
+
if (ctx->rc != SQLITE_OK) goto done;
|
|
238
331
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
case SQLITE_BUSY:
|
|
244
|
-
case SQLITE_ERROR:
|
|
245
|
-
case SQLITE_MISUSE:
|
|
246
|
-
return NULL;
|
|
247
|
-
}
|
|
332
|
+
ctx->total_changes += sqlite3_changes(ctx->db);
|
|
333
|
+
finalize_stmt_ctx(ctx);
|
|
334
|
+
|
|
335
|
+
if (rest == end) return NULL;
|
|
248
336
|
str = rest;
|
|
249
337
|
}
|
|
338
|
+
done:
|
|
339
|
+
finalize_stmt(&next_stmt);
|
|
340
|
+
finalize_stmt_ctx(ctx);
|
|
250
341
|
return NULL;
|
|
251
342
|
}
|
|
252
343
|
|
|
344
|
+
inline int raise_error(stmt_ctx *ctx) {
|
|
345
|
+
switch (ctx->rc) {
|
|
346
|
+
case SQLITE_BUSY:
|
|
347
|
+
rb_raise(cBusyError, "Database is busy");
|
|
348
|
+
case SQLITE_ERROR:
|
|
349
|
+
rb_raise(cSQLError, "%s", sqlite3_errmsg(ctx->db));
|
|
350
|
+
case SQLITE_MISUSE:
|
|
351
|
+
rb_raise(cError, "Multiple statements cannot take parameters");
|
|
352
|
+
default:
|
|
353
|
+
rb_raise(cError, "%s", sqlite3_errmsg(ctx->db));
|
|
354
|
+
}
|
|
355
|
+
return 0;
|
|
356
|
+
}
|
|
357
|
+
|
|
253
358
|
/*
|
|
254
359
|
This function prepares a statement from an SQL string containing one or more SQL
|
|
255
360
|
statements. It will release the GVL while the statements are being prepared and
|
|
256
361
|
executed. All statements excluding the last one are executed. The last statement
|
|
257
362
|
is not executed, but instead handed back to the caller for looping over results.
|
|
363
|
+
|
|
364
|
+
@return [int] total changes
|
|
258
365
|
*/
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
RB_GC_GUARD(sql);
|
|
366
|
+
int exec_multi_stmt(stmt_ctx *ctx) {
|
|
367
|
+
gvl_call(ctx->gvl_mode, exec_multi_stmt_impl, (void *)ctx);
|
|
368
|
+
if (ctx->rc == SQLITE_OK) return ctx->total_changes;
|
|
263
369
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
case SQLITE_BUSY:
|
|
268
|
-
rb_raise(cBusyError, "Database is busy");
|
|
269
|
-
case SQLITE_ERROR:
|
|
270
|
-
rb_raise(cSQLError, "%s", sqlite3_errmsg(db));
|
|
271
|
-
default:
|
|
272
|
-
rb_raise(cError, "%s", sqlite3_errmsg(db));
|
|
273
|
-
}
|
|
370
|
+
if (*(ctx->stmtptr)) sqlite3_finalize(*(ctx->stmtptr));
|
|
371
|
+
|
|
372
|
+
return raise_error(ctx);
|
|
274
373
|
}
|
|
275
374
|
|
|
276
|
-
|
|
375
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
376
|
+
|
|
377
|
+
void *prep_single_stmt_impl(void *ptr) {
|
|
378
|
+
stmt_ctx *ctx = (stmt_ctx *)ptr;
|
|
379
|
+
|
|
380
|
+
if (ctx->flags & STMT_CTX_F_CACHE_HIT) return NULL;
|
|
277
381
|
|
|
278
|
-
void *prepare_single_stmt_impl(void *ptr) {
|
|
279
|
-
prepare_stmt_ctx *ctx = (prepare_stmt_ctx *)ptr;
|
|
280
382
|
const char *rest = NULL;
|
|
281
383
|
const char *str = ctx->str;
|
|
282
384
|
const char *end = ctx->str + ctx->len;
|
|
283
385
|
|
|
284
|
-
ctx->rc = sqlite3_prepare_v2(ctx->db, str, end - str, ctx->
|
|
285
|
-
if (ctx->rc)
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
ctx->rc = SQLITE_MULTI_STMT;
|
|
289
|
-
goto discard_stmt;
|
|
386
|
+
ctx->rc = sqlite3_prepare_v2(ctx->db, str, end - str, ctx->stmtptr, &rest);
|
|
387
|
+
if (ctx->rc != SQLITE_OK) {
|
|
388
|
+
finalize_stmt(ctx->stmtptr);
|
|
389
|
+
return NULL;
|
|
290
390
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
391
|
+
if (rest != end) {
|
|
392
|
+
sqlite3_stmt *next = NULL;
|
|
393
|
+
ctx->rc = sqlite3_prepare_v2(ctx->db, rest, end - rest, &next, NULL);
|
|
394
|
+
if (next) {
|
|
395
|
+
sqlite3_finalize(next);
|
|
396
|
+
ctx->rc = SQLITE_MISUSE;
|
|
397
|
+
}
|
|
398
|
+
if (ctx->rc != SQLITE_OK) finalize_stmt(ctx->stmtptr);
|
|
296
399
|
}
|
|
297
|
-
end:
|
|
298
400
|
return NULL;
|
|
299
401
|
}
|
|
300
402
|
|
|
301
|
-
void
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
RB_GC_GUARD(sql);
|
|
403
|
+
void prep_single_stmt(stmt_ctx *ctx) {
|
|
404
|
+
gvl_call(ctx->gvl_mode, prep_single_stmt_impl, (void *)ctx);
|
|
405
|
+
if (ctx->rc == SQLITE_OK) return;
|
|
305
406
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
case SQLITE_BUSY:
|
|
310
|
-
rb_raise(cBusyError, "Database is busy");
|
|
311
|
-
case SQLITE_ERROR:
|
|
312
|
-
rb_raise(cSQLError, "%s", sqlite3_errmsg(db));
|
|
313
|
-
case SQLITE_MULTI_STMT:
|
|
314
|
-
rb_raise(cError, "A prepared statement does not accept SQL strings with multiple queries");
|
|
315
|
-
default:
|
|
316
|
-
rb_raise(cError, "%s", sqlite3_errmsg(db));
|
|
317
|
-
}
|
|
407
|
+
if (*(ctx->stmtptr)) sqlite3_finalize(*(ctx->stmtptr));
|
|
408
|
+
|
|
409
|
+
raise_error(ctx);
|
|
318
410
|
}
|
|
319
411
|
|
|
320
412
|
struct step_ctx {
|
|
@@ -362,7 +454,13 @@ inline int stmt_iterate(query_ctx *ctx) {
|
|
|
362
454
|
}
|
|
363
455
|
|
|
364
456
|
VALUE cleanup_stmt(query_ctx *ctx) {
|
|
365
|
-
if (ctx->stmt)
|
|
457
|
+
if (!ctx->stmt) goto done;
|
|
458
|
+
|
|
459
|
+
if (!(ctx->flags & STMT_CTX_F_USE_CACHE))
|
|
460
|
+
sqlite3_finalize(ctx->stmt);
|
|
461
|
+
else if (!(ctx->flags & STMT_CTX_F_CACHE_HIT))
|
|
462
|
+
rb_hash_aset(ctx->db->stmt_cache, ctx->sql, ULONG2NUM((uint64_t)(ctx->stmt)));
|
|
463
|
+
done:
|
|
366
464
|
return Qnil;
|
|
367
465
|
}
|
|
368
466
|
|
|
@@ -503,8 +601,6 @@ VALUE run_transform_no_identity(
|
|
|
503
601
|
static inline VALUE run_transform(
|
|
504
602
|
VALUE identity_storage, struct transform_node *node, sqlite3_stmt *stmt
|
|
505
603
|
) {
|
|
506
|
-
// fprintf(stdout, "transform_container: %p flags: %02x identity_idx: %d\n", node, node->flags, node->identity_idx);
|
|
507
|
-
// if (node->flags & TRANSFORM_F_NAME) INSPECT(" name", node->name);
|
|
508
604
|
if (node->identity_node) {
|
|
509
605
|
return run_transform_with_identity(identity_storage, node, stmt);
|
|
510
606
|
}
|
|
@@ -517,7 +613,7 @@ VALUE safe_query_transform(query_ctx *ctx) {
|
|
|
517
613
|
VALUE array = rb_ary_new();
|
|
518
614
|
VALUE identity_storage = rb_hash_new();
|
|
519
615
|
VALUE row = Qnil;
|
|
520
|
-
// int column_count = sqlite3_column_count(ctx->
|
|
616
|
+
// int column_count = sqlite3_column_count(ctx->stmtptr);
|
|
521
617
|
struct transform_node *transform_root = get_transform_root(ctx->transform);
|
|
522
618
|
|
|
523
619
|
int row_count = 0;
|
|
@@ -542,17 +638,17 @@ done:
|
|
|
542
638
|
return rb_ary_entry(array, 0);
|
|
543
639
|
}
|
|
544
640
|
|
|
545
|
-
return Qnil;
|
|
546
641
|
RB_GC_GUARD(identity_storage);
|
|
547
642
|
RB_GC_GUARD(row);
|
|
548
643
|
RB_GC_GUARD(array);
|
|
644
|
+
return Qnil;
|
|
549
645
|
}
|
|
550
646
|
|
|
551
647
|
VALUE safe_query_single_row_transform(query_ctx *ctx) {
|
|
552
648
|
VALUE array = rb_ary_new();
|
|
553
649
|
VALUE identity_storage = rb_hash_new();
|
|
554
650
|
VALUE row = Qnil;
|
|
555
|
-
// int column_count = sqlite3_column_count(ctx->
|
|
651
|
+
// int column_count = sqlite3_column_count(ctx->stmtptr);
|
|
556
652
|
struct transform_node *transform_root = get_transform_root(ctx->transform);
|
|
557
653
|
|
|
558
654
|
int row_count = 0;
|
|
@@ -572,10 +668,10 @@ VALUE safe_query_single_row_transform(query_ctx *ctx) {
|
|
|
572
668
|
return row;
|
|
573
669
|
}
|
|
574
670
|
|
|
575
|
-
return Qnil;
|
|
576
671
|
RB_GC_GUARD(identity_storage);
|
|
577
672
|
RB_GC_GUARD(row);
|
|
578
673
|
RB_GC_GUARD(array);
|
|
674
|
+
return Qnil;
|
|
579
675
|
}
|
|
580
676
|
|
|
581
677
|
VALUE safe_query_splat(query_ctx *ctx);
|
|
@@ -1008,3 +1104,7 @@ VALUE safe_query_changes(query_ctx *ctx) {
|
|
|
1008
1104
|
while (stmt_iterate(ctx));
|
|
1009
1105
|
return INT2FIX(sqlite3_changes(ctx->sqlite3_db));
|
|
1010
1106
|
}
|
|
1107
|
+
|
|
1108
|
+
VALUE safe_total_changes(query_ctx *ctx) {
|
|
1109
|
+
return INT2FIX(ctx->total_changes);
|
|
1110
|
+
}
|