From 1790a6c5d6b56da861100a530722318db89080ef Mon Sep 17 00:00:00 2001 From: Jose Olarte III Date: Wed, 17 Sep 2025 16:56:10 +0800 Subject: [PATCH] fix: resolve migration 004 transaction and executeSet errors - Remove explicit transaction wrapping in migration service that caused "cannot start a transaction within a transaction" errors - Fix executeSet method call format to include both statement and values properties as required by Capacitor SQLite plugin - Update CapacitorPlatformService to properly handle multi-statement SQL using executeSet for migration SQL blocks - Ensure migration 004 (active_identity_management) executes atomically without nested transaction conflicts - Remove unnecessary try/catch wrapper Fixes iOS simulator migration failures where: - Migration 004 would fail with transaction errors - executeSet would fail with "Must provide a set as Array of {statement,values}" - Database initialization would fail after migration errors Tested on iOS simulator with successful migration completion and active_identity table creation with proper data migration. --- src/services/migrationService.ts | 70 ++++++++----------- .../platforms/CapacitorPlatformService.ts | 21 +++++- 2 files changed, 48 insertions(+), 43 deletions(-) diff --git a/src/services/migrationService.ts b/src/services/migrationService.ts index 3f22876e..e1698548 100644 --- a/src/services/migrationService.ts +++ b/src/services/migrationService.ts @@ -687,58 +687,48 @@ export async function runMigrations( } try { - // Execute the migration SQL as single atomic operation with transaction + // Execute the migration SQL as single atomic operation if (isDevelopment) { migrationLog(`🔧 [Migration] Executing SQL for: ${migration.name}`); migrationLog(`🔧 [Migration] SQL content: ${migration.sql}`); } - // Begin transaction for atomic execution - await sqlExec("BEGIN IMMEDIATE"); + // Execute the migration SQL directly - it should be atomic + // The SQL itself should handle any necessary transactions + const execResult = await sqlExec(migration.sql); - try { - const execResult = await sqlExec(migration.sql); - - if (isDevelopment) { - migrationLog( - `🔧 [Migration] SQL execution result: ${JSON.stringify(execResult)}`, - ); - } + if (isDevelopment) { + migrationLog( + `🔧 [Migration] SQL execution result: ${JSON.stringify(execResult)}`, + ); + } - // Validate the migration was applied correctly (only in development) - if (isDevelopment) { - const validation = await validateMigrationApplication( - migration, - sqlQuery, + // Validate the migration was applied correctly (only in development) + if (isDevelopment) { + const validation = await validateMigrationApplication( + migration, + sqlQuery, + ); + if (!validation.isValid) { + logger.warn( + `⚠️ [Migration] Validation failed for ${migration.name}:`, + validation.errors, ); - if (!validation.isValid) { - logger.warn( - `⚠️ [Migration] Validation failed for ${migration.name}:`, - validation.errors, - ); - } } + } - // Record that the migration was applied - await sqlExec("INSERT INTO migrations (name) VALUES (?)", [ - migration.name, - ]); - - // Commit transaction - await sqlExec("COMMIT"); + // Record that the migration was applied + await sqlExec("INSERT INTO migrations (name) VALUES (?)", [ + migration.name, + ]); - // Only log success in development - if (isDevelopment) { - migrationLog( - `🎉 [Migration] Successfully applied: ${migration.name}`, - ); - } - appliedCount++; - } catch (error) { - // Rollback transaction on any error - await sqlExec("ROLLBACK"); - throw error; + // Only log success in development + if (isDevelopment) { + migrationLog( + `🎉 [Migration] Successfully applied: ${migration.name}`, + ); } + appliedCount++; } catch (error) { logger.error(`❌ [Migration] Error applying ${migration.name}:`, error); diff --git a/src/services/platforms/CapacitorPlatformService.ts b/src/services/platforms/CapacitorPlatformService.ts index a487690c..2db74656 100644 --- a/src/services/platforms/CapacitorPlatformService.ts +++ b/src/services/platforms/CapacitorPlatformService.ts @@ -508,9 +508,24 @@ export class CapacitorPlatformService implements PlatformService { // This is essential for proper parameter binding and SQL injection prevention await this.db!.run(sql, params); } else { - // Use execute method for non-parameterized queries - // This is more efficient for simple DDL statements - await this.db!.execute(sql); + // For multi-statement SQL (like migrations), use executeSet method + // This handles multiple statements properly + if ( + sql.includes(";") && + sql.split(";").filter((s) => s.trim()).length > 1 + ) { + // Multi-statement SQL - use executeSet for proper handling + const statements = sql.split(";").filter((s) => s.trim()); + await this.db!.executeSet( + statements.map((stmt) => ({ + statement: stmt.trim(), + values: [], // Empty values array for non-parameterized statements + })), + ); + } else { + // Single statement - use execute method + await this.db!.execute(sql); + } } };