From 544e59bdf5607ee3355c761659d701b11b124410 Mon Sep 17 00:00:00 2001 From: Scott McKendry <39483124+scottmckendry@users.noreply.github.com> Date: Sat, 1 Feb 2025 16:11:41 +1300 Subject: [PATCH] feat(db): add migrations logic and scripts for changes to services --- data.go | 76 ++++++++++++++++++++++++------ migrations/001_copy_services.sql | 4 ++ migrations/002_remove_dog_cols.sql | 3 ++ 3 files changed, 69 insertions(+), 14 deletions(-) create mode 100644 migrations/001_copy_services.sql create mode 100644 migrations/002_remove_dog_cols.sql diff --git a/data.go b/data.go index 57086e8..06af89d 100644 --- a/data.go +++ b/data.go @@ -3,6 +3,9 @@ package main import ( "database/sql" "fmt" + "os" + "path/filepath" + "sort" "strings" _ "github.com/tursodatabase/libsql-client-go/libsql" @@ -48,9 +51,9 @@ func Init() error { return fmt.Errorf("error creating tables: %v", err) } - err = updateTables() + err = applyMigrations() if err != nil { - return fmt.Errorf("error updating tables: %v", err) + return fmt.Errorf("error applying migrations: %v", err) } return nil @@ -105,27 +108,72 @@ func createTables() error { sent TEXT ); `) - if err != nil { return fmt.Errorf("error creating email_queue table: %v", err) } + _, err = db.Exec(` + CREATE TABLE IF NOT EXISTS migrations ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT, + applied TEXT + ); + `) + if err != nil { + return fmt.Errorf("error creating migrations table: %v", err) + } + return nil } -func updateTables() error { - _, err := db.Exec(` - ALTER TABLE dogs ADD COLUMN grouping INTEGER; - `) - if err != nil && !strings.Contains(err.Error(), "duplicate column name") { - return fmt.Errorf("error updating dogs table: %v", err) +func applyMigrations() error { + files, err := filepath.Glob(filepath.Join("migrations", "*.sql")) + if err != nil { + return fmt.Errorf("error reading migrations directory: %v", err) } - _, err = db.Exec(` - UPDATE dogs SET grouping = 0 WHERE grouping IS NULL - `) - if err != nil { - return fmt.Errorf("error updating dogs table: %v", err) + sort.Strings(files) + + for _, file := range files { + var count int + err := db.QueryRow("SELECT COUNT(*) FROM migrations WHERE name = ?", + filepath.Base(file)).Scan(&count) + if err != nil { + return fmt.Errorf("error checking migration status: %v", err) + } + + if count > 0 { + continue + } + + content, err := os.ReadFile(file) + if err != nil { + return fmt.Errorf("error reading migration file %s: %v", file, err) + } + + tx, err := db.Begin() + if err != nil { + return fmt.Errorf("error beginning transaction: %v", err) + } + + _, err = tx.Exec(string(content)) + if err != nil { + tx.Rollback() + return fmt.Errorf("error executing migration %s: %v", file, err) + } + + _, err = tx.Exec( + "INSERT INTO migrations (name, applied) VALUES (?, datetime('now'))", + filepath.Base(file)) + if err != nil { + tx.Rollback() + return fmt.Errorf("error recording migration %s: %v", file, err) + } + + err = tx.Commit() + if err != nil { + return fmt.Errorf("error committing migration %s: %v", file, err) + } } return nil diff --git a/migrations/001_copy_services.sql b/migrations/001_copy_services.sql new file mode 100644 index 0000000..961bbbe --- /dev/null +++ b/migrations/001_copy_services.sql @@ -0,0 +1,4 @@ +-- Insert into services and capture the mapping +INSERT INTO dog_services (dog_id, service, quantity, price) +SELECT id, service, quantity, price +FROM dogs; diff --git a/migrations/002_remove_dog_cols.sql b/migrations/002_remove_dog_cols.sql new file mode 100644 index 0000000..2025c1c --- /dev/null +++ b/migrations/002_remove_dog_cols.sql @@ -0,0 +1,3 @@ +ALTER TABLE dogs DROP COLUMN service; +ALTER TABLE dogs DROP COLUMN quantity; +ALTER TABLE dogs DROP COLUMN price;