migration

package
v0.4.20 Latest Latest
Warning

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

Go to latest
Published: Sep 17, 2026 License: Apache-2.0 Imports: 9 Imported by: 0

Documentation

Overview

Package migration reads the module's sql/ directory and applies up/down migrations as a stateless executor.

The SDK does NOT maintain a tracking table and does NOT know which migrations were previously applied. The platform's control plane owns that state and passes explicit {from, to} ranges to the lifecycle handlers; this package just reads the requested slice of SQL files and runs them inside a per-migration transaction.

Migrations live under sql/app/ and, optionally, sql/module/. Module developers embed both via:

//go:embed sql/*
var sqlFiles embed.FS

and pass sqlFiles to ms.Config.SQL.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Apply

func Apply(ctx context.Context, runTx TxRunner, fsys fs.FS, migrations []Migration) (applied []string, err error)

Apply runs the given migrations in order, each inside its own transaction via runTx. Returns the versions actually applied.

The SDK is a stateless executor: it does NOT maintain a tracking table and does NOT skip already-applied migrations. The platform owns the "which versions are installed where" state in its control plane and decides the exact {from, to} range to pass through the lifecycle endpoint. If the platform asks to re-run a migration that was already applied, Postgres will surface the duplicate error (e.g., "relation already exists") — that's a platform bug, not an SDK bug.

On the first failure, returns the partial applied list plus the error so the platform can update its own state with what actually ran.

Known limitation: each migration's SQL runs inside a transaction. Postgres statements that cannot run inside a transaction block — CREATE INDEX CONCURRENTLY, VACUUM, CLUSTER, REINDEX CONCURRENTLY, ALTER SYSTEM — will fail with an opaque error. Split such operations into a separate migration or document the limitation in the module's README.

func ApplyDown

func ApplyDown(ctx context.Context, runTx TxRunner, fsys fs.FS, migrations []Migration) (reverted []string, err error)

ApplyDown runs the .down.sql for each migration in the order given (callers reverse the slice for downgrade). Each runs in its own transaction.

Pre-validates that every migration has a down file BEFORE running any SQL — half-applied downgrades are worse than failing fast on a missing file.

Like Apply, this is stateless: the caller is responsible for passing the right slice of migrations to revert. The SDK does not consult any tracking table to verify the versions were previously applied. If the platform asks to revert a migration that never ran, the down SQL will fail with a Postgres error.

func LatestVersion

func LatestVersion(fsys fs.FS, scope Scope) (string, error)

LatestVersion returns the highest migration version string in the sql/{scope}/ directory of fsys, or "" when the directory has no .up.sql files. See List for the missing-directory and numeric-comparison contracts.

Types

type Migration

type Migration struct {
	Version  string // numeric prefix from filename, e.g., "0008"
	Name     string // human-readable slug, e.g., "add_index"
	UpFile   string // path within fsys, e.g., "sql/app/0008_add_index.up.sql"
	DownFile string // path within fsys, "" if no down file present
}

Migration represents one migration on disk: an up-file with a numeric version prefix, an optional matching down-file, and the human-readable slug.

func List

func List(fsys fs.FS, scope Scope) ([]Migration, error)

List returns all migrations from sql/{scope}/, sorted by numeric version (ascending). A migration is identified by its .up.sql file; the matching .down.sql is recorded if present (empty otherwise — downgrades will fail for that version). Versions are compared numerically so a module that mixes widths ("9", "10") resolves in the right order.

Returns an empty slice (not an error) if fsys is nil or the scope's directory does not exist — a module that uses only one scope is the common case.

func Slice

func Slice(migrations []Migration, fromVersion, toVersion string) ([]Migration, error)

Slice returns the subset of migrations strictly after fromVersion and up to (and including) toVersion, sorted ascending by version. Used by Apply for upgrades. Returns an empty slice if there's no work to do (toVersion <= fromVersion or both unknown).

fromVersion may be "" (treated as "before all migrations") so install can share this function — Slice("", "0008") returns everything up to 0008.

An unknown toVersion that does not appear in migrations returns an error because the caller asked to upgrade to a version this module doesn't have.

func SliceDown

func SliceDown(migrations []Migration, fromVersion, toVersion string) ([]Migration, error)

SliceDown returns migrations to revert when going from fromVersion down to toVersion (where toVersion < fromVersion), sorted descending so each is applied newest-first. Both versions must be real migration numbers — unlike Slice, there is no "" special case for "before everything" because a downgrade always has a known current version (the caller is reversing something that was installed).

type Scope

type Scope string

Scope is an independent migration track with its own version sequence. Distinct from internal/registry.Scope (which is an auth boundary, not a migration directory).

const (
	// ScopeApp reads sql/app/.
	ScopeApp Scope = "app"
	// ScopeModule reads sql/module/.
	ScopeModule Scope = "module"
)

func AllScopes

func AllScopes() []Scope

AllScopes returns the canonical ordering of migration scopes. Callers that iterate every scope (manifest reporting, lifecycle endpoint mounting, etc.) should use this so a future scope addition is picked up automatically.

func (Scope) Dir

func (s Scope) Dir() string

Dir is the directory under the embedded fs.FS that this scope reads from.

type TxRunner

type TxRunner func(ctx context.Context, fn func(q db.Querier) error) error

TxRunner runs fn inside a database transaction. Matches Module.Tx signature so handlers can pass it directly without leaking pgxpool out of the system package.

Jump to

Keyboard shortcuts

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