extralite-bundle 3.1.0 → 3.1.2
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 +11 -0
- data/Gemfile +0 -4
- data/LICENSE +1 -1
- data/README.md +37 -2
- data/TODO.md +6 -0
- data/ext/extralite/common.c +11 -10
- data/ext/extralite/database.c +24 -4
- data/gemspec.rb +3 -3
- data/lib/extralite/version.rb +1 -1
- data/lib/extralite.rb +25 -1
- data/test/helper.rb +10 -0
- data/test/test_database.rb +146 -0
- data/test/test_sequel.rb +1 -0
- metadata +11 -11
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9eac653308555c057d121db6b0e17905fff645a62d926bdd3ac07323f40f8f9e
|
|
4
|
+
data.tar.gz: 2bcfeb952ce57afa647b969016891edae0fd615fc78f97a0a6b0336cc35791d6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: aa4d929c51e1f2c05fc0e67aa37447281c50d697b9dff242ca6f96e3723027e03b3db5b4c437399f28045a12d251dfff3e0f3a47709302b132347cca9c7fa514
|
|
7
|
+
data.tar.gz: 9809ca98906d5b853e646814057b976a55171249ae85b0502eea8ab39032d725889d736d3e9eff71ee46c9ca31d64e81f42102936753e69a2714188abec3bca5
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
# 3.1.2 2026-10-03
|
|
2
|
+
|
|
3
|
+
- Fix handling of failed BEGIN in #transaction (#89)
|
|
4
|
+
- Reset of cached statements post-query (#88)
|
|
5
|
+
- Add missing wal checkpoint, integrity_check docs to README
|
|
6
|
+
- Implement `Database#integrity_check`, `Database#quick_check`
|
|
7
|
+
|
|
8
|
+
# 3.1.1 2026-09-26
|
|
9
|
+
|
|
10
|
+
- Fix clearing of stmt cache after `DB#close` or after GC (#87)
|
|
11
|
+
|
|
1
12
|
# 3.1.0 2026-09-22
|
|
2
13
|
|
|
3
14
|
- Update bundled SQLite to 3.53.4
|
data/Gemfile
CHANGED
data/LICENSE
CHANGED
data/README.md
CHANGED
|
@@ -49,14 +49,14 @@ 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] for queries
|
|
52
|
+
- [Automatic query caching](#automatic-query-caching) for parametric queries.
|
|
53
53
|
- [Batch execution](#batch-execution-of-queries) of queries.
|
|
54
54
|
- [transactions and savepoints](#transactions-and-savepoints).
|
|
55
55
|
- Advanced features: load [SQLite extensions](#loading-extensions), create
|
|
56
56
|
[backups](#creating-backups), retrieve [status
|
|
57
57
|
information](#retrieving-status-information), work with
|
|
58
58
|
[changesets](#working-with-changesets), interrogate [database
|
|
59
|
-
limits](#working-with-database-limits), [trace](#tracing-sql-statements)
|
|
59
|
+
limits](#working-with-database-limits), [trace](#tracing-sql-statements), [WAL checkpoints](#wal-checkpoints), and run [integrity checks](#integrity-checks).
|
|
60
60
|
queries.
|
|
61
61
|
- [Sequel](#usage-with-sequel) adapter.
|
|
62
62
|
|
|
@@ -363,6 +363,16 @@ To use the transform, pass it along with the SQL string to `Database#query`:
|
|
|
363
363
|
db.qurey(transform, sql) #=> [...]
|
|
364
364
|
```
|
|
365
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
|
+
|
|
366
376
|
Transforms can also be used with [prepared
|
|
367
377
|
queries](#transforms-in-prepared-queries).
|
|
368
378
|
|
|
@@ -1303,6 +1313,31 @@ db.query('select ?, ?', 1, 2)
|
|
|
1303
1313
|
# { sql: "select ?, ?", args: [1, 2] }
|
|
1304
1314
|
```
|
|
1305
1315
|
|
|
1316
|
+
### WAL Checkpoints
|
|
1317
|
+
|
|
1318
|
+
Exrtralite lets you manually perform a WAL checkpoint in order to truncate the
|
|
1319
|
+
WAL file at any given moment using the `Database#wal_checkpoint` method:
|
|
1320
|
+
|
|
1321
|
+
```ruby
|
|
1322
|
+
db.wal_checkpoint(:full)
|
|
1323
|
+
```
|
|
1324
|
+
|
|
1325
|
+
The `#wal_checkpoint` method takes a mode argument, which should be one
|
|
1326
|
+
`:passive`, `:full`, `:restart`, `:truncate`. For a discussion of the different
|
|
1327
|
+
modes, you can refer to the [SQLite
|
|
1328
|
+
documentation](https://sqlite.org/c3ref/wal_checkpoint_v2.html).
|
|
1329
|
+
|
|
1330
|
+
### Integrity Checks
|
|
1331
|
+
|
|
1332
|
+
Extralite provides two methods for performing integrity checks,
|
|
1333
|
+
`Database#integrity_check` and `Database#quick_check`, which map to the
|
|
1334
|
+
corresponding pragmas. The two methods return an array of error messages. In the
|
|
1335
|
+
case of a normal, uncorrupted database, the methods will return `['ok']`:
|
|
1336
|
+
|
|
1337
|
+
```ruby
|
|
1338
|
+
db.integrity_check #=> ['ok'] (hopefully)
|
|
1339
|
+
```
|
|
1340
|
+
|
|
1306
1341
|
## Usage with Sequel
|
|
1307
1342
|
|
|
1308
1343
|
Extralite includes an adapter for
|
data/TODO.md
CHANGED
data/ext/extralite/common.c
CHANGED
|
@@ -226,6 +226,8 @@ static inline void finalize_stmt_ctx(stmt_ctx *ctx) {
|
|
|
226
226
|
return;
|
|
227
227
|
}
|
|
228
228
|
|
|
229
|
+
sqlite3_reset(*(ctx->stmtptr));
|
|
230
|
+
sqlite3_clear_bindings(*(ctx->stmtptr));
|
|
229
231
|
if (!(ctx->flags & STMT_CTX_F_CACHE_HIT))
|
|
230
232
|
rb_hash_aset(ctx->stmt_cache, ctx->sql, ULONG2NUM((uint64_t)*(ctx->stmtptr)));
|
|
231
233
|
}
|
|
@@ -241,11 +243,7 @@ void make_stmt_ctx(
|
|
|
241
243
|
|
|
242
244
|
int use_cache = (argc > 0) && (db->flags & DB_F_STMT_CACHE);
|
|
243
245
|
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
|
-
}
|
|
246
|
+
if (use_cache) lookup_cache_entry(ctx);
|
|
249
247
|
|
|
250
248
|
if (!use_cache || !(*(ctx->stmtptr))) {
|
|
251
249
|
ctx->str = RSTRING_PTR(sql);
|
|
@@ -318,6 +316,7 @@ void *exec_multi_stmt_impl(void *ptr) {
|
|
|
318
316
|
int res = sqlite3_prepare_v2(ctx->db, rest, end-rest, &next_stmt, NULL);
|
|
319
317
|
if (next_stmt) res = SQLITE_MISUSE;
|
|
320
318
|
if (res != SQLITE_OK) {
|
|
319
|
+
ctx->flags &= ~STMT_CTX_F_USE_CACHE;
|
|
321
320
|
ctx->rc = res;
|
|
322
321
|
goto done;
|
|
323
322
|
}
|
|
@@ -367,6 +366,7 @@ int exec_multi_stmt(stmt_ctx *ctx) {
|
|
|
367
366
|
if (ctx->rc == SQLITE_OK) return ctx->total_changes;
|
|
368
367
|
|
|
369
368
|
if (*(ctx->stmtptr)) sqlite3_finalize(*(ctx->stmtptr));
|
|
369
|
+
|
|
370
370
|
return raise_error(ctx);
|
|
371
371
|
}
|
|
372
372
|
|
|
@@ -403,6 +403,7 @@ void prep_single_stmt(stmt_ctx *ctx) {
|
|
|
403
403
|
if (ctx->rc == SQLITE_OK) return;
|
|
404
404
|
|
|
405
405
|
if (*(ctx->stmtptr)) sqlite3_finalize(*(ctx->stmtptr));
|
|
406
|
+
|
|
406
407
|
raise_error(ctx);
|
|
407
408
|
}
|
|
408
409
|
|
|
@@ -453,12 +454,14 @@ inline int stmt_iterate(query_ctx *ctx) {
|
|
|
453
454
|
VALUE cleanup_stmt(query_ctx *ctx) {
|
|
454
455
|
if (!ctx->stmt) goto done;
|
|
455
456
|
|
|
456
|
-
if (ctx->flags & STMT_CTX_F_USE_CACHE)
|
|
457
|
+
if (!(ctx->flags & STMT_CTX_F_USE_CACHE))
|
|
458
|
+
sqlite3_finalize(ctx->stmt);
|
|
459
|
+
else {
|
|
460
|
+
sqlite3_reset(ctx->stmt);
|
|
461
|
+
sqlite3_clear_bindings(ctx->stmt);
|
|
457
462
|
if (!(ctx->flags & STMT_CTX_F_CACHE_HIT))
|
|
458
463
|
rb_hash_aset(ctx->db->stmt_cache, ctx->sql, ULONG2NUM((uint64_t)(ctx->stmt)));
|
|
459
464
|
}
|
|
460
|
-
else
|
|
461
|
-
sqlite3_finalize(ctx->stmt);
|
|
462
465
|
done:
|
|
463
466
|
return Qnil;
|
|
464
467
|
}
|
|
@@ -600,8 +603,6 @@ VALUE run_transform_no_identity(
|
|
|
600
603
|
static inline VALUE run_transform(
|
|
601
604
|
VALUE identity_storage, struct transform_node *node, sqlite3_stmt *stmt
|
|
602
605
|
) {
|
|
603
|
-
// fprintf(stdout, "transform_container: %p flags: %02x identity_idx: %d\n", node, node->flags, node->identity_idx);
|
|
604
|
-
// if (node->flags & TRANSFORM_F_NAME) INSPECT(" name", node->name);
|
|
605
606
|
if (node->identity_node) {
|
|
606
607
|
return run_transform_with_identity(identity_storage, node, stmt);
|
|
607
608
|
}
|
data/ext/extralite/database.c
CHANGED
|
@@ -72,9 +72,26 @@ static void Database_compact(void *ptr) {
|
|
|
72
72
|
db->progress_handler.proc = rb_gc_location(db->progress_handler.proc);
|
|
73
73
|
}
|
|
74
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_finalize(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_GC_GUARD(stmt_cache);
|
|
87
|
+
}
|
|
88
|
+
|
|
75
89
|
static void Database_free(void *ptr) {
|
|
76
90
|
Database_t *db = ptr;
|
|
77
|
-
if (db->sqlite3_db)
|
|
91
|
+
if (db->sqlite3_db) {
|
|
92
|
+
stmt_cache_finalize(db->sqlite3_db, db->stmt_cache);
|
|
93
|
+
sqlite3_close_v2(db->sqlite3_db);
|
|
94
|
+
}
|
|
78
95
|
free(ptr);
|
|
79
96
|
}
|
|
80
97
|
|
|
@@ -261,6 +278,8 @@ VALUE Database_close(VALUE self) {
|
|
|
261
278
|
int rc;
|
|
262
279
|
Database_t *db = self_to_database(self);
|
|
263
280
|
|
|
281
|
+
stmt_cache_finalize(db->sqlite3_db, db->stmt_cache);
|
|
282
|
+
rb_hash_clear(db->stmt_cache);
|
|
264
283
|
rc = sqlite3_close_v2(db->sqlite3_db);
|
|
265
284
|
if (rc) {
|
|
266
285
|
rb_raise(cError, "%s", sqlite3_errmsg(db->sqlite3_db));
|
|
@@ -573,13 +592,14 @@ VALUE Database_batch_execute(VALUE self, VALUE sql, VALUE parameters) {
|
|
|
573
592
|
if (RSTRING_LEN(sql) == 0) return Qnil;
|
|
574
593
|
|
|
575
594
|
stmt_ctx stmt_ctx;
|
|
576
|
-
make_stmt_ctx(&stmt_ctx, db, &stmt, sql,
|
|
595
|
+
make_stmt_ctx(&stmt_ctx, db, &stmt, sql, 1, NULL);
|
|
577
596
|
prep_single_stmt(&stmt_ctx);
|
|
578
597
|
// prepare_xsingle_stmt(DB_GVL_MODE(db), db->stmt_cache, db->sqlite3_db, &stmt, sql, 0, NULL);
|
|
579
598
|
query_ctx ctx = QUERY_CTX(
|
|
580
599
|
self, sql, db, stmt, parameters,
|
|
581
600
|
Qnil, QUERY_HASH, ROW_MULTI, ALL_ROWS
|
|
582
601
|
);
|
|
602
|
+
ctx.flags = stmt_ctx.flags;
|
|
583
603
|
|
|
584
604
|
return rb_ensure(SAFE(safe_batch_execute), (VALUE)&ctx, SAFE(cleanup_stmt), (VALUE)&ctx);
|
|
585
605
|
}
|
|
@@ -1545,11 +1565,11 @@ int checkpoint_mode_symbol_to_int(VALUE mode) {
|
|
|
1545
1565
|
*
|
|
1546
1566
|
* @overload wal_checkpoint(mode)
|
|
1547
1567
|
* @param mode [Symbol] checkpoint mode (`:passive`, `:full`, `:restart`, `:truncate`)
|
|
1548
|
-
* @return [Array<
|
|
1568
|
+
* @return [Array<Integer>] total and checkpointed frame count
|
|
1549
1569
|
* @overload wal_checkpoint(mode, db_name)
|
|
1550
1570
|
* @param mode [Symbol] checkpoint mode (`:passive`, `:full`, `:restart`, `:truncate`)
|
|
1551
1571
|
* @param db_name [String] attached database name
|
|
1552
|
-
* @return [Array<
|
|
1572
|
+
* @return [Array<Integer>] total and checkpointed frame count
|
|
1553
1573
|
*/
|
|
1554
1574
|
VALUE Database_wal_checkpoint(int argc, VALUE *argv, VALUE self) {
|
|
1555
1575
|
Database_t *db = self_to_open_database(self);
|
data/gemspec.rb
CHANGED
|
@@ -19,7 +19,7 @@ def common_spec(s)
|
|
|
19
19
|
|
|
20
20
|
s.add_development_dependency 'rake-compiler', '1.3.1'
|
|
21
21
|
s.add_development_dependency 'minitest'
|
|
22
|
-
s.add_development_dependency 'simplecov'
|
|
23
|
-
s.add_development_dependency 'yard'
|
|
24
|
-
s.add_development_dependency 'sequel', '5.
|
|
22
|
+
s.add_development_dependency 'simplecov'
|
|
23
|
+
s.add_development_dependency 'yard'
|
|
24
|
+
s.add_development_dependency 'sequel', '5.108.0'
|
|
25
25
|
end
|
data/lib/extralite/version.rb
CHANGED
data/lib/extralite.rb
CHANGED
|
@@ -215,6 +215,29 @@ module Extralite
|
|
|
215
215
|
value.is_a?(Hash) ? pragma_set(value) : pragma_get(value)
|
|
216
216
|
end
|
|
217
217
|
|
|
218
|
+
# Performs a low-level formatting and consistency check of the database
|
|
219
|
+
# using PRAGMA integrity_check. See:
|
|
220
|
+
# https://www.sqlite.org/pragma.html#pragma_integrity_check
|
|
221
|
+
#
|
|
222
|
+
# @param v [Integer, String, nil] max errors or table name
|
|
223
|
+
# @return [Array<String>] array of error strings or 'ok'
|
|
224
|
+
def integrity_check(v = nil)
|
|
225
|
+
query_splat(
|
|
226
|
+
v ? "pragma integrity_check(#{v})" : 'pragma integrity_check'
|
|
227
|
+
)
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
# Performs a quicker integrity check of the database using PRAGMA
|
|
231
|
+
# quick_check. See: https://www.sqlite.org/pragma.html#pragma_quick_check
|
|
232
|
+
#
|
|
233
|
+
# @param v [Integer, String, nil] max errors or table name
|
|
234
|
+
# @return [Array<String>] array of error strings or 'ok'
|
|
235
|
+
def quick_check(v = nil)
|
|
236
|
+
query_splat(
|
|
237
|
+
v ? "pragma quick_check(#{v})" : 'pragma quick_check'
|
|
238
|
+
)
|
|
239
|
+
end
|
|
240
|
+
|
|
218
241
|
# Error class used to roll back a transaction without propagating an
|
|
219
242
|
# exception.
|
|
220
243
|
class Rollback < Error
|
|
@@ -237,12 +260,13 @@ module Extralite
|
|
|
237
260
|
def transaction(mode = :immediate)
|
|
238
261
|
abort = false
|
|
239
262
|
execute "begin #{mode} transaction"
|
|
263
|
+
started = true
|
|
240
264
|
yield self
|
|
241
265
|
rescue => e
|
|
242
266
|
abort = true
|
|
243
267
|
e.is_a?(Rollback) ? nil : raise
|
|
244
268
|
ensure
|
|
245
|
-
execute(abort ? 'rollback' : 'commit')
|
|
269
|
+
execute(abort ? 'rollback' : 'commit') if started
|
|
246
270
|
end
|
|
247
271
|
|
|
248
272
|
# Creates a savepoint with the given name. For more information on
|
data/test/helper.rb
CHANGED
data/test/test_database.rb
CHANGED
|
@@ -372,6 +372,86 @@ class DatabaseTest < Minitest::Test
|
|
|
372
372
|
# assert_equal 1, @db.pragma('foreign_keys')
|
|
373
373
|
end
|
|
374
374
|
|
|
375
|
+
def test_integrity_check_ok
|
|
376
|
+
r = @db.integrity_check
|
|
377
|
+
assert_equal ['ok'], r
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
def test_integrity_check_bad_var
|
|
381
|
+
assert_raises(Extralite::Error) { @db.integrity_check('foobar') }
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
def test_integrity_check_damaged
|
|
385
|
+
fn = Tempfile.new('extralite_test_integrity_check_damaged').path
|
|
386
|
+
db = Extralite::Database.new(fn)
|
|
387
|
+
|
|
388
|
+
db.execute <<~SQL
|
|
389
|
+
create table foo (x);
|
|
390
|
+
SQL
|
|
391
|
+
|
|
392
|
+
db.batch_execute("insert into foo values (?)", 1..100)
|
|
393
|
+
|
|
394
|
+
assert_equal (1..100).to_a, db.query_splat('select * from foo')
|
|
395
|
+
db.close
|
|
396
|
+
|
|
397
|
+
File.open(fn, 'r+b') do |f|
|
|
398
|
+
1.times do
|
|
399
|
+
f.seek(8015)
|
|
400
|
+
f.write('\x00' * 16)
|
|
401
|
+
end
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
db = Extralite::Database.new(fn, legacy: true)
|
|
405
|
+
assert_raises(Extralite::Error) { db.query_splat('select * from foo') }
|
|
406
|
+
r = db.integrity_check
|
|
407
|
+
assert_equal 2, r.size
|
|
408
|
+
assert_equal 'database disk image is malformed', r[1]
|
|
409
|
+
|
|
410
|
+
r = db.integrity_check(1)
|
|
411
|
+
assert_equal 1, r.size
|
|
412
|
+
assert_match /^\*\*\* in database main \*\*\*/, r[0]
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
def test_quick_check_ok
|
|
416
|
+
r = @db.quick_check
|
|
417
|
+
assert_equal ['ok'], r
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
def test_quick_check_bad_var
|
|
421
|
+
assert_raises(Extralite::Error) { @db.quick_check('foobar') }
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
def test_quick_check_damaged
|
|
425
|
+
fn = Tempfile.new('extralite_test_quick_check_damaged').path
|
|
426
|
+
db = Extralite::Database.new(fn)
|
|
427
|
+
|
|
428
|
+
db.execute <<~SQL
|
|
429
|
+
create table foo (x);
|
|
430
|
+
SQL
|
|
431
|
+
|
|
432
|
+
db.batch_execute("insert into foo values (?)", 1..100)
|
|
433
|
+
|
|
434
|
+
assert_equal (1..100).to_a, db.query_splat('select * from foo')
|
|
435
|
+
db.close
|
|
436
|
+
|
|
437
|
+
File.open(fn, 'r+b') do |f|
|
|
438
|
+
1.times do
|
|
439
|
+
f.seek(8015)
|
|
440
|
+
f.write('\x00' * 16)
|
|
441
|
+
end
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
db = Extralite::Database.new(fn, legacy: true)
|
|
445
|
+
assert_raises(Extralite::Error) { db.query_splat('select * from foo') }
|
|
446
|
+
r = db.quick_check
|
|
447
|
+
assert_equal 2, r.size
|
|
448
|
+
assert_equal 'database disk image is malformed', r[1]
|
|
449
|
+
|
|
450
|
+
r = db.integrity_check(1)
|
|
451
|
+
assert_equal 1, r.size
|
|
452
|
+
assert_match /^\*\*\* in database main \*\*\*/, r[0]
|
|
453
|
+
end
|
|
454
|
+
|
|
375
455
|
def test_execute
|
|
376
456
|
changes = @db.execute('update t set x = 42')
|
|
377
457
|
assert_equal 2, changes
|
|
@@ -2225,4 +2305,70 @@ class StmtCacheText < Minitest::Test
|
|
|
2225
2305
|
assert_equal [{ x: 42 }], rows
|
|
2226
2306
|
assert_equal({}, @db.stmt_cache)
|
|
2227
2307
|
end
|
|
2308
|
+
|
|
2309
|
+
def test_stmt_cache_close
|
|
2310
|
+
fn = Tempfile.new('extralite_test_stmt_cache_close').path
|
|
2311
|
+
|
|
2312
|
+
db = Extralite::Database.new(fn)
|
|
2313
|
+
db.query('create table t (x)')
|
|
2314
|
+
|
|
2315
|
+
db.execute <<~SQL
|
|
2316
|
+
insert into t values (42)
|
|
2317
|
+
SQL
|
|
2318
|
+
assert File.file?("#{fn}-wal"), "WAL file should exist"
|
|
2319
|
+
|
|
2320
|
+
assert_equal [{x: 42}], db.query('select * from t where x = ?', 42)
|
|
2321
|
+
|
|
2322
|
+
db.close
|
|
2323
|
+
refute File.file?("#{fn}-wal"), "WAL file should not exist after close"
|
|
2324
|
+
end
|
|
2325
|
+
|
|
2326
|
+
def test_stmt_cache_gc
|
|
2327
|
+
fn = Tempfile.new('extralite_test_stmt_cache_gc').path
|
|
2328
|
+
|
|
2329
|
+
db = Extralite::Database.new(fn)
|
|
2330
|
+
db.query('create table t (x)')
|
|
2331
|
+
|
|
2332
|
+
db.execute <<~SQL
|
|
2333
|
+
insert into t values (42)
|
|
2334
|
+
SQL
|
|
2335
|
+
assert File.file?("#{fn}-wal"), "WAL file should exist"
|
|
2336
|
+
|
|
2337
|
+
assert_equal [{x: 42}], db.query('select * from t where x = ?', 42)
|
|
2338
|
+
|
|
2339
|
+
db = nil
|
|
2340
|
+
GC.start
|
|
2341
|
+
refute File.file?("#{fn}-wal"), "WAL file should not exist after GC"
|
|
2342
|
+
end
|
|
2343
|
+
|
|
2344
|
+
def test_stmt_cache_concurrent_access
|
|
2345
|
+
# https://github.com/digital-fabric/extralite/issues/88
|
|
2346
|
+
fn = Tempfile.new('extralite_test_stmt_cache_concurrent_access').path
|
|
2347
|
+
|
|
2348
|
+
a = Extralite::Database.new(fn)
|
|
2349
|
+
a.execute("pragma journal_mode=wal")
|
|
2350
|
+
a.execute("create table t (id integer primary key, x)")
|
|
2351
|
+
a.execute("insert into t (x) values (1)")
|
|
2352
|
+
a.query_single("select * from t where id = ?", 1)
|
|
2353
|
+
|
|
2354
|
+
b = Extralite::Database.new(fn)
|
|
2355
|
+
b.execute("insert into t (x) values (2)")
|
|
2356
|
+
|
|
2357
|
+
assert_equal 2, b.query_single_splat("select count(*) from t")
|
|
2358
|
+
assert_equal 2, a.query_single_splat("select count(*) from t")
|
|
2359
|
+
end
|
|
2360
|
+
|
|
2361
|
+
def test_stmt_cache_concurrent_transactions
|
|
2362
|
+
# https://github.com/digital-fabric/extralite/issues/89
|
|
2363
|
+
fn = Tempfile.new('extralite_test_stmt_cache_concurrent_access').path
|
|
2364
|
+
|
|
2365
|
+
a = Extralite::Database.new(fn)
|
|
2366
|
+
a.execute('create table t (x)')
|
|
2367
|
+
b = Extralite::Database.new(fn)
|
|
2368
|
+
b.execute('begin immediate transaction')
|
|
2369
|
+
|
|
2370
|
+
assert_raises(Extralite::BusyError) {
|
|
2371
|
+
a.transaction { a.execute("insert into t values (1)") }
|
|
2372
|
+
}
|
|
2373
|
+
end
|
|
2228
2374
|
end
|
data/test/test_sequel.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: extralite-bundle
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.1.
|
|
4
|
+
version: 3.1.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sharon Rosner
|
|
@@ -41,44 +41,44 @@ dependencies:
|
|
|
41
41
|
name: simplecov
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
|
43
43
|
requirements:
|
|
44
|
-
- -
|
|
44
|
+
- - ">="
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
|
-
version: 0
|
|
46
|
+
version: '0'
|
|
47
47
|
type: :development
|
|
48
48
|
prerelease: false
|
|
49
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
50
|
requirements:
|
|
51
|
-
- -
|
|
51
|
+
- - ">="
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
|
-
version: 0
|
|
53
|
+
version: '0'
|
|
54
54
|
- !ruby/object:Gem::Dependency
|
|
55
55
|
name: yard
|
|
56
56
|
requirement: !ruby/object:Gem::Requirement
|
|
57
57
|
requirements:
|
|
58
|
-
- -
|
|
58
|
+
- - ">="
|
|
59
59
|
- !ruby/object:Gem::Version
|
|
60
|
-
version: 0
|
|
60
|
+
version: '0'
|
|
61
61
|
type: :development
|
|
62
62
|
prerelease: false
|
|
63
63
|
version_requirements: !ruby/object:Gem::Requirement
|
|
64
64
|
requirements:
|
|
65
|
-
- -
|
|
65
|
+
- - ">="
|
|
66
66
|
- !ruby/object:Gem::Version
|
|
67
|
-
version: 0
|
|
67
|
+
version: '0'
|
|
68
68
|
- !ruby/object:Gem::Dependency
|
|
69
69
|
name: sequel
|
|
70
70
|
requirement: !ruby/object:Gem::Requirement
|
|
71
71
|
requirements:
|
|
72
72
|
- - '='
|
|
73
73
|
- !ruby/object:Gem::Version
|
|
74
|
-
version: 5.
|
|
74
|
+
version: 5.108.0
|
|
75
75
|
type: :development
|
|
76
76
|
prerelease: false
|
|
77
77
|
version_requirements: !ruby/object:Gem::Requirement
|
|
78
78
|
requirements:
|
|
79
79
|
- - '='
|
|
80
80
|
- !ruby/object:Gem::Version
|
|
81
|
-
version: 5.
|
|
81
|
+
version: 5.108.0
|
|
82
82
|
email: sharon@noteflakes.com
|
|
83
83
|
executables: []
|
|
84
84
|
extensions:
|