migrate

package module
v0.6.1 Latest Latest
Warning

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

Go to latest
Published: Jul 10, 2026 License: MIT Imports: 11 Imported by: 0

README

migrate

Doc Go Release Test License

Database schema migrations written as Go code and compiled into your binary: no SQL files to ship, no CLI to install, no third-party dependencies.

A migration declares its changes once on a fluent schema builder. One declaration produces:

  • Dialect-specific SQL for PostgreSQL, MySQL, and SQLite.
  • Automatic rollback with no hand-written down migration.
  • A reviewable dry-run plan.
  • A checksum that detects migrations edited after they ran.
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.ForeignID("team_id").Constrained().CascadeOnDelete()
			t.Timestamps()
		})
	})
}
db, err := sql.Open("pgx", dsn) // any database/sql driver you already use
...
m, err := migrate.New(db, migrate.Postgres)
...
if err := m.Up(ctx); err != nil {
	log.Fatal(err)
}

Features

Feature Behavior
Migrations are code One Go file per migration, self-registered in init; go build packages the full history into the binary. A broken migration is a compile error.
Automatic rollbacks Rollback reverses recorded operations in reverse order (e.g. an added index drops before its column). Information-discarding operations — dropping tables/columns, raw SQL, Go functions — need an explicit WithDown, else fail with ErrIrreversible.
No dirty state The migration row is written atomically with the migration; on Postgres and SQLite a failure rolls it fully back and the next Up retries. No force flag. MySQL commits DDL implicitly, so failures name the failing statement and those already committed, or report a clean rollback when no DDL ran.
Concurrency-safe A session-level advisory lock on a dedicated connection serializes racing replicas and is released by the database on crash. SQLite has none: each migration's first write is its own records row, so a losing racer fails on the records table before touching the schema, with rerun guidance. Opt out with WithoutLock.
Tamper detection Each applied migration records a checksum of its compiled SQL. On drift, Up warns (or fails under WithStrictChecksum), Status reports it, and Repair re-records after review.
Repeatable migrations AddRepeatable re-runs views, functions, triggers, and reference data when their declaration changes (see below).
Reviewable Plan renders pending SQL against a live database; Collection.SQL renders a collection offline with no database; PlanRollback previews a rollback.
Dialect types Compiles to each engine's best-practice types: JSONB, TIMESTAMPTZ, identity columns on Postgres; DATETIME(6), native ENUM on MySQL. Unsupported operations fail at compile time, not silently.
Batch history Each Up is one batch; Rollback, RollbackBatch, Reset, and Baseline manage history (see below).
Zero dependencies Only database/sql and the standard library; pass in your own *sql.DB and driver.

Installation

go get github.com/go-rio/migrate

Moved from github.com/libtnb/migrate in v0.5.0 — part of the go-rio family alongside the rio ORM. Releases up to v0.4.0 remain installable from the old path; new releases ship here only. The advisory lock namespace was renamed with the module, so avoid running migrations concurrently from pre-v0.5.0 and post-v0.5.0 binaries during a rolling upgrade.

Writing migrations

Conventional layout: a migrations package, one file per migration, imported for effect from main.

app/
├── main.go
└── migrations/
    ├── 20260708100000_create_users.go
    ├── 20260712093000_create_posts.go
    └── 20260801154500_backfill_display_names.go
// main.go
import _ "app/migrations"

Names order lexically and are recorded in the database, so start them with a sortable timestamp. Registration panics on duplicate or malformed names at init time.

Columns
s.Create("articles", func(t *migrate.Table) {
	t.ID()                              // auto-incrementing 64-bit primary key
	                                    // (t.Integer("id").AutoIncrement() for other widths)
	t.String("slug", 80).Unique()       // VARCHAR(80) + unique index
	t.Text("body")                      // unbounded text
	t.Integer("views").Default(0)
	t.Decimal("rating", 3, 1).Nullable()
	t.Boolean("published").Default(false)
	t.JSON("meta").Nullable()           // JSONB / JSON / TEXT
	t.UUID("public_id").DefaultExpr("gen_random_uuid()")
	t.Enum("state", "draft", "live")    // native ENUM or CHECK constraint
	t.TimestampTz("published_at").Nullable()
	t.Timestamps()                      // created_at, updated_at
	t.SoftDeletes()                     // deleted_at
	t.Column("tags", "text[]")          // any dialect type, verbatim
})

Columns are NOT NULL unless declared .Nullable(). Modifiers chain:

  • Any engine: .Default(v), .DefaultExpr(expr), .UseCurrent(), .Unsigned(), .Unique(), .Index(), .Primary(), .AutoIncrement(), .Comment(...).
  • Generated columns: .StoredAs(expr), .VirtualAs(expr).
  • MySQL only: .After(...), .First(), .UseCurrentOnUpdate().

Table names may be schema-qualified: s.Create("analytics.events", ...) renders "analytics"."events", with conventional constraint names inside the schema.

CHECK constraints must be named; anonymous checks are rejected (an unnamed constraint cannot be dropped later).

t.Check("orders_price_positive", "price > 0")   // in Create or Table
t.DropCheck("orders_price_positive")            // reverses an added check

.AutoIncrement() makes any integer column the database-generated primary key, in each engine's form:

Engine Form
Postgres identity column, GENERATED BY DEFAULT AS IDENTITY (not legacy serial)
MySQL AUTO_INCREMENT
SQLite INTEGER PRIMARY KEY AUTOINCREMENT

t.ID() is shorthand for t.BigInteger("id").Unsigned().AutoIncrement().

Indexes and foreign keys
t.Index("a", "b")                 // articles_a_b_index
t.Unique("slug").Name("custom")   // custom name
t.Primary("a", "b")               // composite primary key

t.ForeignID("user_id").Constrained()              // → users.id, inferred
t.ForeignID("category_id").Constrained().NullOnDelete()
t.Foreign("code").References("regions", "code")   // existing column, explicit

Names follow {table}_{columns}_{index|unique|foreign}, so dropping by columns (t.DropIndex("a", "b"), t.DropForeign("user_id")) reconstructs the created name.

Altering tables
migrate.Add("20260801120000_polish_users", func(s *migrate.Schema) {
	s.Table("users", func(t *migrate.Table) {
		t.String("nickname", 50).Nullable().Index()
		t.RenameColumn("name", "full_name")
		t.DropColumn("legacy_flags")
	})
	s.Rename("groups", "teams")
})

Each change compiles to its own statement and reverses individually; the migration runs in one transaction where the engine allows. Also: t.RenameIndex(from, to), t.DropIndex/DropUnique/DropForeign (by columns, via conventional names), t.DropPrimary(), and table comments via t.Comment(...).

Rebuilding tables

Recreate handles what ALTER TABLE cannot (on SQLite, any constraint change): declare the full target table and the migrator rebuilds it around the data (create temporary → copy rows → capture triggers → drop old → rename → rebuild indexes → recreate triggers), within the migration's transaction on Postgres and SQLite, so a failure leaves the original untouched. MySQL refuses to compile Recreate: its implicit DDL commits would open a crash window with the live table dropped, and its native ALTER TABLE changes types and constraints directly.

s.Recreate("users", func(t *migrate.Table) {
	t.ID()
	t.String("email").Unique()                                 // the new constraint
	t.Integer("logins").Default(0).SkipCopy()                  // brand-new column
})
  • Recreate requires its transaction; combining it with WithoutTransaction is a compile-time error.
  • On Postgres, a table referenced by other tables' foreign keys or by views cannot be rebuilt; the drop is refused and the transaction rolls back cleanly. Use native ALTER there.

Columns copy by name. SkipCopy marks columns absent from the old table; CopyFrom substitutes a SELECT expression, renaming and retyping a column in one rebuild:

t.Integer("age").CopyFrom("CAST(age AS INTEGER)")

Conventional constraint and index names come out for the final table name, so later DropUnique/DropForeign still resolve. Recreate discards the old definition, so rolling back needs a WithDown (usually another Recreate for the previous shape).

SQLite caveat: with PRAGMA foreign_keys=ON and child rows referencing the table, run on a connection with enforcement off (off by default in SQLite and most drivers).

Triggers (created via Exec, since the builder does not declare them) are captured and recreated verbatim after the rename, which DROP TABLE would otherwise remove. A trigger the new shape breaks fails the replay and rolls back; drop it with Exec before the Recreate and declare its successor after.

There is no Change() for redefining a column type in place; SQLite cannot without rebuilding. Use SQL — reviewable in Plan and checksummed:

migrate.Add("20260812100000_widen_amounts",
	func(s *migrate.Schema) {
		s.Exec(`ALTER TABLE orders ALTER COLUMN amount TYPE NUMERIC(12, 2)`)
	},
	migrate.WithDown(func(s *migrate.Schema) {
		s.Exec(`ALTER TABLE orders ALTER COLUMN amount TYPE NUMERIC(8, 2)`)
	}),
)
Raw SQL and data migrations
migrate.Add("20260805090000_backfill",
	func(s *migrate.Schema) {
		s.Exec(`UPDATE users SET plan = 'free' WHERE plan IS NULL`)
		s.Run(func(ctx context.Context, db migrate.DB) error {
			// arbitrary Go: batched backfills, API lookups, encoding changes
			_, err := db.ExecContext(ctx, `UPDATE users SET score = score * 10`)
			return err
		})
	},
	migrate.WithDown(func(s *migrate.Schema) {
		s.Exec(`UPDATE users SET score = score / 10`)
	}),
)

Run receives the migration's transaction when present; migrate.DB is satisfied by both *sql.Tx and *sql.DB. Statements that cannot run in a transaction (e.g. CREATE INDEX CONCURRENTLY) set migrate.WithoutTransaction():

migrate.Add("20260810110000_index_events",
	func(s *migrate.Schema) {
		s.Exec(`CREATE INDEX CONCURRENTLY events_at_index ON events (at)`)
	},
	migrate.WithoutTransaction(),
	migrate.WithDown(func(s *migrate.Schema) {
		s.Exec(`DROP INDEX events_at_index`)
	}),
)
Repeatable migrations

A versioned migration runs once; a repeatable migration runs whenever its compiled SQL differs from the last recorded value. Use it for views, stored functions, triggers, and reference data.

migrate.AddRepeatable("active_users_view", func(s *migrate.Schema) {
	s.Exec(`CREATE OR REPLACE VIEW active_users AS
	        SELECT * FROM users WHERE deleted_at IS NULL`)
})
  • Change the declaration and deploy; the next Up re-runs it, after all versioned migrations, in name order.
  • Declarations must be idempotent: CREATE OR REPLACE, or DROP ... IF EXISTS + CREATE on SQLite (no OR REPLACE).
  • Status shows a changed repeatable as drifted-pending; Plan renders what would re-run; rollbacks leave repeatables untouched (Reset forgets their records, so a fresh Up re-runs them).
  • A Run body is invisible to the checksum; edit SQL, not Go, to trigger a re-run.
  • Postgres refuses to roll back a versioned migration whose table a live repeatable view depends on; drop the dependent object first.

Running

Call Effect
m.Up(ctx) apply all pending migrations as one batch
m.Rollback(ctx, 1) undo the most recently applied migration
m.Rollback(ctx, n) undo the n most recent migrations
m.RollbackBatch(ctx) undo the latest batch — everything the last Up applied
m.Reset(ctx) undo everything
m.Status(ctx) applied / pending / drifted / unregistered, per migration
m.Plan(ctx) / m.PlanRollback(ctx, n) / m.PlanRollbackBatch(ctx) the SQL that would run, without running it
m.Baseline(ctx) mark migrations applied without executing (existing databases)
m.Repair(ctx) re-record versioned checksums after a reviewed change (repeatables stay due)
m.Fresh(ctx) development only: drop every table, re-run everything

