goose/migration_sql.go

101 lines
2.6 KiB
Go

package goose
import (
"database/sql"
"os"
"regexp"
"github.com/pkg/errors"
)
// Run a migration specified in raw SQL.
//
// Sections of the script can be annotated with a special comment,
// starting with "-- +goose" to specify whether the section should
// be applied during an Up or Down migration
//
// All statements following an Up or Down directive are grouped together
// until another direction directive is found.
func runSQLMigration(db *sql.DB, sqlFile string, v int64, direction bool) error {
f, err := os.Open(sqlFile)
if err != nil {
return errors.Wrap(err, "failed to open SQL migration file")
}
defer f.Close()
statements, useTx, err := parseSQLMigrationFile(f, direction)
if err != nil {
return errors.Wrap(err, "failed to parse SQL migration file")
}
if useTx {
// TRANSACTION.
printInfo("Begin transaction\n")
tx, err := db.Begin()
if err != nil {
errors.Wrap(err, "failed to begin transaction")
}
for _, query := range statements {
printInfo("Executing statement: %s\n", clearStatement(query))
if _, err = tx.Exec(query); err != nil {
printInfo("Rollback transaction\n")
tx.Rollback()
return errors.Wrapf(err, "failed to execute SQL query %q", clearStatement(query))
}
}
if direction {
if _, err := tx.Exec(GetDialect().insertVersionSQL(), v, direction); err != nil {
printInfo("Rollback transaction\n")
tx.Rollback()
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 errors.Wrap(err, "failed to delete goose version")
}
}
printInfo("Commit transaction\n")
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", clearStatement(query))
if _, err := db.Exec(query); err != nil {
return errors.Wrapf(err, "failed to execute SQL query %q", clearStatement(query))
}
}
if _, err := db.Exec(GetDialect().insertVersionSQL(), v, direction); err != nil {
return errors.Wrap(err, "failed to insert new goose version")
}
return nil
}
func printInfo(s string, args ...interface{}) {
if verbose {
log.Printf(s, args...)
}
}
var (
matchSQLComments = regexp.MustCompile(`(?m)^--.*$[\r\n]*`)
matchEmptyLines = regexp.MustCompile(`(?m)^$[\r\n]*`) // TODO: Duplicate
)
func clearStatement(s string) string {
s = matchSQLComments.ReplaceAllString(s, ``)
return matchEmptyLines.ReplaceAllString(s, ``)
}