Help us shape the future of Go! Take the 2026 Go Developer Survey

migrate

package
v1.0.0-beta.1 Latest Latest
Warning

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

Go to latest
Published: Nov 2, 2026 License: MIT Imports: 22 Imported by: 0

Documentation

Overview

Package migrate runs SQL migrations for the Dygo sql module, on Postgres and MySQL, with no other dependency.

Migrations are pairs of files named by a version number and a name:

migrations/0001_create_users.up.sql
migrations/0001_create_users.down.sql

embedded in the binary with an embed.FS, so production needs nothing but the binary. dygo g migration writes the next pair. A dygo_migrations table records what ran, and a database lock (pg_advisory_lock on Postgres, GET_LOCK on MySQL) makes a second instance wait instead of migrating at the same time.

Each migration runs in a transaction together with its dygo_migrations row, so on Postgres a failing migration leaves nothing behind. MySQL commits every DDL statement on its own, so a migration that fails half way leaves the statements before the failure applied: keep MySQL migrations to one change each. A file whose first line is

-- dygo:no-transaction

runs outside a transaction, for statements that refuse one, such as CREATE INDEX CONCURRENTLY. An up file with

-- dygo:after-deploy

among its first comment lines is the contract half of an expand and contract change (dygo g migration -safe): Migrator.Up stops before it, and before every later migration, unless the Migrator has Contract (dygo db migrate --contract). Migrator.Held reports where it stopped.

Run migrations from the command line with Main (dygo db migrate, or ./app db:migrate in production), from code with Migrator, or at boot with OnBoot for a small single instance service.

Index

Constants

View Source
const AfterDeployMarker = "-- dygo:after-deploy"

AfterDeployMarker, among the comment lines at the top of an up file, holds the migration back until the code that no longer needs the old schema is deployed: Up stops before it unless the Migrator was made with Contract (dygo db migrate --contract). dygo g migration -safe writes it on the contract half of an expand and contract change.

View Source
const DefaultTable = "dygo_migrations"

DefaultTable records the applied migrations.

Variables

View Source
var Commands = []string{"db:create", "db:drop", "db:migrate", "db:rollback", "db:status", "db:schema:create", "db:schema:drop"}

Commands are the names Main handles.

Functions

func Main

func Main(ctx context.Context, d sql.Dialect, fsys fs.FS, args []string, opts ...MainOption) (code int, handled bool)

Main runs the database command named by args[0] (db:migrate, db:status, ...) and reports handled=false, doing nothing, for any other argument, so main can call it first:

if code, ok := migrate.Main(ctx, postgres.Dialect(), migrations.FS, os.Args[1:]); ok {
	os.Exit(code)
}

It runs before the application is created: it reads DATABASE_URL from the environment and .env files itself, and opens one connection, so it works on a database the app cannot boot against yet (db:create) and needs no other service the app depends on. dygo db migrate runs it through go run.

func OnBoot

func OnBoot(fsys fs.FS, opts ...Option) *dygo.Module

OnBoot returns a module that applies the pending migrations in fsys while the app boots, before it serves traffic. Use it for a small single instance service; with several instances, run db:migrate as a deploy step instead, so a failing migration stops the deploy rather than every instance. The lock still keeps two instances from migrating at once.

Imports: []*dygo.Module{postgres.Module(), migrate.OnBoot(migrations.FS)}

Only providers that inject Applied are sure to be built after the migrations ran; others that query the database in their constructor should inject it too.

func ServerURL

func ServerURL(kind, raw string) (server, name string, err error)

ServerURL returns the URL of the server without the database (Postgres: the postgres maintenance database) and the database name, so the database itself can be created or dropped. kind is "postgres" or "mysql".

func Split

func Split(src string, mysql bool) []string

