migrate

package module
v0.14.0 Latest Latest
Warning

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

Go to latest
Published: Sep 2, 2026 License: MIT Imports: 15 Imported by: 0

README

migrate

Doc Go Release Test License

Schema migrations written as Go code and compiled into your binary: one declaration compiles to PostgreSQL, MySQL, SQLite, or single-server ClickHouse SQL, with automatic rollback, dry-run plans, checksums, and repeatable migrations. No SQL files to ship, no CLI to install, no dependencies beyond database/sql.

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.Enum("role", "admin", "member").Default("member")
			t.ForeignID("team_id").Constrained().CascadeOnDelete()
			t.Timestamps()
		})
	})
}

Getting started

Requires Go 1.27+.

go get github.com/go-rio/migrate

Register migrations from init functions and apply them at startup:

// migrations/20260708100000_create_users.go
package migrations

import "github.com/go-rio/migrate"

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.Timestamps()
		})
	})
}
// main.go
package main

import (
	"context"
	"database/sql"
	"log"

	"github.com/go-rio/migrate"
	_ "github.com/jackc/pgx/v5/stdlib" // any database/sql driver

	_ "app/migrations" // registers the migrations
)

func main() {
	db, err := sql.Open("pgx", "postgres://localhost/app")
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	m, err := migrate.New(db, migrate.Postgres)
	if err != nil {
		log.Fatal(err)
	}
	if err := m.Up(context.Background()); err != nil {
		log.Fatal(err)
	}
}

Dialects are migrate.Postgres, migrate.MySQL, migrate.SQLite, and migrate.ClickHouse. New accepts any *sql.DB (rdb.Unwrap() from the rio ORM) and never closes it. Each run uses one dedicated *sql.Conn: lock, DDL, and bookkeeping share a session.

Features

  • One declaration, four dialects. PostgreSQL, MySQL, SQLite, and single-server ClickHouse; unsupported operations fail at compile time.
  • Automatic rollback. Structural operations reverse themselves; information-discarding ones (drops, raw SQL, Go functions) need an explicit WithDown or fail with ErrIrreversible.
  • Reviewable and tamper-evident. Plan previews pending SQL, Collection.SQL renders offline, checksums flag applied migrations edited afterwards.
  • Serialized and safe. Advisory locks keep concurrent migrators apart; a safety analysis flags risky operations before anything executes.
Migration files

Keep one file per migration in a migrations package, imported for effect from main (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. Declarations must be deterministic — they re-run for planning, checksums, application, and rollback. migrate.Add registers into the package-level collection; migrate.NewCollection with WithCollection keeps an explicit one.

Columns
s.Create("articles", func(t *migrate.Table) {
	t.ID()                            // auto-incrementing 64-bit primary key
	t.String("slug", 80).Unique()     // VARCHAR(80), defaults to 255
	t.Text("body")                    // unbounded text
	t.Decimal("rating", 3, 1).Nullable()
	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.Timestamps()                    // nullable created_at, updated_at
	t.SoftDeletes()                   // nullable deleted_at
	t.Column("tags", "text[]")        // any dialect type, verbatim
})
  • Columns are NOT NULL unless marked .Nullable().
  • Default takes portable scalars, including defined types — t.Enum("state", StateDraft, StateLive).Default(StateDraft) works. SQL expressions go through DefaultExpr; UseCurrent() defaults to the current timestamp.
  • Relational engines: .Unique(), .Index(), .Primary(), .AutoIncrement(); ClickHouse rejects these with an alternative.
  • Also: .Unsigned() (MySQL, ClickHouse), .StoredAs(expr) / .VirtualAs(expr) generated columns, .Comment(...), .Collation(name) (the dialect's own collation name; ClickHouse rejects it), .After(...) / .First() on MySQL and ClickHouse alterations, .UseCurrentOnUpdate() (MySQL only).
  • Table names may be schema-qualified: s.Create("analytics.events", ...).
  • CHECK constraints must be named so they can be dropped later: t.Check("orders_price_positive", "price > 0"); t.DropCheck(name).

t.ID() is t.BigInteger("id").Unsigned().AutoIncrement(); .AutoIncrement() compiles per engine:

Engine Form
PostgreSQL identity column, GENERATED BY DEFAULT AS IDENTITY (not legacy serial)
MySQL AUTO_INCREMENT
SQLite INTEGER PRIMARY KEY AUTOINCREMENT
ClickHouse unsupported — generate IDs in the application
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.Foreign("code").References("regions", "code")  // existing column, explicit

Referential actions chain: CascadeOnDelete, RestrictOnDelete, NullOnDelete, and the OnUpdate variants; Deferrable() checks the key at commit on PostgreSQL and SQLite, so rows can reference each other within one transaction (MySQL rejects it). Names follow {table}_{columns}_{index|unique|foreign}, so dropping by columns (t.DropIndex("a", "b"), t.DropForeign("user_id")) reconstructs the created name; DropIndexByName/DropForeignByName take exact names. Unsupported index modifier combinations fail at compile time with advice:

t.Index("created_at", "id").Desc("created_at") // mixed-direction key, as keyset paging orders (MySQL 8.0+)
t.Unique("name").Where("deleted_at IS NULL")  // partial index (Postgres, SQLite)
t.Index("payload").Using("gin")               // method (Postgres); btree/hash on MySQL
t.Unique("email").Include("name")             // covering index (Postgres)
t.Unique("email").NullsNotDistinct()          // one NULL at most (Postgres 15+)
t.Index("user_id").Concurrently()             // online build (Postgres)
t.FullText("title", "body")                   // MySQL; Postgres: IndexExpr + gin
t.Spatial("location")                         // MySQL geometry index
t.IndexExpr("users_email_lower_index", "lower(email)")  // name required
t.UniqueExpr("users_email_lower_unique", "lower(email)")

A partial unique index is the soft-delete pattern: deleted rows release the name, live rows still cannot share it. .Concurrently() cannot run inside a transaction — declare the migration WithoutTransaction(), which compile enforces; its rollback drops concurrently too. Other dialects ignore it.

IndexExpr expressions render verbatim into the index key list on PostgreSQL and SQLite: parenthesize PostgreSQL arithmetic yourself ("(a + b)") and append anything that belongs outside the parentheses, such as an operator class ("lower(email) text_pattern_ops"). MySQL alone requires every functional key part parenthesized, so each expression gains one pair there.

MySQL indexes a Text, Binary, or JSON column only by prefix or through a generated column, so a plain index over one fails at compile time with both alternatives named; FullText is the exception.

Named unique constraints
t.UniqueConstraint("uk_inventory_ref", "owner_id", "client_ref") // ON CONFLICT ON CONSTRAINT target
t.DropConstraint("uk_inventory_ref")                             // irreversible

Inline in Create, ADD CONSTRAINT in Table (MySQL DROP CONSTRAINT needs 8.0.19+). SQLite alters via Recreate, which keeps the name. Partial and expression uniqueness stay indexes.

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. Also: RenameIndex, DropUnique, DropFullText, DropSpatial, DropPrimary, t.Comment(...).

To alter a column, restate its complete target definition with .Change():

t.String("name", 500).Nullable().Change()        // widen + allow NULL
t.Integer("age").Change().Using("age::integer")  // Postgres cast for old rows

Change is a restatement — an omitted default drops any existing one. MySQL and ClickHouse compile it to MODIFY COLUMN; Postgres emits separate ALTER COLUMN statements for type (with the optional USING), nullability, and default; SQLite cannot alter columns — use Recreate. Index modifiers, primary keys, auto-increment, and generated expressions cannot be restated. The old definition is discarded, so rolling back needs WithDown.

Rebuilding tables

Recreate declares the full target table and rebuilds it around the data (create temporary, copy rows, capture triggers, drop old, rename, rebuild indexes, recreate triggers) — for whatever ALTER TABLE cannot do, which on SQLite is any constraint change. Postgres and SQLite run the rebuild inside the migration's transaction, so a failure leaves the original untouched; MySQL and ClickHouse reject Recreate at compile time because no transaction protects the copy/drop/rename sequence.

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
	t.Integer("age").CopyFrom("CAST(age AS INTEGER)")  // retype during copy
})
  • Columns copy by name; SkipCopy marks columns absent from the old table, CopyFrom substitutes a SELECT expression.
  • Combining Recreate with WithoutTransaction is a compile-time error, and rolling back needs WithDown.
  • Postgres refuses to rebuild a table referenced by foreign keys or views; the transaction rolls back cleanly. Use native ALTER there.
  • Constraint and index names resolve for the final table name, so later DropUnique/DropForeign still work.
  • Triggers (created via Exec) are captured and recreated verbatim; a trigger the new shape breaks fails the replay and rolls back.
  • SQLite: with PRAGMA foreign_keys=ON and child rows referencing the table, run on a connection with enforcement off (the default).
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 {
			_, 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`)
	}),
)

Exec is checksummed and shows in plans. Run is arbitrary Go: it receives the migration's transaction when there is one (the dedicated *sql.Conn otherwise) and is invisible to checksums; migrate.DB is satisfied by *sql.Tx, *sql.Conn, and *sql.DB. Keep DDL out of Run on MySQL — implicit commits inside an opaque function escape the failure report. Statements that refuse transactions (CREATE INDEX CONCURRENTLY) need migrate.WithoutTransaction(); each statement then commits as it runs.

Repeatable migrations

A repeatable migration re-runs whenever its compiled SQL changes — for views, 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`)
})