Run Up at startup (safe across replicas via the advisory lock) or from a dedicated deploy step:

m, err := migrate.New(db, migrate.Postgres,
	migrate.WithLogger(slog.Default()),
	migrate.WithLockTimeout(2*time.Minute), // wait for a sibling deploy
)

Options: WithCollection (explicit collection instead of the global registry), WithTable (records table name), WithoutLock, WithLockTimeout, WithStrictChecksum, WithLogger, WithClock.

Safety analysis

Some operations are safe on an empty database but dangerous on a loaded one: dropping a column live code still reads, adding a NOT NULL column to a populated table, building an index that blocks writes. The migrator detects these before executing anything.

Mode Behavior
SafetyWarn (default) logs each finding through WithLogger and proceeds
WithSafety(migrate.SafetyStrict) Up fails with ErrUnsafe before executing, listing every finding across the run; wire it into CI
Assured() marks a reviewed migration so the analysis skips it

Plan attaches findings to each planned migration. Creating tables never warns; raw Exec/Run are not analyzed. The analysis covers declarative operations: destructive drops, backward-incompatible renames, NOT NULL additions without defaults, and Postgres index/foreign-key builds that lock large tables (the message names the CONCURRENTLY / NOT VALID escape routes).

Transactions and engines

PostgreSQL MySQL SQLite
DDL in transactions yes — failed migrations roll back completely no — DDL commits implicitly yes — failed migrations roll back completely
Advisory lock pg_advisory_lock, session-level GET_LOCK, session-level none — the single-writer file and record-first bookkeeping arbitrate
Altering constraints full support full support compile-time error with guidance

Each migration runs in its own transaction by default. On MySQL the transaction still protects data statements, but a mid-migration failure leaves earlier DDL in effect; the error says so, names the failing statement, and does not record the migration, so a fixed version runs again. Keep MySQL migrations to a single DDL statement where possible.

Session-level advisory locks do not survive transaction-pooling proxies (PgBouncer in transaction mode). Point the migrator at the database directly or through a session-mode pool.

Design notes

  • Declarations are data. Nothing runs while declaring; declarations must be deterministic — derive nothing from the clock or environment.
  • Forward-first. Rollbacks come free for structural operations; irreversibility is an explicit, typed error.
  • No CLI. Status, plans, and baselines are method calls, not a separate tool or login wall.
  • Convention with overrides. Inferred parent tables, conventional constraint names, and portable column types each take an explicit override (Constrained("people"), .Name("..."), t.Column(name, "any type"), s.Exec("any SQL")).

License

go-rio/migrate is released under the MIT License, © 2026-now TreeNewBee.

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)
	}
}
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")
			})
		}),
	)
}

Index

Examples

Constants

This section is empty.

Variables

View Source
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.

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

type Clock interface {
	Now() time.Time
}

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 NewCollection

func NewCollection() *Collection

NewCollection returns an empty collection.

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

func (c *Column) After(column string) *Column

After places the column after an existing one when altering a MySQL table. Column order is cosmetic; other dialects ignore it.

func (*Column) AutoIncrement

func (c *Column) AutoIncrement() *Column

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

func (c *Column) Comment(comment string) *Column

Comment attaches a comment to the column. SQLite has no column comments and ignores it.

func (*Column) CopyFrom

func (c *Column) CopyFrom(expr string) *Column

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

func (c *Column) Default(value any) *Column

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

func (c *Column) DefaultExpr(expr string) *Column

DefaultExpr sets a default SQL expression, written verbatim:

t.UUID("token").DefaultExpr("gen_random_uuid()")

func (*Column) First

func (c *Column) First() *Column

First places the column first when altering a MySQL table. Column order is cosmetic; other dialects ignore it.

func (*Column) Index

