Merge pull request #150 from pressly/76

Improve errors & remove log.Fatal() from goose pkg
pull/152/head
Vojtech Vitek 2019-03-04 21:21:39 -05:00 committed by GitHub
commit e60424535d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 67 additions and 43 deletions

View File

@ -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

View File

@ -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
}

View File

@ -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" {

View File

@ -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, ``)
}

View File

@ -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 {

View File

@ -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
}