From 3af24f08154e9d202c11f4fa027aa75d29e17dab Mon Sep 17 00:00:00 2001 From: Rajarshee Chatterjee Date: Sun, 16 Nov 2025 13:18:58 +0530 Subject: [PATCH] fix: Improve Migration Error Handling --- src/database/db.ts | 24 ++- .../migrations/002_add_novel_counters.ts | 173 ++++++++++-------- src/database/utils/migrationRunner.ts | 17 +- 3 files changed, 125 insertions(+), 89 deletions(-) diff --git a/src/database/db.ts b/src/database/db.ts index db084201..947f59b6 100644 --- a/src/database/db.ts +++ b/src/database/db.ts @@ -31,7 +31,7 @@ export const db = SQLite.openDatabaseSync(dbName); /** * Creates the initial database schema for fresh installations - * Sets up all tables, indexes, triggers and sets version to 1 + * Sets up all tables, indexes, triggers and sets version to 2 */ const createInitialSchema = () => { db.execSync('PRAGMA journal_mode = WAL'); @@ -52,7 +52,7 @@ const createInitialSchema = () => { db.runSync(createNovelTriggerQueryUpdate); db.runSync(createNovelTriggerQueryDelete); - db.execSync('PRAGMA user_version = 1'); + db.execSync('PRAGMA user_version = 2'); }); }; @@ -65,9 +65,23 @@ export const initializeDatabase = () => { db.execSync('PRAGMA cache_size = 10000'); db.execSync('PRAGMA foreign_keys = ON'); - const userVersion = - db.getFirstSync<{ user_version: number }>('PRAGMA user_version') - ?.user_version ?? 0; + let userVersion = 0; + try { + const result = db.getFirstSync<{ user_version: number }>( + 'PRAGMA user_version', + ); + userVersion = result?.user_version ?? 0; + } catch (error) { + // If PRAGMA query fails, assume fresh database + if (__DEV__) { + // eslint-disable-next-line no-console + console.warn( + 'Failed to get database version, assuming fresh install:', + error, + ); + } + userVersion = 0; + } if (userVersion === 0) { createInitialSchema(); diff --git a/src/database/migrations/002_add_novel_counters.ts b/src/database/migrations/002_add_novel_counters.ts index 83400bcc..9fed022e 100644 --- a/src/database/migrations/002_add_novel_counters.ts +++ b/src/database/migrations/002_add_novel_counters.ts @@ -27,86 +27,99 @@ export const migration002: Migration = { version: 2, description: 'Add counter columns and triggers to Novel table', migrate: db => { - if (!columnExists(db, 'Novel', 'chaptersDownloaded')) { - db.runSync(` - ALTER TABLE Novel - ADD COLUMN chaptersDownloaded INTEGER DEFAULT 0 - `); + const addColumnSafely = (columnName: string, columnDefinition: string) => { + if (!columnExists(db, 'Novel', columnName)) { + try { + db.runSync(` + ALTER TABLE Novel + ADD COLUMN ${columnName} ${columnDefinition} + `); + } catch (error) { + // Gracefully handle ALTER TABLE failures (e.g., table doesn't exist) + // Columns will be created when table is created in initial schema + if (__DEV__) { + // eslint-disable-next-line no-console + console.warn( + `Failed to add column ${columnName} to Novel table:`, + error, + ); + } + } + } + }; + + addColumnSafely('chaptersDownloaded', 'INTEGER DEFAULT 0'); + addColumnSafely('chaptersUnread', 'INTEGER DEFAULT 0'); + addColumnSafely('totalChapters', 'INTEGER DEFAULT 0'); + addColumnSafely('lastReadAt', 'TEXT'); + addColumnSafely('lastUpdatedAt', 'TEXT'); + + // Verify all columns exist before running UPDATE queries to prevent SQL errors + const allColumnsExist = + columnExists(db, 'Novel', 'chaptersDownloaded') && + columnExists(db, 'Novel', 'chaptersUnread') && + columnExists(db, 'Novel', 'totalChapters') && + columnExists(db, 'Novel', 'lastReadAt') && + columnExists(db, 'Novel', 'lastUpdatedAt'); + + if (allColumnsExist) { + try { + db.runSync(` + UPDATE Novel + SET chaptersDownloaded = ( + SELECT COUNT(*) + FROM Chapter + WHERE Chapter.novelId = Novel.id + AND Chapter.isDownloaded = 1 + ) + `); + + db.runSync(` + UPDATE Novel + SET chaptersUnread = ( + SELECT COUNT(*) + FROM Chapter + WHERE Chapter.novelId = Novel.id + AND Chapter.unread = 1 + ) + `); + + db.runSync(` + UPDATE Novel + SET totalChapters = ( + SELECT COUNT(*) + FROM Chapter + WHERE Chapter.novelId = Novel.id + ) + `); + + db.runSync(` + UPDATE Novel + SET lastReadAt = ( + SELECT MAX(readTime) + FROM Chapter + WHERE Chapter.novelId = Novel.id + ) + `); + + db.runSync(` + UPDATE Novel + SET lastUpdatedAt = ( + SELECT MAX(updatedTime) + FROM Chapter + WHERE Chapter.novelId = Novel.id + ) + `); + } catch (error) { + // Gracefully handle UPDATE failures - columns already added with defaults + if (__DEV__) { + // eslint-disable-next-line no-console + console.warn( + 'Failed to populate counter columns in Novel table:', + error, + ); + } + } } - - if (!columnExists(db, 'Novel', 'chaptersUnread')) { - db.runSync(` - ALTER TABLE Novel - ADD COLUMN chaptersUnread INTEGER DEFAULT 0 - `); - } - - if (!columnExists(db, 'Novel', 'totalChapters')) { - db.runSync(` - ALTER TABLE Novel - ADD COLUMN totalChapters INTEGER DEFAULT 0 - `); - } - - if (!columnExists(db, 'Novel', 'lastReadAt')) { - db.runSync(` - ALTER TABLE Novel - ADD COLUMN lastReadAt TEXT - `); - } - - if (!columnExists(db, 'Novel', 'lastUpdatedAt')) { - db.runSync(` - ALTER TABLE Novel - ADD COLUMN lastUpdatedAt TEXT - `); - } - - db.runSync(` - UPDATE Novel - SET chaptersDownloaded = ( - SELECT COUNT(*) - FROM Chapter - WHERE Chapter.novelId = Novel.id - AND Chapter.isDownloaded = 1 - ) - `); - - db.runSync(` - UPDATE Novel - SET chaptersUnread = ( - SELECT COUNT(*) - FROM Chapter - WHERE Chapter.novelId = Novel.id - AND Chapter.unread = 1 - ) - `); - - db.runSync(` - UPDATE Novel - SET totalChapters = ( - SELECT COUNT(*) - FROM Chapter - WHERE Chapter.novelId = Novel.id - ) - `); - - db.runSync(` - UPDATE Novel - SET lastReadAt = ( - SELECT MAX(readTime) - FROM Chapter - WHERE Chapter.novelId = Novel.id - ) - `); - - db.runSync(` - UPDATE Novel - SET lastUpdatedAt = ( - SELECT MAX(updatedTime) - FROM Chapter - WHERE Chapter.novelId = Novel.id - ) - `); }, }; diff --git a/src/database/utils/migrationRunner.ts b/src/database/utils/migrationRunner.ts index 4ca3f468..3a103c0a 100644 --- a/src/database/utils/migrationRunner.ts +++ b/src/database/utils/migrationRunner.ts @@ -49,10 +49,19 @@ export class MigrationRunner { } private getCurrentVersion(db: SQLiteDatabase): number { - return ( - db.getFirstSync<{ user_version: number }>('PRAGMA user_version') - ?.user_version ?? 0 - ); + try { + const result = db.getFirstSync<{ user_version: number }>( + 'PRAGMA user_version', + ); + return result?.user_version ?? 0; + } catch (error) { + // If PRAGMA query fails, assume version 0 (fresh database) + if (__DEV__) { + // eslint-disable-next-line no-console + console.warn('Failed to get database version, assuming 0:', error); + } + return 0; + } } private setVersion(db: SQLiteDatabase, version: number): void {