func (c *Column) Index() *Column

Index adds an index on this column, named {table}_{column}_index.

func (*Column) Nullable

func (c *Column) Nullable() *Column

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

func (c *Column) Primary() *Column

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

func (c *Column) SkipCopy() *Column

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

func (c *Column) StoredAs(expr string) *Column

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) Unique

func (c *Column) Unique() *Column

Unique adds a unique index on this column, named {table}_{column}_unique.

func (*Column) Unsigned

func (c *Column) Unsigned() *Column

Unsigned restricts an integer column to non-negative values on MySQL. Postgres and SQLite have no unsigned types and ignore it.

func (*Column) UseCurrent

func (c *Column) UseCurrent() *Column

UseCurrent defaults the column to the current timestamp.

func (*Column) UseCurrentOnUpdate

func (c *Column) UseCurrentOnUpdate() *Column

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

func (c *Column) VirtualAs(expr string) *Column

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.

func (*Index) Name

func (i *Index) Name(name string) *Index

Name overrides the conventional {table}_{columns}_{index|unique} name. A named index must be dropped with DropIndexByName.

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.

func (*Migration) Name

func (m *Migration) Name() string

Name returns the migration's registered name.

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

func New(db *sql.DB, dialect Dialect, opts ...Option) (*Migrator, error)

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

func (m *Migrator) Baseline(ctx context.Context, upTo ...string) error

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

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

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

func (m *Migrator) Plan(ctx context.Context) ([]Planned, error)

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

func (m *Migrator) PlanRollback(ctx context.Context, steps int) ([]Planned, error)

PlanRollback renders the SQL that Rollback(ctx, steps) would execute, without executing anything.

func (*Migrator) PlanRollbackBatch

func (m *Migrator) PlanRollbackBatch(ctx context.Context) ([]Planned, error)

PlanRollbackBatch renders the SQL that RollbackBatch would execute, without executing anything.

func (*Migrator) Repair

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

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

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

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

func (m *Migrator) Rollback(ctx context.Context, steps int) error

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

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

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

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

Status returns the merged view of registered and applied migrations in name order. It reads without taking the migration lock.

func (*Migrator) Up

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

Up applies every registered migration that has not been applied yet, in name order, as one batch. Each migration runs in its own transaction unless it opted out; a failure stops the run, leaves no partial bookkeeping and returns an error identifying the failing statement.

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

func WithClock(c Clock) Option

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

func WithLockTimeout(d time.Duration) Option

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

func WithLogger(l *slog.Logger) Option

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

func WithTable(name string) Option

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

func (s *Schema) Create(table string, fn func(*Table))

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

func (s *Schema) Drop(table string)

Drop removes a table. Dropping is irreversible: rolling back requires an explicit down migration declared with WithDown.

func (*Schema) DropIfExists

func (s *Schema) DropIfExists(table string)

DropIfExists removes a table if it exists. Like Drop, it is irreversible.

func (*Schema) Exec

func (s *Schema) Exec(query string, args ...any)

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

func (s *Schema) Recreate(table string, fn func(*Table))

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

func (s *Schema) Rename(from, to string)

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

func (s *Schema) Run(fn func(ctx context.Context, db DB) error)

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.

func (*Schema) Table

func (s *Schema) Table(table string, fn func(*Table))

Table declares changes to an existing table: adding or dropping columns, indexes and foreign keys, or renaming columns. Each change compiles to its own statement; rolling back reverses the changes in reverse order.

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

func (t *Table) BigInteger(name string) *Column

BigInteger declares a 64-bit integer.

func (*Table) Binary

func (t *Table) Binary(name string) *Column

Binary declares a binary blob column.

func (*Table) Boolean

func (t *Table) Boolean(name string) *Column

Boolean declares a boolean column (TINYINT(1) on MySQL).

func (*Table) Char

func (t *Table) Char(name string, length ...int) *Column

Char declares a fixed-length CHAR column. The length defaults to 255.

func (*Table) Check

