From 4a9c331ef946245098097e9b4bbd1af98b957f39 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Mon, 30 Mar 2026 15:58:21 -0600 Subject: [PATCH] Fix INSERT INTO _migrations ordering in create and fixup The create subcommand generated .up.sql files with INSERT INTO _migrations as the FIRST statement, before the actual DDL. If the DDL fails, the migration is incorrectly marked as applied. Move the INSERT to be the LAST statement, matching how .down.sql already puts DELETE FROM _migrations last. Also fix the automatic fixup logic to append (not prepend) missing INSERT statements to existing .up.sql files. Fixes #86 --- cmd/sql-migrate/main.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cmd/sql-migrate/main.go b/cmd/sql-migrate/main.go index 093c928..e84a4c7 100644 --- a/cmd/sql-migrate/main.go +++ b/cmd/sql-migrate/main.go @@ -660,7 +660,7 @@ func (state *State) parseAndFixupBatches(text string) error { func showFixes(fixedUp, fixedDown []string) { if len(fixedUp) > 0 { - fmt.Fprintf(os.Stderr, "Fixup: prepended missing 'INSERT INTO _migrations ...' to:\n") + fmt.Fprintf(os.Stderr, "Fixup: appended missing 'INSERT INTO _migrations ...' to:\n") for _, up := range fixedUp { fmt.Fprintf(os.Stderr, " %s\n", up) } @@ -743,7 +743,7 @@ func create(state *State, desc string) error { // We trust the person running the migrations to not use malicious names. // (we don't want to embed db-specific logic here, and SQL doesn't define escaping) migrationInsert := fmt.Sprintf("INSERT INTO _migrations (name, id) VALUES ('%s', '%s');", basename, id) - upContent := fmt.Appendf(nil, "-- leave this as the first line\n%s\n\n-- %s (up)\nSELECT 'place your UP migration here';\n", migrationInsert, desc) + upContent := fmt.Appendf(nil, "-- %s (up)\nSELECT 'place your UP migration here';\n\n-- leave this as the last line\n%s\n", desc, migrationInsert) _ = os.WriteFile(upPath, upContent, 0644) migrationDelete := fmt.Sprintf("DELETE FROM _migrations WHERE id = '%s';", id) downContent := fmt.Appendf(nil, "-- %s (down)\nSELECT 'place your DOWN migration here';\n\n-- leave this as the last line\n%s\n", desc, migrationDelete) @@ -801,10 +801,10 @@ func fixupMigration(dir string, basename string) (up, down bool, warn error, err return false, false, warn, nil } - migrationInsertLn := fmt.Sprintf("INSERT INTO _migrations (name, id) VALUES ('%s', '%s');\n\n", basename, id) - upBytes = append([]byte(migrationInsertLn), upBytes...) + migrationInsertLn := fmt.Sprintf("\n-- leave this as the last line\nINSERT INTO _migrations (name, id) VALUES ('%s', '%s');\n", basename, id) + upBytes = append(upBytes, []byte(migrationInsertLn)...) if err = os.WriteFile(upPath, upBytes, 0644); err != nil { - warn = fmt.Errorf("failed to prepend 'INSERT INTO _migrations ...' to %s: %w", upPath, err) + warn = fmt.Errorf("failed to append 'INSERT INTO _migrations ...' to %s: %w", upPath, err) return false, false, warn, nil } up = true