mirror of https://github.com/pressly/goose.git
Merge pull request #150 from pressly/76
Improve errors & remove log.Fatal() from goose pkgpull/152/head
commit
e60424535d
2
goose.go
2
goose.go
|
@ -3,7 +3,6 @@ package goose
|
|||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"sync"
|
||||
)
|
||||
|
@ -14,7 +13,6 @@ var (
|
|||
maxVersion = int64((1 << 63) - 1)
|
||||
timestampFormat = "20060102150405"
|
||||
verbose = false
|
||||
reMatchSQLComments = regexp.MustCompile(`(--.*)`)
|
||||
)
|
||||
|
||||
// SetVerbose set the goose verbosity mode
|
||||
|
|
10
migrate.go
10
migrate.go
|
@ -2,13 +2,14 @@ package goose
|
|||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -30,7 +31,7 @@ func (ms Migrations) Len() int { return len(ms) }
|
|||
func (ms Migrations) Swap(i, j int) { ms[i], ms[j] = ms[j], ms[i] }
|
||||
func (ms Migrations) Less(i, j int) bool {
|
||||
if ms[i].Version == ms[j].Version {
|
||||
log.Fatalf("goose: duplicate version %v detected:\n%v\n%v", ms[i].Version, ms[i].Source, ms[j].Source)
|
||||
panic(fmt.Sprintf("goose: duplicate version %v detected:\n%v\n%v", ms[i].Version, ms[i].Source, ms[j].Source))
|
||||
}
|
||||
return ms[i].Version < ms[j].Version
|
||||
}
|
||||
|
@ -251,7 +252,7 @@ func EnsureDBVersion(db *sql.DB) (int64, error) {
|
|||
for rows.Next() {
|
||||
var row MigrationRecord
|
||||
if err = rows.Scan(&row.VersionID, &row.IsApplied); err != nil {
|
||||
log.Fatal("error scanning rows:", err)
|
||||
return 0, errors.Wrap(err, "failed to scan row")
|
||||
}
|
||||
|
||||
// have we already marked this version to be skipped?
|
||||
|
@ -275,6 +276,9 @@ func EnsureDBVersion(db *sql.DB) (int64, error) {
|
|||
// latest version of migration has not been applied.
|
||||
toSkip = append(toSkip, row.VersionID)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return 0, errors.Wrap(err, "failed to get next row")
|
||||
}
|
||||
|
||||
return 0, ErrNoNextVersion
|
||||
}
|
||||
|
|
23
migration.go
23
migration.go
|
@ -2,12 +2,13 @@ package goose
|
|||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// MigrationRecord struct.
|
||||
|
@ -54,16 +55,16 @@ func (m *Migration) run(db *sql.DB, direction bool) error {
|
|||
switch filepath.Ext(m.Source) {
|
||||
case ".sql":
|
||||
if err := runSQLMigration(db, m.Source, m.Version, direction); err != nil {
|
||||
return fmt.Errorf("FAIL %v, quitting migration", err)
|
||||
return errors.Wrapf(err, "failed to run SQL migration %q", filepath.Base(m.Source))
|
||||
}
|
||||
|
||||
case ".go":
|
||||
if !m.Registered {
|
||||
log.Fatalf("failed to apply Go migration %q: Go functions must be registered and built into a custom binary (see https://github.com/pressly/goose/tree/master/examples/go-migrations)", m.Source)
|
||||
return errors.Errorf("failed to run Go migration %q: Go functions must be registered and built into a custom binary (see https://github.com/pressly/goose/tree/master/examples/go-migrations)", m.Source)
|
||||
}
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
log.Fatal("db.Begin: ", err)
|
||||
return errors.Wrap(err, "failed to begin transaction")
|
||||
}
|
||||
|
||||
fn := m.UpFn
|
||||
|
@ -73,24 +74,27 @@ func (m *Migration) run(db *sql.DB, direction bool) error {
|
|||
if fn != nil {
|
||||
if err := fn(tx); err != nil {
|
||||
tx.Rollback()
|
||||
log.Fatalf("FAIL %s (%v), quitting migration.", filepath.Base(m.Source), err)
|
||||
return err
|
||||
return errors.Wrapf(err, "failed to run Go migration %q", filepath.Base(m.Source))
|
||||
}
|
||||
}
|
||||
|
||||
if direction {
|
||||
if _, err := tx.Exec(GetDialect().insertVersionSQL(), m.Version, direction); err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
return errors.Wrap(err, "failed to execute transaction")
|
||||
}
|
||||
} else {
|
||||
if _, err := tx.Exec(GetDialect().deleteVersionSQL(), m.Version); err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
return errors.Wrap(err, "failed to execute transaction")
|
||||
}
|
||||
}
|
||||
|
||||
return tx.Commit()
|
||||
if err := tx.Commit(); err != nil {
|
||||
return errors.Wrap(err, "failed to commit transaction")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -100,7 +104,6 @@ func (m *Migration) run(db *sql.DB, direction bool) error {
|
|||
// XXX_descriptivename.ext where XXX specifies the version number
|
||||
// and ext specifies the type of migration
|
||||
func NumericComponent(name string) (int64, error) {
|
||||
|
||||
base := filepath.Base(name)
|
||||
|
||||
if ext := filepath.Ext(base); ext != ".go" && ext != ".sql" {
|
||||
|
|
|
@ -7,8 +7,11 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const sqlCmdPrefix = "-- +goose "
|
||||
|
@ -153,10 +156,10 @@ func getSQLStatements(r io.Reader, direction bool) ([]string, bool, error) {
|
|||
//
|
||||
// All statements following an Up or Down directive are grouped together
|
||||
// until another direction directive is found.
|
||||
func runSQLMigration(db *sql.DB, scriptFile string, v int64, direction bool) error {
|
||||
f, err := os.Open(scriptFile)
|
||||
func runSQLMigration(db *sql.DB, sqlFile string, v int64, direction bool) error {
|
||||
f, err := os.Open(sqlFile)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return errors.Wrap(err, "failed to open SQL migration file")
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
|
@ -172,15 +175,15 @@ func runSQLMigration(db *sql.DB, scriptFile string, v int64, direction bool) err
|
|||
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
errors.Wrap(err, "failed to begin transaction")
|
||||
}
|
||||
|
||||
for _, query := range statements {
|
||||
printInfo("Executing statement : %s\n", cleanStatement(query))
|
||||
printInfo("Executing statement: %s\n", clearStatement(query))
|
||||
if _, err = tx.Exec(query); err != nil {
|
||||
printInfo("Rollback transaction\n")
|
||||
tx.Rollback()
|
||||
return err
|
||||
return errors.Wrapf(err, "failed to execute SQL query %q", clearStatement(query))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -188,29 +191,33 @@ func runSQLMigration(db *sql.DB, scriptFile string, v int64, direction bool) err
|
|||
if _, err := tx.Exec(GetDialect().insertVersionSQL(), v, direction); err != nil {
|
||||
printInfo("Rollback transaction\n")
|
||||
tx.Rollback()
|
||||
return err
|
||||
return errors.Wrap(err, "failed to insert new goose version")
|
||||
}
|
||||
} else {
|
||||
if _, err := tx.Exec(GetDialect().deleteVersionSQL(), v); err != nil {
|
||||
printInfo("Rollback transaction\n")
|
||||
tx.Rollback()
|
||||
return err
|
||||
return errors.Wrap(err, "failed to delete goose version")
|
||||
}
|
||||
}
|
||||
|
||||
printInfo("Commit transaction\n")
|
||||
return tx.Commit()
|
||||
if err := tx.Commit(); err != nil {
|
||||
return errors.Wrap(err, "failed to commit transaction")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NO TRANSACTION.
|
||||
for _, query := range statements {
|
||||
printInfo("Executing statement : %s\n", cleanStatement(query))
|
||||
printInfo("Executing statement: %s\n", clearStatement(query))
|
||||
if _, err := db.Exec(query); err != nil {
|
||||
return err
|
||||
return errors.Wrapf(err, "failed to execute SQL query %q", clearStatement(query))
|
||||
}
|
||||
}
|
||||
if _, err := db.Exec(GetDialect().insertVersionSQL(), v, direction); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "failed to insert new goose version")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -222,6 +229,12 @@ func printInfo(s string, args ...interface{}) {
|
|||
}
|
||||
}
|
||||
|
||||
func cleanStatement(s string) string {
|
||||
return reMatchSQLComments.ReplaceAllString(s, ``)
|
||||
var (
|
||||
matchSQLComments = regexp.MustCompile(`(?m)^--.*$[\r\n]*`)
|
||||
matchEmptyLines = regexp.MustCompile(`(?m)^$[\r\n]*`)
|
||||
)
|
||||
|
||||
func clearStatement(s string) string {
|
||||
s = matchSQLComments.ReplaceAllString(s, ``)
|
||||
return matchEmptyLines.ReplaceAllString(s, ``)
|
||||
}
|
||||
|
|
12
reset.go
12
reset.go
|
@ -3,17 +3,19 @@ package goose
|
|||
import (
|
||||
"database/sql"
|
||||
"sort"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Reset rolls back all migrations
|
||||
func Reset(db *sql.DB, dir string) error {
|
||||
migrations, err := CollectMigrations(dir, minVersion, maxVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "failed to collect migrations")
|
||||
}
|
||||
statuses, err := dbMigrationsStatus(db)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "failed to get status of migrations")
|
||||
}
|
||||
sort.Sort(sort.Reverse(migrations))
|
||||
|
||||
|
@ -22,7 +24,7 @@ func Reset(db *sql.DB, dir string) error {
|
|||
continue
|
||||
}
|
||||
if err = migration.Down(db); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "failed to db-down")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,7 +34,7 @@ func Reset(db *sql.DB, dir string) error {
|
|||
func dbMigrationsStatus(db *sql.DB) (map[int64]bool, error) {
|
||||
rows, err := GetDialect().dbVersionQuery(db)
|
||||
if err != nil {
|
||||
return map[int64]bool{}, createVersionTable(db)
|
||||
return map[int64]bool{}, nil
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
|
@ -44,7 +46,7 @@ func dbMigrationsStatus(db *sql.DB) (map[int64]bool, error) {
|
|||
for rows.Next() {
|
||||
var row MigrationRecord
|
||||
if err = rows.Scan(&row.VersionID, &row.IsApplied); err != nil {
|
||||
log.Fatal("error scanning rows:", err)
|
||||
return nil, errors.Wrap(err, "failed to scan row")
|
||||
}
|
||||
|
||||
if _, ok := result[row.VersionID]; ok {
|
||||
|
|
22
status.go
22
status.go
|
@ -5,6 +5,8 @@ import (
|
|||
"fmt"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Status prints the status of all migrations.
|
||||
|
@ -12,34 +14,35 @@ func Status(db *sql.DB, dir string) error {
|
|||
// collect all migrations
|
||||
migrations, err := CollectMigrations(dir, minVersion, maxVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "failed to collect migrations")
|
||||
}
|
||||
|
||||
// must ensure that the version table exists if we're running on a pristine DB
|
||||
if _, err := EnsureDBVersion(db); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "failed to ensure DB version")
|
||||
}
|
||||
|
||||
log.Println(" Applied At Migration")
|
||||
log.Println(" =======================================")
|
||||
for _, migration := range migrations {
|
||||
printMigrationStatus(db, migration.Version, filepath.Base(migration.Source))
|
||||
if err := printMigrationStatus(db, migration.Version, filepath.Base(migration.Source)); err != nil {
|
||||
return errors.Wrap(err, "failed to print status")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func printMigrationStatus(db *sql.DB, version int64, script string) {
|
||||
var row MigrationRecord
|
||||
func printMigrationStatus(db *sql.DB, version int64, script string) error {
|
||||
q := fmt.Sprintf("SELECT tstamp, is_applied FROM %s WHERE version_id=%d ORDER BY tstamp DESC LIMIT 1", TableName(), version)
|
||||
e := db.QueryRow(q).Scan(&row.TStamp, &row.IsApplied)
|
||||
|
||||
if e != nil && e != sql.ErrNoRows {
|
||||
log.Fatal(e)
|
||||
var row MigrationRecord
|
||||
err := db.QueryRow(q).Scan(&row.TStamp, &row.IsApplied)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return errors.Wrap(err, "failed to query the latest migration")
|
||||
}
|
||||
|
||||
var appliedAt string
|
||||
|
||||
if row.IsApplied {
|
||||
appliedAt = row.TStamp.Format(time.ANSIC)
|
||||
} else {
|
||||
|
@ -47,4 +50,5 @@ func printMigrationStatus(db *sql.DB, version int64, script string) {
|
|||
}
|
||||
|
||||
log.Printf(" %-24s -- %v\n", appliedAt, script)
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue