MCPcopy
hub / github.com/autobrr/qui / applyAllMigrations

Method applyAllMigrations

internal/database/db.go:1417–1537  ·  view source on GitHub ↗

applyAllMigrations applies pending database migrations in order. MIGRATION FAILURE HANDLING: If a migration fails, any strings interned during the migration will remain in string_pool. These orphaned strings will be automatically cleaned up by the periodic cleanup loop (24hrs). This is acceptable b

(ctx context.Context, migrations []string)

Source from the content-addressed store, hash-verified

1415//
1416// db.CleanupUnusedStrings(context.Background())
1417func (db *DB) applyAllMigrations(ctx context.Context, migrations []string) error {
1418 // Migrations that need foreign keys disabled due to table recreation
1419 needsForeignKeysOff := map[string]bool{
1420 "010_add_files_cache_and_string_interning.sql": true,
1421 }
1422
1423 // Begin single transaction for all migrations using BeginTx for proper connection handling
1424 tx, err := db.writerConn.BeginTx(ctx, nil)
1425 if err != nil {
1426 return fmt.Errorf("failed to begin transaction: %w", err)
1427 }
1428
1429 // CRITICAL: Track whether we have an active transaction to rollback
1430 // This prevents double-rollback issues when recreating transactions mid-migration
1431 rollbackActive := func() {
1432 if tx != nil {
1433 tx.Rollback()
1434 tx = nil
1435 }
1436 }
1437 defer rollbackActive()
1438
1439 // Apply each migration within the transaction
1440 for _, filename := range migrations {
1441 // Check if this migration needs foreign keys disabled
1442 if needsForeignKeysOff[filename] {
1443 // Commit current transaction before disabling foreign keys
1444 if err := tx.Commit(); err != nil {
1445 return fmt.Errorf("failed to commit before %s: %w", filename, err)
1446 }
1447 tx = nil // Clear tx so rollbackActive() won't try to rollback committed tx
1448
1449 // Ensure foreign keys are re-enabled even if migration fails
1450 defer func() {
1451 if _, err := db.writerConn.ExecContext(context.Background(), "PRAGMA foreign_keys = ON"); err != nil {
1452 log.Error().Err(err).Msg("CRITICAL: Failed to re-enable foreign keys after migration - manual intervention required")
1453 }
1454 }()
1455
1456 // Disable foreign keys (must be done outside transaction)
1457 if _, err := db.writerConn.ExecContext(ctx, "PRAGMA foreign_keys = OFF"); err != nil {
1458 return fmt.Errorf("failed to disable foreign keys for %s: %w", filename, err)
1459 }
1460
1461 // Run migration within explicit transaction while foreign keys are disabled
1462 if err := func() error {
1463 fkTx, err := db.writerConn.BeginTx(ctx, nil)
1464 if err != nil {
1465 return fmt.Errorf("failed to begin migration transaction for %s: %w", filename, err)
1466 }
1467 committed := false
1468 defer func() {
1469 if !committed {
1470 if rollErr := fkTx.Rollback(); rollErr != nil && !errors.Is(rollErr, sql.ErrTxDone) {
1471 log.Error().Err(rollErr).Str("migration", filename).Msg("rollback failed for migration transaction")
1472 }
1473 }
1474 }()

Callers 1

migrateMethod · 0.95

Calls 7

InfoMethod · 0.80
BeginTxMethod · 0.65
RollbackMethod · 0.65
CommitMethod · 0.65
ExecContextMethod · 0.65
ErrorMethod · 0.45
IsMethod · 0.45

Tested by

no test coverage detected