feat: Gracefully recover Go migrations that panic (#643)

pull/644/head
Michael Fridman 2023-11-12 12:06:20 -05:00 committed by GitHub
parent 9e6ef20c4f
commit 1e40462848
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 36 additions and 1 deletions

View File

@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"io/fs"
"runtime/debug"
"sort"
"strconv"
"strings"
@ -424,7 +425,13 @@ func runMigration(ctx context.Context, db database.DBTxConn, m *Migration, direc
// runGo is a helper function that runs the given Go functions in the given direction. It must only
// be called after the migration has been initialized.
func runGo(ctx context.Context, db database.DBTxConn, m *Migration, direction bool) error {
func runGo(ctx context.Context, db database.DBTxConn, m *Migration, direction bool) (retErr error) {
defer func() {
if r := recover(); r != nil {
retErr = fmt.Errorf("panic: %v\n%s", r, debug.Stack())
}
}()
switch db := db.(type) {
case *sql.Conn:
return fmt.Errorf("go migrations are not supported with *sql.Conn")

View File

@ -724,6 +724,34 @@ func TestSQLiteSharedCache(t *testing.T) {
})
}
func TestGoMigrationPanic(t *testing.T) {
t.Parallel()
ctx := context.Background()
const (
wantErrString = "panic: runtime error: index out of range [7] with length 0"
)
migration := goose.NewGoMigration(
1,
&goose.GoFunc{RunTx: func(ctx context.Context, tx *sql.Tx) error {
var ss []int
_ = ss[7]
return nil
}},
nil,
)
p, err := goose.NewProvider(goose.DialectSQLite3, newDB(t), nil,
goose.WithGoMigrations(migration), // Add a Go migration that panics.
)
check.NoError(t, err)
_, err = p.Up(ctx)
check.HasError(t, err)
check.Contains(t, err.Error(), wantErrString)
var expected *goose.PartialError
check.Bool(t, errors.As(err, &expected), true)
check.Contains(t, expected.Err.Error(), wantErrString)
}
func TestCustomStoreTableExists(t *testing.T) {
t.Parallel()