extralite-bundle 3.1.0 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c98b87050195caee2a3ac6bbbefdd3bf77ed3fe29add59cf18d5162299856d93
4
- data.tar.gz: b0796d7fba1df64d3095d29ff6d8f00bb6f66320467cf54d0ab6f31a164c7c59
3
+ metadata.gz: 7fffde36ad772833bc816f85c26c6472d1c9e6c8e4ba91092734efeb24e2541a
4
+ data.tar.gz: c4200d06ffece67d1241297567b37b3ddb6bc882d72dd6b4ba402a2611f4a1a1
5
5
  SHA512:
6
- metadata.gz: 34f204b0d4abf91ac7b2f114c2e6838515498a068e750c476c7c473a3823e33f871088974b204aed8e98192c1bcfbdec2f8d5f2df1bff770a523724e662a79b7
7
- data.tar.gz: c6b48bf9540dd37a663ab21db610f6aeee0193a3cf539b5cff8ba600475b15cc91c9e8679f7c0a897af0b227b39f597aa8d2da4b378c6a5c2d8aaf64d6a6dc49
6
+ metadata.gz: 97fa8bbe57df8c8f679703764a7b6a185c235cf53c40738bf46f06bea82385fe1a7deed5d2990cb9837a1e1aaf858a5a4a88e5baccbb6117526dce8c3591fd32
7
+ data.tar.gz: 07716c4ba47c25b2ee796ce287e7a87e051176cdbd79badb906ea86fe45fd7526b3cd0180f077cfae3ff6311a7e182b351c36d8696c10feb8ac50714f8dd1dd0
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ # 3.1.1 2026-09-26
2
+
3
+ - Fix clearing of stmt cache after `DB#close` or after GC (#87)
4
+
1
5
  # 3.1.0 2026-09-22
2
6
 
3
7
  - Update bundled SQLite to 3.53.4
data/README.md CHANGED
@@ -49,7 +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] for queries with parameters.
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
@@ -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
 
@@ -318,6 +318,7 @@ void *exec_multi_stmt_impl(void *ptr) {
318
318
  int res = sqlite3_prepare_v2(ctx->db, rest, end-rest, &next_stmt, NULL);
319
319
  if (next_stmt) res = SQLITE_MISUSE;
320
320
  if (res != SQLITE_OK) {
321
+ ctx->flags &= ~STMT_CTX_F_USE_CACHE;
321
322
  ctx->rc = res;
322
323
  goto done;
323
324
  }
@@ -367,6 +368,7 @@ int exec_multi_stmt(stmt_ctx *ctx) {
367
368
  if (ctx->rc == SQLITE_OK) return ctx->total_changes;
368
369
 
369
370
  if (*(ctx->stmtptr)) sqlite3_finalize(*(ctx->stmtptr));
371
+
370
372
  return raise_error(ctx);
371
373
  }
372
374
 
@@ -403,6 +405,7 @@ void prep_single_stmt(stmt_ctx *ctx) {
403
405
  if (ctx->rc == SQLITE_OK) return;
404
406
 
405
407
  if (*(ctx->stmtptr)) sqlite3_finalize(*(ctx->stmtptr));
408
+
406
409
  raise_error(ctx);
407
410
  }
408
411
 
@@ -453,12 +456,10 @@ inline int stmt_iterate(query_ctx *ctx) {
453
456
  VALUE cleanup_stmt(query_ctx *ctx) {
454
457
  if (!ctx->stmt) goto done;
455
458
 
456
- if (ctx->flags & STMT_CTX_F_USE_CACHE) {
457
- if (!(ctx->flags & STMT_CTX_F_CACHE_HIT))
458
- rb_hash_aset(ctx->db->stmt_cache, ctx->sql, ULONG2NUM((uint64_t)(ctx->stmt)));
459
- }
460
- else
459
+ if (!(ctx->flags & STMT_CTX_F_USE_CACHE))
461
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)));
462
463
  done:
463
464
  return Qnil;
464
465
  }
@@ -600,8 +601,6 @@ VALUE run_transform_no_identity(
600
601
  static inline VALUE run_transform(
601
602
  VALUE identity_storage, struct transform_node *node, sqlite3_stmt *stmt
602
603
  ) {
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
604
  if (node->identity_node) {
606
605
  return run_transform_with_identity(identity_storage, node, stmt);
607
606
  }
@@ -72,9 +72,27 @@ 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_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
+
75
90
  static void Database_free(void *ptr) {
76
91
  Database_t *db = ptr;
77
- if (db->sqlite3_db) sqlite3_close_v2(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
+ }
78
96
  free(ptr);
79
97
  }
80
98
 
@@ -261,6 +279,7 @@ VALUE Database_close(VALUE self) {
261
279
  int rc;
262
280
  Database_t *db = self_to_database(self);
263
281
 
282
+ stmt_cache_clear(db->sqlite3_db, 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, RARRAY_LEN(parameters), NULL);
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
  }
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Extralite
4
- VERSION = '3.1.0'
4
+ VERSION = '3.1.1'
5
5
  end
data/test/helper.rb CHANGED
@@ -16,3 +16,13 @@ module Minitest::Assertions
16
16
  assert exp_range.include?(act), msg
17
17
  end
18
18
  end
19
+
20
+ module TestExtensions
21
+ def teardown
22
+ @db = nil
23
+ GC.start
24
+ super
25
+ end
26
+ end
27
+
28
+ Minitest::Test.prepend(TestExtensions)
@@ -2225,4 +2225,39 @@ class StmtCacheText < Minitest::Test
2225
2225
  assert_equal [{ x: 42 }], rows
2226
2226
  assert_equal({}, @db.stmt_cache)
2227
2227
  end
2228
+
2229
+ def test_stmt_cache_close
2230
+ fn = Tempfile.new('extralite_test_stmt_cache_close').path
2231
+
2232
+ db = Extralite::Database.new(fn)
2233
+ db.query('create table t (x)')
2234
+
2235
+ db.execute <<~SQL
2236
+ insert into t values (42)
2237
+ SQL
2238
+ assert File.file?("#{fn}-wal"), "WAL file should exist"
2239
+
2240
+ assert_equal [{x: 42}], db.query('select * from t where x = ?', 42)
2241
+
2242
+ db.close
2243
+ refute File.file?("#{fn}-wal"), "WAL file should not exist after close"
2244
+ end
2245
+
2246
+ def test_stmt_cache_gc
2247
+ fn = Tempfile.new('extralite_test_stmt_cache_close').path
2248
+
2249
+ db = Extralite::Database.new(fn)
2250
+ db.query('create table t (x)')
2251
+
2252
+ db.execute <<~SQL
2253
+ insert into t values (42)
2254
+ SQL
2255
+ assert File.file?("#{fn}-wal"), "WAL file should exist"
2256
+
2257
+ assert_equal [{x: 42}], db.query('select * from t where x = ?', 42)
2258
+
2259
+ db = nil
2260
+ GC.start
2261
+ refute File.file?("#{fn}-wal"), "WAL file should not exist after GC"
2262
+ end
2228
2263
  end
data/test/test_sequel.rb CHANGED
@@ -20,6 +20,7 @@ class SequelExtraliteTest < Minitest::Test
20
20
 
21
21
  def teardown
22
22
  @db.disconnect
23
+ super
23
24
  end
24
25
 
25
26
  def test_sequel
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.0
4
+ version: 3.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner