fix: tolerate duplicate column errors in migration runner
The previous SAVEPOINT approach failed because Go's tx.Exec() stops at the first error and never reaches the ROLLBACK TO statements. This caused every subsequent startup to re-run migration 0002, fail on the first ALTER (column already exists from a prior partial run), and crash. Fix runMigration() to: - Execute migrations without a wrapping transaction (DDL in SQLite is auto-commit anyway) - Treat "duplicate column name" / "already exists" errors as success and record the migration anyway, covering cases where a previous failed attempt already partially modified the schema Also simplify 0002_nfs_structured.sql back to plain ALTER TABLE statements (no SAVEPOINT needed with the new Go-level tolerance). Bump VERSION 0.1.9 -> 0.1.10
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
BINARY=nasctl
|
BINARY=nasctl
|
||||||
VERSION?=0.1.9
|
VERSION?=0.1.10
|
||||||
GO?=go
|
GO?=go
|
||||||
LDFLAGS=-s -w -X github.com/darroyo/nasctl/internal/web.Version=$(VERSION) -X github.com/darroyo/nasctl/internal/web.Commit=$(shell git rev-parse --short HEAD 2>/dev/null || echo unknown)
|
LDFLAGS=-s -w -X github.com/darroyo/nasctl/internal/web.Version=$(VERSION) -X github.com/darroyo/nasctl/internal/web.Commit=$(shell git rev-parse --short HEAD 2>/dev/null || echo unknown)
|
||||||
BUILD_FLAGS=CGO_ENABLED=0
|
BUILD_FLAGS=CGO_ENABLED=0
|
||||||
|
|||||||
+17
-13
@@ -100,25 +100,29 @@ func (d *DB) isMigrationApplied(name string) (bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *DB) runMigration(name, content string) error {
|
func (d *DB) runMigration(name, content string) error {
|
||||||
tx, err := d.conn.Begin()
|
if _, err := d.conn.Exec(content); err != nil {
|
||||||
if err != nil {
|
if isIgnorableMigrationError(err) {
|
||||||
return fmt.Errorf("begin transaction: %w", err)
|
// Schema is already in target state (e.g. columns already added
|
||||||
}
|
// by a previous partial run). Continue to record the migration.
|
||||||
defer tx.Rollback()
|
} else {
|
||||||
|
return fmt.Errorf("execute: %w", err)
|
||||||
if _, err := tx.Exec(content); err != nil {
|
}
|
||||||
return fmt.Errorf("execute: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := tx.Exec(
|
if _, err := d.conn.Exec(
|
||||||
"INSERT INTO schema_migrations (name) VALUES (?)",
|
"INSERT INTO schema_migrations (name) VALUES (?)",
|
||||||
name,
|
name,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return fmt.Errorf("record migration: %w", err)
|
return fmt.Errorf("record migration: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := tx.Commit(); err != nil {
|
|
||||||
return fmt.Errorf("commit: %w", err)
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isIgnorableMigrationError(err error) bool {
|
||||||
|
if err == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
msg := strings.ToLower(err.Error())
|
||||||
|
return strings.Contains(msg, "duplicate column name") ||
|
||||||
|
strings.Contains(msg, "already exists")
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,27 +1,8 @@
|
|||||||
SAVEPOINT sp1;
|
ALTER TABLE nfs_exports ADD COLUMN read_only INTEGER NOT NULL DEFAULT 0;
|
||||||
ALTER TABLE nfs_exports ADD COLUMN read_only INTEGER NOT NULL DEFAULT 0;
|
ALTER TABLE nfs_exports ADD COLUMN async_ INTEGER NOT NULL DEFAULT 0;
|
||||||
ROLLBACK TO sp1;
|
ALTER TABLE nfs_exports ADD COLUMN root_squash INTEGER NOT NULL DEFAULT 1;
|
||||||
|
|
||||||
SAVEPOINT sp2;
|
|
||||||
ALTER TABLE nfs_exports ADD COLUMN async_ INTEGER NOT NULL DEFAULT 0;
|
|
||||||
ROLLBACK TO sp2;
|
|
||||||
|
|
||||||
SAVEPOINT sp3;
|
|
||||||
ALTER TABLE nfs_exports ADD COLUMN root_squash INTEGER NOT NULL DEFAULT 1;
|
|
||||||
ROLLBACK TO sp3;
|
|
||||||
|
|
||||||
SAVEPOINT sp4;
|
|
||||||
ALTER TABLE nfs_exports ADD COLUMN subtree_check INTEGER NOT NULL DEFAULT 0;
|
ALTER TABLE nfs_exports ADD COLUMN subtree_check INTEGER NOT NULL DEFAULT 0;
|
||||||
ROLLBACK TO sp4;
|
ALTER TABLE nfs_exports ADD COLUMN fsid INTEGER NOT NULL DEFAULT 0;
|
||||||
|
ALTER TABLE nfs_exports ADD COLUMN advanced TEXT NOT NULL DEFAULT '{}';
|
||||||
SAVEPOINT sp5;
|
|
||||||
ALTER TABLE nfs_exports ADD COLUMN fsid INTEGER NOT NULL DEFAULT 0;
|
|
||||||
ROLLBACK TO sp5;
|
|
||||||
|
|
||||||
SAVEPOINT sp6;
|
|
||||||
ALTER TABLE nfs_exports ADD COLUMN advanced TEXT NOT NULL DEFAULT '{}';
|
|
||||||
ROLLBACK TO sp6;
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_nfs_exports_fsid ON nfs_exports(fsid);
|
CREATE INDEX IF NOT EXISTS idx_nfs_exports_fsid ON nfs_exports(fsid);
|
||||||
|
|
||||||
INSERT INTO schema_migrations (name) VALUES ('0002_nfs_structured.sql');
|
|
||||||
|
|||||||
Reference in New Issue
Block a user