Repeatables run after versioned migrations, in name order, and must be idempotent (CREATE OR REPLACE; on SQLite DROP ... IF EXISTS then CREATE). Status shows a changed one as drifted-pending, Plan renders it, and rollbacks leave repeatables untouched — Reset forgets their records so the next Up re-runs them all. A Run body is invisible to the checksum: edit SQL, not Go, to trigger a re-run. Postgres refuses to roll back a migration whose table a live view depends on; drop the view first.

Running
Call Effect
m.Up(ctx) apply pending migrations as one batch, then changed repeatables
m.Rollback(ctx, n) undo the n most recently applied migrations
m.RollbackBatch(ctx) undo everything the last Up applied
m.Reset(ctx) undo everything, baselined rows included
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) record migrations as applied without executing; an optional name bounds it
m.Repair(ctx) re-record versioned checksums after a reviewed edit
m.Fresh(ctx) development only: drop every table, re-run everything

Rollback and RollbackBatch skip baselined rows; only Reset reverses them. Run Up at startup where the dialect provides a lock, or from a dedicated deploy step. Options: WithCollection (explicit collection instead of the global registry), WithTable (records table and lock namespace), WithoutLock, WithLockTimeout (default one minute), WithStrictChecksum, WithSafety, WithLogger, WithClock.

Each applied migration records a checksum of its compiled SQL and arguments (type-tagged, length-prefixed; pointer arguments hash by value). On drift, Up warns — or fails with ErrChecksumMismatch under WithStrictChecksumStatus reports it, and Repair re-records after review.

Safety analysis

Before executing anything, the migrator checks declarative operations that are safe empty but dangerous on a loaded database: destructive drops, backward-incompatible renames, NOT NULL additions without defaults, column changes that rewrite tables, and Postgres index and foreign-key builds that lock large tables. On ClickHouse it warns that old rows read a new non-Nullable column's zero value, that type changes rewrite data, and that a new CHECK does not validate historical rows. Creating tables never warns; Exec, Run, and engine fragments are manual-review boundaries and are not parsed. Plan attaches findings to each planned migration.

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
WithSafety(migrate.SafetyOff) disables the analysis
Assured() (per migration) marks a reviewed migration; the analysis skips it
Dialect differences
PostgreSQL MySQL SQLite ClickHouse
Execution full migration transaction transaction protects DML; DDL commits implicitly full migration transaction no transaction; ordered statements on one dedicated connection
Built-in lock session advisory lock GET_LOCK, session-level single writer + record-first bookkeeping none; fails with ErrLockUnsupported
Failure everything rolls back error names the prefix committed by implicit DDL everything rolls back error names the statement and the possibly-effective prefix
Change() per-property ALTER COLUMN (+ USING) MODIFY COLUMN compile error — use Recreate MODIFY COLUMN
Constraint changes full support full support compile error — use Recreate named CHECK add/drop only
Notable types JSONB, TIMESTAMPTZ, UUID DATETIME(6), native ENUM, JSON typed affinities, enum via CHECK DateTime64(6), JSON, UUID, Enum8/16

Failed runs never add a migration record, and there is deliberately no dirty-state force API. Session 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.

ClickHouse

Support targets one ClickHouse 26.0+ server and one local database — no ON CLUSTER, Keeper/ZooKeeper, replicated databases, Distributed tables, or multi-replica consistency. With no built-in lock, every writing method (Up, rollbacks, Reset, Baseline, Repair, Fresh) fails with ErrLockUnsupported until the deploy system serializes execution and you pass WithoutLock; Status, Plan, and Collection.SQL need no option.

Every Create must declare its storage engine and sorting key:

s.Create("events", func(t *migrate.Table) {
	t.UUID("id")
	t.TimestampTz("occurred_at")
	t.JSON("payload")
	t.ClickHouseEngine("MergeTree() PARTITION BY toYYYYMM(occurred_at) ORDER BY occurred_at")
})

ClickHouseEngine is the trusted fragment after ENGINE = — parameters, PARTITION BY, ORDER BY, PRIMARY KEY, SAMPLE BY, TTL, SETTINGS; omit ENGINE =, a table COMMENT, and the trailing semicolon. Other dialects ignore it, so a shared migration still compiles everywhere.

Relational uniqueness cannot be preserved, so ClickHouse rejects ID/AutoIncrement, Primary/Unique, every index builder and index drop/rename, foreign keys, UseCurrentOnUpdate, Using, primary-key alterations, and Recreate. Each error names the alternative: sorting keys in ClickHouseEngine, a plain UInt64 for an unconstrained ForeignID, Exec("ALTER TABLE ... ADD INDEX ... TYPE ...") plus WithDown for data-skipping indexes.

Declaration ClickHouse type
String, Text, Binary String (length is not a constraint)
Char(n) FixedString(n)
TinyIntegerBigInteger Int8Int64; Unsigned() gives UInt8UInt64
Boolean, Float, Double Bool, Float32, Float64
Decimal(p,s), Date, Time Decimal(p,s), Date, Time
DateTime, Timestamp DateTime64(6)
TimestampTz DateTime64(6, 'UTC')
JSON, UUID JSON, UUID
Enum stable positive numbering: Enum8 to 127 values, Enum16 to 32767
Column(name, sqlType) the type, verbatim

Nullable() wraps the type in Nullable(T); defaults, now64(6), MATERIALIZED/ALIAS, comments, and named CHECK constraints compile natively. The history insert runs last with async_insert = 0; record updates and deletes use mutations_sync = 1. A duplicate history version is a hard error — proof the external serialization guarantee was violated; after any failure, reconcile schema and history before retrying or baselining.

Contributing

Bug reports, questions, and pull requests are welcome; see CONTRIBUTING.md for the test setup, commit conventions, comment style, and release process.

Contributors

Thanks to everyone who has contributed.

License

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

Documentation

Overview

Package migrate applies schema migrations declared with Go builders and compiled into the binary. One declaration compiles to PostgreSQL, MySQL, SQLite, or single-server ClickHouse SQL, with rollbacks, dry-run plans, checksums, and repeatable migrations.

Entry points: Add and AddRepeatable register migrations in the default Collection (NewCollection and WithCollection keep an explicit one); New wraps a caller-owned *sql.DB with one of Postgres, MySQL, SQLite, or ClickHouse; Migrator.Up, Rollback, Status, and Plan drive it. Irreversible operations require WithDown. The package owns no connections or drivers.

Example (ClickHouse)

ClickHouse tables require an explicit storage engine.

package main

import (
	"context"
	"database/sql"
	"log"

	"github.com/go-rio/migrate"
)

type role string

const (
	roleAdmin  role = "admin"
	roleMember role = "member"
)

// Applications register migrations from init functions in a migrations
// package imported for effect.
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", roleAdmin, roleMember).Default(roleMember)
			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() {
	c := migrate.NewCollection()
	c.Add("20260809100000_create_events", func(s *migrate.Schema) {
		s.Create("events", func(t *migrate.Table) {
			t.UUID("id")
			t.String("tenant_id")
			t.TimestampTz("occurred_at")
			t.JSON("payload")
			t.ClickHouseEngine(
				"MergeTree() PARTITION BY toYYYYMM(occurred_at) " +
					"ORDER BY (tenant_id, occurred_at)",
			)
		})
	})

	db, err := sql.Open("clickhouse", "clickhouse://localhost:9000/default")
	if err != nil {
		log.Fatal(err)
	}
	defer func() { _ = db.Close() }()

	m, err := migrate.New(db, migrate.ClickHouse,
		migrate.WithCollection(c),
		migrate.WithoutLock(), // the deploy system must serialize this call
	)
	if err != nil {
		log.Fatal(err)
	}
	if err := m.Up(context.Background()); err != nil {
		log.Fatal(err)
	}
}

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrIrreversible marks a migration with no automatic or explicit rollback.
	ErrIrreversible = errors.New("migrate: migration cannot be automatically reversed")
	// ErrLockTimeout marks a timed-out advisory lock acquisition.
	ErrLockTimeout = errors.New("migrate: timed out waiting for the migration lock")
	// ErrLockUnsupported indicates that the dialect has no built-in migration
	// lock. Callers may use WithoutLock only after serializing deployments
	// externally.
	ErrLockUnsupported = errors.New("migrate: migration lock unsupported")
	// ErrChecksumMismatch marks an edited applied migration.
	ErrChecksumMismatch = errors.New("migrate: checksum mismatch")
)
View Source
var ErrUnsafe = errors.New("migrate: unsafe migration")

ErrUnsafe marks a migration rejected by SafetyStrict.

Functions

func Add

func Add(name string, up func(*Schema), opts ...MigrationOption)

Add registers a migration in the default collection.

Example

Builders reverse themselves; raw SQL and Go functions discard information, so a migration using them declares its rollback with WithDown.

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

func AddRepeatable

func AddRepeatable(name string, run func(*Schema), opts ...MigrationOption)

AddRepeatable registers a repeatable migration in the default collection.

Types

type Clock

type Clock interface {
	Now() time.Time
}

Clock supplies applied_at timestamps for migration records; WithClock injects one, typically in tests.

type Collection

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

Collection is a named migration set. Package-level Add uses a default one.

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 lexically ordered migration. It panics on an empty, whitespace-padded, or over-191-character name, on a duplicate name, or on a nil declaration.

func (*Collection) AddRepeatable

func (c *Collection) AddRepeatable(name string, run func(*Schema), opts ...MigrationOption)

AddRepeatable registers an idempotent declaration that reruns when its SQL checksum changes. Repeatables run after versioned migrations and have no rollback; Reset forgets their records.

func (*Collection) SQL

func (c *Collection) SQL(dialect Dialect) ([]Planned, error)

SQL renders the entire collection offline: versioned migrations first, then repeatables.

Example

Collection.SQL renders migrations without a database.

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 configures a declared column.

func (*Column) After

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

After positions an altered MySQL or ClickHouse column after another column.

func (*Column) AutoIncrement

func (c *Column) AutoIncrement() *Column

AutoIncrement makes an integer column a database-generated primary key. It cannot be nullable or have a default.

func (*Column) Change added in v0.7.0

func (c *Column) Change() *Column

Change restates a column's complete target definition inside Schema.Table. SQLite requires Schema.Recreate. The change is irreversible without WithDown; indexes and constraints must be changed separately.

func (*Column) Collation added in v0.14.0

func (c *Column) Collation(name string) *Column

Collation sets the column's collation by the dialect's own name: a PostgreSQL collation ("C", "und-x-icu"), a MySQL collation (utf8mb4_bin), or a SQLite one (NOCASE). ClickHouse rejects it.

func (*Column) Comment

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

Comment sets a column comment; SQLite ignores it.

func (*Column) CopyFrom

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

CopyFrom sets the SELECT expression used to fill this column during Schema.Recreate. It has no effect elsewhere.

func (*Column) Default

func (c *Column) Default[V DefaultLiteral](value V) *Column

Default sets a portable scalar default. Use DefaultExpr for SQL.

func (*Column) DefaultExpr

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

DefaultExpr sets a verbatim SQL default expression.

func (*Column) First

func (c *Column) First() *Column

First positions an altered MySQL or ClickHouse column first.

func (*Column) Index

func (c *Column) Index() *Column

Index adds a conventionally named index.

func (*Column) Nullable

func (c *Column) Nullable() *Column

Nullable allows NULL values; columns are NOT NULL by default.

func (*Column) Primary

func (c *Column) Primary() *Column

Primary makes this column the table's primary key.

func (*Column) SkipCopy

func (c *Column) SkipCopy() *Column

SkipCopy omits this column while Schema.Recreate copies old rows.

func (*Column) StoredAs

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

StoredAs makes this a stored generated column using a verbatim expression.

func (*Column) Unique

func (c *Column) Unique() *Column

Unique adds a conventionally named unique index.

func (*Column) Unsigned

func (c *Column) Unsigned() *Column

Unsigned uses MySQL's or ClickHouse's unsigned integer type. Other dialects 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 enables MySQL's automatic timestamp refresh. ClickHouse rejects it; other dialects ignore it.

func (*Column) Using added in v0.7.0

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

Using sets the PostgreSQL conversion expression for Change. Other dialects reject it.

func (*Column) VirtualAs

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

VirtualAs makes this a virtual generated column using a verbatim expression.

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 database/sql surface available to Run functions; *sql.DB, *sql.Conn, and *sql.Tx satisfy it.

type DefaultLiteral added in v0.8.0

type DefaultLiteral interface {
	~bool | ~string |
		~int | ~int8 | ~int16 | ~int32 | ~int64 |
		~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 |
		~float32 | ~float64
}

DefaultLiteral contains the scalar types Default renders portably. SQL expressions belong in DefaultExpr.

type Dialect

type Dialect interface {
	// contains filtered or unexported methods
}

Dialect compiles and executes migrations for one database engine. Built-in values are Postgres, MySQL, SQLite, and ClickHouse; the methods are unexported, so no other implementation exists.

var ClickHouse Dialect = clickHouseDialect{}

ClickHouse is the single-server ClickHouse dialect. It never emits ON CLUSTER and does not provide an in-database migration lock.

var MySQL Dialect = mysqlDialect{}

MySQL targets MySQL 8.0+ or an equivalent MariaDB release. DDL commits implicitly, so failures report the statement and persisted prefix.

var Postgres Dialect = postgresDialect{}

Postgres is the PostgreSQL dialect. Migrations run inside a transaction, and a session-level advisory lock serializes concurrent migrators.

var SQLite Dialect = sqliteDialect{}

SQLite targets SQLite 3.35+. Constraint alterations require Schema.Recreate. Its single-writer transaction and record-first bookkeeping replace advisory locking.

type ForeignColumn

type ForeignColumn struct {
	*Column
	// contains filtered or unexported fields
}

