Documentation
¶
Overview ¶
Package migrate runs database schema migrations that are written as Go code and compiled into the binary — no SQL files to ship, no external tools.
A migration declares its changes on a fluent schema builder:
func init() {
migrate.Add("20260708093000_create_users", func(s *migrate.Schema) {
s.Create("users", func(t *migrate.Table) {
t.ID()
t.String("email").Unique()
t.String("name")
t.ForeignID("team_id").Constrained().CascadeOnDelete()
t.Timestamps()
})
})
}
and the application applies whatever is pending at a moment of its choosing, typically startup or a deploy job:
db, err := sql.Open("pgx", dsn)
...
m, err := migrate.New(db, migrate.Postgres)
...
if err := m.Up(ctx); err != nil {
log.Fatal(err)
}
The declaration is data, not executed SQL, which is what the rest of the package is built on: the same declaration compiles to the configured dialect (Postgres, MySQL or SQLite), reverses itself for Rollback without a hand-written down migration, renders as reviewable SQL in Plan before anything runs, and hashes into a checksum that detects migrations edited after they were applied.
Operations that discard information — dropping tables or columns, raw Exec, Run functions — cannot be reversed automatically; rolling them back requires an explicit down declared with WithDown, and is otherwise refused with ErrIrreversible.
Migrations registered with AddRepeatable run whenever their declaration changes rather than once: views, stored functions and reference data are edited in place, and the next Up re-runs them after all versioned migrations.
Concurrent migrators (replicas racing at deploy time) are serialized with a session-level advisory lock on Postgres and MySQL, which the database releases automatically if a migrator crashes; on SQLite the single-writer file arbitrates instead, and a racer that loses fails cleanly on the records table with guidance to rerun. A failed migration rolls back with its transaction on Postgres and SQLite and is never half-recorded; there is no "dirty" state to clear by hand. MySQL commits DDL implicitly and cannot offer that atomicity — failures there report exactly which statement failed and what was already committed.
The package depends only on the standard library: open *sql.DB with whatever driver you already use and pass it in.
Example ¶
The application applies pending migrations at startup. Everything is compiled in: deploying the binary deploys the migrations.
package main
import (
"context"
"database/sql"
"log"
"log/slog"
"time"
"github.com/go-rio/migrate"
)
// Each migration lives in a Go file of the application (conventionally a
// migrations package imported for effect) and registers itself at init time.
func init() {
migrate.Add("20260708100000_create_users", func(s *migrate.Schema) {
s.Create("users", func(t *migrate.Table) {
t.ID()
t.String("email").Unique()
t.String("name", 100)
t.Enum("role", "admin", "member").Default("member")
t.Timestamps()
})
})
migrate.Add("20260708110000_create_posts", func(s *migrate.Schema) {
s.Create("posts", func(t *migrate.Table) {
t.ID()
t.ForeignID("user_id").Constrained().CascadeOnDelete()
t.String("title")
t.Text("body").Nullable()
t.JSON("meta").Nullable()
t.Index("user_id", "title")
})
})
}
func main() {
db, err := sql.Open("pgx", "postgres://localhost/app") // driver of your choice
if err != nil {
log.Fatal(err)
}
defer func() { _ = db.Close() }()
m, err := migrate.New(db, migrate.Postgres,
migrate.WithLogger(slog.Default()),
migrate.WithLockTimeout(2*time.Minute),
)
if err != nil {
log.Fatal(err)
}
if err := m.Up(context.Background()); err != nil {
log.Fatal(err)
}
}
Output:
Example (DataMigration) ¶
A data migration mixes schema changes with Go logic; raw SQL and Go functions are irreversible, so a rollback needs an explicit down.
package main
import (
"context"
"github.com/go-rio/migrate"
)
func main() {
migrate.Add("20260709120000_backfill_names",
func(s *migrate.Schema) {
s.Table("users", func(t *migrate.Table) {
t.String("display_name").Nullable()
})
s.Run(func(ctx context.Context, db migrate.DB) error {
_, err := db.ExecContext(ctx, "UPDATE users SET display_name = name WHERE display_name IS NULL")
return err
})
},
migrate.WithDown(func(s *migrate.Schema) {
s.Table("users", func(t *migrate.Table) {
t.DropColumn("display_name")
})
}),
)
}
Output:
Index ¶
- Variables
- func Add(name string, up func(*Schema), opts ...MigrationOption)
- func AddRepeatable(name string, run func(*Schema), opts ...MigrationOption)
- type Clock
- type Collection
- type Column
- func (c *Column) After(column string) *Column
- func (c *Column) AutoIncrement() *Column
- func (c *Column) Comment(comment string) *Column
- func (c *Column) CopyFrom(expr string) *Column
- func (c *Column) Default(value any) *Column
- func (c *Column) DefaultExpr(expr string) *Column
- func (c *Column) First() *Column
- func (c *Column) Index() *Column
- func (c *Column) Nullable() *Column
- func (c *Column) Primary() *Column
- func (c *Column) SkipCopy() *Column
- func (c *Column) StoredAs(expr string) *Column
- func (c *Column) Unique() *Column
- func (c *Column) Unsigned() *Column
- func (c *Column) UseCurrent() *Column
- func (c *Column) UseCurrentOnUpdate() *Column
- func (c *Column) VirtualAs(expr string) *Column
- type DB
- type Dialect
- type ForeignColumn
- func (fc *ForeignColumn) CascadeOnDelete() *ForeignColumn
- func (fc *ForeignColumn) Constrained(table ...string) *ForeignColumn
- func (fc *ForeignColumn) Index() *ForeignColumn
- func (fc *ForeignColumn) NullOnDelete() *ForeignColumn
- func (fc *ForeignColumn) Nullable() *ForeignColumn
- func (fc *ForeignColumn) References(table string, columns ...string) *ForeignColumn
- func (fc *ForeignColumn) RestrictOnDelete() *ForeignColumn
- func (fc *ForeignColumn) Unique() *ForeignColumn
- type ForeignKey
- func (f *ForeignKey) CascadeOnDelete() *ForeignKey
- func (f *ForeignKey) CascadeOnUpdate() *ForeignKey
- func (f *ForeignKey) Name(name string) *ForeignKey
- func (f *ForeignKey) NoActionOnDelete() *ForeignKey
- func (f *ForeignKey) NullOnDelete() *ForeignKey
- func (f *ForeignKey) NullOnUpdate() *ForeignKey
- func (f *ForeignKey) References(table string, columns ...string) *ForeignKey
- func (f *ForeignKey) RestrictOnDelete() *ForeignKey
- func (f *ForeignKey) RestrictOnUpdate() *ForeignKey
- type Index
- type Migration
- type MigrationOption
- type Migrator
- func (m *Migrator) Baseline(ctx context.Context, upTo ...string) error
- func (m *Migrator) Fresh(ctx context.Context) error
- func (m *Migrator) Plan(ctx context.Context) ([]Planned, error)
- func (m *Migrator) PlanRollback(ctx context.Context, steps int) ([]Planned, error)
- func (m *Migrator) PlanRollbackBatch(ctx context.Context) ([]Planned, error)
- func (m *Migrator) Repair(ctx context.Context) error
- func (m *Migrator) Reset(ctx context.Context) error
- func (m *Migrator) Rollback(ctx context.Context, steps int) error
- func (m *Migrator) RollbackBatch(ctx context.Context) error
- func (m *Migrator) Status(ctx context.Context) ([]Status, error)
- func (m *Migrator) Up(ctx context.Context) error
- type Option
- type Planned
- type SafetyLevel
- type Schema
- func (s *Schema) Create(table string, fn func(*Table))
- func (s *Schema) Drop(table string)
- func (s *Schema) DropIfExists(table string)
- func (s *Schema) Exec(query string, args ...any)
- func (s *Schema) Recreate(table string, fn func(*Table))
- func (s *Schema) Rename(from, to string)
- func (s *Schema) Run(fn func(ctx context.Context, db DB) error)
- func (s *Schema) Table(table string, fn func(*Table))
- type Status
- type Table
- func (t *Table) BigInteger(name string) *Column
- func (t *Table) Binary(name string) *Column
- func (t *Table) Boolean(name string) *Column
- func (t *Table) Char(name string, length ...int) *Column
- func (t *Table) Check(name, expr string)
- func (t *Table) Column(name, sqlType string) *Column
- func (t *Table) Comment(comment string)
- func (t *Table) Date(name string) *Column
- func (t *Table) DateTime(name string) *Column
- func (t *Table) Decimal(name string, precision, scale int) *Column
- func (t *Table) Double(name string) *Column
- func (t *Table) DropCheck(name string)
- func (t *Table) DropColumn(names ...string)
- func (t *Table) DropForeign(columns ...string)
- func (t *Table) DropForeignByName(name string)
- func (t *Table) DropIndex(columns ...string)
- func (t *Table) DropIndexByName(name string)
- func (t *Table) DropPrimary()
- func (t *Table) DropUnique(columns ...string)
- func (t *Table) Enum(name string, values ...string) *Column
- func (t *Table) Float(name string) *Column
- func (t *Table) Foreign(columns ...string) *ForeignKey
- func (t *Table) ForeignID(name string) *ForeignColumn
- func (t *Table) ID(name ...string) *Column
- func (t *Table) Index(columns ...string) *Index
- func (t *Table) Integer(name string) *Column
- func (t *Table) JSON(name string) *Column
- func (t *Table) Primary(columns ...string)
- func (t *Table) RenameColumn(from, to string)
- func (t *Table) RenameIndex(from, to string)
- func (t *Table) SmallInteger(name string) *Column
- func (t *Table) SoftDeletes(name ...string) *Column
- func (t *Table) String(name string, length ...int) *Column
- func (t *Table) Text(name string) *Column
- func (t *Table) Time(name string) *Column
- func (t *Table) Timestamp(name string) *Column
- func (t *Table) TimestampTz(name string) *Column
- func (t *Table) Timestamps()
- func (t *Table) TinyInteger(name string) *Column
- func (t *Table) UUID(name string) *Column
- func (t *Table) Unique(columns ...string) *Index
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrIrreversible marks a rollback of a migration that cannot be // reversed automatically and declares no explicit down. ErrIrreversible = errors.New("migrate: migration cannot be automatically reversed") // ErrLockTimeout marks a failure to acquire the advisory lock, meaning // another migrator held it for the whole wait. ErrLockTimeout = errors.New("migrate: timed out waiting for the migration lock") // ErrChecksumMismatch marks an applied migration whose declaration no // longer produces the SQL it was applied with. ErrChecksumMismatch = errors.New("migrate: checksum mismatch") )
Sentinel errors. Failures wrap these, so errors.Is works across the additional context.
var ErrUnsafe = errors.New("migrate: unsafe migration")
ErrUnsafe marks an Up refused under SafetyStrict because pending migrations contain operations that lock or break a live production system. Each violation carries advice; Assured() acknowledges a reviewed migration.
Functions ¶
func Add ¶
func Add(name string, up func(*Schema), opts ...MigrationOption)
Add registers a migration in the default collection, the usual form inside a migrations package where each file registers itself:
func init() {
migrate.Add("20260708093000_create_users", func(s *migrate.Schema) {
s.Create("users", func(t *migrate.Table) {
t.ID()
t.String("email").Unique()
t.Timestamps()
})
})
}
See Collection.Add for naming rules and panics.
func AddRepeatable ¶
func AddRepeatable(name string, run func(*Schema), opts ...MigrationOption)
AddRepeatable registers a repeatable migration in the default collection. See Collection.AddRepeatable for semantics.
Types ¶
type Clock ¶
Clock supplies the current time for applied-at records. Tests can substitute a fake via WithClock.
type Collection ¶
type Collection struct {
// contains filtered or unexported fields
}
Collection is an ordered, named set of migrations. The package-level Add registers into a default collection, which suits the common one-app layout; tests and libraries embedding several migration sets can keep explicit collections instead.
func (*Collection) Add ¶
func (c *Collection) Add(name string, up func(*Schema), opts ...MigrationOption)
Add registers a migration. The name orders migrations lexically and is recorded in the database, so give it a sortable timestamp prefix:
c.Add("20260708093000_create_users", func(s *migrate.Schema) { ... })
Add panics on an empty, oversized or duplicate name or a nil function: registration happens at init time, where a broken migration set should stop the program.
func (*Collection) AddRepeatable ¶
func (c *Collection) AddRepeatable(name string, run func(*Schema), opts ...MigrationOption)
AddRepeatable registers a repeatable migration: instead of running once, it runs whenever its declaration compiles to different SQL than last recorded — after all versioned migrations, in name order. Views, stored functions, triggers and reference data live here, declared idempotently (CREATE OR REPLACE ...), so editing the declaration in place is the whole workflow:
c.AddRepeatable("active_users_view", func(s *migrate.Schema) {
s.Exec(`CREATE OR REPLACE VIEW active_users AS SELECT ...`)
})
Rollback never touches repeatable migrations (there is nothing to return to); Reset forgets their records so the next Up runs them again. A Run function's body is invisible to the checksum, so editing only Go logic does not trigger a re-run. Declaring WithDown panics — a repeatable migration has no down.
func (*Collection) SQL ¶
func (c *Collection) SQL(dialect Dialect) ([]Planned, error)
SQL renders every migration in the collection for a dialect without touching any database — offline review, docs, or handing a script to a DBA. Unlike Migrator.Plan it does not know what is already applied, so it renders everything: versioned migrations in order, then repeatable ones.
Example ¶
Collection.SQL renders migrations offline — no database — for review or for handing a script to a DBA. Migrator.Plan does the same against a live database, limited to what is actually pending.
package main
import (
"fmt"
"log"
"github.com/go-rio/migrate"
)
func main() {
c := migrate.NewCollection()
c.Add("20260708100000_create_teams", func(s *migrate.Schema) {
s.Create("teams", func(t *migrate.Table) {
t.ID()
t.String("name").Unique()
})
})
plans, err := c.SQL(migrate.Postgres)
if err != nil {
log.Fatal(err)
}
for _, p := range plans {
fmt.Printf("-- %s\n", p.Name)
for _, stmt := range p.Statements {
fmt.Printf("%s;\n", stmt)
}
}
}
Output: -- 20260708100000_create_teams CREATE TABLE "teams" ( "id" BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, "name" VARCHAR(255) NOT NULL ); CREATE UNIQUE INDEX "teams_name_unique" ON "teams" ("name");
type Column ¶
type Column struct {
// contains filtered or unexported fields
}
Column is the fluent handle returned by column declarations. Modifiers return the same handle so declarations chain:
t.String("email").Nullable().Default("unknown").Unique()
func (*Column) After ¶
After places the column after an existing one when altering a MySQL table. Column order is cosmetic; other dialects ignore it.
func (*Column) AutoIncrement ¶
AutoIncrement makes an integer column the table's auto-incrementing primary key, generated by the database: an identity column on Postgres (GENERATED BY DEFAULT AS IDENTITY), AUTO_INCREMENT on MySQL, INTEGER PRIMARY KEY AUTOINCREMENT on SQLite. ID declares the conventional 64-bit form in one call.
Auto-increment implies the primary key — MySQL and SQLite accept nothing else — so it cannot combine with other primary key declarations, defaults or Nullable.
func (*Column) Comment ¶
Comment attaches a comment to the column. SQLite has no column comments and ignores it.
func (*Column) CopyFrom ¶
CopyFrom sets the SELECT expression that fills this column during a Schema.Recreate, instead of copying a column of the same name — renames and type conversions in one step:
s.Recreate("events", func(t *migrate.Table) {
t.ID()
t.Integer("age_years").CopyFrom("CAST(age AS INTEGER)")
})
Outside Recreate it has no effect.
func (*Column) Default ¶
Default sets a literal default value, rendered safely for the dialect: strings are quoted, booleans become the dialect's literal, nil means NULL. For an SQL expression, use DefaultExpr.
func (*Column) DefaultExpr ¶
DefaultExpr sets a default SQL expression, written verbatim:
t.UUID("token").DefaultExpr("gen_random_uuid()")
func (*Column) First ¶
First places the column first when altering a MySQL table. Column order is cosmetic; other dialects ignore it.
func (*Column) Nullable ¶
Nullable allows NULL values. Columns are NOT NULL by default: required-ness is the safer starting point for schema design, and opting out is explicit.
func (*Column) Primary ¶
Primary makes this column the table's primary key. ID declares the usual auto-incrementing key; Primary suits natural keys like a code or UUID.
func (*Column) SkipCopy ¶
SkipCopy marks a column that does not exist in the old table during a Schema.Recreate: the row copy leaves it out, so it starts from its default (or NULL). Outside Recreate it has no effect.
func (*Column) StoredAs ¶
StoredAs makes the column a stored generated column, computed from the expression and written to disk:
t.String("full_name").StoredAs("first_name || ' ' || last_name")
Generated columns take no Default and cannot auto-increment. Postgres supports stored generated columns from version 12.
func (*Column) Unsigned ¶
Unsigned restricts an integer column to non-negative values on MySQL. Postgres and SQLite have no unsigned types and ignore it.
func (*Column) UseCurrent ¶
UseCurrent defaults the column to the current timestamp.
func (*Column) UseCurrentOnUpdate ¶
UseCurrentOnUpdate makes MySQL refresh the column to the current timestamp whenever the row is updated. Other dialects have no equivalent and ignore it; use a trigger or set the value in application code instead.
func (*Column) VirtualAs ¶
VirtualAs makes the column a virtual generated column, computed on read:
t.JSON("meta")
t.String("kind", 32).VirtualAs("json_extract(meta, '$.kind')")
MySQL and SQLite compute virtual columns natively; Postgres added them in version 18 — on older servers the statement fails.
type DB ¶
type DB interface {
ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)
QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)
QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row
}
DB is the querying surface handed to Run functions. Both *sql.Tx and *sql.DB satisfy it, so data migrations read the same whether the migration runs inside a transaction (the default) or outside one (WithoutTransaction).
type Dialect ¶
type Dialect interface {
// contains filtered or unexported methods
}
Dialect generates SQL for and drives one database engine. The built-in dialects are Postgres, MySQL and SQLite; the interface is opaque so the grammar can evolve without breaking third parties.
var MySQL Dialect = mysqlDialect{}
MySQL is the MySQL dialect. It expects MySQL 8.0 or newer (or an equivalent MariaDB release) for RENAME COLUMN and expression defaults.
MySQL commits implicitly around every DDL statement, so unlike Postgres and SQLite a failed migration cannot roll back the DDL it already executed; the error identifies the failing statement so the database can be reconciled.
var Postgres Dialect = postgresDialect{}
Postgres is the PostgreSQL dialect.
var SQLite Dialect = sqliteDialect{}
SQLite is the SQLite dialect. It expects SQLite 3.35 or newer for ALTER TABLE DROP COLUMN.
SQLite cannot alter constraints after a table exists: adding or dropping foreign keys and primary keys compiles to a clear error rather than silently skipping the change — declare them when creating the table, or change them with Schema.Recreate, which rebuilds the table while keeping its rows. Advisory locking is a no-op: the single-writer database file serializes the migration transactions themselves, and each migration records itself as its transaction's first write, so a racing migrator loses on the records table — cleanly, before touching the schema — rather than halfway through with "already exists".
type ForeignColumn ¶
type ForeignColumn struct {
*Column
// contains filtered or unexported fields
}
ForeignColumn is the fluent handle returned by Table.ForeignID: a Column that can also declare the foreign key constraint tying it to its parent.
func (*ForeignColumn) CascadeOnDelete ¶
func (fc *ForeignColumn) CascadeOnDelete() *ForeignColumn
CascadeOnDelete deletes child rows when the parent row is deleted.
func (*ForeignColumn) Constrained ¶
func (fc *ForeignColumn) Constrained(table ...string) *ForeignColumn
Constrained adds the foreign key constraint, inferring the parent table from the column name (user_id → users) unless one is given. The referenced column is "id". The inferred name is bare: when the parent lives in another schema, pass it explicitly (Constrained("crm.users")).
func (*ForeignColumn) Index ¶
func (fc *ForeignColumn) Index() *ForeignColumn
Index adds an index on the column, shadowing Column.Index to keep the chain.
func (*ForeignColumn) NullOnDelete ¶
func (fc *ForeignColumn) NullOnDelete() *ForeignColumn
NullOnDelete sets the column to NULL when the parent row is deleted.
func (*ForeignColumn) Nullable ¶
func (fc *ForeignColumn) Nullable() *ForeignColumn
Nullable allows NULL values, shadowing Column.Nullable to keep the chain.
func (*ForeignColumn) References ¶
func (fc *ForeignColumn) References(table string, columns ...string) *ForeignColumn
References adds the foreign key constraint with an explicit parent table and columns; columns default to "id".
func (*ForeignColumn) RestrictOnDelete ¶
func (fc *ForeignColumn) RestrictOnDelete() *ForeignColumn
RestrictOnDelete rejects deleting a parent row that still has children.
func (*ForeignColumn) Unique ¶
func (fc *ForeignColumn) Unique() *ForeignColumn
Unique adds a unique index on the column, shadowing Column.Unique to keep the chain.
type ForeignKey ¶
type ForeignKey struct {
// contains filtered or unexported fields
}
ForeignKey is the fluent handle returned by Table.Foreign.
func (*ForeignKey) CascadeOnDelete ¶
func (f *ForeignKey) CascadeOnDelete() *ForeignKey
CascadeOnDelete deletes child rows when the parent row is deleted.
func (*ForeignKey) CascadeOnUpdate ¶
func (f *ForeignKey) CascadeOnUpdate() *ForeignKey
CascadeOnUpdate propagates key updates to child rows.
func (*ForeignKey) Name ¶
func (f *ForeignKey) Name(name string) *ForeignKey
Name overrides the conventional {table}_{columns}_foreign name. A named key must be dropped with DropForeignByName.
func (*ForeignKey) NoActionOnDelete ¶
func (f *ForeignKey) NoActionOnDelete() *ForeignKey
NoActionOnDelete leaves enforcement to the end of the statement (or transaction, where supported).
func (*ForeignKey) NullOnDelete ¶
func (f *ForeignKey) NullOnDelete() *ForeignKey
NullOnDelete sets the child columns to NULL when the parent row is deleted.
func (*ForeignKey) NullOnUpdate ¶
func (f *ForeignKey) NullOnUpdate() *ForeignKey
NullOnUpdate sets the child columns to NULL when the parent key changes.
func (*ForeignKey) References ¶
func (f *ForeignKey) References(table string, columns ...string) *ForeignKey
References sets the parent table and columns; columns default to "id".
func (*ForeignKey) RestrictOnDelete ¶
func (f *ForeignKey) RestrictOnDelete() *ForeignKey
RestrictOnDelete rejects deleting a parent row that still has children.
func (*ForeignKey) RestrictOnUpdate ¶
func (f *ForeignKey) RestrictOnUpdate() *ForeignKey
RestrictOnUpdate rejects updating a key that still has children.
type Index ¶
type Index struct {
// contains filtered or unexported fields
}
Index is the fluent handle returned by Table.Index and Table.Unique.
type Migration ¶
type Migration struct {
// contains filtered or unexported fields
}
Migration is a registered migration: a name that orders and identifies it, and a declaration function. Values are created by Add or AddRepeatable and immutable afterwards.
type MigrationOption ¶
type MigrationOption func(*Migration)
MigrationOption configures a single migration at registration time.
func Assured ¶
func Assured() MigrationOption
Assured marks a migration as reviewed: the safety analysis skips it. Use it to acknowledge a finding after deciding the operation is fine — the table is small, the app no longer reads the column, the maintenance window is open.
func WithDown ¶
func WithDown(down func(*Schema)) MigrationOption
WithDown declares an explicit down migration, needed when up records irreversible operations (Drop, DropColumn, Exec, Run) and the migration should still be able to roll back.
func WithoutTransaction ¶
func WithoutTransaction() MigrationOption
WithoutTransaction runs the migration outside a transaction, required for statements that refuse to run inside one, such as Postgres's CREATE INDEX CONCURRENTLY. Without a transaction a mid-migration failure leaves earlier statements applied — keep such migrations to a single statement where possible.
type Migrator ¶
type Migrator struct {
// contains filtered or unexported fields
}
Migrator applies and rolls back the migrations of one collection against one database. Methods are safe to call from concurrent processes: runs are serialized by a database advisory lock (see WithoutLock to opt out).
func New ¶
New creates a Migrator for db, which stays owned by the caller. The dialect must match the driver the caller opened db with — this package imports no drivers:
db, _ := sql.Open("pgx", dsn)
m, err := migrate.New(db, migrate.Postgres)
func (*Migrator) Baseline ¶
Baseline records registered migrations as applied without executing them, for adopting this package on a database whose schema already exists. With no argument every registered migration is baselined; with a name, versioned migrations up to and including it. Repeatable migrations are always baselined at their current checksum. Baselined rows carry batch 0, so the first real Rollback never touches them (Reset still does).
func (*Migrator) Fresh ¶
Fresh drops every table in the database — the migration records included — and runs all migrations from scratch. It exists for development flow: when an irreversible migration blocks Rollback, Fresh starts over instead. It destroys all data; nothing about it belongs near production.
Tables are dropped in passes until none remain, which unwinds foreign key dependencies without touching session state; Postgres drops CASCADE, so dependent objects such as views go too. Other standalone objects survive, and idempotent repeatable migrations recreate theirs on the way back up.
func (*Migrator) Plan ¶
Plan renders the SQL that Up would execute, without executing anything: review it, hand it to a DBA, or diff it in CI. Pending versioned migrations come first, then the repeatable migrations due for a re-run. Reading the applied set is the only database access.
func (*Migrator) PlanRollback ¶
PlanRollback renders the SQL that Rollback(ctx, steps) would execute, without executing anything.
func (*Migrator) PlanRollbackBatch ¶
PlanRollbackBatch renders the SQL that RollbackBatch would execute, without executing anything.
func (*Migrator) Repair ¶
Repair re-records the checksum of every applied versioned migration to its current value, accepting drift after a reviewed change — most commonly an upgrade of this package that renders SQL differently. Repeatable records are left alone: for those a changed checksum means a pending re-run, and rewriting it would silently cancel that re-run.
func (*Migrator) Reset ¶
Reset rolls back everything that was ever applied — baselined rows included — leaving an empty database. Every applied migration must be reversible or declare a down.
func (*Migrator) Rollback ¶
Rollback undoes the steps most recently applied migrations in reverse application order — Rollback(ctx, 1) undoes just the newest one. steps must be positive. Migrations declared with WithDown run their explicit down; the rest reverse their recorded operations automatically. A migration whose operations discard information (Drop, DropColumn, Exec, Run) fails with ErrIrreversible instead of guessing. Baselined rows are never touched.
func (*Migrator) RollbackBatch ¶
RollbackBatch undoes the most recent batch: everything the last Up applied together, which after a first deploy is every migration. Baselined rows are never touched.
func (*Migrator) Status ¶
Status returns the merged view of registered and applied migrations in name order. It reads without taking the migration lock.
type Option ¶
type Option func(*config)
Option configures a Migrator. Options are applied by New and validated together; an invalid combination makes New return an error instead of degrading silently at runtime.
func WithClock ¶
WithClock substitutes the time source used for applied-at records, letting tests pin time.
func WithCollection ¶
func WithCollection(c *Collection) Option
WithCollection uses an explicit migration collection instead of the default one that the package-level Add registers into.
func WithLockTimeout ¶
WithLockTimeout sets how long to wait for another migrator to release the advisory lock before failing with ErrLockTimeout. The default is one minute.
func WithLogger ¶
WithLogger sets the logger for migration progress and warnings. The default discards everything.
func WithSafety ¶
func WithSafety(level SafetyLevel) Option
WithSafety sets the safety level. The default is SafetyWarn.
func WithStrictChecksum ¶
func WithStrictChecksum() Option
WithStrictChecksum makes Up fail with ErrChecksumMismatch when an applied migration no longer compiles to the SQL it was applied with. The default only logs a warning, because the compiled SQL can also drift legitimately — for example when a new version of this package renders a clause differently; Repair re-records the current checksums after such an upgrade.
func WithTable ¶
WithTable sets the name of the migration records table. The default is "schema_migrations". The advisory lock is derived from this name, so two migrators using different tables on one database do not exclude each other.
func WithoutLock ¶
func WithoutLock() Option
WithoutLock disables the advisory lock that serializes concurrent migrators. Only do this when something else already guarantees a single migrator, such as a deployment job that runs exactly once.
type Planned ¶
type Planned struct {
Name string
// Statements holds the SQL in execution order. A Run function appears as
// a comment placeholder — its effect cannot be rendered.
Statements []string
// Warnings holds the safety findings for this migration (empty for
// migrations marked Assured or when safety is off).
Warnings []string
}
Planned is one migration as a dry run would execute it.
type SafetyLevel ¶
type SafetyLevel int
SafetyLevel selects what happens when a pending migration contains an operation that is dangerous on a live system — locking a large table, breaking code that is still deployed.
const ( // SafetyWarn, the default, logs each finding through the configured // logger and proceeds. SafetyWarn SafetyLevel = iota // SafetyStrict refuses to run: Up fails with ErrUnsafe before executing // anything, listing every finding. Meant for CI. SafetyStrict // SafetyOff disables the analysis. SafetyOff )
type Schema ¶
type Schema struct {
// contains filtered or unexported fields
}
Schema records the operations a migration declares. A migration function receives a fresh Schema, declares changes with its methods, and returns; nothing touches the database while declaring. The recorded operations are then compiled to dialect SQL to apply the migration, reversed to roll it back, rendered for dry runs, and hashed into the migration's checksum.
Because the function is re-run each time the migration is applied, rolled back or planned, it must be deterministic: derive nothing from the clock, randomness or external state.
func (*Schema) Create ¶
Create declares a new table. The function receives a Table on which columns, indexes and foreign keys are declared:
s.Create("users", func(t *migrate.Table) {
t.ID()
t.String("email").Unique()
t.Timestamps()
})
Rolling back a Create drops the table.
func (*Schema) Drop ¶
Drop removes a table. Dropping is irreversible: rolling back requires an explicit down migration declared with WithDown.
func (*Schema) DropIfExists ¶
DropIfExists removes a table if it exists. Like Drop, it is irreversible.
func (*Schema) Exec ¶
Exec declares a raw SQL statement for whatever the schema builder does not cover. The statement participates in dry runs and the checksum, but cannot be automatically reversed; rolling back requires WithDown.
Args use the dialect's native placeholders ($1 for Postgres, ? otherwise).
func (*Schema) Recreate ¶
Recreate replaces a table with a new definition while preserving its rows, the portable way to change what ALTER TABLE cannot — on SQLite that is any constraint change. The function declares the complete target table, exactly like Create; every declared column is copied from the old table by name, except those marked SkipCopy, which start from their default:
s.Recreate("users", func(t *migrate.Table) {
t.ID()
t.String("email").Unique()
t.ForeignID("team_id").Constrained().Nullable().SkipCopy() // new column
})
It compiles to: create a temporary table with the new shape, copy the rows, capture the table's triggers, drop the old table, rename into place, rebuild indexes, recreate the triggers. On Postgres and SQLite the whole sequence runs inside the migration's transaction, so a failure leaves the original table untouched; combining Recreate with WithoutTransaction is refused at compile time for the same reason. MySQL refuses Recreate entirely: its implicit DDL commits would leave a crash window with the live table dropped, and native ALTER TABLE already covers every Recreate use case there.
Triggers on the table (created through Exec — the builder does not declare them) would silently vanish with the dropped table; instead their DDL is read at migration time and replayed verbatim after the rename. A trigger whose body no longer matches the new shape fails the replay and rolls the whole migration back — drop it with Exec before the Recreate and declare its successor afterwards. Views are unaffected: SQLite leaves them in place across the rebuild, and Postgres refuses to drop a table a view depends on.
Dependent objects bound the rebuild. On Postgres a table referenced by other tables' foreign keys or by views cannot be dropped, so its Recreate fails cleanly (transaction rolled back, original table intact) — change such tables with native ALTER via Table or Exec instead. On SQLite child foreign keys resolve by name again once the rename lands; with enforcement enabled (PRAGMA foreign_keys=ON) and referencing rows present, run the migration on a connection with enforcement off.
Recreate discards the previous definition and is therefore irreversible; rolling back requires WithDown.
func (*Schema) Rename ¶
Rename renames a table within its schema; it reverses to the opposite rename. Moving a table between schemas is refused at compile time on Postgres (use Exec with ALTER TABLE ... SET SCHEMA) and SQLite; MySQL renames across databases.
func (*Schema) Run ¶
Run declares a Go function, for data migrations that need queries or application logic. The function runs inside the migration's transaction when there is one and must not retain db after returning.
A Go function is opaque: dry runs list it without SQL, the checksum does not cover its body, and it cannot be automatically reversed.
type Status ¶
type Status struct {
Name string
// Applied reports whether the database has a record of the migration.
Applied bool
Batch int // batch it was applied in; 0 also marks baselined rows, -1 repeatable ones
AppliedAt time.Time // zero when not applied
// Registered is false when the database has a record but the collection
// has no migration by that name — usually a deleted migration file.
Registered bool
// Repeatable marks migrations registered with AddRepeatable.
Repeatable bool
// Drifted reports that the registered declaration no longer compiles to
// the SQL recorded when it was applied. For a repeatable migration that
// simply means the next Up re-runs it.
Drifted bool
}
Status describes one migration: registered in the collection, applied in the database, or both.
type Table ¶
type Table struct {
// contains filtered or unexported fields
}
Table declares columns, indexes and foreign keys. Inside a Create call the declarations describe a new table; inside a Table call they describe changes to an existing one. Declaration methods never fail in place — mistakes are collected and reported together when the migration compiles, so the fluent style stays uncluttered.
func (*Table) BigInteger ¶
BigInteger declares a 64-bit integer.
func (*Table) Check ¶
Check declares a named CHECK constraint over an expression written verbatim:
t.Check("orders_price_positive", "price > 0")
The name is required — anonymous constraints cannot be dropped or reasoned about later. Inside Schema.Table, SQLite cannot add constraints; use Schema.Recreate there.
func (*Table) Column ¶
Column declares a column with a dialect type written verbatim, for types the portable set does not cover, e.g. Column("location", "geography(point)").
func (*Table) Comment ¶
Comment attaches a comment to the table itself (Postgres and MySQL; SQLite has no table comments and ignores it). Inside Schema.Table, changing a comment discards the previous one and is therefore irreversible.
func (*Table) DateTime ¶
DateTime declares a wall-clock timestamp without time zone: TIMESTAMP on Postgres, DATETIME(6) on MySQL.
func (*Table) Decimal ¶
Decimal declares an exact fixed-point column, e.g. Decimal("amount", 10, 2).
func (*Table) DropCheck ¶
DropCheck removes a CHECK constraint by name. Dropping discards the expression and is irreversible.
func (*Table) DropColumn ¶
DropColumn removes the given columns. Dropping is irreversible: rolling back requires an explicit down migration declared with WithDown.
SQLite refuses to drop columns that are indexed or referenced by other schema objects; such changes need a manual table rebuild.
func (*Table) DropForeign ¶
DropForeign removes the foreign key that Foreign or Constrained would have created on the given columns, using the conventional {table}_{columns}_foreign name. For a key with a custom name, use DropForeignByName.
func (*Table) DropForeignByName ¶
DropForeignByName removes a foreign key by its exact name.
func (*Table) DropIndex ¶
DropIndex removes the index that Index would have created on the given columns, using the conventional {table}_{columns}_index name. For an index with a custom name, use DropIndexByName.
func (*Table) DropIndexByName ¶
DropIndexByName removes an index by its exact name.
func (*Table) DropPrimary ¶
func (t *Table) DropPrimary()
DropPrimary removes the table's primary key.
func (*Table) DropUnique ¶
DropUnique removes the unique index that Unique would have created on the given columns, using the conventional {table}_{columns}_unique name.
func (*Table) Enum ¶
Enum declares a column restricted to the given values: a native ENUM on MySQL, VARCHAR plus a named CHECK constraint elsewhere.
func (*Table) Foreign ¶
func (t *Table) Foreign(columns ...string) *ForeignKey
Foreign declares a foreign key on existing columns:
t.Foreign("user_id").References("users") // references users.id
t.Foreign("a", "b").References("parents", "a", "b")
For declaring the column and its key together, prefer ForeignID.
func (*Table) ForeignID ¶
func (t *Table) ForeignID(name string) *ForeignColumn
ForeignID declares a column typed to match ID primary keys, ready to be constrained to its parent table:
t.ForeignID("user_id").Constrained() // references users.id
t.ForeignID("author_id").References("users") // explicit table
t.ForeignID("user_id").Constrained().CascadeOnDelete()
func (*Table) ID ¶
ID declares a 64-bit auto-incrementing primary key named "id" (or the given name) — shorthand for BigInteger(name).Unsigned().AutoIncrement(): BIGINT GENERATED BY DEFAULT AS IDENTITY on Postgres, BIGINT UNSIGNED AUTO_INCREMENT on MySQL, INTEGER AUTOINCREMENT on SQLite. For a different integer width, declare the column yourself:
t.Integer("id").AutoIncrement()
func (*Table) Index ¶
Index declares an index over the given columns, named {table}_{columns}_index unless renamed with Name.
func (*Table) JSON ¶
JSON declares a JSON document column, mapped to each dialect's preferred binary representation: JSONB on Postgres, JSON on MySQL, TEXT on SQLite. For Postgres's plain json type, use Column(name, "json").
func (*Table) Primary ¶
Primary declares a composite primary key. For the common single auto-incrementing key, use ID instead.
func (*Table) RenameColumn ¶
RenameColumn renames a column. It reverses to the opposite rename.
func (*Table) RenameIndex ¶
RenameIndex renames an index. It reverses to the opposite rename. SQLite cannot rename indexes; dropping and re-declaring is the portable route.
func (*Table) SmallInteger ¶
SmallInteger declares a 16-bit integer.
func (*Table) SoftDeletes ¶
SoftDeletes declares a nullable deleted_at TimestampTz column (or the given name) for soft-deletion schemes.
func (*Table) Text ¶
Text declares an unbounded text column (TEXT, or LONGTEXT on MySQL where TEXT is capped at 64 KB).
func (*Table) TimestampTz ¶
TimestampTz declares a point-in-time column: TIMESTAMPTZ on Postgres (the recommended default for recording instants), DATETIME(6) on MySQL, which has no time-zone-aware type.
func (*Table) Timestamps ¶
func (t *Table) Timestamps()
Timestamps declares nullable created_at and updated_at TimestampTz columns.
func (*Table) TinyInteger ¶
TinyInteger declares an 8-bit integer (SMALLINT on Postgres, which has no 8-bit type).