provider_options.go

  1package goose
  2
  3import (
  4	"errors"
  5	"fmt"
  6
  7	"github.com/pressly/goose/v3/database"
  8	"github.com/pressly/goose/v3/lock"
  9)
 10
 11const (
 12	// DefaultTablename is the default name of the database table used to track history of applied
 13	// migrations.
 14	DefaultTablename = "goose_db_version"
 15)
 16
 17// ProviderOption is a configuration option for a goose goose.
 18type ProviderOption interface {
 19	apply(*config) error
 20}
 21
 22// WithStore configures the provider with a custom [database.Store] implementation.
 23//
 24// By default, the provider uses the [database.NewStore] function to create a store backed by the
 25// given dialect. However, this option allows users to provide their own implementation or call
 26// [database.NewStore] with custom options, such as setting the table name.
 27//
 28// Example:
 29//
 30//	// Create a store with a custom table name.
 31//	store, err := database.NewStore(database.DialectPostgres, "my_custom_table_name")
 32//	if err != nil {
 33//	    return err
 34//	}
 35//	// Create a provider with the custom store.
 36//	provider, err := goose.NewProvider("", db, nil, goose.WithStore(store))
 37//	if err != nil {
 38//	    return err
 39//	}
 40func WithStore(store database.Store) ProviderOption {
 41	return configFunc(func(c *config) error {
 42		if c.store != nil {
 43			return fmt.Errorf("store already set: %T", c.store)
 44		}
 45		if store == nil {
 46			return errors.New("store must not be nil")
 47		}
 48		if store.Tablename() == "" {
 49			return errors.New("store implementation must set the table name")
 50		}
 51		c.store = store
 52		return nil
 53	})
 54}
 55
 56// WithVerbose enables verbose logging.
 57func WithVerbose(b bool) ProviderOption {
 58	return configFunc(func(c *config) error {
 59		c.verbose = b
 60		return nil
 61	})
 62}
 63
 64// WithSessionLocker enables locking using the provided SessionLocker.
 65//
 66// If WithSessionLocker is not called, locking is disabled.
 67func WithSessionLocker(locker lock.SessionLocker) ProviderOption {
 68	return configFunc(func(c *config) error {
 69		if c.lockEnabled {
 70			return errors.New("lock already enabled")
 71		}
 72		if c.sessionLocker != nil {
 73			return errors.New("session locker already set")
 74		}
 75		if locker == nil {
 76			return errors.New("session locker must not be nil")
 77		}
 78		c.lockEnabled = true
 79		c.sessionLocker = locker
 80		return nil
 81	})
 82}
 83
 84// WithExcludeNames excludes the given file name from the list of migrations. If called multiple
 85// times, the list of excludes is merged.
 86func WithExcludeNames(excludes []string) ProviderOption {
 87	return configFunc(func(c *config) error {
 88		for _, name := range excludes {
 89			if _, ok := c.excludePaths[name]; ok {
 90				return fmt.Errorf("duplicate exclude file name: %s", name)
 91			}
 92			c.excludePaths[name] = true
 93		}
 94		return nil
 95	})
 96}
 97
 98// WithExcludeVersions excludes the given versions from the list of migrations. If called multiple
 99// times, the list of excludes is merged.
100func WithExcludeVersions(versions []int64) ProviderOption {
101	return configFunc(func(c *config) error {
102		for _, version := range versions {
103			if version < 1 {
104				return errInvalidVersion
105			}
106			if _, ok := c.excludeVersions[version]; ok {
107				return fmt.Errorf("duplicate excludes version: %d", version)
108			}
109			c.excludeVersions[version] = true
110		}
111		return nil
112	})
113}
114
115// WithGoMigrations registers Go migrations with the provider. If a Go migration with the same
116// version has already been registered, an error will be returned.
117//
118// Go migrations must be constructed using the [NewGoMigration] function.
119func WithGoMigrations(migrations ...*Migration) ProviderOption {
120	return configFunc(func(c *config) error {
121		for _, m := range migrations {
122			if _, ok := c.registered[m.Version]; ok {
123				return fmt.Errorf("go migration with version %d already registered", m.Version)
124			}
125			if err := checkGoMigration(m); err != nil {
126				return fmt.Errorf("invalid go migration: %w", err)
127			}
128			c.registered[m.Version] = m
129		}
130		return nil
131	})
132}
133
134// WithDisableGlobalRegistry prevents the provider from registering Go migrations from the global
135// registry. By default, goose will register all Go migrations including those registered globally.
136func WithDisableGlobalRegistry(b bool) ProviderOption {
137	return configFunc(func(c *config) error {
138		c.disableGlobalRegistry = b
139		return nil
140	})
141}
142
143// WithAllowOutofOrder allows the provider to apply missing (out-of-order) migrations. By default,
144// goose will raise an error if it encounters a missing migration.
145//
146// For example: migrations 1,3 are applied and then version 2,6 are introduced. If this option is
147// true, then goose will apply 2 (missing) and 6 (new) instead of raising an error. The final order
148// of applied migrations will be: 1,3,2,6. Out-of-order migrations are always applied first,
149// followed by new migrations.
150func WithAllowOutofOrder(b bool) ProviderOption {
151	return configFunc(func(c *config) error {
152		c.allowMissing = b
153		return nil
154	})
155}
156
157// WithDisableVersioning disables versioning. Disabling versioning allows applying migrations
158// without tracking the versions in the database schema table. Useful for tests, seeding a database
159// or running ad-hoc queries. By default, goose will track all versions in the database schema
160// table.
161func WithDisableVersioning(b bool) ProviderOption {
162	return configFunc(func(c *config) error {
163		c.disableVersioning = b
164		return nil
165	})
166}
167
168// WithLogger will set a custom Logger, which will override the default logger.
169func WithLogger(l Logger) ProviderOption {
170	return configFunc(func(c *config) error {
171		c.logger = l
172		return nil
173	})
174}
175
176type config struct {
177	store database.Store
178
179	verbose         bool
180	excludePaths    map[string]bool
181	excludeVersions map[int64]bool
182
183	// Go migrations registered by the user. These will be merged/resolved against the globally
184	// registered migrations.
185	registered map[int64]*Migration
186
187	// Locking options
188	lockEnabled   bool
189	sessionLocker lock.SessionLocker
190
191	// Feature
192	disableVersioning     bool
193	allowMissing          bool
194	disableGlobalRegistry bool
195
196	logger Logger
197}
198
199type configFunc func(*config) error
200
201func (f configFunc) apply(cfg *config) error {
202	return f(cfg)
203}