down.go

  1package goose
  2
  3import (
  4	"context"
  5	"database/sql"
  6	"fmt"
  7)
  8
  9// Down rolls back a single migration from the current version.
 10func Down(db *sql.DB, dir string, opts ...OptionsFunc) error {
 11	ctx := context.Background()
 12	return DownContext(ctx, db, dir, opts...)
 13}
 14
 15// DownContext rolls back a single migration from the current version.
 16func DownContext(ctx context.Context, db *sql.DB, dir string, opts ...OptionsFunc) error {
 17	option := &options{}
 18	for _, f := range opts {
 19		f(option)
 20	}
 21	migrations, err := CollectMigrations(dir, minVersion, maxVersion)
 22	if err != nil {
 23		return err
 24	}
 25	if option.noVersioning {
 26		if len(migrations) == 0 {
 27			return nil
 28		}
 29		currentVersion := migrations[len(migrations)-1].Version
 30		// Migrate only the latest migration down.
 31		return downToNoVersioning(ctx, db, migrations, currentVersion-1)
 32	}
 33	currentVersion, err := GetDBVersionContext(ctx, db)
 34	if err != nil {
 35		return err
 36	}
 37	current, err := migrations.Current(currentVersion)
 38	if err != nil {
 39		return fmt.Errorf("migration %v: %w", currentVersion, err)
 40	}
 41	return current.DownContext(ctx, db)
 42}
 43
 44// DownTo rolls back migrations to a specific version.
 45func DownTo(db *sql.DB, dir string, version int64, opts ...OptionsFunc) error {
 46	ctx := context.Background()
 47	return DownToContext(ctx, db, dir, version, opts...)
 48}
 49
 50// DownToContext rolls back migrations to a specific version.
 51func DownToContext(ctx context.Context, db *sql.DB, dir string, version int64, opts ...OptionsFunc) error {
 52	option := &options{}
 53	for _, f := range opts {
 54		f(option)
 55	}
 56	migrations, err := CollectMigrations(dir, minVersion, maxVersion)
 57	if err != nil {
 58		return err
 59	}
 60	if option.noVersioning {
 61		return downToNoVersioning(ctx, db, migrations, version)
 62	}
 63
 64	for {
 65		currentVersion, err := GetDBVersionContext(ctx, db)
 66		if err != nil {
 67			return err
 68		}
 69
 70		if currentVersion == 0 {
 71			log.Printf("goose: no migrations to run. current version: %d", currentVersion)
 72			return nil
 73		}
 74		current, err := migrations.Current(currentVersion)
 75		if err != nil {
 76			log.Printf("goose: migration file not found for current version (%d), error: %s", currentVersion, err)
 77			return err
 78		}
 79
 80		if current.Version <= version {
 81			log.Printf("goose: no migrations to run. current version: %d", currentVersion)
 82			return nil
 83		}
 84
 85		if err = current.DownContext(ctx, db); err != nil {
 86			return err
 87		}
 88	}
 89}
 90
 91// downToNoVersioning applies down migrations down to, but not including, the
 92// target version.
 93func downToNoVersioning(ctx context.Context, db *sql.DB, migrations Migrations, version int64) error {
 94	var finalVersion int64
 95	for i := len(migrations) - 1; i >= 0; i-- {
 96		if version >= migrations[i].Version {
 97			finalVersion = migrations[i].Version
 98			break
 99		}
100		migrations[i].noVersioning = true
101		if err := migrations[i].DownContext(ctx, db); err != nil {
102			return err
103		}
104	}
105	log.Printf("goose: down to current file version: %d", finalVersion)
106	return nil
107}