migrate

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 4, 2026 License: Apache-2.0 Imports: 12 Imported by: 3

Documentation

Overview

Package migrate is schema versioning over authored SQL: an ordered set of migrations, each 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.

Index

Constants

This section is empty.

Variables

View Source
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")
)

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

type DirtyError struct {
	Version int
	Err     error
}

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 Migration

type Migration struct {
	Version       int
	Name          string
	Up            string
	Down          string
	Transactional bool
}

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

func Files(fsys fs.FS, dir string) ([]Migration, error)

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 a migration set against a database.

func New

func New(db *sqlate.DB, migrations []Migration, opts Options) (*Migrator, error)

New validates the set (versions positive and strictly increasing, names and up texts present) and prepares the migrator: the lock capability and the Catalog are taken from the dialect when it has them. It performs no I/O.

func (*Migrator) Down

func (m *Migrator) Down(ctx context.Context, n int) error

Down reverts the n most recently applied migrations.

func (*Migrator) Force

func (m *Migrator) Force(ctx context.Context, version int) error

Force sets the 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.

func (*Migrator) Migrations

func (m *Migrator) Migrations() []Migration

Migrations returns a copy of the set.

func (*Migrator) Steps

func (m *Migrator) Steps(ctx context.Context, n int) error

Steps applies the next n pending migrations when n is positive, or reverts the last -n applied when negative; fewer remaining is not an error.

func (*Migrator) Up

func (m *Migrator) Up(ctx context.Context) error

Up applies every pending migration.

func (*Migrator) Verify

func (m *Migrator) Verify(ctx context.Context) error

Verify checks, without the lock, that the history is a clean, complete prefix of the set: a dirty row is a *DirtyError, a row the set does not contain is an *UnknownVersionError, and unapplied migrations are a *PendingError.

func (*Migrator) Version

func (m *Migrator) Version(ctx context.Context) (Version, error)

Version reads the history's head without taking the lock. A missing history table is the zero Version.

type Options

type Options struct {
	// Table names the history table; default "schema_version".
	Table string
	// LockName names the lock a run holds; default "migrate.<table>", so two
	// migrators over different tables never contend 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; concurrent starters are then unsafe.
	Unlocked bool
	// Logger records each applied and reverted migration; 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 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: PostgreSQL, MySQL, and MariaDB as they ship. 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

type UnknownVersionError

type UnknownVersionError struct {
	Version int
	Name    string
}

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

type Version

type Version struct {
	Version int
	Dirty   bool
}

Version is the history's head: the highest applied version and whether its row is dirty. A zero Version means nothing is applied.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL