From 36331c22c0407a9b0f835fb895b410e4880cc7c9 Mon Sep 17 00:00:00 2001 From: "Kamat, Trivikram" <16024985+trivikr@users.noreply.github.com> Date: Mon, 27 Jul 2026 19:45:54 -0700 Subject: [PATCH 1/3] sqlite: reject deserialize() while in a callback deserialize() could be called from a user-defined function invoked during statement execution, tearing down the database connection while sqlite3_step() was still using it. Reuse the existing callback depth check to throw ERR_INVALID_STATE instead, matching the guard already in place for close(). Signed-off-by: Kamat, Trivikram <16024985+trivikr@users.noreply.github.com> Assisted-by: codex:gpt-5.6-sol --- src/node_sqlite.cc | 4 ++++ test/parallel/test-sqlite-serialize.js | 17 +++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/node_sqlite.cc b/src/node_sqlite.cc index 1272deb42f11..490ab90679a8 100644 --- a/src/node_sqlite.cc +++ b/src/node_sqlite.cc @@ -1849,6 +1849,10 @@ void DatabaseSync::Deserialize(const FunctionCallbackInfo& args) { ASSIGN_OR_RETURN_UNWRAP(&db, args.This()); Environment* env = Environment::GetCurrent(args); THROW_AND_RETURN_ON_BAD_STATE(env, !db->IsOpen(), "database is not open"); + THROW_AND_RETURN_ON_BAD_STATE( + env, + db->IsInCallback(), + "database cannot be deserialized while in a callback"); if (!args[0]->IsUint8Array()) { THROW_ERR_INVALID_ARG_TYPE(env->isolate(), diff --git a/test/parallel/test-sqlite-serialize.js b/test/parallel/test-sqlite-serialize.js index 77b9d9c5f483..6944c3bb3118 100644 --- a/test/parallel/test-sqlite-serialize.js +++ b/test/parallel/test-sqlite-serialize.js @@ -82,6 +82,23 @@ suite('DatabaseSync.prototype.serialize()', () => { }); suite('DatabaseSync.prototype.deserialize()', () => { + test('cannot deserialize the database while in a callback', (t) => { + const source = new DatabaseSync(':memory:'); + const serialized = source.serialize(); + source.close(); + + const db = new DatabaseSync(':memory:'); + t.after(() => db.close()); + db.function('deserialize_database', () => db.deserialize(serialized)); + const stmt = db.prepare('SELECT deserialize_database()'); + + t.assert.throws(() => stmt.get(), { + code: 'ERR_INVALID_STATE', + message: 'database cannot be deserialized while in a callback', + }); + t.assert.strictEqual(db.isOpen, true); + }); + test('loads a serialized database', (t) => { const db1 = new DatabaseSync(':memory:'); db1.exec('CREATE TABLE t(id INTEGER PRIMARY KEY, name TEXT)'); From 6c6dcfc91dacc3d8e067c858f39da6907917b9f3 Mon Sep 17 00:00:00 2001 From: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Date: Thu, 6 Aug 2026 18:18:19 +0000 Subject: [PATCH 2/3] fixup! test: reorder and rename test --- test/parallel/test-sqlite-serialize.js | 34 +++++++++++++------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/test/parallel/test-sqlite-serialize.js b/test/parallel/test-sqlite-serialize.js index 6944c3bb3118..e54cfa3c6a75 100644 --- a/test/parallel/test-sqlite-serialize.js +++ b/test/parallel/test-sqlite-serialize.js @@ -82,23 +82,6 @@ suite('DatabaseSync.prototype.serialize()', () => { }); suite('DatabaseSync.prototype.deserialize()', () => { - test('cannot deserialize the database while in a callback', (t) => { - const source = new DatabaseSync(':memory:'); - const serialized = source.serialize(); - source.close(); - - const db = new DatabaseSync(':memory:'); - t.after(() => db.close()); - db.function('deserialize_database', () => db.deserialize(serialized)); - const stmt = db.prepare('SELECT deserialize_database()'); - - t.assert.throws(() => stmt.get(), { - code: 'ERR_INVALID_STATE', - message: 'database cannot be deserialized while in a callback', - }); - t.assert.strictEqual(db.isOpen, true); - }); - test('loads a serialized database', (t) => { const db1 = new DatabaseSync(':memory:'); db1.exec('CREATE TABLE t(id INTEGER PRIMARY KEY, name TEXT)'); @@ -207,6 +190,23 @@ suite('DatabaseSync.prototype.deserialize()', () => { }); }); + test('throws if called while in a callback', (t) => { + const source = new DatabaseSync(':memory:'); + const serialized = source.serialize(); + source.close(); + + const db = new DatabaseSync(':memory:'); + t.after(() => db.close()); + db.function('deserialize_database', () => db.deserialize(serialized)); + const stmt = db.prepare('SELECT deserialize_database()'); + + t.assert.throws(() => stmt.get(), { + code: 'ERR_INVALID_STATE', + message: 'database cannot be deserialized while in a callback', + }); + t.assert.strictEqual(db.isOpen, true); + }); + test('throws if buffer argument is not a Uint8Array', (t) => { const db = new DatabaseSync(':memory:'); t.assert.throws(() => { From 0081aa8b699481afbaf6e21e8e86b0db462f0c5e Mon Sep 17 00:00:00 2001 From: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Date: Thu, 6 Aug 2026 18:19:50 +0000 Subject: [PATCH 3/3] fixup! doc: update doc --- doc/api/sqlite.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/api/sqlite.md b/doc/api/sqlite.md index 40ec877ddde3..602a97803736 100644 --- a/doc/api/sqlite.md +++ b/doc/api/sqlite.md @@ -613,8 +613,10 @@ added: Loads a serialized database into this connection, replacing the current database. The deserialized database is writable. Existing prepared statements are finalized before deserialization is attempted, even if the operation -subsequently fails. This method is a wrapper around -[`sqlite3_deserialize()`][]. +subsequently fails. An \[`ERR_INVALID_STATE`]\[] error is thrown if the method is +called while a statement is executing, for example from a user-defined function, +an aggregate function, or an authorizer callback. This method is a wrapper +around [`sqlite3_deserialize()`][]. ```mjs import { DatabaseSync } from 'node:sqlite';