migrate

package
v0.0.0-...-575b669 Latest Latest
Warning

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

Go to latest
Published: Nov 4, 2026 License: MIT Imports: 20 Imported by: 0

Documentation

Overview

Package migrate runs database schema migrations written with callbacks that receive an Operator. A Migration's Up and Down functions call op.CreateTable / op.AddColumn / op.DropColumn / op.CreateIndex / op.CreateForeignKey / ... to perform the change; columns are declared with the decl DSL (portable types and options), and the migrate package never asks the author to touch the sql package directly. The Migrator applies pending migrations inside a transaction and records every apply and rollback in an append-only history table (schema_migrations by default).

Migrations form a DAG: each carries a Revision id and the DownRevision it builds on (empty for the first). The Migrator resolves the apply order from those links, not from declaration order. Keep each migration in its own file and register it with a Registry:

var _ = Registry.Add(migrate.Migration{
	Revision:     "8f3a1c", // unique id; migrate.NewRevision() generates one
	DownRevision: "",       // the revision this builds on ("" = first)
	Name:         "create_tags",
	Up: func(op *migrate.Operator) {
		op.CreateTable("tags",
			decl.Column("id", decl.BigInt, decl.PrimaryKey(), decl.Default()),
			decl.Column("name", decl.VarChar(64), decl.Unique()),
		)
	},
	Down: func(op *migrate.Operator) { op.DropTable("tags") },
})

m := Registry.NewMigrator(db)
err := m.Up(ctx)

Transactions: each migration applies in one transaction so its operations and its history row commit together. PostgreSQL DDL is transactional, so a failed migration rolls back cleanly. MySQL commits DDL implicitly and cannot roll a partly-applied migration back; treat MySQL migrations as non-atomic. Set DisableTx for a migration that must not run in a transaction (e.g. PostgreSQL CREATE INDEX CONCURRENTLY).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Diff

func Diff(old, next meta.Metadata, dialect sql.Dialect) ([]sql.Statement, error)

Diff compares an old schema to a desired new schema and returns the ordered DDL statements that migrate old into new — the forward (Up) plan for a migration. It takes a dialect because column-type equality and rendering are dialect-resolved.

It detects added/dropped tables; added/dropped columns; column type, nullability, and default changes; primary-key membership changes; added/dropped/changed indexes, foreign keys (including ON DELETE/UPDATE actions), and CHECK constraints; table/column comment changes; and added/dropped/changed non-table objects — extensions, enum types, sequences, materialized views, and grants — ordered so each is created before the tables that depend on it and dropped after (a changed object is dropped and recreated). Column modifications render for both PostgreSQL (separate ALTER COLUMN actions) and MySQL (a single MODIFY COLUMN). Statements are ordered for safety: drop foreign keys, drop indexes, and drop tables first; then create tables, alter columns, create indexes, add foreign keys, and set comments last.

