@ -87,18 +87,24 @@ export async function runMigrations<T>(
console . log ( "📋 [Migration] Checking migration status..." ) ;
// Create migrations table if it doesn't exist
console . log ( "🔧 [Migration] Creating migrations table if it doesn't exist..." ) ;
await sqlExec ( `
CREATE TABLE IF NOT EXISTS migrations (
name TEXT PRIMARY KEY ,
applied_at TEXT DEFAULT CURRENT_TIMESTAMP
) ;
` );
console . log ( "✅ [Migration] Migrations table ready" ) ;
// Get list of already applied migrations
console . log ( "🔍 [Migration] Querying existing migrations..." ) ;
const appliedMigrationsResult = await sqlQuery (
"SELECT name FROM migrations" ,
) ;
console . log ( "📊 [Migration] Raw query result:" , appliedMigrationsResult ) ;
const appliedMigrations = extractMigrationNames ( appliedMigrationsResult ) ;
console . log ( "📋 [Migration] Extracted applied migrations:" , Array . from ( appliedMigrations ) ) ;
// Get all registered migrations
const migrations = migrationRegistry . getMigrations ( ) ;
@ -110,6 +116,7 @@ export async function runMigrations<T>(
}
console . log ( ` 📊 [Migration] Found ${ migrations . length } total migrations, ${ appliedMigrations . size } already applied ` ) ;
console . log ( ` 📝 [Migration] Registered migrations: ${ migrations . map ( m = > m . name ) . join ( ', ' ) } ` ) ;
let appliedCount = 0 ;
let skippedCount = 0 ;
@ -125,18 +132,17 @@ export async function runMigrations<T>(
console . log ( ` 🔄 [Migration] Applying migration: ${ migration . name } ` ) ;
try {
// Special handling for column addition migrations
if ( migration . sql . includes ( "ALTER TABLE" ) && migration . sql . includes ( "ADD COLUMN" ) ) {
await handleColumnAddition ( migration , sqlExec , sqlQuery ) ;
} else {
// Execute the migration SQL
await sqlExec ( migration . sql ) ;
}
// Execute the migration SQL
console . log ( ` 🔧 [Migration] Executing SQL for ${ migration . name } ... ` ) ;
await sqlExec ( migration . sql ) ;
console . log ( ` ✅ [Migration] SQL executed successfully for ${ migration . name } ` ) ;
// Record that the migration was applied
await sqlExec ( "INSERT INTO migrations (name) VALUES (?)" , [
console . log ( ` 📝 [Migration] Recording migration ${ migration . name } as applied... ` ) ;
const insertResult = await sqlExec ( "INSERT INTO migrations (name) VALUES (?)" , [
migration . name ,
] ) ;
console . log ( ` ✅ [Migration] Migration record inserted: ` , insertResult ) ;
console . log ( ` ✅ [Migration] Successfully applied: ${ migration . name } ` ) ;
logger . info (
@ -144,14 +150,17 @@ export async function runMigrations<T>(
) ;
appliedCount ++ ;
} catch ( error ) {
console . error ( ` ❌ [Migration] Error applying ${ migration . name } : ` , error ) ;
// Handle specific cases where the migration might be partially applied
const errorMessage = String ( error ) . toLowerCase ( ) ;
// Check if it's a duplicate column error - this means the column already exists
// Check if it's a duplicate table/column error - this means the schema already exists
if (
errorMessage . includes ( "duplicate column" ) ||
errorMessage . includes ( "column already exists" ) ||
errorMessage . includes ( "already exists" )
errorMessage . includes ( "already exists" ) ||
errorMessage . includes ( "table" ) && errorMessage . includes ( "already exists" )
) {
console . log ( ` ⚠️ [Migration] ${ migration . name } appears already applied ( ${ errorMessage } ). Marking as complete. ` ) ;
logger . warn (
@ -160,9 +169,11 @@ export async function runMigrations<T>(
// Mark the migration as applied since the schema change already exists
try {
await sqlExec ( "INSERT INTO migrations (name) VALUES (?)" , [
console . log ( ` 📝 [Migration] Attempting to record ${ migration . name } as applied despite error... ` ) ;
const insertResult = await sqlExec ( "INSERT INTO migrations (name) VALUES (?)" , [
migration . name ,
] ) ;
console . log ( ` ✅ [Migration] Migration record inserted after error: ` , insertResult ) ;
console . log ( ` ✅ [Migration] Marked as applied: ${ migration . name } ` ) ;
logger . info (
` [MigrationService] Successfully marked migration as applied: ${ migration . name } ` ,
@ -196,53 +207,4 @@ export async function runMigrations<T>(
}
}
/ * *
* Handle column addition migrations with proper existence checking
*
* @param migration - The migration containing column addition
* @param sqlExec - Function to execute SQL statements
* @param sqlQuery - Function to query SQL data
* /
async function handleColumnAddition < T > (
migration : Migration ,
sqlExec : ( sql : string , params? : unknown [ ] ) = > Promise < unknown > ,
sqlQuery : ( sql : string , params? : unknown [ ] ) = > Promise < T > ,
) : Promise < void > {
// Extract table name and column name from ALTER TABLE statement
const alterMatch = migration . sql . match ( /ALTER TABLE\s+(\w+)\s+ADD COLUMN\s+(\w+)/i ) ;
if ( ! alterMatch ) {
// If we can't parse it, just try to execute normally
await sqlExec ( migration . sql ) ;
return ;
}
const [ , tableName , columnName ] = alterMatch ;
try {
// Check if column already exists
const columnCheckResult = await sqlQuery (
` SELECT COUNT(*) as count FROM pragma_table_info(' ${ tableName } ') WHERE name = ? ` ,
[ columnName ]
) as any ;
const columnExists = columnCheckResult ? . values ? . [ 0 ] ? . count > 0 ||
columnCheckResult ? . count > 0 ||
( Array . isArray ( columnCheckResult ) && columnCheckResult . length > 0 ) ;
if ( columnExists ) {
console . log ( ` ⏭️ [Migration] Column ${ columnName } already exists in table ${ tableName } , skipping ` ) ;
return ;
}
// Column doesn't exist, so add it
console . log ( ` 🔄 [Migration] Adding column ${ columnName } to table ${ tableName } ` ) ;
await sqlExec ( migration . sql ) ;
console . log ( ` ✅ [Migration] Successfully added column ${ columnName } to table ${ tableName } ` ) ;
} catch ( error ) {
// If the column check fails, try the original migration and let error handling catch duplicates
console . log ( ` ⚠️ [Migration] Column check failed, attempting migration anyway: ${ error } ` ) ;
await sqlExec ( migration . sql ) ;
}
}