ForeignColumn configures a ForeignID column and its constraint.

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 references id on the given table, or infers it from a *_id name.

func (*ForeignColumn) Deferrable added in v0.14.0

func (fc *ForeignColumn) Deferrable() *ForeignColumn

Deferrable checks the key at commit; see ForeignKey.Deferrable.

func (*ForeignColumn) Index

func (fc *ForeignColumn) Index() *ForeignColumn

Index adds an index while preserving the ForeignColumn 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 while preserving the ForeignColumn chain.

func (*ForeignColumn) References

func (fc *ForeignColumn) References(table string, columns ...string) *ForeignColumn

References adds a foreign key to the table; 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 while preserving the ForeignColumn chain.

type ForeignKey

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

ForeignKey configures a declared foreign key.

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) Deferrable added in v0.14.0

func (f *ForeignKey) Deferrable() *ForeignKey

Deferrable checks the key at commit (DEFERRABLE INITIALLY DEFERRED) on PostgreSQL and SQLite, so rows can reference each other within one transaction. MySQL rejects it.

func (*ForeignKey) Name

func (f *ForeignKey) Name(name string) *ForeignKey

Name overrides the conventional foreign-key name.

func (*ForeignKey) NoActionOnDelete

func (f *ForeignKey) NoActionOnDelete() *ForeignKey

NoActionOnDelete defers enforcement where the dialect supports it.

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 configures a declared index.

func (*Index) Concurrently added in v0.7.0

func (i *Index) Concurrently() *Index

Concurrently builds and drops a PostgreSQL index without blocking writes. The migration must use WithoutTransaction. Other dialects ignore it.

func (*Index) Desc added in v0.13.0

func (i *Index) Desc(columns ...string) *Index

Desc indexes the named columns descending, so the key matches a scan that mixes directions (ORDER BY created_at DESC, id). Every dialect honors it; MySQL before 8.0 parsed and ignored it. Expression indexes carry the direction inside the expression instead.

func (*Index) Include added in v0.7.0

func (i *Index) Include(columns ...string) *Index

Include adds PostgreSQL covering-index columns. Other dialects reject it.

func (*Index) Name

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

Name overrides the conventional index name.

func (*Index) NullsNotDistinct added in v0.7.0

func (i *Index) NullsNotDistinct() *Index

NullsNotDistinct enables PostgreSQL 15+'s NULLS NOT DISTINCT.

func (*Index) Using added in v0.7.0

func (i *Index) Using(method string) *Index

Using sets the PostgreSQL or MySQL index method. SQLite rejects it.

func (*Index) Where added in v0.7.0

func (i *Index) Where(predicate string) *Index

Where makes this a partial index with a verbatim predicate. MySQL rejects partial indexes.

type Migration

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

Migration is one registered declaration; Collection.Add creates it and Name identifies it in the records table.

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 and skips safety analysis.

func WithDown

func WithDown(down func(*Schema)) MigrationOption

WithDown defines rollback for otherwise irreversible operations.

func WithoutTransaction

func WithoutTransaction() MigrationOption

WithoutTransaction permits statements such as PostgreSQL CREATE INDEX CONCURRENTLY. Earlier statements may remain applied after a failure.

type Migrator

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

Migrator applies one Collection to a database. Advisory locks serialize concurrent processes unless WithoutLock is set.

func New

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

New creates a Migrator without taking ownership of db. The dialect must match the database driver. New fails on a nil db or dialect and on invalid options; each Option documents its constraints.

Example

New wraps a caller-owned *sql.DB; the dialect must match the driver, and options tune locking, checksums, safety, and logging.

package main

import (
	"context"
	"database/sql"
	"fmt"
	"log"
	"log/slog"
	"time"

	"github.com/go-rio/migrate"
)

type role string

const (
	roleAdmin  role = "admin"
	roleMember role = "member"
)

// Applications register migrations from init functions in a migrations
// package imported for effect.
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", roleAdmin, roleMember).Default(roleMember)
			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),
		migrate.WithStrictChecksum(),
		migrate.WithSafety(migrate.SafetyStrict),
	)
	if err != nil {
		log.Fatal(err)
	}

	statuses, err := m.Status(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	for _, st := range statuses {
		fmt.Printf("%s applied=%t drifted=%t\n", st.Name, st.Applied, st.Drifted)
	}
}

func (*Migrator) Baseline

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

Baseline records existing schema without executing migrations. An optional name limits versioned migrations; repeatables are always included. Rollback skips baselined rows, while Reset includes them.

func (*Migrator) Fresh

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

Fresh drops every table and reapplies all migrations. It is a destructive development operation and must not be used on production data.

func (*Migrator) Plan

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

Plan renders pending SQL without executing it. Versioned migrations precede changed repeatables.

