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). The NNNN_name.{up,down}.sql file layout is a helper, never the contract.
A run waits for the lock without holding a statement open when the dialect implements sqlate.TryLocker: it polls TryLock, backing off between refusals, and stops with the context's error when the context ends. A holder's non-transactional CREATE INDEX CONCURRENTLY waits for every open transaction, so a waiter blocked in a lock statement would deadlock with it. A dialect with sqlate.Locker alone waits in its blocking Lock.
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.
Force is the operator repair for a dirty set. The repair is: fix the failed migration's objects by hand, Force to the version that is applied, then Up.
The package exports:
- Migrator, which runs one or more sets, built by New with its Options
- Set, one migration layer, with DefaultTable, the history table a set without one uses
- Migration, one schema step, and Files, which reads the file layout into a set's migrations
- Layer, a handle on one of a migrator's sets
- Version, a history's head, and SetStatus, one set's state
- Catalog, the engine-specific half of the history protocol, with HistoryUpgrader, its capability to upgrade an earlier table, and StandardCatalog, the one a dialect without its own gets
- SetError, an error naming its set, over the details DirtyError, PendingError, and UnknownVersionError and their classes ErrDirty, ErrPending, and ErrUnknownVersion
- ErrNoLocker, ErrNoDown, ErrVersionNotFound, ErrAboveApplied, and ErrBelowPending, the refusals of a run, a revert, a force, and the layer ordering
Index ¶
- Constants
- Variables
- type Catalog
- type DirtyError
- type HistoryUpgrader
- 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 (the instant the row was written, not null,
// defaulting to the current time; a time-zone-aware type where the
// engine has one, so the instant does not depend on the session's
// zone)
// - 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 HistoryUpgrader ¶ added in v0.5.0
type HistoryUpgrader interface {
// HistoryOutdated returns the query that yields one row whose first
// column is nonzero when the history table has an earlier release's
// shape. The table name is the query's one argument, bound at param,
// the dialect's placeholder.
HistoryOutdated(param string) string
// UpgradeHistory returns the DDL that converts table to the current
// shape in place, keeping every row and the instant each records.
UpgradeHistory(table string) string
}
HistoryUpgrader is the Catalog capability that brings a history table an earlier release created to the shape CreateHistory creates now. A dialect provides it by implementing the two methods alongside Catalog's. Each locked run (Up, Steps, Down, Reset, Force) runs HistoryOutdated after CreateHistory, inside the lock and before it reads the history, and runs UpgradeHistory only when the query reports the table outdated, so a current table is read and never rewritten, and the upgrade is idempotent however the engine's DDL behaves on a second run.
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, and every migration up to and including it has its row marked clean, inserted where absent, so the history is the set's prefix through version however far below it the history stood. 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.
Its applied_at is a plain timestamp, the one spelling all its engines accept. On MySQL and MariaDB a timestamp is stored as an instant, so the column is time-zone-aware there; on PostgreSQL it is a wall clock without zone, which is one more reason the postgres dialect implements its own Catalog, with timestamp with time zone. StandardCatalog has no HistoryUpgrader, since there is nothing for its engines to upgrade.
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