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

xmigrator

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Sep 30, 2026 License: MIT Imports: 19 Imported by: 2

README

xmigrator

A SQL migration library for Go 1.27+ with PostgreSQL (pgx v5) and SQLite support. The core has no external dependencies; database drivers, CLI integrations, and the standalone command are separate Go modules.

  • Apply or roll back migrations with a step limit.
  • Apply newly added migrations even when their timestamp precedes already applied migrations.
  • Run each migration and its typed Go hooks in one transaction, with locking across the entire run.
  • Create migration files, inspect status, validate sources, drop application objects, and repair down metadata.
  • Configure checksum verification and plug in your own logger.

Packages

Module Purpose
github.com/sxwebdev/xmigrator Migration runner, sources, hooks, and logging
github.com/sxwebdev/xmigrator/driver/pgx PostgreSQL driver using native pgx v5
github.com/sxwebdev/xmigrator/driver/sqlite SQLite driver using your existing *sql.DB; your application chooses the SQLite engine
github.com/sxwebdev/xmigrator/cli/urfavecli Ready-to-use urfave/cli v3 commands for your application
github.com/sxwebdev/xmigrator/cmd/xmigrator Standalone CLI with PostgreSQL and SQLite engines included

Library usage

Load migrations from any fs.FS, including embedded files, and use your application's connection pool:

package database

import (
    "context"
    "embed"

    "github.com/jackc/pgx/v5/pgxpool"
    "github.com/sxwebdev/xmigrator"
    pgdriver "github.com/sxwebdev/xmigrator/driver/pgx"
)

//go:embed migrations/*.sql
var files embed.FS

func Migrate(ctx context.Context, pool *pgxpool.Pool) error {
    source, err := xmigrator.NewSource(files, "migrations")
    if err != nil {
        return err
    }
    driver, err := pgdriver.FromPool(pool, pgdriver.Config{})
    if err != nil {
        return err
    }
    runner, err := xmigrator.New(source, driver)
    if err != nil {
        return err
    }
    _, err = runner.Up(ctx, 0) // Apply all pending migrations.
    return err
}

For SQLite, create the driver with sqlite.FromDB(db, sqlite.Config{}) from github.com/sxwebdev/xmigrator/driver/sqlite and pass it to the same xmigrator.New constructor. Importing this driver does not bring a SQLite engine into your application.

_, err = runner.Up(ctx, 2)   // Apply up to two pending migrations.
_, err = runner.Down(ctx, 1) // Roll back the last applied migration.
_, err = runner.DownAll(ctx) // Roll back all applied migrations.
status, err := runner.Status(ctx)

Checksum verification defaults to strict and can be changed through WithChecksumPolicy[T]. Register transaction hooks with WithHooks[T] and a logger with WithLogger[T]; NewSlogLogger adapts the standard library logger.

See the usage guide for migration files, hooks, configuration, and embedding commands into an urfave/cli v3 application.

Standalone CLI

Install the standalone command:

go install github.com/sxwebdev/xmigrator/cmd/xmigrator@latest

To build from this checkout:

go run tools/dev.go workspace
make build

Create migrations, edit the generated SQL files, then apply them. For a local build, use ./build/xmigrator in place of xmigrator:

mkdir -p migrations
xmigrator create --path ./migrations --name create_users
xmigrator validate --driver sqlite --path ./migrations
xmigrator up --driver sqlite --dsn ./app.db --path ./migrations
xmigrator down --driver sqlite --dsn ./app.db --path ./migrations --steps 1

For PostgreSQL, use --driver pgx and a PostgreSQL connection string through --dsn or XMIGRATOR_DSN. Run xmigrator --help or xmigrator <command> --help for all options. Commands: up, down, drop, create, status, validate, and repair-down.

Roadmap

  • MariaDB driver.
  • ClickHouse driver.
  • Additional CLI framework adapters.

See the documentation for drivers, migration files, hooks, CLI integration, and operations.

Release archives are available on GitHub Releases. See the release guide for maintainers.

Changes are tracked in the changelog. Licensed under MIT.

Documentation

Overview