func (*Migrator) PlanRollback

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

PlanRollback renders Rollback without executing it.

func (*Migrator) PlanRollbackBatch

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

PlanRollbackBatch renders RollbackBatch without executing it.

func (*Migrator) Repair

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

Repair accepts reviewed checksum drift for versioned migrations. Repeatables are unchanged because drift schedules their next run.

func (*Migrator) Reset

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

Reset reverses all versioned migrations, including baselined rows.

func (*Migrator) Rollback

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

Rollback reverses the latest steps versioned migrations; steps must be positive. Irreversible operations fail with ErrIrreversible, and baselined rows are not touched.

func (*Migrator) RollbackBatch

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

RollbackBatch reverses the latest batch without touching baselined rows.

func (*Migrator) Status

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

Status returns registered and recorded migrations in name order.

func (*Migrator) Up

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

Up applies pending versioned migrations as one batch, then runs changed repeatables. Each migration uses its own transaction when the dialect supports one and the migration has not opted out. Up fails before executing anything with ErrChecksumMismatch under WithStrictChecksum, ErrUnsafe under SafetyStrict, and ErrLockTimeout or ErrLockUnsupported from the lock.

Example

Up applies pending migrations as one batch, then changed repeatables; Plan previews the SQL and safety findings first.

package main

import (
	"context"
	"database/sql"
	"fmt"
	"log"

	"github.com/go-rio/migrate"
)

type role string

const (
	roleAdmin  role = "admin"
	roleMember role = "member"
)

// Applications register migrations from init functions in a migrations
// package imported for effect.
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", roleAdmin, roleMember).Default(roleMember)
			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")
	if err != nil {
		log.Fatal(err)
	}
	defer func() { _ = db.Close() }()

	m, err := migrate.New(db, migrate.Postgres)
	if err != nil {
		log.Fatal(err)
	}

	ctx := context.Background()
	plans, err := m.Plan(ctx)
	if err != nil {
		log.Fatal(err)
	}
	for _, p := range plans {
		fmt.Printf("-- %s: %d statements, %d warnings\n", p.Name, len(p.Statements), len(p.Warnings))
	}
	if err := m.Up(ctx); err != nil {
		log.Fatal(err)
	}
}

type Option

type Option func(*config)

Option configures a Migrator at construction time; pass options to New.

func WithClock

func WithClock(c Clock) Option

WithClock replaces the migration-record time source; nil keeps the system clock.

func WithCollection

func WithCollection(c *Collection) Option

WithCollection replaces the package-level default collection.

func WithLockTimeout

func WithLockTimeout(d time.Duration) Option

WithLockTimeout sets the advisory-lock wait; the default is one minute and New rejects non-positive durations.

func WithLogger

func WithLogger(l *slog.Logger) Option

WithLogger sets the progress logger; the default discards logs and New rejects nil.

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 instead of warning. Repair accepts reviewed drift.

func WithTable

func WithTable(name string) Option

WithTable sets the records table name and advisory-lock namespace. The name may be schema-qualified; New rejects empty names, names over 128 bytes, and names containing quotes or NUL.

func WithoutLock

func WithoutLock() Option

WithoutLock disables serialization. Use it only with an external single-run guarantee.

type Planned

type Planned struct {
	Name string
	// Statements contains SQL in execution order; Run appears as a placeholder.
	Statements []string
	// Warnings contains safety findings.
	Warnings []string
}

Planned is one migration in a dry-run plan.

type SafetyLevel

type SafetyLevel int

SafetyLevel controls handling of potentially disruptive operations.

const (
	// SafetyWarn logs findings and proceeds.
	SafetyWarn SafetyLevel = iota
	// SafetyStrict rejects all findings before execution.
	SafetyStrict
	// SafetyOff disables the analysis.
	SafetyOff
)

type Schema

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

Schema records migration operations without touching the database. Declarations must be deterministic because they are re-run for planning, checksums, application, and rollback.

func (*Schema) Create

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

Create declares a new table; rollback drops it.

func (*Schema) Drop

func (s *Schema) Drop(table string)

Drop removes a table and is irreversible without 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 records raw SQL in plans and checksums. It is irreversible without WithDown and uses native dialect placeholders.

func (*Schema) Recreate

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

Recreate rebuilds a table while preserving rows and triggers. Columns copy by name unless CopyFrom or SkipCopy says otherwise. PostgreSQL and SQLite run the rebuild transactionally; MySQL and ClickHouse reject it.

Recreate is irreversible without WithDown. PostgreSQL also rejects rebuilds blocked by dependent foreign keys or views.

func (*Schema) Rename

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

Rename renames a table and reverses automatically. PostgreSQL and SQLite reject cross-schema renames.

func (*Schema) Run

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

Run records an opaque Go data migration. It runs in the migration transaction when the dialect provides one, or on the dedicated connection otherwise. It is excluded from checksums and is irreversible.

Keep DDL out of fn: rio cannot see inside it, so on MySQL DDL would commit implicitly without appearing in failure reports. Use the builders or Exec.

func (*Schema) Table

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

Table declares reversible alterations to an existing table.

type Status

type Status struct {
	Name       string
	Applied    bool      // the database has a record
	Batch      int       // batch it was applied in; 0 also marks baselined rows, -1 repeatable ones
	AppliedAt  time.Time // zero when not applied
	Registered bool      // the current collection contains the migration
	Repeatable bool
	Drifted    bool // declaration checksum differs from the record
}

Status describes a registered or recorded migration.

type Table

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

Table builds a new table or a set of alterations. Declaration errors are collected and returned when the migration compiles.

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.

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 with a verbatim SQL expression. SQLite alterations must use Schema.Recreate instead.

func (*Table) ClickHouseEngine added in v0.9.0

func (t *Table) ClickHouseEngine(clause string)

ClickHouseEngine sets the complete ClickHouse storage fragment rendered after ENGINE =. It may include engine parameters, PARTITION BY, ORDER BY, PRIMARY KEY, SAMPLE BY, TTL, and storage SETTINGS. It is only valid inside Schema.Create; omit ENGINE =, table COMMENT, and a trailing semicolon. Other dialects ignore it.

func (*Table) Column

func (t *Table) Column(name, sqlType string) *Column

Column declares a column with a dialect-specific type written verbatim.

func (*Table) Comment

func (t *Table) Comment(comment string)

Comment sets a table comment. SQLite ignores comments; altering one is 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 timestamp without a time zone.

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 and is irreversible.

func (*Table) DropColumn

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

DropColumn removes columns and is irreversible without WithDown.

func (*Table) DropConstraint added in v0.12.0

func (t *Table) DropConstraint(name string)

DropConstraint drops any named table constraint. Irreversible without WithDown; SQLite alters via Schema.Recreate.

func (*Table) DropForeign

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

DropForeign removes the conventional foreign key for the columns.

func (*Table) DropForeignByName

func (t *Table) DropForeignByName(name string)

DropForeignByName removes a foreign key by its exact name.

func (*Table) DropFullText added in v0.7.0

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

DropFullText removes the conventional full-text index for the columns.

func (*Table) DropIndex

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

DropIndex removes the conventional index for the columns.

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) DropSpatial added in v0.7.0

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

DropSpatial removes the conventional spatial index for the columns.

func (*Table) DropUnique

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

DropUnique removes the conventional unique index for the columns.

func (*Table) Enum

func (t *Table) Enum[V ~string](name string, values ...V) *Column

Enum declares a native MySQL ENUM or an equivalent checked VARCHAR. Values may use any defined string type.

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.

func (*Table) ForeignID

func (t *Table) ForeignID(name string) *ForeignColumn

ForeignID declares an unsigned 64-bit foreign-key column.

func (*Table) FullText added in v0.7.0

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

FullText declares a MySQL FULLTEXT index. Other dialects reject it.

func (*Table) ID

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

ID declares a 64-bit auto-incrementing primary key named "id" by default.

func (*Table) Index

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

Index declares a conventionally named index over the columns.

func (*Table) IndexExpr added in v0.7.0

func (t *Table) IndexExpr(name string, exprs ...string) *Index

IndexExpr declares a named index over SQL expressions written verbatim into the index key list; wrap an expression when the database demands it and append what belongs outside the parentheses, like an operator class in "lower(email) text_pattern_ops". Exception: MySQL requires every functional key part parenthesized, so each expression gains one pair there.

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 JSONB, JSON, or TEXT column according to the dialect.

func (*Table) Primary

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

Primary declares a composite primary key.

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.

func (*Table) Spatial added in v0.7.0

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

Spatial declares a MySQL SPATIAL index. Other dialects reject it.

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.

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.

func (*Table) TimestampTz

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

TimestampTz declares an instant, using the closest type each dialect offers.

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, or SMALLINT on PostgreSQL.

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 conventionally named unique index.

func (*Table) UniqueConstraint added in v0.12.0

func (t *Table) UniqueConstraint(name string, columns ...string)

UniqueConstraint declares a named table-level UNIQUE constraint — the form ON CONFLICT ON CONSTRAINT can reference, unlike Unique's index. SQLite supports it in Create only; alter via Schema.Recreate.

func (*Table) UniqueExpr added in v0.7.0

func (t *Table) UniqueExpr(name string, exprs ...string) *Index

UniqueExpr is the unique form of IndexExpr.

Jump to

Keyboard shortcuts

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