Documentation
¶
Overview ¶
Package migrate is schema versioning over authored SQL: ordered sets of migrations, each migration SQL text plus a version, applied against a history table on one pinned connection under the dialect's lock capability. It designs for the cases a mature library has met: dirty state after a failed non-transactional migration (recorded, reported, cleared only by Force), engines without transactional DDL (a migration headed "-- transaction: none" runs outside a transaction and contains exactly one statement by convention), and concurrent starters (the lock; a dialect without one fails unless the consumer opts into an unlocked run). Files packages the NNNN_name.{up,down}.sql layout as a helper, never the contract.
Sets ¶
A Set is one migration layer as a library ships it or a program declares it: a name, the history table its migrations are recorded in, and its migrations in version order. A Migrator runs one or more sets, declared bottom-first, each over its own history table: a set's migrations may reference the objects of the sets declared before it, never the objects of the sets declared after it. A set that leaves Set.Table empty uses DefaultTable, so at most one set per migrator may leave it empty. A program installed under an earlier single-set migrator adopts sets without migrating its history: the default table and the history's columns are the same.
The run model ¶
Every run that writes takes one lock, pins one connection, and creates and reads every set's history table before any set's first migration runs. A dirty row or a history that does not match its set refuses the run there, naming the set, with nothing run against the schema. The read happens inside the lock, so no starter can change a history between the check and the run. Options.Unlocked skips the lock; its intended shape is a caller that holds a lock of its own around every run, and concurrent starters without one are unsafe.
Verbs ¶
A Migrator method that names no set acts on the top set, the last one declared: Version, Migrations, Steps, Down, and Force. Up, Verify, Reset, and Status cover every set, in declared order, and Reset reverts in reverse declared order and drops each set's history table once that set is reverted.
Layer is a handle on one named set, from Migrator.Layers or Migrator.Layer, and its verbs act on that set alone under the migrator's ordering rules: a revert is ErrAboveApplied while a layer above still has applied migrations, and an apply is ErrBelowPending while a layer below still has pending ones. Reverting more migrations than a set has applied is not an error, so Down with the set's whole length reverts everything it has applied.
Force is the operator repair for a dirty set. It sets a set's history to a version without running anything against the schema, and it is the one verb that checks no set's history first, since the set it repairs is the dirty one. The repair is: fix the failed migration's objects by hand, Force to the version that is applied, then Up.
Index ¶
- Constants
- Variables
- type Catalog
- type DirtyError
- type Layer
- func (l Layer) Down(ctx context.Context, n int) error
- func (l Layer) Force(ctx context.Context, version int) error
- func (l Layer) Migrations() []Migration
- func (l Layer) Name() string
- func (l Layer) Status(ctx context.Context) (SetStatus, error)
- func (l Layer) Steps(ctx context.Context, n int) error
- func (l Layer) Table() string
- func (l Layer) Verify(ctx context.Context) error
- func (l Layer) Version(ctx context.Context) (Version, error)
- type Migration
- type Migrator
- func (m *Migrator) Down(ctx context.Context, n int) error
- func (m *Migrator) Force(ctx context.Context, version int) error
- func (m *Migrator) Layer(name string) (Layer, bool)
- func (m *Migrator) Layers() []Layer
- func (m *Migrator) Migrations() []Migration
- func (m *Migrator) Reset(ctx context.Context) error
- func (m *Migrator) Status(ctx context.Context) ([]SetStatus, error)
- func (m *Migrator) Steps(ctx context.Context, n int) error
- func (m *Migrator) Up(ctx context.Context) error
- func (m *Migrator) Verify(ctx context.Context) error
- func (m *Migrator) Version(ctx context.Context) (Version, error)
- type Options
- type PendingError
- type Set
- type SetError
- type SetStatus
- type StandardCatalog
- type UnknownVersionError
- type Version
Constants ¶
const DefaultTable = "schema_version"
DefaultTable is the history table a Set uses when its Table is empty. At most one of a Migrator's sets may leave Table empty.
Variables ¶
var ( // ErrNoLocker reports a run that needs the lock on a dialect without the // capability; Options.Unlocked opts out. ErrNoLocker = errors.New("migrate: dialect has no lock capability") // ErrDirty is the class a *DirtyError unwraps to. ErrDirty = errors.New("migrate: schema is dirty") // ErrPending is the class a *PendingError unwraps to. ErrPending = errors.New("migrate: migrations pending") // ErrUnknownVersion is the class an *UnknownVersionError unwraps to. ErrUnknownVersion = errors.New("migrate: history does not match the migration set") // ErrNoDown reports a Down of a migration that has no down text. ErrNoDown = errors.New("migrate: migration has no down") // ErrVersionNotFound reports a Force to a version outside the set. ErrVersionNotFound = errors.New("migrate: version not in the migration set") // ErrAboveApplied reports a revert of a layer refused because a layer // above it still has applied migrations; revert that layer first. ErrAboveApplied = errors.New("migrate: a layer above has applied migrations") // ErrBelowPending reports an apply on a layer refused because a layer // below it still has pending migrations; Up applies every layer in order. ErrBelowPending = errors.New("migrate: a layer below has pending migrations") )
Functions ¶
This section is empty.
Types ¶
type Catalog ¶
type Catalog interface {
// CreateHistory returns the DDL that creates table when it does not
// exist, with the columns the protocol reads and writes:
//
// - version (integer, primary key)
// - name (text, not null)
// - applied_at (timestamp, not null, defaulting to the current time)
// - dirty (boolean, not null, defaulting to false)
CreateHistory(table string) string
// HistoryExists returns the query that yields one row whose first
// column is nonzero when the history table exists. The table name is
// the query's one argument, bound at param, the dialect's placeholder.
HistoryExists(param string) string
}
Catalog is the engine-specific half of the history protocol: the two statements standard SQL cannot express the same way on every engine. A dialect provides it by implementing the methods; a dialect that does not gets StandardCatalog. Everything else the migrator runs is standard DML with bound parameters.
type DirtyError ¶
DirtyError reports a version whose non-transactional migration failed midway: the history row is marked dirty and every run refuses until Force clears it. Err is the failure that dirtied it, when this run caused it.
func (*DirtyError) Error ¶
func (e *DirtyError) Error() string
func (*DirtyError) Unwrap ¶
func (e *DirtyError) Unwrap() []error
type Layer ¶ added in v0.2.0
type Layer struct {
// contains filtered or unexported fields
}
Layer is a handle on one of a Migrator's sets. Its verbs act on that set alone, under the migrator's lock and its ordering rules: a revert is refused while a layer above has applied migrations, and an apply is refused while a layer below has pending ones. The zero Layer is not usable; Migrator.Layers and Migrator.Layer return usable ones.
func (Layer) Down ¶ added in v0.2.0
Down reverts the set's n most recently applied migrations; n larger than the applied count reverts them all. A caller that wants every set reverted without dropping any history table iterates Layers in reverse and calls Down(ctx, len(l.Migrations())) on each; Migrator.Reset reverts every set and drops the tables as well.
func (Layer) Force ¶ added in v0.2.0
Force sets the set's history to version as an operator override: rows above it are deleted, its row is inserted if absent and marked clean, and version 0 empties the history. Nothing runs against the schema itself, and no set's history is checked first, since Force is the repair for a dirty one.
func (Layer) Migrations ¶ added in v0.2.0
Migrations returns a copy of the set's migrations.
func (Layer) Steps ¶ added in v0.2.0
Steps applies the set's next n pending migrations when n is positive, or reverts its last -n applied when negative; fewer remaining is not an error, so -len(Migrations()) reverts the whole set. Every set's history is checked first. A positive n is refused with ErrBelowPending while a layer below has pending migrations; a negative n is refused with ErrAboveApplied while a layer above has applied ones.
func (Layer) Table ¶ added in v0.2.0
Table returns the set's history table, after DefaultTable is applied.
func (Layer) Verify ¶ added in v0.2.0
Verify checks, without the lock, that the set's history is a clean, complete prefix of its migrations. Every fault is a *SetError naming the set: a dirty row unwraps to a *DirtyError, a row the set does not carry to an *UnknownVersionError, and unapplied migrations to a *PendingError.
type Migration ¶
Migration is one schema step: the version and name that identify it in the history, the SQL that applies and reverts it, and whether it runs in a transaction. Down may be empty.
func Files ¶
Files reads the NNNN_name.{up,down}.sql layout under dir in fsys into a version-ordered set. The up file's "--|" header decides Transactional: the "transaction" declaration, "none" opting out and "required" or absence keeping the transaction; a down file that declares differently is an error. Up and Down are the files' bodies; the engine never sees a header. Versions must be unique. A down without its up is an error; an up without its down is allowed.
type Migrator ¶
type Migrator struct {
// contains filtered or unexported fields
}
Migrator runs one or more migration sets against a database: the sets in declared order, each over its own history table, every run on one pinned connection under one lock. Its methods without a set act on the top set, except Up, Verify, Reset, and Status, which cover every set.
func New ¶
New validates the sets and prepares the migrator: at least one set, each with a name no other set uses and a history table (DefaultTable when empty, so at most one set may leave it empty) no other set uses, and each set's migrations with positive, strictly increasing versions and names and up texts present. The lock capability and the Catalog are taken from the dialect when it has them. New performs no I/O.
func (*Migrator) Down ¶
Down reverts the top set's n most recently applied migrations. A layer above with applied migrations is ErrAboveApplied; Layer.Down reverts one named set.
func (*Migrator) Force ¶
Force sets the top set's history to version as an operator override. Layer.Force forces one named set.
func (*Migrator) Layer ¶ added in v0.2.0
Layer returns a handle on the set called name, reporting whether the migrator holds one.
func (*Migrator) Migrations ¶
Migrations returns a copy of the top set's migrations.
func (*Migrator) Reset ¶ added in v0.2.0
Reset returns the database to its state before the first Up: under one lock, after every set's history is checked, it reverts every set in reverse declared order and drops each set's history table once that set is reverted, so a set above never blocks the revert of the set it references. A later Up replays every set from zero. A caller that wants every set reverted with the tables left in place iterates Layers in reverse and calls Down(ctx, len(l.Migrations())) on each.
func (*Migrator) Status ¶ added in v0.2.0
Status reads every set's state in declared order without the lock. It runs on the pool, so it never waits on a running Up, and a report read while a run is in progress can show a set mid-run.
func (*Migrator) Steps ¶
Steps applies the top set's next n pending migrations when n is positive, or reverts its last -n applied when negative; fewer remaining is not an error.
func (*Migrator) Up ¶
Up applies every pending migration of every set, sets in declared order, under one lock on one connection. Every set's history is checked first, so a dirty set or a history that does not match its set refuses the run before any migration runs. The first failure stops the run; the sets before it stay applied.
type Options ¶
type Options struct {
// LockName names the lock a run holds; default "migrate.<table>" over
// the top set's history table, so a program's older single-set binary
// and its multi-set successor contend for the same lock mid-rollout,
// and no lock of the program's own can collide with it by number.
LockName string
// Unlocked allows runs on a dialect without the lock capability, or with
// it, without taking the lock. Its intended shape is a caller that holds
// a lock of its own around every run; concurrent starters without one
// are unsafe.
Unlocked bool
// Logger records each applied and reverted migration, each forced
// version, and each history table Reset drops; nil is silent.
Logger *slog.Logger
}
Options configures a Migrator. Every field has a default.
type PendingError ¶
type PendingError struct{ Versions []int }
PendingError reports migrations in the set the history has not applied.
func (*PendingError) Error ¶
func (e *PendingError) Error() string
func (*PendingError) Unwrap ¶
func (e *PendingError) Unwrap() error
type Set ¶ added in v0.2.0
Set is one migration layer as a library ships it or a program declares it: a name, the history table its migrations are recorded in (empty for DefaultTable), and its migrations in version order. A Migrator runs several, declared bottom-first: a set's migrations may reference the objects of the sets declared before it, never after.
type SetError ¶ added in v0.2.0
SetError is an error from one set's history or migrations, naming the set. It unwraps to the set's own error, so errors.Is classifies it (ErrDirty, ErrPending, ErrUnknownVersion) and errors.As reaches the detail (*DirtyError, *PendingError, *UnknownVersionError).
type SetStatus ¶ added in v0.2.0
type SetStatus struct {
Name string
Table string
Version int
Latest int
Pending []Migration
Dirty bool
}
SetStatus is one layer's state as Status reads it, without the lock: the layer's name and history table, the highest applied version (zero when nothing is applied), the layer's latest version, the migrations the history has not applied, and whether the history holds a dirty row.
type StandardCatalog ¶
type StandardCatalog struct{}
StandardCatalog is the Catalog for engines that accept CREATE TABLE IF NOT EXISTS with text and boolean columns and expose information_schema: MySQL and MariaDB as they ship, and PostgreSQL as a fallback for a dialect that does not implement its own (see HistoryExists's limitation). SQL Server (no IF NOT EXISTS, no boolean), Oracle (no information_schema), and SQLite (sqlite_master) provide their own.
func (StandardCatalog) CreateHistory ¶
func (StandardCatalog) CreateHistory(table string) string
func (StandardCatalog) HistoryExists ¶
func (StandardCatalog) HistoryExists(param string) string
HistoryExists checks information_schema.tables by name alone, which spans every schema on the search path: a same-named table in an unrelated schema satisfies the check even though the current schema's own history table does not exist. A dialect for an engine where this matters — as PostgreSQL's own Dialect does — implements Catalog itself with the engine's own schema-qualified form instead of falling back to this one.
type UnknownVersionError ¶
UnknownVersionError reports an applied row the migration set does not contain at that position: a version the set lacks, or a name that differs.
func (*UnknownVersionError) Error ¶
func (e *UnknownVersionError) Error() string
func (*UnknownVersionError) Unwrap ¶
func (e *UnknownVersionError) Unwrap() error