Split cuts a migration file into statements at the semicolons outside strings, quoted identifiers and comments. Statements that hold only comments or white space are dropped. mysql selects MySQL lexing (backslash escapes in strings, # comments, backtick identifiers); otherwise Postgres lexing applies ($tag$ dollar quoted bodies, E'...' escapes).

MySQL's DELIMITER command belongs to the mysql client, not the server, so it is not supported: write a stored routine's body without semicolons, or run it from a -- dygo:no-transaction file with the multiStatements DSN option.

Types

type Applied

type Applied []Migration

Applied is what OnBoot provides: the migrations it applied at boot.

type MainOption

type MainOption func(*mainOptions)

MainOption configures Main.

func Files

func Files(paths ...string) MainOption

Files sets the .env files to read, earlier ones winning. The default is .env.local then .env, as in projects made by dygo new.

func Output

func Output(stdout, stderr io.Writer) MainOption

Output sends the command's output to stdout and its errors to stderr.

func Prefix

func Prefix(p string) MainOption

Prefix reads REPORTS_DATABASE_URL and friends with Prefix("REPORTS_"), as sql.Prefix does for the module.

func WithConfig

func WithConfig(cfg sql.Config) MainOption

WithConfig uses cfg instead of reading the environment.

func WithTable

func WithTable(name string) MainOption

WithTable records migrations in name instead of dygo_migrations.

type Migration

type Migration struct {
	Version int64
	Name    string
	Up      string
	Down    string
	HasDown bool
	// UpNoTx and DownNoTx are set by the -- dygo:no-transaction marker.
	UpNoTx, DownNoTx bool
	// AfterDeploy is set by AfterDeployMarker in the up file.
	AfterDeploy bool
}

Migration is one version: its up SQL and, when there is a down file, its down SQL.

func Load

func Load(fsys fs.FS) ([]Migration, error)

Load reads the migrations in the root of fsys, sorted by version. Files that are not NNNN_name.up.sql or NNNN_name.down.sql are ignored. Two migrations with the same version, or a down file without an up file, are errors.

func (Migration) String

func (m Migration) String() string

String is the file name without its direction: 0001_create_users.

type Migrator

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

Migrator applies and rolls back the migrations of one database.

func New

func New(db *sql.DB, fsys fs.FS, opts ...Option) (*Migrator, error)

New loads the migrations in fsys for db. It does not touch the database.

func (*Migrator) Down

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

Down rolls back the newest steps applied migrations, newest first.

func (*Migrator) DownTo

func (m *Migrator) DownTo(ctx context.Context, version int64) ([]Migration, error)

DownTo rolls back every applied migration newer than version, newest first. DownTo(ctx, 0) rolls back everything.

func (*Migrator) Held

func (m *Migrator) Held() (Migration, bool)

Held returns the after-deploy migration that the last Pending or Up stopped before, and false when it stopped at none (see Contract).

func (*Migrator) Migrations

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

Migrations returns the loaded migrations, oldest first.

func (*Migrator) Pending

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

Pending returns the migrations Up would apply, up to version to (0 for all), without applying them.

func (*Migrator) Status

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

Status lists every migration, from the files and from the database, oldest first.

func (*Migrator) Up

func (m *Migrator) Up(ctx context.Context, to int64) ([]Migration, error)

Up applies the pending migrations, oldest first, up to and including version to (0 for all). It returns the migrations it applied, also when a later one failed.

type Option

type Option func(*Migrator)

Option configures a Migrator.

func Contract

func Contract() Option

Contract lets Up apply migrations marked with AfterDeployMarker. Without it, Up stops before the first of them; Held reports which one.

func Log

func Log(w io.Writer) Option

Log writes one line per migration run to w.

func OutOfOrder

func OutOfOrder() Option

OutOfOrder lets Up apply a pending migration older than the newest applied one, which happens when two branches each add a migration. Without it, Up refuses, so the order the database saw stays the order of the files.

func Table

func Table(name string) Option

Table records the applied migrations in name instead of dygo_migrations.

type State

type State struct {
	Version   int64
	Name      string
	Applied   bool
	AppliedAt time.Time
	// Missing means the database recorded the version but no file has it.
	Missing bool
	// AfterDeploy marks a migration that waits for Contract (see
	// AfterDeployMarker).
	AfterDeploy bool
}

State is one migration's status.

Jump to

Keyboard shortcuts

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