func (t *Table) Check(name, expr string)

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

func (t *Table) Column(name, sqlType string) *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

func (t *Table) Comment(comment string)

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) Date

func (t *Table) Date(name string) *Column

Date declares a calendar date column.

func (*Table) DateTime

func (t *Table) DateTime(name string) *Column

DateTime declares a wall-clock timestamp without time zone: TIMESTAMP on Postgres, DATETIME(6) on MySQL.

func (*Table) Decimal

func (t *Table) Decimal(name string, precision, scale int) *Column

Decimal declares an exact fixed-point column, e.g. Decimal("amount", 10, 2).

func (*Table) Double

func (t *Table) Double(name string) *Column

Double declares a double-precision floating point column.

func (*Table) DropCheck

func (t *Table) DropCheck(name string)

DropCheck removes a CHECK constraint by name. Dropping discards the expression and is irreversible.

func (*Table) DropColumn

func (t *Table) DropColumn(names ...string)

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

func (t *Table) DropForeign(columns ...string)

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

func (t *Table) DropForeignByName(name string)

DropForeignByName removes a foreign key by its exact name.

func (*Table) DropIndex

func (t *Table) DropIndex(columns ...string)

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

func (t *Table) DropIndexByName(name string)

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

func (t *Table) DropUnique(columns ...string)

DropUnique removes the unique index that Unique would have created on the given columns, using the conventional {table}_{columns}_unique name.

func (*Table) Enum

func (t *Table) Enum(name string, values ...string) *Column

Enum declares a column restricted to the given values: a native ENUM on MySQL, VARCHAR plus a named CHECK constraint elsewhere.

func (*Table) Float

func (t *Table) Float(name string) *Column

Float declares a single-precision floating point column.

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

func (t *Table) ID(name ...string) *Column

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

func (t *Table) Index(columns ...string) *Index

Index declares an index over the given columns, named {table}_{columns}_index unless renamed with Name.

func (*Table) Integer

func (t *Table) Integer(name string) *Column

Integer declares a 32-bit integer.

func (*Table) JSON

func (t *Table) JSON(name string) *Column

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

func (t *Table) Primary(columns ...string)

Primary declares a composite primary key. For the common single auto-incrementing key, use ID instead.

func (*Table) RenameColumn

func (t *Table) RenameColumn(from, to string)

RenameColumn renames a column. It reverses to the opposite rename.

func (*Table) RenameIndex

func (t *Table) RenameIndex(from, to string)

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

func (t *Table) SmallInteger(name string) *Column

SmallInteger declares a 16-bit integer.

func (*Table) SoftDeletes

func (t *Table) SoftDeletes(name ...string) *Column

SoftDeletes declares a nullable deleted_at TimestampTz column (or the given name) for soft-deletion schemes.

func (*Table) String

func (t *Table) String(name string, length ...int) *Column

String declares a VARCHAR column. The length defaults to 255.

func (*Table) Text

func (t *Table) Text(name string) *Column

Text declares an unbounded text column (TEXT, or LONGTEXT on MySQL where TEXT is capped at 64 KB).

func (*Table) Time

func (t *Table) Time(name string) *Column

Time declares a time-of-day column.

func (*Table) Timestamp

func (t *Table) Timestamp(name string) *Column

Timestamp is an alias for DateTime, for schemas that prefer the name.

func (*Table) TimestampTz

func (t *Table) TimestampTz(name string) *Column

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

func (t *Table) TinyInteger(name string) *Column

TinyInteger declares an 8-bit integer (SMALLINT on Postgres, which has no 8-bit type).

func (*Table) UUID

func (t *Table) UUID(name string) *Column

UUID declares a UUID column (native on Postgres, CHAR(36) elsewhere).

func (*Table) Unique

func (t *Table) Unique(columns ...string) *Index

Unique declares a unique index over the given columns, named {table}_{columns}_unique unless renamed with Name.

Jump to

Keyboard shortcuts

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