From 391dee38cdb3c34f945e3e095bb65c291aa2c302 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/parallel/test-sqlite-database-sync.js | 130 +++++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/test/parallel/test-sqlite-database-sync.js b/test/parallel/test-sqlite-database-sync.js index ac3a3c66d6469a..7f3e82e20eef6e 100644 --- a/test/parallel/test-sqlite-database-sync.js +++ b/test/parallel/test-sqlite-database-sync.js @@ -331,6 +331,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()', () => { @@ -522,4 +610,46 @@ suite('DatabaseSync.prototype[Symbol.dispose]', () => { t.assert.strictEqual(db.isOpen, false); db[Symbol.dispose](); }); + + 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 (?, ?)'); + + db[Symbol.dispose](); + 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/, + }); + }); }); From ad0690e2b2f4d16a9de90cb648cebfcf1c2daa0e 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/parallel/test-sqlite-database-sync.js | 39 ++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/test/parallel/test-sqlite-database-sync.js b/test/parallel/test-sqlite-database-sync.js index 7f3e82e20eef6e..917135cae0865b 100644 --- a/test/parallel/test-sqlite-database-sync.js +++ b/test/parallel/test-sqlite-database-sync.js @@ -341,6 +341,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); @@ -366,12 +367,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) => { @@ -387,6 +400,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(); @@ -403,12 +417,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(), @@ -620,6 +646,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](); t.assert.strictEqual(db.isOpen, false); @@ -645,11 +672,23 @@ suite('DatabaseSync.prototype[Symbol.dispose]', () => { 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/, + }); }); });