remove legacy migration conversion code
All checks were successful
Check / check (pull_request) Successful in 3m15s

Pre-1.0 software with no installed base — no need to handle
converting from old TEXT-based schema_migrations format.

Removed: convertLegacyMigrations, ensureBootstrapVersion,
readLegacyVersions, rebuildMigrationsTable, and
TestApplyMigrationsLegacyConversion.

Simplified bootstrapMigrationsTable to only check existence
and run 000_migration.sql if missing.
This commit is contained in:
user
2026-03-26 08:37:44 -07:00
parent 22cc1a7fb1
commit 6591f77dac
2 changed files with 2 additions and 213 deletions

View File

@@ -83,9 +83,7 @@ func collectMigrations() ([]string, error) {
}
// bootstrapMigrationsTable ensures the schema_migrations table exists by
// applying 000_migration.sql if the table is missing. For databases with a
// legacy TEXT-based schema_migrations table, it converts to the new INTEGER
// format.
// applying 000_migration.sql if the table is missing.
func bootstrapMigrationsTable(ctx context.Context, db *sql.DB, log *slog.Logger) error {
var tableExists int
@@ -100,8 +98,7 @@ func bootstrapMigrationsTable(ctx context.Context, db *sql.DB, log *slog.Logger)
return applyBootstrapMigration(ctx, db, log)
}
// Table exists — check for and convert legacy TEXT-based versions.
return convertLegacyMigrations(ctx, db, log)
return nil
}
// applyBootstrapMigration reads and executes 000_migration.sql to create the
@@ -124,139 +121,6 @@ func applyBootstrapMigration(ctx context.Context, db *sql.DB, log *slog.Logger)
return nil
}
// convertLegacyMigrations converts a schema_migrations table that uses
// TEXT filename-based versions (e.g. "001_initial.sql") to INTEGER versions
// (e.g. 1). This is a one-time migration for existing databases.
func convertLegacyMigrations(ctx context.Context, db *sql.DB, log *slog.Logger) error {
// Check if any version looks like a legacy filename (contains underscore).
var legacyCount int
err := db.QueryRowContext(ctx,
"SELECT COUNT(*) FROM schema_migrations WHERE INSTR(CAST(version AS TEXT), '_') > 0",
).Scan(&legacyCount)
if err != nil {
return fmt.Errorf("failed to check for legacy migrations: %w", err)
}
if legacyCount == 0 {
return ensureBootstrapVersion(ctx, db)
}
if log != nil {
log.Info("converting legacy schema_migrations from TEXT to INTEGER format",
"legacy_entries", legacyCount)
}
intVersions, err := readLegacyVersions(ctx, db)
if err != nil {
return err
}
err = rebuildMigrationsTable(ctx, db, intVersions)
if err != nil {
return err
}
if log != nil {
log.Info("legacy migration conversion complete", "versions_converted", len(intVersions))
}
return nil
}
// ensureBootstrapVersion inserts version 0 if it is not already present.
func ensureBootstrapVersion(ctx context.Context, db *sql.DB) error {
_, err := db.ExecContext(ctx,
"INSERT OR IGNORE INTO schema_migrations (version) VALUES (0)")
if err != nil {
return fmt.Errorf("failed to ensure bootstrap version: %w", err)
}
return nil
}
// readLegacyVersions reads all version entries from the legacy schema_migrations
// table and parses them into integer versions.
func readLegacyVersions(ctx context.Context, db *sql.DB) ([]int, error) {
rows, err := db.QueryContext(ctx, "SELECT version FROM schema_migrations")
if err != nil {
return nil, fmt.Errorf("failed to read legacy migrations: %w", err)
}
defer func() { _ = rows.Close() }()
var intVersions []int
for rows.Next() {
var version string
scanErr := rows.Scan(&version)
if scanErr != nil {
return nil, fmt.Errorf("failed to scan legacy version: %w", scanErr)
}
v, parseErr := ParseMigrationVersion(version)
if parseErr != nil {
return nil, fmt.Errorf("failed to parse legacy version %q: %w", version, parseErr)
}
intVersions = append(intVersions, v)
}
rowsErr := rows.Err()
if rowsErr != nil {
return nil, fmt.Errorf("failed to iterate legacy versions: %w", rowsErr)
}
return intVersions, nil
}
// rebuildMigrationsTable drops the old schema_migrations table, recreates it
// via 000_migration.sql, and re-inserts the given integer versions. The entire
// operation runs in a single transaction so a crash cannot lose migration data.
func rebuildMigrationsTable(ctx context.Context, db *sql.DB, versions []int) error {
content, err := migrationsFS.ReadFile("migrations/000_migration.sql")
if err != nil {
return fmt.Errorf("failed to read bootstrap migration 000_migration.sql: %w", err)
}
tx, err := db.BeginTx(ctx, nil)
if err != nil {
return fmt.Errorf("failed to begin transaction for legacy conversion: %w", err)
}
defer func() {
if err != nil {
_ = tx.Rollback()
}
}()
_, err = tx.ExecContext(ctx, "DROP TABLE schema_migrations")
if err != nil {
return fmt.Errorf("failed to drop legacy migrations table: %w", err)
}
_, err = tx.ExecContext(ctx, string(content))
if err != nil {
return fmt.Errorf("failed to create new migrations table: %w", err)
}
for _, v := range versions {
_, err = tx.ExecContext(ctx,
"INSERT OR IGNORE INTO schema_migrations (version) VALUES (?)", v)
if err != nil {
return fmt.Errorf("failed to insert converted version %d: %w", v, err)
}
}
err = tx.Commit()
if err != nil {
return fmt.Errorf("failed to commit legacy conversion: %w", err)
}
return nil
}
// ApplyMigrations applies all pending migrations to db. An optional logger
// may be provided for informational output; pass nil for silent operation.
// This is exported so tests can apply the real schema without the full fx