diff --git a/Makefile b/Makefile index f9a1bc8..4e818f4 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ BINARY=nasctl -VERSION?=0.1.9 +VERSION?=0.1.10 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) BUILD_FLAGS=CGO_ENABLED=0 diff --git a/internal/db/db.go b/internal/db/db.go index d156aec..6f62049 100644 --- a/internal/db/db.go +++ b/internal/db/db.go @@ -100,25 +100,29 @@ func (d *DB) isMigrationApplied(name string) (bool, error) { } func (d *DB) runMigration(name, content string) error { - tx, err := d.conn.Begin() - if err != nil { - return fmt.Errorf("begin transaction: %w", err) - } - defer tx.Rollback() - - if _, err := tx.Exec(content); err != nil { - return fmt.Errorf("execute: %w", err) + if _, err := d.conn.Exec(content); err != nil { + if isIgnorableMigrationError(err) { + // Schema is already in target state (e.g. columns already added + // by a previous partial run). Continue to record the migration. + } else { + return fmt.Errorf("execute: %w", err) + } } - if _, err := tx.Exec( + if _, err := d.conn.Exec( "INSERT INTO schema_migrations (name) VALUES (?)", name, ); err != nil { return fmt.Errorf("record migration: %w", err) } - - if err := tx.Commit(); err != nil { - return fmt.Errorf("commit: %w", err) - } 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") +} diff --git a/internal/db/migrations/0002_nfs_structured.sql b/internal/db/migrations/0002_nfs_structured.sql index c9b0b52..5805e05 100644 --- a/internal/db/migrations/0002_nfs_structured.sql +++ b/internal/db/migrations/0002_nfs_structured.sql @@ -1,27 +1,8 @@ -SAVEPOINT sp1; -ALTER TABLE nfs_exports ADD COLUMN read_only INTEGER NOT NULL DEFAULT 0; -ROLLBACK TO sp1; - -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 read_only INTEGER NOT NULL DEFAULT 0; +ALTER TABLE nfs_exports ADD COLUMN async_ INTEGER NOT NULL DEFAULT 0; +ALTER TABLE nfs_exports ADD COLUMN root_squash INTEGER NOT NULL DEFAULT 1; ALTER TABLE nfs_exports ADD COLUMN subtree_check INTEGER NOT NULL DEFAULT 0; -ROLLBACK TO sp4; - -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; +ALTER TABLE nfs_exports ADD COLUMN fsid INTEGER NOT NULL DEFAULT 0; +ALTER TABLE nfs_exports ADD COLUMN advanced TEXT NOT NULL DEFAULT '{}'; CREATE INDEX IF NOT EXISTS idx_nfs_exports_fsid ON nfs_exports(fsid); - -INSERT INTO schema_migrations (name) VALUES ('0002_nfs_structured.sql');