From ac3d251a1a53dfb2ec6f371e58e693c1ff5e0d7e Mon Sep 17 00:00:00 2001 From: nyatoru Date: Tue, 24 Feb 2026 00:35:29 +0700 Subject: [PATCH] fix(db): clear prepared statements on backup restore --- src/lib/db/apiKeys.ts | 25 +++++++++++++++++++++++++ src/lib/db/backup.ts | 4 ++++ src/lib/localDb.ts | 2 ++ 3 files changed, 31 insertions(+) diff --git a/src/lib/db/apiKeys.ts b/src/lib/db/apiKeys.ts index 8bde82bc9a..dd9bb0689d 100644 --- a/src/lib/db/apiKeys.ts +++ b/src/lib/db/apiKeys.ts @@ -350,6 +350,22 @@ export async function isModelAllowedForKey(key, modelId) { return allowed; } +/** + * Clear prepared statements cache (called on database reset/restore) + * Prepared statements are bound to a specific database connection, + * so they must be cleared when the connection is reset. + */ +function clearPreparedStatementCache() { + _stmtGetAllKeys = null; + _stmtGetKeyById = null; + _stmtValidateKey = null; + _stmtGetKeyMetadata = null; + _stmtInsertKey = null; + _stmtUpdatePermissions = null; + _stmtDeleteKey = null; + _schemaChecked = false; // Also reset schema check for new connection +} + /** * Clear all caches (exported for testing/debugging) */ @@ -358,3 +374,12 @@ export function clearApiKeyCaches() { _modelPermissionCache.clear(); _regexCache.clear(); } + +/** + * Reset all cached state for database connection reset/restore. + * Called by backup.ts when the database is restored. + */ +export function resetApiKeyState() { + clearPreparedStatementCache(); + clearApiKeyCaches(); +} diff --git a/src/lib/db/backup.ts b/src/lib/db/backup.ts index 105cf54446..e9989ed118 100644 --- a/src/lib/db/backup.ts +++ b/src/lib/db/backup.ts @@ -14,6 +14,7 @@ import { DB_BACKUPS_DIR, DATA_DIR, } from "./core"; +import { resetApiKeyState } from "./apiKeys"; // ──────────────── Backup Config ──────────────── @@ -181,6 +182,9 @@ export async function restoreDbBackup(backupId) { // Close and reset current connection resetDbInstance(); + // Clear all cached prepared statements and other state bound to the old connection + resetApiKeyState(); + // Remove main file and WAL sidecars to avoid stale frame replay after restore. const sqliteFilesToReplace = [ SQLITE_FILE, diff --git a/src/lib/localDb.ts b/src/lib/localDb.ts index 4d1bcaf412..d90fa68939 100644 --- a/src/lib/localDb.ts +++ b/src/lib/localDb.ts @@ -62,6 +62,8 @@ export { getApiKeyMetadata, updateApiKeyPermissions, isModelAllowedForKey, + clearApiKeyCaches, + resetApiKeyState, } from "./db/apiKeys"; export {