package controller import ( "fmt" "os" "time" "github.com/jmoiron/sqlx" ) const DB_VERSION int = 3 func CheckDBVersionAndMigrate(db *sqlx.DB) { db.MustExec("CREATE SCHEMA IF NOT EXISTS arimelody") db.MustExec("SET search_path TO arimelody, public") db.MustExec( "CREATE TABLE IF NOT EXISTS arimelody.schema_version (" + "version INTEGER PRIMARY KEY, " + "applied_at TIMESTAMP DEFAULT current_timestamp)", ) oldDBVersion := 0 schemaVersionCount := 0 err := db.Get(&schemaVersionCount, "SELECT COUNT(*) FROM schema_version") if err != nil { panic(err) } if schemaVersionCount > 0 { err := db.Get(&oldDBVersion, "SELECT MAX(version) FROM schema_version") if err != nil { panic(err) } } for oldDBVersion < DB_VERSION { switch oldDBVersion { case 0: // default case; assume no database exists ApplyMigration(db, "000-init") oldDBVersion = DB_VERSION case 1: // the irony is i actually have to awkwardly shove schema_version // into the old database in order for this to work LOL ApplyMigration(db, "001-pre-versioning") oldDBVersion = 2 case 2: ApplyMigration(db, "002-audit-logs") oldDBVersion = 3 } } fmt.Printf("Database schema up to date.\n") } func ApplyMigration(db *sqlx.DB, scriptFile string) { fmt.Printf("Applying schema migration %s...\n", scriptFile) bytes, err := os.ReadFile("schema-migration/" + scriptFile + ".sql") if err != nil { fmt.Fprintf(os.Stderr, "FATAL: Failed to open schema file \"%s\": %v\n", scriptFile, err) os.Exit(1) } script := string(bytes) tx, err := db.Begin() if err != nil { fmt.Fprintf(os.Stderr, "FATAL: Failed to begin migration: %v\n", err) os.Exit(1) } _, err = tx.Exec(script) if err != nil { tx.Rollback() fmt.Fprintf(os.Stderr, "FATAL: Failed to apply migration: %v\n", err) os.Exit(1) } _, err = tx.Exec( "INSERT INTO schema_version (version, applied_at) " + "VALUES ($1, $2)", DB_VERSION, time.Now(), ) if err != nil { tx.Rollback() fmt.Fprintf(os.Stderr, "FATAL: Failed to update schema version: %v\n", err) os.Exit(1) } err = tx.Commit() if err != nil { fmt.Fprintf(os.Stderr, "FATAL: Failed to commit transaction: %v\n", err) os.Exit(1) } }