Package xmigrator executes ordered, transactional SQL migrations using separately installed drivers.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrInvalidConfig        = errors.New("xmigrator: invalid configuration")
	ErrInvalidSource        = errors.New("xmigrator: invalid source")
	ErrInvalidSteps         = errors.New("xmigrator: invalid steps")
	ErrVersionExists        = errors.New("xmigrator: version exists")
	ErrUnknownApplied       = errors.New("xmigrator: unknown applied migration")
	ErrChecksumMismatch     = errors.New("xmigrator: checksum mismatch")
	ErrDefinitionMismatch   = errors.New("xmigrator: definition mismatch")
	ErrHookMismatch         = errors.New("xmigrator: hook mismatch")
	ErrIrreversible         = errors.New("xmigrator: irreversible migration")
	ErrMetadataConflict     = errors.New("xmigrator: metadata conflict")
	ErrMetadataVersion      = errors.New("xmigrator: unsupported metadata version")
	ErrHistoryConflict      = errors.New("xmigrator: history precondition failed")
	ErrNotApplied           = errors.New("xmigrator: migration not applied")
	ErrUnsafeSQL            = errors.New("xmigrator: unsafe SQL")
	ErrCommitOutcomeUnknown = errors.New("xmigrator: commit outcome unknown")
	ErrDropScope            = errors.New("xmigrator: invalid drop scope")
	ErrForeignKey           = errors.New("xmigrator: foreign key violation")
)

Functions

func ValidateSQL

func ValidateSQL(s Script, dialect string) error

ValidateSQL enforces driver transaction ownership using dialect-aware statement boundaries.

Types

type Action

type Action struct {
	Version    Version
	Name       string
	Direction  Direction
	OutOfOrder bool
}

type ChecksumPolicy

type ChecksumPolicy uint8
const (
	ChecksumStrict ChecksumPolicy = iota
	ChecksumWarn
	ChecksumDisabled
)

type CreateOptions

type CreateOptions struct {
	Dir, Name string
	Version   Version
	Now       func() time.Time
}

type CreatedFiles

type CreatedFiles struct {
	Version  Version
	Up, Down string
}

func Create

Create writes an exclusive up/down pair. A process crash may leave an incomplete pair; validation rejects it. Only files created by this call are removed on failure.

type Direction

type Direction string
const (
	Up   Direction = "up"
	Down Direction = "down"
)

type DownDefinition

type DownDefinition struct {
	Checksum     [32]byte
	Kind         DownKind
	HookRevision string
}

type DownKind

type DownKind string
const (
	DownSQL          DownKind = "sql"
	DownNoop         DownKind = "noop"
	DownIrreversible DownKind = "irreversible"
)

type Driver

type Driver[T any] interface {
	WithSession(context.Context, func(Session[T]) error) error
	ReadHistorySnapshot(context.Context) ([]Record, error)
	ValidateScript(Script) error
}

Driver owns connection lifetimes and a lock covering the complete callback.

type Hooks

type Hooks[T any] struct {
	UpRevision, DownRevision                 string
	BeforeUp, AfterUp, BeforeDown, AfterDown func(context.Context, T) error
}

type Issue

type Issue struct {
	Version   Version
	Kind      string
	Direction Direction
}

type Logger

type Logger interface {
	Debugw(string, ...any)
	Infow(string, ...any)
	Warnw(string, ...any)
	Errorw(string, ...any)
}

func NewSlogLogger

func NewSlogLogger(l *slog.Logger) Logger

type Migration

type Migration struct {
	Version  Version
	Name     string
	Up, Down Script
}

type MigrationError

type MigrationError struct {
	Version   Version
	Name      string
	Direction Direction
	Stage     string
	Err       error
}

MigrationError identifies a failed migration while preserving its original cause.

func (*MigrationError) Error

func (e *MigrationError) Error() string

func (*MigrationError) Unwrap

func (e *MigrationError) Unwrap() error

type MigrationStatus

type MigrationStatus struct {
	Version       Version
	Name          string
	State         string
	ApplyOrder    int64
	OutOfOrder    bool
	DownKind      DownKind
	ChecksumState string
}

type Migrator

type Migrator[T any] struct {
	// contains filtered or unexported fields
}

func New

func New[T any](s Source, d Driver[T], opts ...Option[T]) (*Migrator[T], error)

func (*Migrator[T]) Down

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

func (*Migrator[T]) DownAll

func (m *Migrator[T]) DownAll(ctx context.Context) (Result, error)

func (*Migrator[T]) Drop

func (m *Migrator[T]) Drop(ctx context.Context) error

func (*Migrator[T]) PlanRepairDown

func (m *Migrator[T]) PlanRepairDown(ctx context.Context, v Version) (RepairPlan, error)

func (*Migrator[T]) RepairDown

func (m *Migrator[T]) RepairDown(ctx context.Context, v Version) (out RepairResult, err error)

func (*Migrator[T]) Status

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

func (*Migrator[T]) Up

func (m *Migrator[T]) Up(ctx context.Context, steps int) (Result, error)

type Option

type Option[T any] func(*Migrator[T]) error

func WithChecksumPolicy

func WithChecksumPolicy[T any](p ChecksumPolicy) Option[T]

