From 59ce0fc553528b06560baeefdd45028a5bffe042 Mon Sep 17 00:00:00 2001 From: Jochen Meyer Date: Wed, 15 Apr 2026 09:57:51 +0200 Subject: [PATCH] fix: exclude primary key index from unique constraint check in migration 7 (#1771) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: exclude primary key index from unique constraint check in migration 7 PRAGMA index_list returns all indexes including those backing PRIMARY KEY columns (origin='pk'), which always have unique=1. The check was therefore always true, causing migration 7 to run the full DROP/CREATE/RENAME table sequence on every worker startup instead of short-circuiting once the UNIQUE constraint had already been removed. Fix: filter to non-PK indexes by requiring idx.origin !== 'pk'. The origin field is already present on the existing IndexInfo interface. Fixes #1749 * fix: apply pk-origin guard to all three migration code paths CodeRabbit correctly identified that the origin !== 'pk' fix was only applied to MigrationRunner.ts but not to the two other active code paths that run the same removeSessionSummariesUniqueConstraint logic: - SessionStore.ts:220 — used by DatabaseManager and worker-service - plugin/scripts/context-generator.cjs — bundled artifact (minified) All three paths now consistently exclude primary-key indexes when detecting unique constraints on session_summaries. --- plugin/scripts/context-generator.cjs | 2 +- src/services/sqlite/SessionStore.ts | 2 +- src/services/sqlite/migrations/runner.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugin/scripts/context-generator.cjs b/plugin/scripts/context-generator.cjs index 06697dc7..81549f1a 100644 --- a/plugin/scripts/context-generator.cjs +++ b/plugin/scripts/context-generator.cjs @@ -69,7 +69,7 @@ ${o.stack}`:` ${o.message}`:this.getLevel()===0&&typeof o=="object"?_=` CREATE INDEX IF NOT EXISTS idx_session_summaries_sdk_session ON session_summaries(memory_session_id); CREATE INDEX IF NOT EXISTS idx_session_summaries_project ON session_summaries(project); CREATE INDEX IF NOT EXISTS idx_session_summaries_created ON session_summaries(created_at_epoch DESC); - `),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(4,new Date().toISOString())}ensureWorkerPortColumn(){this.db.query("PRAGMA table_info(sdk_sessions)").all().some(s=>s.name==="worker_port")||(this.db.run("ALTER TABLE sdk_sessions ADD COLUMN worker_port INTEGER"),m.debug("DB","Added worker_port column to sdk_sessions table")),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(5,new Date().toISOString())}ensurePromptTrackingColumns(){this.db.query("PRAGMA table_info(sdk_sessions)").all().some(a=>a.name==="prompt_counter")||(this.db.run("ALTER TABLE sdk_sessions ADD COLUMN prompt_counter INTEGER DEFAULT 0"),m.debug("DB","Added prompt_counter column to sdk_sessions table")),this.db.query("PRAGMA table_info(observations)").all().some(a=>a.name==="prompt_number")||(this.db.run("ALTER TABLE observations ADD COLUMN prompt_number INTEGER"),m.debug("DB","Added prompt_number column to observations table")),this.db.query("PRAGMA table_info(session_summaries)").all().some(a=>a.name==="prompt_number")||(this.db.run("ALTER TABLE session_summaries ADD COLUMN prompt_number INTEGER"),m.debug("DB","Added prompt_number column to session_summaries table")),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(6,new Date().toISOString())}removeSessionSummariesUniqueConstraint(){if(!this.db.query("PRAGMA index_list(session_summaries)").all().some(s=>s.unique===1)){this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(7,new Date().toISOString());return}m.debug("DB","Removing UNIQUE constraint from session_summaries.memory_session_id"),this.db.run("BEGIN TRANSACTION"),this.db.run("DROP TABLE IF EXISTS session_summaries_new"),this.db.run(` + `),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(4,new Date().toISOString())}ensureWorkerPortColumn(){this.db.query("PRAGMA table_info(sdk_sessions)").all().some(s=>s.name==="worker_port")||(this.db.run("ALTER TABLE sdk_sessions ADD COLUMN worker_port INTEGER"),m.debug("DB","Added worker_port column to sdk_sessions table")),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(5,new Date().toISOString())}ensurePromptTrackingColumns(){this.db.query("PRAGMA table_info(sdk_sessions)").all().some(a=>a.name==="prompt_counter")||(this.db.run("ALTER TABLE sdk_sessions ADD COLUMN prompt_counter INTEGER DEFAULT 0"),m.debug("DB","Added prompt_counter column to sdk_sessions table")),this.db.query("PRAGMA table_info(observations)").all().some(a=>a.name==="prompt_number")||(this.db.run("ALTER TABLE observations ADD COLUMN prompt_number INTEGER"),m.debug("DB","Added prompt_number column to observations table")),this.db.query("PRAGMA table_info(session_summaries)").all().some(a=>a.name==="prompt_number")||(this.db.run("ALTER TABLE session_summaries ADD COLUMN prompt_number INTEGER"),m.debug("DB","Added prompt_number column to session_summaries table")),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(6,new Date().toISOString())}removeSessionSummariesUniqueConstraint(){if(!this.db.query("PRAGMA index_list(session_summaries)").all().some(s=>s.unique===1&&s.origin!=="pk")){this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(7,new Date().toISOString());return}m.debug("DB","Removing UNIQUE constraint from session_summaries.memory_session_id"),this.db.run("BEGIN TRANSACTION"),this.db.run("DROP TABLE IF EXISTS session_summaries_new"),this.db.run(` CREATE TABLE session_summaries_new ( id INTEGER PRIMARY KEY AUTOINCREMENT, memory_session_id TEXT NOT NULL, diff --git a/src/services/sqlite/SessionStore.ts b/src/services/sqlite/SessionStore.ts index d7d853a7..748b3026 100644 --- a/src/services/sqlite/SessionStore.ts +++ b/src/services/sqlite/SessionStore.ts @@ -217,7 +217,7 @@ export class SessionStore { private removeSessionSummariesUniqueConstraint(): void { // Check actual constraint state — don't rely on version tracking alone (issue #979) const summariesIndexes = this.db.query('PRAGMA index_list(session_summaries)').all() as IndexInfo[]; - const hasUniqueConstraint = summariesIndexes.some(idx => idx.unique === 1); + const hasUniqueConstraint = summariesIndexes.some(idx => idx.unique === 1 && idx.origin !== 'pk'); if (!hasUniqueConstraint) { // Already migrated (no constraint exists) diff --git a/src/services/sqlite/migrations/runner.ts b/src/services/sqlite/migrations/runner.ts index 2e404735..a3d31a29 100644 --- a/src/services/sqlite/migrations/runner.ts +++ b/src/services/sqlite/migrations/runner.ts @@ -189,7 +189,7 @@ export class MigrationRunner { private removeSessionSummariesUniqueConstraint(): void { // Check actual constraint state — don't rely on version tracking alone (issue #979) const summariesIndexes = this.db.query('PRAGMA index_list(session_summaries)').all() as IndexInfo[]; - const hasUniqueConstraint = summariesIndexes.some(idx => idx.unique === 1); + const hasUniqueConstraint = summariesIndexes.some(idx => idx.unique === 1 && idx.origin !== 'pk'); if (!hasUniqueConstraint) { // Already migrated (no constraint exists)