From 85c2832504cef466be5f50661c447c7af9a1e3c9 Mon Sep 17 00:00:00 2001 From: umuoy1 Date: Mon, 27 Apr 2026 02:54:11 +0800 Subject: [PATCH 1/2] test: cover sqlite statements after database close --- .../test-sqlite-database-sync-dispose.js | 42 +++++++++ test/parallel/test-sqlite-database-sync.js | 88 +++++++++++++++++++ 2 files changed, 130 insertions(+) diff --git a/test/parallel/test-sqlite-database-sync-dispose.js b/test/parallel/test-sqlite-database-sync-dispose.js index 67a1ab6757b848..5c0cd35bb9cabe 100644 --- a/test/parallel/test-sqlite-database-sync-dispose.js +++ b/test/parallel/test-sqlite-database-sync-dispose.js @@ -30,4 +30,46 @@ suite('DatabaseSync.prototype[Symbol.dispose]()', () => { db.close(); }, /database is not open/); }); + + test('invalidates prepared statements', () => { + const db = new DatabaseSync(nextDb()); + db.exec(` + CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER); + INSERT INTO data (key, val) VALUES (1, 2); + `); + + const select = db.prepare('SELECT * FROM data'); + const insert = db.prepare('INSERT INTO data (key, val) VALUES (?, ?)'); + + db[Symbol.dispose](); + assert.strictEqual(db.isOpen, false); + + for (const method of ['prepare', 'exec']) { + assert.throws(() => { + db[method]('SELECT 1'); + }, { + code: 'ERR_INVALID_STATE', + message: /database is not open/, + }); + } + + assert.throws(() => { + select.get(); + }, { + code: 'ERR_INVALID_STATE', + message: /statement has been finalized/, + }); + assert.throws(() => { + select.all(); + }, { + code: 'ERR_INVALID_STATE', + message: /statement has been finalized/, + }); + assert.throws(() => { + insert.run(2, 4); + }, { + code: 'ERR_INVALID_STATE', + message: /statement has been finalized/, + }); + }); }); diff --git a/test/parallel/test-sqlite-database-sync.js b/test/parallel/test-sqlite-database-sync.js index d778f839098737..91bc5428594046 100644 --- a/test/parallel/test-sqlite-database-sync.js +++ b/test/parallel/test-sqlite-database-sync.js @@ -343,6 +343,94 @@ suite('DatabaseSync.prototype.close()', () => { }); t.assert.strictEqual(db.isOpen, false); }); + + test('invalidates prepared statements', (t) => { + const db = new DatabaseSync(nextDb()); + db.exec(` + CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER); + INSERT INTO data (key, val) VALUES (1, 2); + `); + + const select = db.prepare('SELECT * FROM data'); + const insert = db.prepare('INSERT INTO data (key, val) VALUES (?, ?)'); + + t.assert.strictEqual(db.close(), undefined); + t.assert.strictEqual(db.isOpen, false); + + for (const method of ['prepare', 'exec']) { + t.assert.throws(() => { + db[method]('SELECT 1'); + }, { + code: 'ERR_INVALID_STATE', + message: /database is not open/, + }); + } + + t.assert.throws(() => { + select.get(); + }, { + code: 'ERR_INVALID_STATE', + message: /statement has been finalized/, + }); + t.assert.throws(() => { + select.all(); + }, { + code: 'ERR_INVALID_STATE', + message: /statement has been finalized/, + }); + t.assert.throws(() => { + insert.run(2, 4); + }, { + code: 'ERR_INVALID_STATE', + message: /statement has been finalized/, + }); + }); + + test('keeps prepared statements invalid after reopening', (t) => { + const db = new DatabaseSync(nextDb()); + t.after(() => { + if (db.isOpen) db.close(); + }); + + db.exec(` + CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER); + INSERT INTO data (key, val) VALUES (1, 2); + `); + + const select = db.prepare('SELECT * FROM data'); + const insert = db.prepare('INSERT INTO data (key, val) VALUES (?, ?)'); + + db.close(); + db.open(); + + t.assert.throws(() => { + select.get(); + }, { + code: 'ERR_INVALID_STATE', + message: /statement has been finalized/, + }); + t.assert.throws(() => { + select.all(); + }, { + code: 'ERR_INVALID_STATE', + message: /statement has been finalized/, + }); + t.assert.throws(() => { + insert.run(2, 4); + }, { + code: 'ERR_INVALID_STATE', + message: /statement has been finalized/, + }); + + t.assert.deepStrictEqual( + db.prepare('SELECT * FROM data').all(), + [{ __proto__: null, key: 1, val: 2 }], + ); + t.assert.strictEqual( + db.exec('INSERT INTO data (key, val) VALUES (2, 4)'), + undefined, + ); + }); }); suite('DatabaseSync.prototype.prepare()', () => { From 81d2d8f32fba03c74ec1891314a32228477832de Mon Sep 17 00:00:00 2001 From: umuoy1 Date: Mon, 27 Apr 2026 03:11:06 +0800 Subject: [PATCH 2/2] test: cover sqlite iterators after database close --- .../test-sqlite-database-sync-dispose.js | 13 ++++++++++ test/parallel/test-sqlite-database-sync.js | 26 +++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/test/parallel/test-sqlite-database-sync-dispose.js b/test/parallel/test-sqlite-database-sync-dispose.js index 5c0cd35bb9cabe..7fe18c29f7d12c 100644 --- a/test/parallel/test-sqlite-database-sync-dispose.js +++ b/test/parallel/test-sqlite-database-sync-dispose.js @@ -40,6 +40,7 @@ suite('DatabaseSync.prototype[Symbol.dispose]()', () => { const select = db.prepare('SELECT * FROM data'); const insert = db.prepare('INSERT INTO data (key, val) VALUES (?, ?)'); + const iterator = select.iterate(); db[Symbol.dispose](); assert.strictEqual(db.isOpen, false); @@ -65,11 +66,23 @@ suite('DatabaseSync.prototype[Symbol.dispose]()', () => { code: 'ERR_INVALID_STATE', message: /statement has been finalized/, }); + assert.throws(() => { + select.iterate(); + }, { + code: 'ERR_INVALID_STATE', + message: /statement has been finalized/, + }); assert.throws(() => { insert.run(2, 4); }, { code: 'ERR_INVALID_STATE', message: /statement has been finalized/, }); + assert.throws(() => { + iterator.next(); + }, { + code: 'ERR_INVALID_STATE', + message: /statement has been finalized/, + }); }); }); diff --git a/test/parallel/test-sqlite-database-sync.js b/test/parallel/test-sqlite-database-sync.js index 91bc5428594046..c0e17da5aa174d 100644 --- a/test/parallel/test-sqlite-database-sync.js +++ b/test/parallel/test-sqlite-database-sync.js @@ -353,6 +353,7 @@ suite('DatabaseSync.prototype.close()', () => { const select = db.prepare('SELECT * FROM data'); const insert = db.prepare('INSERT INTO data (key, val) VALUES (?, ?)'); + const iterator = select.iterate(); t.assert.strictEqual(db.close(), undefined); t.assert.strictEqual(db.isOpen, false); @@ -378,12 +379,24 @@ suite('DatabaseSync.prototype.close()', () => { code: 'ERR_INVALID_STATE', message: /statement has been finalized/, }); + t.assert.throws(() => { + select.iterate(); + }, { + code: 'ERR_INVALID_STATE', + message: /statement has been finalized/, + }); t.assert.throws(() => { insert.run(2, 4); }, { code: 'ERR_INVALID_STATE', message: /statement has been finalized/, }); + t.assert.throws(() => { + iterator.next(); + }, { + code: 'ERR_INVALID_STATE', + message: /statement has been finalized/, + }); }); test('keeps prepared statements invalid after reopening', (t) => { @@ -399,6 +412,7 @@ suite('DatabaseSync.prototype.close()', () => { const select = db.prepare('SELECT * FROM data'); const insert = db.prepare('INSERT INTO data (key, val) VALUES (?, ?)'); + const iterator = select.iterate(); db.close(); db.open(); @@ -415,12 +429,24 @@ suite('DatabaseSync.prototype.close()', () => { code: 'ERR_INVALID_STATE', message: /statement has been finalized/, }); + t.assert.throws(() => { + select.iterate(); + }, { + code: 'ERR_INVALID_STATE', + message: /statement has been finalized/, + }); t.assert.throws(() => { insert.run(2, 4); }, { code: 'ERR_INVALID_STATE', message: /statement has been finalized/, }); + t.assert.throws(() => { + iterator.next(); + }, { + code: 'ERR_INVALID_STATE', + message: /statement has been finalized/, + }); t.assert.deepStrictEqual( db.prepare('SELECT * FROM data').all(),