func WithHooks

func WithHooks[T any](hooks map[Version]Hooks[T]) Option[T]

func WithLogger

func WithLogger[T any](l Logger) Option[T]

func WithUnknownAppliedPolicy

func WithUnknownAppliedPolicy[T any](p UnknownAppliedPolicy) Option[T]

type Record

type Record struct {
	Version          Version
	Name             string
	UpChecksum       [32]byte
	DownChecksum     [32]byte
	DownKind         DownKind
	UpHookRevision   string
	DownHookRevision string
	AppliedAt        time.Time
	ApplyOrder       int64
}

func (Record) DownDefinition

func (r Record) DownDefinition() DownDefinition

type RepairPlan

type RepairPlan struct {
	Version       Version
	WouldChange   bool
	Before, After DownDefinition
	Issues        []Issue
}

type RepairResult

type RepairResult struct {
	Version            Version
	Confirmed, Changed bool
	Before, After      DownDefinition
	Issues             []Issue
}

type Result

type Result struct {
	Direction Direction
	Actions   []Action
	Issues    []Issue
}

type Script

type Script struct {
	SQL          string
	Checksum     [32]byte
	Kind         DownKind
	HookRevision string
	ForeignKeys  *bool
}

Script is a normalized snapshot. SQL is executed verbatim, never split at semicolons.

func (Script) DownDefinition

func (s Script) DownDefinition() DownDefinition

type Session

type Session[T any] interface {
	EnsureMetadata(context.Context) error
	ReadExistingHistory(context.Context) ([]Record, error)
	ReadHistory(context.Context) ([]Record, error)
	InTx(context.Context, Script, func(Transaction[T]) error) (TxOutcome, error)
	Drop(context.Context) error
}

type Source

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

func NewSource

func NewSource(files fs.FS, dir string, options ...SourceOption) (Source, error)
Example
package main

import (
	"testing/fstest"

	x "github.com/sxwebdev/xmigrator"
)

func main() {
	s, _ := x.NewSource(fstest.MapFS{"1_users.up.sql": {Data: []byte("CREATE TABLE users(id INTEGER);")}, "1_users.down.sql": {Data: []byte("DROP TABLE users;")}}, ".")
	_ = s
}

func (Source) Dialect

func (s Source) Dialect() string

Dialect returns the explicitly configured dialect, or an empty string when unspecified.

func (Source) Snapshot

func (s Source) Snapshot(ctx context.Context) ([]Migration, error)

func (Source) WithDialect

func (s Source) WithDialect(dialect string) (Source, error)

WithDialect returns a copy with the selected SQL dialect.

type SourceError

type SourceError struct {
	File    string
	Version Version
	Err     error
}

SourceError identifies the migration file that failed validation.

func (*SourceError) Error

func (e *SourceError) Error() string

func (*SourceError) Unwrap

func (e *SourceError) Unwrap() error

type SourceOption

type SourceOption func(*Source) error

func WithDialect

func WithDialect(dialect string) SourceOption

WithDialect explicitly selects lexical rules for source parsing. An unspecified source dialect falls back to pgx in Snapshot and Validate; built-in runners infer the driver dialect, while the CLI adapter requires an explicit dialect for validation.

type Status

type Status struct {
	Migrations []MigrationStatus
	Issues     []Issue
}

type Transaction

type Transaction[T any] interface {
	Executor() T
	ExecScript(context.Context, Script) error
	ReadHistory(context.Context) ([]Record, error)
	Insert(context.Context, Record) error
	Delete(context.Context, Version) error
	UpdateDownMetadata(context.Context, Version, DownDefinition) error
}

type TxOutcome

type TxOutcome uint8
const (
	TxNotCommitted TxOutcome = iota
	TxCommitted
	TxUnknown
)

type UnknownAppliedPolicy

type UnknownAppliedPolicy uint8
const (
	UnknownAppliedAllow UnknownAppliedPolicy = iota
	UnknownAppliedError
)

type ValidationReport

type ValidationReport struct{ Migrations int }

func Validate

func Validate(ctx context.Context, s Source) (ValidationReport, error)

type Version

type Version int64

Directories

Path Synopsis
cli
urfavecli module
driver
pgx module
internal
contracttest
Package contracttest exercises observable migration behavior against every backend.
Package contracttest exercises observable migration behavior against every backend.
metadata
Package metadata defines the shared, versioned history contract.
Package metadata defines the shared, versioned history contract.
sqltext
Package sqltext scans SQL without altering statement boundaries or quoted bodies.
Package sqltext scans SQL without altering statement boundaries or quoted bodies.

Jump to

Keyboard shortcuts

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