From cd3e8986d7060c75d7097e432735de13c79baab3 Mon Sep 17 00:00:00 2001 From: Haileyesus <118998054+blackmammoth@users.noreply.github.com> Date: Wed, 29 Apr 2026 18:06:58 +0300 Subject: [PATCH] refactor(database): make session id the primary key in sessions table --- server/modules/database/migrations.ts | 59 +++++++++++++++---- .../database/repositories/sessions.db.ts | 12 +--- server/modules/database/schema.ts | 2 +- 3 files changed, 49 insertions(+), 24 deletions(-) diff --git a/server/modules/database/migrations.ts b/server/modules/database/migrations.ts index 77347125..390f83ff 100644 --- a/server/modules/database/migrations.ts +++ b/server/modules/database/migrations.ts @@ -57,7 +57,7 @@ const migrateLegacySessionNames = (db: Database): void => { if (hasSessionsTable) { console.log('Running migration: Merging session_names into sessions'); db.exec(` - INSERT OR REPLACE INTO sessions (session_id, provider, custom_name, created_at, updated_at) + INSERT INTO sessions (session_id, provider, custom_name, created_at, updated_at) SELECT session_id, COALESCE(provider, 'claude'), @@ -65,6 +65,11 @@ const migrateLegacySessionNames = (db: Database): void => { COALESCE(created_at, CURRENT_TIMESTAMP), COALESCE(updated_at, CURRENT_TIMESTAMP) FROM session_names + ON CONFLICT(session_id) DO UPDATE SET + provider = excluded.provider, + custom_name = COALESCE(excluded.custom_name, sessions.custom_name), + created_at = COALESCE(sessions.created_at, excluded.created_at), + updated_at = COALESCE(excluded.updated_at, sessions.updated_at) `); db.exec('DROP TABLE session_names'); return; @@ -245,9 +250,9 @@ const rebuildSessionsTableWithProjectSchema = (db: Database): void => { const shouldRebuild = !columnNames.includes('project_path') || - primaryKeyColumns.length !== 2 || + primaryKeyColumns.length !== 1 || primaryKeyColumns[0] !== 'session_id' || - primaryKeyColumns[1] !== 'provider'; + !columnNames.includes('provider'); if (!shouldRebuild) { addColumnToTableIfNotExists(db, 'sessions', columnNames, 'jsonl_path', 'TEXT'); @@ -299,14 +304,42 @@ const rebuildSessionsTableWithProjectSchema = (db: Database): void => { jsonl_path TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (session_id, provider), + PRIMARY KEY (session_id), FOREIGN KEY (project_path) REFERENCES projects(project_path) ON DELETE SET NULL ON UPDATE CASCADE ) `); db.exec(` - INSERT OR REPLACE INTO sessions__new ( + WITH source_rows AS ( + SELECT + session_id, + ${providerExpression} AS provider, + ${customNameExpression} AS custom_name, + ${projectPathExpression} AS project_path, + ${jsonlPathExpression} AS jsonl_path, + ${createdAtExpression} AS created_at, + ${updatedAtExpression} AS updated_at, + rowid AS source_rowid + FROM sessions + WHERE session_id IS NOT NULL AND trim(session_id) <> '' + ), + ranked_rows AS ( + SELECT + session_id, + provider, + custom_name, + project_path, + jsonl_path, + created_at, + updated_at, + ROW_NUMBER() OVER ( + PARTITION BY session_id + ORDER BY datetime(COALESCE(updated_at, created_at)) DESC, source_rowid DESC + ) AS session_rank + FROM source_rows + ) + INSERT INTO sessions__new ( session_id, provider, custom_name, @@ -317,14 +350,14 @@ const rebuildSessionsTableWithProjectSchema = (db: Database): void => { ) SELECT session_id, - ${providerExpression}, - ${customNameExpression}, - ${projectPathExpression}, - ${jsonlPathExpression}, - ${createdAtExpression}, - ${updatedAtExpression} - FROM sessions - WHERE session_id IS NOT NULL AND trim(session_id) <> '' + provider, + custom_name, + project_path, + jsonl_path, + created_at, + updated_at + FROM ranked_rows + WHERE session_rank = 1 `); db.exec('DROP TABLE sessions'); db.exec('ALTER TABLE sessions__new RENAME TO sessions'); diff --git a/server/modules/database/repositories/sessions.db.ts b/server/modules/database/repositories/sessions.db.ts index 967d2d59..19a96a56 100644 --- a/server/modules/database/repositories/sessions.db.ts +++ b/server/modules/database/repositories/sessions.db.ts @@ -55,7 +55,8 @@ export const sessionsDb = { db.prepare( `INSERT INTO sessions (session_id, provider, custom_name, project_path, jsonl_path, created_at, updated_at) VALUES (?, ?, ?, ?, ?, COALESCE(?, CURRENT_TIMESTAMP), COALESCE(?, CURRENT_TIMESTAMP)) - ON CONFLICT(session_id, provider) DO UPDATE SET + ON CONFLICT(session_id) DO UPDATE SET + provider = excluded.provider, updated_at = excluded.updated_at, project_path = excluded.project_path, jsonl_path = excluded.jsonl_path, @@ -82,15 +83,6 @@ export const sessionsDb = { ).run(customName, sessionId); }, - createSessionName(sessionId: string, provider: string, customName: string): void { - const db = getConnection(); - db.prepare( - `UPDATE sessions - SET custom_name = ? - WHERE session_id = ? AND provider = ?` - ).run(customName, sessionId, provider); - }, - getSessionById(sessionId: string): SessionMetadataLookupRow | null { const db = getConnection(); const row = db diff --git a/server/modules/database/schema.ts b/server/modules/database/schema.ts index 690bc7eb..7af3d80d 100644 --- a/server/modules/database/schema.ts +++ b/server/modules/database/schema.ts @@ -88,7 +88,7 @@ CREATE TABLE IF NOT EXISTS sessions ( jsonl_path TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (session_id, provider), + PRIMARY KEY (session_id), FOREIGN KEY (project_path) REFERENCES projects(project_path) ON DELETE SET NULL ON UPDATE CASCADE