Out of scope: column and table renames are reported as drop+add (which loses data — author a hand-written migration, or use the autogenerator's rename hints). Diff produces only the forward plan; author the Down statements by hand (the autogenerator derives a best-effort Down from the reverse diff).

func NewRevision

func NewRevision() string

NewRevision returns a new random revision id (12 hex chars), a convenient collision-resistant value for Migration.Revision.

Types

type AlterColumnChange

type AlterColumnChange func(stmt sql.AlterTableBuilder, column string) sql.AlterTableBuilder

AlterColumnChange is one change applied by AlterColumn (TypeTo, SetNullable, SetDefault, DropDefault).

func DropDefault

func DropDefault() AlterColumnChange

DropDefault drops a column's default.

func SetDefault

func SetDefault(expr string) AlterColumnChange

SetDefault sets a column's default to the given boolean/expression text.

func SetNullable

func SetNullable(nullable bool) AlterColumnChange

SetNullable makes a column nullable (true) or NOT NULL (false).

func TypeTo

func TypeTo(typ decl.DataType) AlterColumnChange

TypeTo changes a column's type to the given portable type.

type CLI

type CLI struct {
	Up      upCmd      `cmd:"" help:"Apply migrations up to a target (default head)."`
	Down    downCmd    `cmd:"" help:"Roll back migrations to a target (default the most recent)."`
	Stamp   stampCmd   `cmd:"" help:"Mark a revision applied without running it."`
	Status  statusCmd  `cmd:"" help:"Show each migration's applied state."`
	Pending pendingCmd `cmd:"" help:"List the unapplied migrations."`
	Current currentCmd `cmd:"" help:"Show the revision(s) the database is at."`
	Heads   headsCmd   `cmd:"" help:"Show the head revision(s)."`
	History historyCmd `cmd:"" help:"Show the apply/rollback timeline."`
	SQL     sqlCmd     `cmd:"" name:"sql" help:"Render migration SQL without running it."`
	New     newCmd     `cmd:"" help:"Scaffold a new migration file."`
}

CLI is the command grammar for running and scaffolding migrations. Its struct tags describe the commands to a CLI framework without this package depending on one: build it with NewCLI, hand it to kong (kong.New(migrate.NewCLI())), and run the parsed command against m.Runner(ctx). To ship a migration binary without pulling in kong, use CLI.Dispatch instead.

Commands: up, down, stamp, status, pending, current, heads, history, sql up|down, and new.

func NewCLI

func NewCLI() *CLI

NewCLI returns a fresh command grammar to hand to a CLI parser. The caller owns the parser (and its dependency): e.g.

cli := migrate.NewCLI()
parser, _ := kong.New(cli, kong.Name("migrate"))
kctx, _ := parser.Parse(os.Args[1:])
err := kctx.Run(m.Runner(ctx))

func (*CLI) Dispatch

func (c *CLI) Dispatch(r *Runner, args []string) error

Dispatch parses args and runs the matching command against r, without any third-party CLI dependency. It accepts the same command set as the kong grammar (up, down, stamp, status, pending, current, heads, history, sql up|down, new) and is what a shipped binary or the sqlkit migrate bridge calls so neither needs kong:

err := migrate.NewCLI().Dispatch(m.Runner(ctx), os.Args[1:])

type Direction

type Direction string

Direction is whether a history event applied a migration or rolled it back.

const (
	DirectionUp   Direction = "up"
	DirectionDown Direction = "down"
)

The two migration directions recorded in the history table.

type Execer

type Execer interface {
	Exec(ctx context.Context, query sql.Query) (stdsql.Result, error)
}

Execer is the execution surface the Operator runs statements against. Both *sqlkit.Database (auto-commit) and *sqlkit.Session (a transaction) satisfy it.

type ForeignKeyOption

type ForeignKeyOption func(*sql.ForeignKeyConstraint)

ForeignKeyOption configures a foreign key added with CreateForeignKey.

func OnDelete

func OnDelete(action meta.ReferentialAction) ForeignKeyOption

OnDelete sets a foreign key's ON DELETE referential action.

func OnUpdate

func OnUpdate(action meta.ReferentialAction) ForeignKeyOption

OnUpdate sets a foreign key's ON UPDATE referential action.

type Migration

type Migration struct {
	Revision      string
	DownRevision  string
	DownRevisions []string // multiple parents (merge); overrides DownRevision
	Name          string
	Up            func(*Operator)
	Down          func(*Operator)

	// DisableTx runs the migration outside a transaction (auto-commit). Needed
	// for statements a transaction rejects, e.g. CREATE INDEX CONCURRENTLY.
	DisableTx bool
}

Migration is one schema change in the migration DAG. Revision is its unique id; DownRevision is the revision it builds on ("" for the first migration). A merge migration builds on several revisions at once: set DownRevisions to the heads it joins (it takes precedence over DownRevision). Up and Down receive an Operator and perform the change with its operations (op.CreateTable, etc.); data backfills use op.Exec / op.Execute. Down may be nil for a migration that cannot be rolled back.

type MigrationEvent

type MigrationEvent struct {
	Revision  string
	Name      string
	Direction Direction
	AppliedAt time.Time
}

MigrationEvent is one recorded apply or rollback from the history table.

type MigrationStatus

type MigrationStatus struct {
	Revision     string
	DownRevision string
	Name         string
	Applied      bool
	AppliedAt    time.Time // zero when not applied
}

MigrationStatus is the applied state of one migration, returned by Status.

type Migrator

type Migrator struct {
	// contains filtered or unexported fields
}

Migrator applies a set of migrations to a database and records their history. Migrations form a DAG linked by Revision/DownRevision(s); the Migrator resolves the apply order topologically. Create one with New.

func New

func New(db *sqlkit.Database, migrations []Migration, opts ...Option) *Migrator

New creates a Migrator for the database and migrations. The migrations' Revision/DownRevision links are resolved into a topological apply order, so the caller's slice order does not matter. Configuration problems (duplicate or dangling revisions, cycles) are reported by the first operation.

func (*Migrator) Current

func (m *Migrator) Current(ctx context.Context) ([]string, error)

Current returns the applied revisions that no applied migration builds on — the revision(s) the database is currently at.

func (*Migrator) Down

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

Down rolls back the most recently applied migration.

func (*Migrator) DownTo

func (m *Migrator) DownTo(ctx context.Context, revision string) error

DownTo rolls back every applied migration that descends from revision (not revision itself). An empty revision rolls back everything.

func (*Migrator) Downgrade

func (m *Migrator) Downgrade(ctx context.Context, target string) error

Downgrade rolls migrations back toward a target, which may be:

  • "base" / "" — every applied migration;
  • "-N" — the N most recently applied migrations;
  • a revision id (or unique prefix) — every applied descendant of it (the database ends up at that revision).

Like Upgrade, it holds the migration lock for the run.

func (*Migrator) DowngradeSQL

func (m *Migrator) DowngradeSQL(from, to string) (string, error)

DowngradeSQL renders, without touching the database, the SQL that downgrading from revision to revision would run (offline mode). from defaults to "head", to defaults to "base" (""). Statements are emitted in reverse topological order.

func (*Migrator) Heads

func (m *Migrator) Heads() []string

Heads returns the revisions no other migration builds on (the tips of the DAG).

func (*Migrator) History

func (m *Migrator) History(ctx context.Context) ([]MigrationEvent, error)

History returns every recorded apply/rollback event, oldest first. It is the full migration timeline persisted in the database.

func (*Migrator) Pending

func (m *Migrator) Pending(ctx context.Context) ([]Migration, error)

Pending returns the migrations that have not been applied, in topological order.

func (*Migrator) Runner

func (m *Migrator) Runner(ctx context.Context) *Runner

Runner returns the binding that CLI commands run against. Hand it to a parsed command, e.g. kong's kctx.Run(m.Runner(ctx)) or CLI.Dispatch(m.Runner(ctx), …).

func (*Migrator) Stamp

func (m *Migrator) Stamp(ctx context.Context, revision string) error

Stamp records the database as being at revision without running any migration — for baselining an existing schema. It writes history rows so the applied set becomes exactly the inclusive ancestors of revision: "head" stamps everything, "base"/"" stamps nothing applied. Migrations newly considered applied get an "up" row; ones no longer in range get a "down" row. No DDL runs.

Like Upgrade, it holds the migration lock while it writes.

func (*Migrator) Status

func (m *Migrator) Status(ctx context.Context) ([]MigrationStatus, error)

Status returns the applied state of every known migration, in topological order.

func (*Migrator) Up

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

Up applies every pending migration in topological order (to all heads).

func (*Migrator) UpTo

func (m *Migrator) UpTo(ctx context.Context, revision string) error

UpTo applies the target revision and any of its unapplied ancestors.

func (*Migrator) Upgrade

func (m *Migrator) Upgrade(ctx context.Context, target string) error

Upgrade applies migrations toward a target, which may be:

  • "head" / "heads" / "" — every pending migration;
  • "+N" — the next N pending migrations in topological order;
  • a revision id (or unique prefix) — that revision and its unapplied ancestors.

On PostgreSQL and MySQL it holds the migration lock for the run, so concurrent Migrators on the same tracking table apply each migration once.

func (*Migrator) UpgradeSQL

func (m *Migrator) UpgradeSQL(from, to string) (string, error)

UpgradeSQL renders, without touching the database, the SQL that upgrading from revision to revision would run (offline mode). from defaults to "base" (""), to defaults to "head". Each migration's operations and its history insert are included, in topological order.

type Operator

type Operator struct {
	// contains filtered or unexported fields
}

Operator performs the schema (and data) changes of one migration. A Migration's Up/Down callbacks receive it and call its operations — CreateTable, AddColumn, CreateForeignKey, and so on — which execute immediately against the migration's transaction. Columns are declared with the decl DSL, and tables are referenced by name; foreign keys reference their target table by name too. The first failing operation is recorded and the rest become no-ops, so callbacks need not check an error on every line; the Migrator inspects Err after running the callback.

Up: func(op *migrate.Operator) {
	op.CreateTable("posts",
		decl.Column("id", decl.BigInt, decl.PrimaryKey(), decl.Default()),
		decl.Column("user_id", decl.BigInt),
		decl.Column("title", decl.Text),
	)
	op.CreateForeignKey("fk_posts_user_id", "posts",
		[]string{"user_id"}, "users", []string{"id"})
}

func (*Operator) AddCheck

func (o *Operator) AddCheck(name, table, expr string)

AddCheck adds a named CHECK constraint with the given boolean expression.

func (*Operator) AddColumn

func (o *Operator) AddColumn(table string, column decl.TableElement)

AddColumn adds a column, declared with a single decl.Column element.

func (*Operator) AddEnumValue

func (o *Operator) AddEnumValue(name, value string)

AddEnumValue adds a label to an existing enum type (ALTER TYPE ... ADD VALUE). PostgreSQL cannot run this inside a transaction block before the value is used; keep it in its own migration.

func (*Operator) AddPrimaryKey

func (o *Operator) AddPrimaryKey(table string, columns ...string)

AddPrimaryKey adds a PRIMARY KEY constraint over the columns.

func (*Operator) AlterColumn

func (o *Operator) AlterColumn(table, column string, changes ...AlterColumnChange)

AlterColumn changes a column's type, nullability, or default (PostgreSQL).

func (*Operator) CreateEnum

func (o *Operator) CreateEnum(name string, values ...string)

CreateEnum creates an enum type (CREATE TYPE name AS ENUM (values...)) in the migration's schema.

func (*Operator) CreateExtension

func (o *Operator) CreateExtension(name string, configure ...func(sql.CreateExtensionBuilder) sql.CreateExtensionBuilder)

CreateExtension installs an extension (CREATE EXTENSION IF NOT EXISTS), so the step is idempotent. The extension name is not schema-qualified; target a schema with the WITH SCHEMA option via a configure callback:

op.CreateExtension("pg_trgm", func(s sql.CreateExtensionStmt) sql.CreateExtensionStmt {
	return s.Schema("public")
})

func (*Operator) CreateForeignKey

func (o *Operator) CreateForeignKey(name, table string, columns []string, refTable string, refColumns []string, opts ...ForeignKeyOption)

CreateForeignKey adds a foreign-key constraint referencing refTable by name (assumed to be in the same schema). Pass OnDelete/OnUpdate options to set its referential actions.

func (*Operator) CreateFunction

func (o *Operator) CreateFunction(name string, configure ...func(sql.CreateFunctionBuilder) sql.CreateFunctionBuilder)

CreateFunction creates a function in the migration's schema. The signature and body are set through configure callbacks on the sql builder:

op.CreateFunction("bump", func(s sql.CreateFunctionStmt) sql.CreateFunctionStmt {
	return s.OrReplace().Returns("trigger").Language("plpgsql").Body("BEGIN ... END;")
})

func (*Operator) CreateIndex

func (o *Operator) CreateIndex(name, table string, columns ...string)

CreateIndex creates a (non-unique) index.

func (*Operator) CreateMaterializedView

func (o *Operator) CreateMaterializedView(name string, query sql.Query, configure ...func(sql.CreateMaterializedViewBuilder) sql.CreateMaterializedViewBuilder)

CreateMaterializedView creates a materialized view named by name in the migration's schema, defined by query (a SELECT built with the sql package; it binds no values, so inline literals with sql.Raw). Pass configure callbacks for WithNoData, explicit Columns, or IfNotExists.

func (*Operator) CreateSchema

func (o *Operator) CreateSchema(name string)

CreateSchema creates a schema (CREATE SCHEMA IF NOT EXISTS), so the step is idempotent and leaves an existing schema and its contents untouched.

func (*Operator) CreateSequence

func (o *Operator) CreateSequence(name string, configure ...func(sql.CreateSequenceBuilder) sql.CreateSequenceBuilder)

CreateSequence creates a sequence in the migration's schema. Configure it with the sql builder's methods through configure callbacks:

op.CreateSequence("order_no", func(s sql.CreateSequenceStmt) sql.CreateSequenceStmt {
	return s.Start(1000).Increment(1)
})

func (*Operator) CreateTable

func (o *Operator) CreateTable(name string, elements ...decl.TableElement)

CreateTable creates a table declared with decl elements (Column, Index, UniqueIndex, Check, PrimaryKey, TableComment). It also emits the table's indexes and comments. Foreign keys belong in CreateForeignKey (referenced by name), not inline.

func (*Operator) CreateTemporaryTable

func (o *Operator) CreateTemporaryTable(name string, elements ...decl.TableElement)

CreateTemporaryTable creates a session-scoped TEMPORARY table, otherwise identical to CreateTable. The engine drops it at the end of the session.

func (*Operator) CreateUniqueIndex

func (o *Operator) CreateUniqueIndex(name, table string, columns ...string)

CreateUniqueIndex creates a unique index.

func (*Operator) CreateView

func (o *Operator) CreateView(name string, query sql.Query, opts ...ViewOption)

CreateView creates a view named by name whose body is query (a SELECT built with the sql package). Pass OrReplace / ViewColumns options to redefine an existing view or name its columns explicitly. The body binds no values; inline any literals with sql.Raw.

func (*Operator) DropColumn

func (o *Operator) DropColumn(table, column string)

DropColumn drops a column.

func (*Operator) DropConstraint

func (o *Operator) DropConstraint(name, table string)

DropConstraint drops a CHECK / UNIQUE / primary-key constraint by name. Use DropForeignKey for foreign keys, whose syntax differs by dialect.

func (*Operator) DropEnum

func (o *Operator) DropEnum(name string)

DropEnum drops an enum type if it exists (DROP TYPE ... IF EXISTS CASCADE).

func (*Operator) DropExtension

func (o *Operator) DropExtension(name string)

DropExtension drops an extension if it exists.

func (*Operator) DropForeignKey

func (o *Operator) DropForeignKey(name, table string)

DropForeignKey drops a foreign-key constraint (dialect-aware).

func (*Operator) DropFunction

func (o *Operator) DropFunction(name string, configure ...func(sql.DropFunctionBuilder) sql.DropFunctionBuilder)

DropFunction drops a function if it exists. For an overloaded function pass its argument types through a configure callback (s.ArgTypes("int", "text")).

func (*Operator) DropIndex

func (o *Operator) DropIndex(name, table string)

DropIndex drops an index.

func (*Operator) DropMaterializedView

func (o *Operator) DropMaterializedView(name string)

DropMaterializedView drops a materialized view if it exists.

func (*Operator) DropPrimaryKey

func (o *Operator) DropPrimaryKey(table string)

DropPrimaryKey drops a table's primary key (MySQL DROP PRIMARY KEY; PostgreSQL drops the conventional "<table>_pkey" constraint).

func (*Operator) DropSchema

func (o *Operator) DropSchema(name string)

DropSchema drops a schema if it exists. On PostgreSQL a non-empty schema needs its objects dropped first; drop them in earlier steps or use Execute with sql.DropSchema(name).Cascade().

func (*Operator) DropSequence

func (o *Operator) DropSequence(name string)

DropSequence drops a sequence if it exists.

func (*Operator) DropTable

func (o *Operator) DropTable(name string)

DropTable drops a table if it exists.

func (*Operator) DropView

func (o *Operator) DropView(name string)

DropView drops a view if it exists.

func (*Operator) Err

func (o *Operator) Err() error

Err returns the first error any operation hit, or nil.

func (*Operator) Exec

func (o *Operator) Exec(query sql.Query)

Exec runs a built query (INSERT/UPDATE/DELETE/SELECT) for data migrations.

func (*Operator) Execute

func (o *Operator) Execute(stmt sql.Statement)

Execute runs a raw DDL statement built with the sql package — an escape hatch for steps the operations above do not cover, and for migrate.Diff output.

func (*Operator) Grant

func (o *Operator) Grant(grant sql.Statement)

Grant runs a GRANT statement built with sql.Grant. It is the typed counterpart of Execute for privileges:

op.Grant(sql.Grant("SELECT").On("public.users").To("readonly"))

func (*Operator) ModifyColumn

func (o *Operator) ModifyColumn(table string, column decl.TableElement)

ModifyColumn restates a column's full definition — type, nullability, and default — from a single decl.Column element, the dialect-agnostic way to change those properties at once. MySQL renders one MODIFY COLUMN; PostgreSQL renders the equivalent ALTER COLUMN TYPE / SET|DROP NOT NULL / SET|DROP DEFAULT actions. It is what the migration autogenerator emits for a column change.

func (*Operator) RefreshMaterializedView

func (o *Operator) RefreshMaterializedView(name string, configure ...func(sql.RefreshMaterializedViewBuilder) sql.RefreshMaterializedViewBuilder)

RefreshMaterializedView refreshes a materialized view's contents. Pass a configure callback for Concurrently or WithNoData.

func (*Operator) RenameColumn

func (o *Operator) RenameColumn(table, from, to string)

RenameColumn renames a column.

func (*Operator) RenameEnumValue

func (o *Operator) RenameEnumValue(name, oldValue, newValue string)

RenameEnumValue renames an enum label (ALTER TYPE ... RENAME VALUE).

func (*Operator) RenameTable

func (o *Operator) RenameTable(from, to string)

RenameTable renames a table.

func (*Operator) Revoke

func (o *Operator) Revoke(revoke sql.Statement)

Revoke runs a REVOKE statement built with sql.Revoke.

type Option

type Option func(*Migrator)

Option configures a Migrator.

func WithOutput

func WithOutput(w io.Writer) Option

WithOutput sets where the CLI writes its output (default os.Stdout).

func WithSchema

func WithSchema(schema string) Option

WithSchema sets the schema for both the tracking table and the tables a migration's operations create or alter.

func WithTableName

func WithTableName(name string) Option

WithTableName overrides the tracking table name (default "schema_migrations").

type Registry

type Registry struct {
	// contains filtered or unexported fields
}

Registry collects migrations declared across several files into one set, so a project can keep each migration in its own file and register it there rather than maintaining a single growing slice. Declare a package-level registry and have each file add to it with a package-level var (Add returns the registry, so the blank identifier discards it). Migrator order does not depend on registration order — it is resolved from each migration's Revision/ DownRevision DAG links:

// migrations/migrations.go
var Registry = migrate.NewRegistry()

// migrations/0001_create_users.go
var _ = Registry.Add(migrate.Migration{Revision: "0001", Name: "create_users", Up: ...})

// main.go
m := migrations.Registry.NewMigrator(db)
err := m.Up(ctx)

A Registry is safe for concurrent Add.

func NewRegistry

func NewRegistry() *Registry

NewRegistry returns an empty registry.

func (*Registry) Add

func (r *Registry) Add(migrations ...Migration) *Registry

Add appends migrations to the registry and returns it, so it can be used as a package-level initializer (var _ = Registry.Add(...)).

func (*Registry) Migrations

func (r *Registry) Migrations() []Migration

Migrations returns a copy of the registered migrations.

func (*Registry) NewMigrator

func (r *Registry) NewMigrator(db *sqlkit.Database, opts ...Option) *Migrator

NewMigrator creates a Migrator for the database from the registry's migrations. It is shorthand for New(db, registry.Migrations(), opts...).

type Runner

type Runner struct {
	// contains filtered or unexported fields
}

Runner carries the migrator and context to each subcommand's Run method. It is the binding a CLI driver hands to the parsed command — pass it to kong's kctx.Run, or to CLI.Dispatch.

type ViewOption

type ViewOption func(sql.CreateViewBuilder) sql.CreateViewBuilder

ViewOption configures a view created with CreateView.

func OrReplace

func OrReplace() ViewOption

OrReplace makes CreateView emit CREATE OR REPLACE VIEW (PostgreSQL/MySQL).

func ViewColumns

func ViewColumns(columns ...string) ViewOption

ViewColumns sets explicit view column names.

Directories

Path Synopsis
Package autogen generates a migration by diffing a source schema against a target schema.
Package autogen generates a migration by diffing a source schema against a target schema.

Jump to

Keyboard shortcuts

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