From f24dfaa4c5a9de9b9321103855135bf3e1840e29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guilherme=20Ara=C3=BAjo?= Date: Wed, 15 Apr 2026 14:25:59 +0100 Subject: [PATCH 1/4] sqlite: add stmt persistent flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Guilherme Araújo --- doc/api/sqlite.md | 9 +++-- src/node_sqlite.cc | 26 +++++++++++++- test/parallel/test-sqlite-statement-sync.js | 39 +++++++++++++++++++++ 3 files changed, 71 insertions(+), 3 deletions(-) diff --git a/doc/api/sqlite.md b/doc/api/sqlite.md index e50a66e92621..5f4eea61e453 100644 --- a/doc/api/sqlite.md +++ b/doc/api/sqlite.md @@ -690,10 +690,14 @@ changes: database options or `true`. * `allowUnknownNamedParameters` {boolean} If `true`, unknown named parameters are ignored. **Default:** inherited from database options or `false`. + * `persistent` {boolean} If `true`, hints to SQLite that this statement will + be reused many times, causing it to use a different memory allocation + strategy that reduces heap fragmentation. Corresponds to the + [`SQLITE_PREPARE_PERSISTENT`][] flag. **Default:** `false`. * Returns: {StatementSync} The prepared statement. Compiles a SQL statement into a [prepared statement][]. This method is a wrapper -around [`sqlite3_prepare_v2()`][]. +around [`sqlite3_prepare_v3()`][]. ### `database.createTagStore([maxSize])` @@ -1860,6 +1864,7 @@ callback function to indicate what type of operation is being authorized. [`SQLITE_DETERMINISTIC`]: https://www.sqlite.org/c3ref/c_deterministic.html [`SQLITE_DIRECTONLY`]: https://www.sqlite.org/c3ref/c_deterministic.html [`SQLITE_MAX_FUNCTION_ARG`]: https://www.sqlite.org/limits.html#max_function_arg +[`SQLITE_PREPARE_PERSISTENT`]: https://sqlite.org/c3ref/c_prepare_dont_log.html#sqlitepreparepersistent [`SQLTagStore`]: #class-sqltagstore [`database.applyChangeset()`]: #databaseapplychangesetchangeset-options [`database.createTagStore()`]: #databasecreatetagstoremaxsize @@ -1885,7 +1890,7 @@ callback function to indicate what type of operation is being authorized. [`sqlite3_get_autocommit()`]: https://sqlite.org/c3ref/get_autocommit.html [`sqlite3_last_insert_rowid()`]: https://www.sqlite.org/c3ref/last_insert_rowid.html [`sqlite3_load_extension()`]: https://www.sqlite.org/c3ref/load_extension.html -[`sqlite3_prepare_v2()`]: https://www.sqlite.org/c3ref/prepare.html +[`sqlite3_prepare_v3()`]: https://www.sqlite.org/c3ref/prepare.html [`sqlite3_serialize()`]: https://sqlite.org/c3ref/serialize.html [`sqlite3_set_authorizer()`]: https://sqlite.org/c3ref/set_authorizer.html [`sqlite3_sql()`]: https://www.sqlite.org/c3ref/expanded_sql.html diff --git a/src/node_sqlite.cc b/src/node_sqlite.cc index 156d4f2e5f5c..f6a9c0f3a1a9 100644 --- a/src/node_sqlite.cc +++ b/src/node_sqlite.cc @@ -1510,6 +1510,7 @@ void DatabaseSync::Prepare(const FunctionCallbackInfo& args) { std::optional use_big_ints; std::optional allow_bare_named_params; std::optional allow_unknown_named_params; + std::optional persistent; if (args.Length() > 1 && !args[1]->IsUndefined()) { if (!args[1]->IsObject()) { @@ -1590,11 +1591,34 @@ void DatabaseSync::Prepare(const FunctionCallbackInfo& args) { } allow_unknown_named_params = allow_unknown_named_params_v->IsTrue(); } + + Local persistent_v; + if (!options + ->Get(env->context(), + FIXED_ONE_BYTE_STRING(env->isolate(), "persistent")) + .ToLocal(&persistent_v)) { + return; + } + if (!persistent_v->IsUndefined()) { + if (!persistent_v->IsBoolean()) { + THROW_ERR_INVALID_ARG_TYPE( + env->isolate(), + "The \"options.persistent\" argument must be a boolean."); + return; + } + persistent = persistent_v->IsTrue(); + } } Utf8Value sql(env->isolate(), args[0].As()); sqlite3_stmt* s = nullptr; - int r = sqlite3_prepare_v2(db->connection_, *sql, -1, &s, nullptr); + + unsigned int prep_flags = + persistent.value_or(false) ? SQLITE_PREPARE_PERSISTENT : 0; + + int r = + sqlite3_prepare_v3(db->connection_, *sql, -1, prep_flags, &s, nullptr); + StatementPtr stmt_ptr(s); CHECK_ERROR_OR_THROW(env->isolate(), db, r, SQLITE_OK, void()); diff --git a/test/parallel/test-sqlite-statement-sync.js b/test/parallel/test-sqlite-statement-sync.js index a52634f59ac6..96e5e1f99771 100644 --- a/test/parallel/test-sqlite-statement-sync.js +++ b/test/parallel/test-sqlite-statement-sync.js @@ -1421,5 +1421,44 @@ suite('StatementSync.prototype[Symbol.dispose]()', () => { const stmt = db.prepare('CREATE TABLE storage(key TEXT, val TEXT)'); stmt[Symbol.dispose](); db.close(); + }) +}) + +suite('options.persistent', () => { + test('statement executes correctly when persistent is true', (t) => { + using db = new DatabaseSync(':memory:'); + db.exec('CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;'); + db.exec('INSERT INTO data (key, val) VALUES (1, 42);'); + using stmt = db.prepare('SELECT val FROM data', { persistent: true }); + t.assert.deepStrictEqual(stmt.get(), { __proto__: null, val: 42 }); + }); + + test('statement executes correctly when persistent is false', (t) => { + using db = new DatabaseSync(':memory:'); + db.exec('CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;'); + db.exec('INSERT INTO data (key, val) VALUES (1, 42);'); + using stmt = db.prepare('SELECT val FROM data', { persistent: false }); + t.assert.deepStrictEqual(stmt.get(), { __proto__: null, val: 42 }); + }); + + test('throws when input is not a boolean', (t) => { + using db = new DatabaseSync(':memory:'); + t.assert.throws(() => { + db.prepare('SELECT 1', { persistent: 'yes' }); + }, { + code: 'ERR_INVALID_ARG_TYPE', + message: /The "options\.persistent" argument must be a boolean/, + }); + }); + + test('can be combined with other options', (t) => { + using db = new DatabaseSync(':memory:'); + db.exec('CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;'); + db.exec('INSERT INTO data (key, val) VALUES (1, 42);'); + using stmt = db.prepare( + 'SELECT val FROM data', + { persistent: true, readBigInts: true } + ); + t.assert.deepStrictEqual(stmt.get(), { __proto__: null, val: 42n }); }); }); From 82b4348f7d6749b2411c9b89cc0686bf6921b87e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guilherme=20Ara=C3=BAjo?= Date: Wed, 15 Apr 2026 16:20:00 +0100 Subject: [PATCH 2/4] sqlite: use persistent statements on tag store --- src/node_sqlite.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/node_sqlite.cc b/src/node_sqlite.cc index f6a9c0f3a1a9..0deb4c0a4b0f 100644 --- a/src/node_sqlite.cc +++ b/src/node_sqlite.cc @@ -3790,8 +3790,12 @@ BaseObjectPtr SQLTagStore::PrepareStatement( if (stmt == nullptr) { sqlite3_stmt* s = nullptr; - int r = sqlite3_prepare_v2( - session->database_->connection_, sql.data(), sql.size(), &s, nullptr); + int r = sqlite3_prepare_v3(session->database_->connection_, + sql.data(), + sql.size(), + SQLITE_PREPARE_PERSISTENT, + &s, + nullptr); StatementPtr stmt_ptr(s); if (r != SQLITE_OK) { From 5a188f70391ab660f41acc5636a5ef27a91d93d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guilherme=20Ara=C3=BAjo?= Date: Wed, 27 May 2026 15:18:29 -0300 Subject: [PATCH 3/4] sqlite: use mern --- test/parallel/test-sqlite-statement-sync.js | 125 +++++++------------- 1 file changed, 43 insertions(+), 82 deletions(-) diff --git a/test/parallel/test-sqlite-statement-sync.js b/test/parallel/test-sqlite-statement-sync.js index 96e5e1f99771..d66c4f0f85cf 100644 --- a/test/parallel/test-sqlite-statement-sync.js +++ b/test/parallel/test-sqlite-statement-sync.js @@ -18,8 +18,7 @@ suite('StatementSync() constructor', () => { suite('StatementSync.prototype.get()', () => { test('executes a query and returns undefined on no results', (t) => { - const db = new DatabaseSync(':memory:'); - t.after(() => { db.close(); }); + using db = new DatabaseSync(nextDb()); let stmt = db.prepare('CREATE TABLE storage(key TEXT, val TEXT)'); t.assert.strictEqual(stmt.get(), undefined); stmt = db.prepare('SELECT * FROM storage'); @@ -27,8 +26,7 @@ suite('StatementSync.prototype.get()', () => { }); test('executes a query and returns the first result', (t) => { - const db = new DatabaseSync(':memory:'); - t.after(() => { db.close(); }); + using db = new DatabaseSync(nextDb()); let stmt = db.prepare('CREATE TABLE storage(key TEXT, val TEXT)'); t.assert.strictEqual(stmt.get(), undefined); stmt = db.prepare('INSERT INTO storage (key, val) VALUES (?, ?)'); @@ -39,8 +37,7 @@ suite('StatementSync.prototype.get()', () => { }); test('executes a query that returns special columns', (t) => { - const db = new DatabaseSync(':memory:'); - t.after(() => { db.close(); }); + using db = new DatabaseSync(nextDb()); const stmt = db.prepare('SELECT 1 as __proto__, 2 as constructor, 3 as toString'); t.assert.deepStrictEqual(stmt.get(), { __proto__: null, ['__proto__']: 1, constructor: 2, toString: 3 }); }); @@ -105,15 +102,13 @@ suite('StatementSync.prototype.get()', () => { suite('StatementSync.prototype.all()', () => { test('executes a query and returns an empty array on no results', (t) => { - const db = new DatabaseSync(':memory:'); - t.after(() => { db.close(); }); + using db = new DatabaseSync(nextDb()); const stmt = db.prepare('CREATE TABLE storage(key TEXT, val TEXT)'); t.assert.deepStrictEqual(stmt.all(), []); }); test('executes a query and returns all results', (t) => { - const db = new DatabaseSync(':memory:'); - t.after(() => { db.close(); }); + using db = new DatabaseSync(nextDb()); let stmt = db.prepare('CREATE TABLE storage(key TEXT, val TEXT)'); t.assert.deepStrictEqual(stmt.run(), { changes: 0, lastInsertRowid: 0 }); stmt = db.prepare('INSERT INTO storage (key, val) VALUES (?, ?)'); @@ -189,8 +184,7 @@ suite('StatementSync.prototype.all()', () => { suite('StatementSync.prototype.iterate()', () => { test('executes a query and returns an empty iterator on no results', (t) => { - const db = new DatabaseSync(':memory:'); - t.after(() => { db.close(); }); + using db = new DatabaseSync(nextDb()); const stmt = db.prepare('CREATE TABLE storage(key TEXT, val TEXT)'); const iter = stmt.iterate(); t.assert.strictEqual(iter instanceof globalThis.Iterator, true); @@ -199,8 +193,7 @@ suite('StatementSync.prototype.iterate()', () => { }); test('executes a query and returns all results', (t) => { - const db = new DatabaseSync(':memory:'); - t.after(() => { db.close(); }); + using db = new DatabaseSync(nextDb()); let stmt = db.prepare('CREATE TABLE storage(key TEXT, val TEXT)'); t.assert.deepStrictEqual(stmt.run(), { changes: 0, lastInsertRowid: 0 }); stmt = db.prepare('INSERT INTO storage (key, val) VALUES (?, ?)'); @@ -402,8 +395,7 @@ suite('StatementSync.prototype.iterate()', () => { suite('StatementSync.prototype.run()', () => { test('executes a query and returns change metadata', (t) => { - const db = new DatabaseSync(':memory:'); - t.after(() => { db.close(); }); + using db = new DatabaseSync(nextDb()); const setup = db.exec(` CREATE TABLE storage(key TEXT, val TEXT); INSERT INTO storage (key, val) VALUES ('foo', 'bar'); @@ -414,8 +406,7 @@ suite('StatementSync.prototype.run()', () => { }); test('SQLite throws when trying to bind too many parameters', (t) => { - const db = new DatabaseSync(':memory:'); - t.after(() => { db.close(); }); + using db = new DatabaseSync(nextDb()); const setup = db.exec( 'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;' ); @@ -432,8 +423,7 @@ suite('StatementSync.prototype.run()', () => { }); test('SQLite defaults to NULL for unbound parameters', (t) => { - const db = new DatabaseSync(':memory:'); - t.after(() => { db.close(); }); + using db = new DatabaseSync(nextDb()); const setup = db.exec( 'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER NOT NULL) STRICT;' ); @@ -469,8 +459,7 @@ suite('StatementSync.prototype.run()', () => { }); test('SQLite defaults unbound ?NNN parameters', (t) => { - const db = new DatabaseSync(':memory:'); - t.after(() => { db.close(); }); + using db = new DatabaseSync(nextDb()); const setup = db.exec( 'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER NOT NULL) STRICT;' ); @@ -488,8 +477,7 @@ suite('StatementSync.prototype.run()', () => { }); test('binds ?NNN params by position', (t) => { - const db = new DatabaseSync(':memory:'); - t.after(() => { db.close(); }); + using db = new DatabaseSync(nextDb()); const setup = db.exec( 'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER NOT NULL) STRICT;' ); @@ -513,8 +501,7 @@ suite('StatementSync.prototype.run()', () => { suite('StatementSync.prototype.sourceSQL', () => { test('equals input SQL', (t) => { - const db = new DatabaseSync(':memory:'); - t.after(() => { db.close(); }); + using db = new DatabaseSync(nextDb()); const setup = db.exec( 'CREATE TABLE types(key INTEGER PRIMARY KEY, val INTEGER) STRICT;' ); @@ -537,8 +524,7 @@ suite('StatementSync.prototype.sourceSQL', () => { suite('StatementSync.prototype.expandedSQL', () => { test('equals expanded SQL', (t) => { - const db = new DatabaseSync(':memory:'); - t.after(() => { db.close(); }); + using db = new DatabaseSync(nextDb()); const setup = db.exec( 'CREATE TABLE types(key INTEGER PRIMARY KEY, val INTEGER) STRICT;' ); @@ -772,8 +758,7 @@ suite('StatementSync.prototype.resetStats()', () => { suite('StatementSync.prototype.setReadBigInts()', () => { test('BigInts support can be toggled', (t) => { - const db = new DatabaseSync(':memory:'); - t.after(() => { db.close(); }); + using db = new DatabaseSync(nextDb()); const setup = db.exec(` CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT; INSERT INTO data (key, val) VALUES (1, 42); @@ -805,8 +790,7 @@ suite('StatementSync.prototype.setReadBigInts()', () => { }); test('throws when input is not a boolean', (t) => { - const db = new DatabaseSync(':memory:'); - t.after(() => { db.close(); }); + using db = new DatabaseSync(nextDb()); const setup = db.exec( 'CREATE TABLE types(key INTEGER PRIMARY KEY, val INTEGER) STRICT;' ); @@ -821,8 +805,7 @@ suite('StatementSync.prototype.setReadBigInts()', () => { }); test('BigInt is required for reading large integers', (t) => { - const db = new DatabaseSync(':memory:'); - t.after(() => { db.close(); }); + using db = new DatabaseSync(nextDb()); const bad = db.prepare(`SELECT ${Number.MAX_SAFE_INTEGER} + 1`); t.assert.throws(() => { bad.get(); @@ -853,8 +836,7 @@ suite('StatementSync.prototype.setReadBigInts()', () => { suite('StatementSync.prototype.setReturnArrays()', () => { test('throws when input is not a boolean', (t) => { - const db = new DatabaseSync(':memory:'); - t.after(() => { db.close(); }); + using db = new DatabaseSync(nextDb()); const setup = db.exec( 'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;' ); @@ -883,8 +865,7 @@ suite('StatementSync.prototype.setReturnArrays()', () => { suite('StatementSync.prototype.get() with array output', () => { test('returns array row when setReturnArrays is true', (t) => { - const db = new DatabaseSync(':memory:'); - t.after(() => { db.close(); }); + using db = new DatabaseSync(nextDb()); const setup = db.exec(` CREATE TABLE data(key INTEGER PRIMARY KEY, val TEXT) STRICT; INSERT INTO data (key, val) VALUES (1, 'one'); @@ -903,8 +884,7 @@ suite('StatementSync.prototype.get() with array output', () => { test('returns array rows with BigInts when both flags are set', (t) => { const expected = [1n, 9007199254740992n]; - const db = new DatabaseSync(':memory:'); - t.after(() => { db.close(); }); + using db = new DatabaseSync(nextDb()); const setup = db.exec(` CREATE TABLE big_data(id INTEGER, big_num INTEGER); INSERT INTO big_data VALUES (1, 9007199254740992); @@ -922,8 +902,7 @@ suite('StatementSync.prototype.get() with array output', () => { suite('StatementSync.prototype.all() with array output', () => { test('returns array rows when setReturnArrays is true', (t) => { - const db = new DatabaseSync(':memory:'); - t.after(() => { db.close(); }); + using db = new DatabaseSync(nextDb()); const setup = db.exec(` CREATE TABLE data(key INTEGER PRIMARY KEY, val TEXT) STRICT; INSERT INTO data (key, val) VALUES (1, 'one'); @@ -963,8 +942,7 @@ suite('StatementSync.prototype.all() with array output', () => { 9, 'text3', ]; - const db = new DatabaseSync(':memory:'); - t.after(() => { db.close(); }); + using db = new DatabaseSync(nextDb()); const setup = db.exec(` CREATE TABLE wide_table( col1 INTEGER, col2 TEXT, col3 REAL, col4 BLOB, col5 INTEGER, @@ -988,8 +966,7 @@ suite('StatementSync.prototype.all() with array output', () => { suite('StatementSync.prototype.iterate() with array output', () => { test('iterates array rows when setReturnArrays is true', (t) => { - const db = new DatabaseSync(':memory:'); - t.after(() => { db.close(); }); + using db = new DatabaseSync(nextDb()); const setup = db.exec(` CREATE TABLE data(key INTEGER PRIMARY KEY, val TEXT) STRICT; INSERT INTO data (key, val) VALUES (1, 'one'); @@ -1057,8 +1034,7 @@ suite('StatementSync.prototype.iterate() with array output', () => { suite('StatementSync.prototype.setAllowBareNamedParameters()', () => { test('bare named parameter support can be toggled', (t) => { - const db = new DatabaseSync(':memory:'); - t.after(() => { db.close(); }); + using db = new DatabaseSync(nextDb()); const setup = db.exec( 'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;' ); @@ -1083,8 +1059,7 @@ suite('StatementSync.prototype.setAllowBareNamedParameters()', () => { }); test('throws when input is not a boolean', (t) => { - const db = new DatabaseSync(':memory:'); - t.after(() => { db.close(); }); + using db = new DatabaseSync(nextDb()); const setup = db.exec( 'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;' ); @@ -1113,8 +1088,7 @@ suite('StatementSync.prototype.setAllowBareNamedParameters()', () => { suite('options.readBigInts', () => { test('BigInts are returned when input is true', (t) => { - const db = new DatabaseSync(':memory:'); - t.after(() => { db.close(); }); + using db = new DatabaseSync(nextDb()); const setup = db.exec(` CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT; INSERT INTO data (key, val) VALUES (1, 42); @@ -1126,8 +1100,7 @@ suite('options.readBigInts', () => { }); test('numbers are returned when input is false', (t) => { - const db = new DatabaseSync(':memory:'); - t.after(() => { db.close(); }); + using db = new DatabaseSync(nextDb()); const setup = db.exec(` CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT; INSERT INTO data (key, val) VALUES (1, 42); @@ -1139,8 +1112,7 @@ suite('options.readBigInts', () => { }); test('throws when input is not a boolean', (t) => { - const db = new DatabaseSync(':memory:'); - t.after(() => { db.close(); }); + using db = new DatabaseSync(nextDb()); const setup = db.exec( 'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;' ); @@ -1154,8 +1126,7 @@ suite('options.readBigInts', () => { }); test('setReadBigInts can override prepare option', (t) => { - const db = new DatabaseSync(':memory:'); - t.after(() => { db.close(); }); + using db = new DatabaseSync(nextDb()); const setup = db.exec(` CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT; INSERT INTO data (key, val) VALUES (1, 42); @@ -1171,8 +1142,7 @@ suite('options.readBigInts', () => { suite('options.returnArrays', () => { test('arrays are returned when input is true', (t) => { - const db = new DatabaseSync(':memory:'); - t.after(() => { db.close(); }); + using db = new DatabaseSync(nextDb()); const setup = db.exec(` CREATE TABLE data(key INTEGER PRIMARY KEY, val TEXT) STRICT; INSERT INTO data (key, val) VALUES (1, 'one'); @@ -1187,8 +1157,7 @@ suite('options.returnArrays', () => { }); test('objects are returned when input is false', (t) => { - const db = new DatabaseSync(':memory:'); - t.after(() => { db.close(); }); + using db = new DatabaseSync(nextDb()); const setup = db.exec(` CREATE TABLE data(key INTEGER PRIMARY KEY, val TEXT) STRICT; INSERT INTO data (key, val) VALUES (1, 'one'); @@ -1203,8 +1172,7 @@ suite('options.returnArrays', () => { }); test('throws when input is not a boolean', (t) => { - const db = new DatabaseSync(':memory:'); - t.after(() => { db.close(); }); + using db = new DatabaseSync(nextDb()); const setup = db.exec( 'CREATE TABLE data(key INTEGER PRIMARY KEY, val TEXT) STRICT;' ); @@ -1218,8 +1186,7 @@ suite('options.returnArrays', () => { }); test('setReturnArrays can override prepare option', (t) => { - const db = new DatabaseSync(':memory:'); - t.after(() => { db.close(); }); + using db = new DatabaseSync(nextDb()); const setup = db.exec(` CREATE TABLE data(key INTEGER PRIMARY KEY, val TEXT) STRICT; INSERT INTO data (key, val) VALUES (1, 'one'); @@ -1236,8 +1203,7 @@ suite('options.returnArrays', () => { }); test('all() returns arrays when input is true', (t) => { - const db = new DatabaseSync(':memory:'); - t.after(() => { db.close(); }); + using db = new DatabaseSync(nextDb()); const setup = db.exec(` CREATE TABLE data(key INTEGER PRIMARY KEY, val TEXT) STRICT; INSERT INTO data (key, val) VALUES (1, 'one'); @@ -1256,8 +1222,7 @@ suite('options.returnArrays', () => { }); test('iterate() returns arrays when input is true', (t) => { - const db = new DatabaseSync(':memory:'); - t.after(() => { db.close(); }); + using db = new DatabaseSync(nextDb()); const setup = db.exec(` CREATE TABLE data(key INTEGER PRIMARY KEY, val TEXT) STRICT; INSERT INTO data (key, val) VALUES (1, 'one'); @@ -1278,8 +1243,7 @@ suite('options.returnArrays', () => { suite('options.allowBareNamedParameters', () => { test('bare named parameters are allowed when input is true', (t) => { - const db = new DatabaseSync(':memory:'); - t.after(() => { db.close(); }); + using db = new DatabaseSync(nextDb()); const setup = db.exec( 'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;' ); @@ -1295,8 +1259,7 @@ suite('options.allowBareNamedParameters', () => { }); test('bare named parameters throw when input is false', (t) => { - const db = new DatabaseSync(':memory:'); - t.after(() => { db.close(); }); + using db = new DatabaseSync(nextDb()); const setup = db.exec( 'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;' ); @@ -1314,8 +1277,7 @@ suite('options.allowBareNamedParameters', () => { }); test('throws when input is not a boolean', (t) => { - const db = new DatabaseSync(':memory:'); - t.after(() => { db.close(); }); + using db = new DatabaseSync(nextDb()); const setup = db.exec( 'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;' ); @@ -1332,8 +1294,7 @@ suite('options.allowBareNamedParameters', () => { }); test('setAllowBareNamedParameters can override prepare option', (t) => { - const db = new DatabaseSync(':memory:'); - t.after(() => { db.close(); }); + using db = new DatabaseSync(nextDb()); const setup = db.exec( 'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;' ); @@ -1426,7 +1387,7 @@ suite('StatementSync.prototype[Symbol.dispose]()', () => { suite('options.persistent', () => { test('statement executes correctly when persistent is true', (t) => { - using db = new DatabaseSync(':memory:'); + using db = new DatabaseSync(nextDb()); db.exec('CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;'); db.exec('INSERT INTO data (key, val) VALUES (1, 42);'); using stmt = db.prepare('SELECT val FROM data', { persistent: true }); @@ -1434,7 +1395,7 @@ suite('options.persistent', () => { }); test('statement executes correctly when persistent is false', (t) => { - using db = new DatabaseSync(':memory:'); + using db = new DatabaseSync(nextDb()); db.exec('CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;'); db.exec('INSERT INTO data (key, val) VALUES (1, 42);'); using stmt = db.prepare('SELECT val FROM data', { persistent: false }); @@ -1442,7 +1403,7 @@ suite('options.persistent', () => { }); test('throws when input is not a boolean', (t) => { - using db = new DatabaseSync(':memory:'); + using db = new DatabaseSync(nextDb()); t.assert.throws(() => { db.prepare('SELECT 1', { persistent: 'yes' }); }, { @@ -1452,7 +1413,7 @@ suite('options.persistent', () => { }); test('can be combined with other options', (t) => { - using db = new DatabaseSync(':memory:'); + using db = new DatabaseSync(nextDb()); db.exec('CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;'); db.exec('INSERT INTO data (key, val) VALUES (1, 42);'); using stmt = db.prepare( From d32e5e618cc2777a8c29f5ed3628d548d230b8d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guilherme=20Ara=C3=BAjo?= Date: Thu, 13 Aug 2026 14:44:48 -0300 Subject: [PATCH 4/4] sqlite: fix missing erm --- test/parallel/test-sqlite-statement-sync.js | 90 ++++++++++----------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/test/parallel/test-sqlite-statement-sync.js b/test/parallel/test-sqlite-statement-sync.js index d66c4f0f85cf..b13da41e9d71 100644 --- a/test/parallel/test-sqlite-statement-sync.js +++ b/test/parallel/test-sqlite-statement-sync.js @@ -18,7 +18,7 @@ suite('StatementSync() constructor', () => { suite('StatementSync.prototype.get()', () => { test('executes a query and returns undefined on no results', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); let stmt = db.prepare('CREATE TABLE storage(key TEXT, val TEXT)'); t.assert.strictEqual(stmt.get(), undefined); stmt = db.prepare('SELECT * FROM storage'); @@ -26,7 +26,7 @@ suite('StatementSync.prototype.get()', () => { }); test('executes a query and returns the first result', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); let stmt = db.prepare('CREATE TABLE storage(key TEXT, val TEXT)'); t.assert.strictEqual(stmt.get(), undefined); stmt = db.prepare('INSERT INTO storage (key, val) VALUES (?, ?)'); @@ -37,7 +37,7 @@ suite('StatementSync.prototype.get()', () => { }); test('executes a query that returns special columns', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); const stmt = db.prepare('SELECT 1 as __proto__, 2 as constructor, 3 as toString'); t.assert.deepStrictEqual(stmt.get(), { __proto__: null, ['__proto__']: 1, constructor: 2, toString: 3 }); }); @@ -102,13 +102,13 @@ suite('StatementSync.prototype.get()', () => { suite('StatementSync.prototype.all()', () => { test('executes a query and returns an empty array on no results', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); const stmt = db.prepare('CREATE TABLE storage(key TEXT, val TEXT)'); t.assert.deepStrictEqual(stmt.all(), []); }); test('executes a query and returns all results', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); let stmt = db.prepare('CREATE TABLE storage(key TEXT, val TEXT)'); t.assert.deepStrictEqual(stmt.run(), { changes: 0, lastInsertRowid: 0 }); stmt = db.prepare('INSERT INTO storage (key, val) VALUES (?, ?)'); @@ -184,7 +184,7 @@ suite('StatementSync.prototype.all()', () => { suite('StatementSync.prototype.iterate()', () => { test('executes a query and returns an empty iterator on no results', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); const stmt = db.prepare('CREATE TABLE storage(key TEXT, val TEXT)'); const iter = stmt.iterate(); t.assert.strictEqual(iter instanceof globalThis.Iterator, true); @@ -193,7 +193,7 @@ suite('StatementSync.prototype.iterate()', () => { }); test('executes a query and returns all results', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); let stmt = db.prepare('CREATE TABLE storage(key TEXT, val TEXT)'); t.assert.deepStrictEqual(stmt.run(), { changes: 0, lastInsertRowid: 0 }); stmt = db.prepare('INSERT INTO storage (key, val) VALUES (?, ?)'); @@ -395,7 +395,7 @@ suite('StatementSync.prototype.iterate()', () => { suite('StatementSync.prototype.run()', () => { test('executes a query and returns change metadata', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); const setup = db.exec(` CREATE TABLE storage(key TEXT, val TEXT); INSERT INTO storage (key, val) VALUES ('foo', 'bar'); @@ -406,7 +406,7 @@ suite('StatementSync.prototype.run()', () => { }); test('SQLite throws when trying to bind too many parameters', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); const setup = db.exec( 'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;' ); @@ -423,7 +423,7 @@ suite('StatementSync.prototype.run()', () => { }); test('SQLite defaults to NULL for unbound parameters', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); const setup = db.exec( 'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER NOT NULL) STRICT;' ); @@ -459,7 +459,7 @@ suite('StatementSync.prototype.run()', () => { }); test('SQLite defaults unbound ?NNN parameters', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); const setup = db.exec( 'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER NOT NULL) STRICT;' ); @@ -477,7 +477,7 @@ suite('StatementSync.prototype.run()', () => { }); test('binds ?NNN params by position', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); const setup = db.exec( 'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER NOT NULL) STRICT;' ); @@ -501,7 +501,7 @@ suite('StatementSync.prototype.run()', () => { suite('StatementSync.prototype.sourceSQL', () => { test('equals input SQL', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); const setup = db.exec( 'CREATE TABLE types(key INTEGER PRIMARY KEY, val INTEGER) STRICT;' ); @@ -524,7 +524,7 @@ suite('StatementSync.prototype.sourceSQL', () => { suite('StatementSync.prototype.expandedSQL', () => { test('equals expanded SQL', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); const setup = db.exec( 'CREATE TABLE types(key INTEGER PRIMARY KEY, val INTEGER) STRICT;' ); @@ -758,7 +758,7 @@ suite('StatementSync.prototype.resetStats()', () => { suite('StatementSync.prototype.setReadBigInts()', () => { test('BigInts support can be toggled', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); const setup = db.exec(` CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT; INSERT INTO data (key, val) VALUES (1, 42); @@ -790,7 +790,7 @@ suite('StatementSync.prototype.setReadBigInts()', () => { }); test('throws when input is not a boolean', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); const setup = db.exec( 'CREATE TABLE types(key INTEGER PRIMARY KEY, val INTEGER) STRICT;' ); @@ -805,7 +805,7 @@ suite('StatementSync.prototype.setReadBigInts()', () => { }); test('BigInt is required for reading large integers', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); const bad = db.prepare(`SELECT ${Number.MAX_SAFE_INTEGER} + 1`); t.assert.throws(() => { bad.get(); @@ -836,7 +836,7 @@ suite('StatementSync.prototype.setReadBigInts()', () => { suite('StatementSync.prototype.setReturnArrays()', () => { test('throws when input is not a boolean', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); const setup = db.exec( 'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;' ); @@ -865,7 +865,7 @@ suite('StatementSync.prototype.setReturnArrays()', () => { suite('StatementSync.prototype.get() with array output', () => { test('returns array row when setReturnArrays is true', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); const setup = db.exec(` CREATE TABLE data(key INTEGER PRIMARY KEY, val TEXT) STRICT; INSERT INTO data (key, val) VALUES (1, 'one'); @@ -884,7 +884,7 @@ suite('StatementSync.prototype.get() with array output', () => { test('returns array rows with BigInts when both flags are set', (t) => { const expected = [1n, 9007199254740992n]; - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); const setup = db.exec(` CREATE TABLE big_data(id INTEGER, big_num INTEGER); INSERT INTO big_data VALUES (1, 9007199254740992); @@ -902,7 +902,7 @@ suite('StatementSync.prototype.get() with array output', () => { suite('StatementSync.prototype.all() with array output', () => { test('returns array rows when setReturnArrays is true', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); const setup = db.exec(` CREATE TABLE data(key INTEGER PRIMARY KEY, val TEXT) STRICT; INSERT INTO data (key, val) VALUES (1, 'one'); @@ -942,7 +942,7 @@ suite('StatementSync.prototype.all() with array output', () => { 9, 'text3', ]; - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); const setup = db.exec(` CREATE TABLE wide_table( col1 INTEGER, col2 TEXT, col3 REAL, col4 BLOB, col5 INTEGER, @@ -966,7 +966,7 @@ suite('StatementSync.prototype.all() with array output', () => { suite('StatementSync.prototype.iterate() with array output', () => { test('iterates array rows when setReturnArrays is true', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); const setup = db.exec(` CREATE TABLE data(key INTEGER PRIMARY KEY, val TEXT) STRICT; INSERT INTO data (key, val) VALUES (1, 'one'); @@ -1034,7 +1034,7 @@ suite('StatementSync.prototype.iterate() with array output', () => { suite('StatementSync.prototype.setAllowBareNamedParameters()', () => { test('bare named parameter support can be toggled', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); const setup = db.exec( 'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;' ); @@ -1059,7 +1059,7 @@ suite('StatementSync.prototype.setAllowBareNamedParameters()', () => { }); test('throws when input is not a boolean', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); const setup = db.exec( 'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;' ); @@ -1088,7 +1088,7 @@ suite('StatementSync.prototype.setAllowBareNamedParameters()', () => { suite('options.readBigInts', () => { test('BigInts are returned when input is true', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); const setup = db.exec(` CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT; INSERT INTO data (key, val) VALUES (1, 42); @@ -1100,7 +1100,7 @@ suite('options.readBigInts', () => { }); test('numbers are returned when input is false', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); const setup = db.exec(` CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT; INSERT INTO data (key, val) VALUES (1, 42); @@ -1112,7 +1112,7 @@ suite('options.readBigInts', () => { }); test('throws when input is not a boolean', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); const setup = db.exec( 'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;' ); @@ -1126,7 +1126,7 @@ suite('options.readBigInts', () => { }); test('setReadBigInts can override prepare option', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); const setup = db.exec(` CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT; INSERT INTO data (key, val) VALUES (1, 42); @@ -1142,7 +1142,7 @@ suite('options.readBigInts', () => { suite('options.returnArrays', () => { test('arrays are returned when input is true', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); const setup = db.exec(` CREATE TABLE data(key INTEGER PRIMARY KEY, val TEXT) STRICT; INSERT INTO data (key, val) VALUES (1, 'one'); @@ -1157,7 +1157,7 @@ suite('options.returnArrays', () => { }); test('objects are returned when input is false', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); const setup = db.exec(` CREATE TABLE data(key INTEGER PRIMARY KEY, val TEXT) STRICT; INSERT INTO data (key, val) VALUES (1, 'one'); @@ -1172,7 +1172,7 @@ suite('options.returnArrays', () => { }); test('throws when input is not a boolean', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); const setup = db.exec( 'CREATE TABLE data(key INTEGER PRIMARY KEY, val TEXT) STRICT;' ); @@ -1186,7 +1186,7 @@ suite('options.returnArrays', () => { }); test('setReturnArrays can override prepare option', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); const setup = db.exec(` CREATE TABLE data(key INTEGER PRIMARY KEY, val TEXT) STRICT; INSERT INTO data (key, val) VALUES (1, 'one'); @@ -1203,7 +1203,7 @@ suite('options.returnArrays', () => { }); test('all() returns arrays when input is true', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); const setup = db.exec(` CREATE TABLE data(key INTEGER PRIMARY KEY, val TEXT) STRICT; INSERT INTO data (key, val) VALUES (1, 'one'); @@ -1222,7 +1222,7 @@ suite('options.returnArrays', () => { }); test('iterate() returns arrays when input is true', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); const setup = db.exec(` CREATE TABLE data(key INTEGER PRIMARY KEY, val TEXT) STRICT; INSERT INTO data (key, val) VALUES (1, 'one'); @@ -1243,7 +1243,7 @@ suite('options.returnArrays', () => { suite('options.allowBareNamedParameters', () => { test('bare named parameters are allowed when input is true', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); const setup = db.exec( 'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;' ); @@ -1259,7 +1259,7 @@ suite('options.allowBareNamedParameters', () => { }); test('bare named parameters throw when input is false', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); const setup = db.exec( 'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;' ); @@ -1277,7 +1277,7 @@ suite('options.allowBareNamedParameters', () => { }); test('throws when input is not a boolean', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); const setup = db.exec( 'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;' ); @@ -1294,7 +1294,7 @@ suite('options.allowBareNamedParameters', () => { }); test('setAllowBareNamedParameters can override prepare option', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); const setup = db.exec( 'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;' ); @@ -1382,12 +1382,12 @@ suite('StatementSync.prototype[Symbol.dispose]()', () => { const stmt = db.prepare('CREATE TABLE storage(key TEXT, val TEXT)'); stmt[Symbol.dispose](); db.close(); - }) -}) + }); +}); suite('options.persistent', () => { test('statement executes correctly when persistent is true', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); db.exec('CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;'); db.exec('INSERT INTO data (key, val) VALUES (1, 42);'); using stmt = db.prepare('SELECT val FROM data', { persistent: true }); @@ -1395,7 +1395,7 @@ suite('options.persistent', () => { }); test('statement executes correctly when persistent is false', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); db.exec('CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;'); db.exec('INSERT INTO data (key, val) VALUES (1, 42);'); using stmt = db.prepare('SELECT val FROM data', { persistent: false }); @@ -1403,7 +1403,7 @@ suite('options.persistent', () => { }); test('throws when input is not a boolean', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); t.assert.throws(() => { db.prepare('SELECT 1', { persistent: 'yes' }); }, { @@ -1413,7 +1413,7 @@ suite('options.persistent', () => { }); test('can be combined with other options', (t) => { - using db = new DatabaseSync(nextDb()); + using db = new DatabaseSync(':memory:'); db.exec('CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;'); db.exec('INSERT INTO data (key, val) VALUES (1, 42);'); using stmt = db.prepare(