forked from trent_larson/crowd-funder-for-time-pwa
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.
This commit is contained in:
@@ -687,58 +687,48 @@ export async function runMigrations<T>(
|
||||
}
|
||||
|
||||
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)}`,
|
||||
);
|
||||
}
|
||||
|
||||
// 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Record that the migration was applied
|
||||
await sqlExec("INSERT INTO migrations (name) VALUES (?)", [
|
||||
migration.name,
|
||||
]);
|
||||
|
||||
// Commit transaction
|
||||
await sqlExec("COMMIT");
|
||||
|
||||
// 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;
|
||||
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,
|
||||
);
|
||||
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,
|
||||
]);
|
||||
|
||||
// Only log success in development
|
||||
if (isDevelopment) {
|
||||
migrationLog(
|
||||
`🎉 [Migration] Successfully applied: ${migration.name}`,
|
||||
);
|
||||
}
|
||||
appliedCount++;
|
||||
} catch (error) {
|
||||
logger.error(`❌ [Migration] Error applying ${migration.name}:`, error);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user