gostgrator

package module
v2.0.2 Latest Latest
Warning

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

Go to latest
Published: Oct 8, 2026 License: MIT Imports: 15 Imported by: 0

README

gostgrator

Actions Status SocketDev PkgGoDev

gostgrator: A low dependency, go stdlib port of postgrator supporting postgres and sqlite.

Install with Homebrew

Install both database-specific commands from the bcomnes/tap tap:

brew install bcomnes/tap/gostgrator
gostgrator-pg -help
gostgrator-sqlite -help

This adds the tap automatically. Update both commands later with brew upgrade gostgrator.

Migrations

Migrations can live in any folder in your project. The default is ./migrations. Migration files are named 001.do.some-optional-description.sql and 001.undo.some-optional-description.sql and come in up and down pairs. The files should contain SQL appropriate for the database you are running them

./migrations
├── 001.do.sql
├── 001.undo.sql
├── 002.do.some-description.sql
├── 002.undo.some-description.sql
├── 003.do.sql
├── 003.undo.sql
├── 004.do.sql
├── 004.undo.sql
├── 005.do.sql
├── 005.undo.sql
├── 006.do.sql
└── 006.undo.sql
Migration Transactions

gostgrator (like postgrator), applies no special or magic transaction around your migrations, other than running multiple statements from a file in one execution which postgres will treat as a transaction. If you need stricter behavior than this, or are migrating databases that don't have this behavior, wrap your migrations in explicite BEGIN/END blocks.

gostgrator CLI

gostgrator is intended to be installed and versioned as a go tool.

Each supported database has it's own CLI you can install.

gostgrator/pg

The gostgrator/pg cli provides migration support for Postgres.

go get -tool github.com/bcomnes/gostgrator/v2/pg
go tool github.com/bcomnes/gostgrator/v2/pg -help
Usage:
  gostgrator-pg [command] [arguments] [options]

Commands:
  migrate [target]    Migrate the schema to a target version (default: "max").
  down [steps]        Roll back the specified number of migrations (default: 1).
  new <desc>          Create a new empty migration pair with the provided description.
  drop-schema         Drop the schema version table.
  list                List available migrations and annotate the migration matching the database version.

Options:
  -config string
    	Path to JSON configuration file (optional)
  -conn string
    	PostgreSQL connection URL. Can be set with DATABASE_URL env var.
  -help
    	Show help message
  -migration-pattern string
    	Glob pattern for migration files when running up or down migrations (default "migrations/*.sql")
  -mode string
    	Migration numbering mode ("int" or "timestamp") when creating new migrations (default "int")
  -schema-table string
    	Name of the schema table migration state is stored in (default "schemaversion")
  -version
    	Show version
gostgrator/sqlite
go get -tool github.com/bcomnes/gostgrator/v2/sqlite
go tool github.com/bcomnes/gostgrator/v2/sqlite -help
Usage:
  gostgrator-sqlite [command] [arguments] [options]

Commands:
  migrate [target]    Migrate the schema to a target version (default: "max").
  down [steps]        Roll back the specified number of migrations (default: 1).
  new <desc>          Create a new empty migration pair with the provided description.
  drop-schema         Drop the schema version table.
  list                List available migrations and annotate the migration matching the database version.

Options:
  -config string
    	Path to JSON configuration file (optional)
  -conn string
    	SQLite connection URL (typically a file path, e.g., "./db.sqlite"). Can also be set via SQLITE_URL env var.
  -help
    	Show help message
  -migration-pattern string
    	Glob pattern for migration files (default "migrations/*.sql")
  -mode string
    	Migration numbering mode ("int" or "timestamp") for new command (default "int")
  -schema-table string
    	Name of the schema table (default "schemaversion")
  -version
    	Show version

Quick tour

# migrate to latest in ./migrations using DATABASE_URL
go tool github.com/bcomnes/gostgrator/v2/pg migrate

# rollback the last two migrations
go tool github.com/bcomnes/gostgrator/v2/pg down 2

# create a timestamp‑based pair
go tool github.com/bcomnes/gostgrator/v2/pg -mode timestamp new "add-users-table"

# list all migrations and mark current
gostgrator-pg list

Library usage

Full API docs live on PkgGoDev.

Database connections and SQL dialects

Use your existing *sql.DB with the matching client:

Database Client constructor Example driver name passed to sql.Open
PostgreSQL NewPostgresClient "pgx"
SQLite NewSqlite3Client "sqlite" with modernc SQLite
client := gostgrator.NewPostgresClient(gostgrator.Config{
    SchemaTable: "app_migrations", // Optional; defaults to "schemaversion".
}, db)

g, err := gostgrator.NewGostgrator(gostgrator.Config{
    Migrations: gostgrator.DiskMigrations{Pattern: "migrations/*.sql"},
}, client)
if err != nil {
    return err
}
_, err = g.Migrate(ctx, "max")

The example assumes a PostgreSQL db and a context.Context named ctx. Your application owns and closes the database connection.

The client chooses the bookkeeping dialect; migration SQL runs unchanged. Set SchemaTable on the client and Migrations on the migrator.

Choose where migrations are loaded from

Set Config.Migrations to either:

  • DiskMigrations{Pattern: "migrations/*.sql"} for SQL files deployed alongside your application.
  • FSMigrations{FS: migrations, Pattern: "migrations/*.sql"} for a supplied Go filesystem, including embedded migrations.

The pattern selects files; their names determine version and action:

migrations/
├── 001.do.create-users.sql
├── 001.undo.create-users.sql
├── 002.do.add-email.sql
└── 002.undo.add-email.sql

Migrate(ctx, "max") applies pending migrations up to the highest available version. Use Migrate(ctx, "0") to undo all migrations or Down(ctx, 1) to roll back one version step. For disk migrations, relative paths are resolved from the application's working directory; absolute paths are also supported.

Filesystem paths and configuration

FSMigrations accepts an fs.FS, Go's standard read-only filesystem interface. Implementations include embed.FS for files compiled into your binary, os.DirFS for a directory on disk, and fstest.MapFS for in-memory test files.

Unlike disk paths, FSMigrations.Pattern is relative to the supplied filesystem's root and uses forward slashes, with no leading slash or . or .. path components. Use migrations/*.sql, not ./migrations/*.sql. To make the migrations directory itself the root, use fs.Sub and then select files with *.sql.

Patterns follow filepath.Glob for DiskMigrations and fs.Glob for FSMigrations. Neither treats ** as a recursive wildcard. Both options require a nonempty pattern, and FSMigrations also requires a nonnil filesystem. Config.Migrations accepts either an option value or a nonnil pointer to one; its interface type is MigrationSource.

Both sources use the same naming, ordering, duplicate detection, and checksum validation. Config.Newline normalizes checksums without changing the SQL executed. Migrations returned by GetMigrations retain their source for RunMigrations, even with a different runner; manually constructed migrations read Filename from disk.

Embed migrations in a binary

Keep the SQL files beside the Go file that embeds them:

myapp/
├── go.mod
├── cmd/
│   └── myapp/
│       └── main.go
└── database/
    ├── migrations.go
    └── migrations/
        ├── 001.do.create-users.sql
        ├── 001.undo.create-users.sql
        ├── 002.do.add-email.sql
        └── 002.undo.add-email.sql

database/migrations.go — embed paths are relative to this file's directory:

package database

import (
    "context"
    "database/sql"
    "embed"

    "github.com/bcomnes/gostgrator/v2"
    _ "modernc.org/sqlite"
)

//go:embed migrations
var migrations embed.FS

func Migrate(ctx context.Context, db *sql.DB) error {
    client := gostgrator.NewSqlite3Client(gostgrator.Config{}, db)
    g, err := gostgrator.NewGostgrator(gostgrator.Config{
        Migrations: gostgrator.FSMigrations{
            FS:      migrations,
            Pattern: "migrations/*.sql",
        },
        ValidateChecksums: true,
    }, client)
    if err != nil {
        return err
    }
    _, err = g.Migrate(ctx, "max")
    return err
}

Pass a SQLite *sql.DB opened with sql.Open("sqlite", ...). Deploy the binary without the SQL directory; rebuild it when migrations change.

//go:embed migrations includes eligible files recursively; migrations/*.sql selects only the directory's immediate SQL files. See Go's embed documentation for path restrictions and including dotfiles with all:.

Create migration files

Create an empty migrations/ directory, then generate a do/undo pair:

err := gostgrator.CreateMigration(gostgrator.Config{
    Migrations: gostgrator.DiskMigrations{Pattern: "migrations/*.sql"},
}, "Add users", "int")
migrations/
├── 001.do.add-users.sql
└── 001.undo.add-users.sql

Subsequent calls increment the version; use "timestamp" for Unix timestamp numbering. Use DiskMigrations for file creation; FSMigrations is read-only. For embedded migrations, edit these files and rebuild.

Advanced: custom clients and dialects

Use a custom Client only when you need an escape hatch for another dialect or custom database behavior. Switching to a compatible PostgreSQL or SQLite driver does not require one.

This wrapper adds execution logging to the built-in SQLite client:

package database

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

    "github.com/bcomnes/gostgrator/v2"
)

type loggingClient struct {
    gostgrator.Client
    logger *log.Logger
}

var _ gostgrator.Client = (*loggingClient)(nil)

func (c *loggingClient) ExecContext(ctx context.Context, script string, args ...any) (sql.Result, error) {
    result, err := c.Client.ExecContext(ctx, script, args...)
    c.logger.Printf("SQL execution succeeded: %t", err == nil)
    return result, err
}

func Migrate(ctx context.Context, db *sql.DB, logger *log.Logger) error {
    client := &loggingClient{
        Client: gostgrator.NewSqlite3Client(gostgrator.Config{
            SchemaTable: "app_migrations",
        }, db),
        logger: logger,
    }
    g, err := gostgrator.NewGostgrator(gostgrator.Config{
        Migrations: gostgrator.DiskMigrations{
            Pattern: "migrations/*.sql",
        },
    }, client)
    if err != nil {
        return err
    }
    _, err = g.Migrate(ctx, "max")
    return err
}

Pass a SQLite *sql.DB and a nonnil logger such as log.Default(). The wrapper logs migration and bookkeeping executions, not SQL text, queries, or calls made internally by the wrapped client's EnsureTable.

For a new dialect, implement all Client methods: SQL execution, tracking-table management, version/checksum queries, and action persistence. The interface uses *sql.Rows and sql.Result, so implementations remain tied to database/sql. See the complete client example for an implementation without wrapping a built-in client.

Bookkeeping SQL and identifiers

SQL builders return a Statement containing SQL and bound values:

return gostgrator.Statement{
    SQL:  `INSERT INTO "app_migrations" (version, name, md5) VALUES (?, ?, ?)`,
    Args: []any{m.Version, m.Name, m.Md5},
}

Use placeholders for values: ? for SQLite, or $1, $2, and so on for PostgreSQL. Forward args... from QueryContext and ExecContext to the database driver.

Parameters cannot substitute table names, schema names, or SQL syntax. Quote dynamic identifiers using the target dialect's rules instead of putting them in Args. The PostgreSQL client quotes each component of SchemaTable (table or schema.table); the SQLite client quotes the whole name, treating dots literally. Pass unquoted names to these constructors; they escape embedded double quotes. Migration scripts remain trusted executable SQL and run without bound arguments.

Configure and manage the client's resources yourself; NewGostgrator does not override them with Config.Driver, Conn, or SchemaTable. Nil clients, including typed nil implementations, are rejected.


Why another migrator?

  • CLI ‑ first – instant productivity; no boilerplate code required.
  • Typed Go API – run migrations directly from your Go application.
  • Embedded migrations – ship SQL files inside your binary with go:embed; no runtime migrations directory needed.
  • Custom adapter clients – implement the Client interface to support another SQL dialect or wrap database operations.
  • Checksum validation – MD5 guardrails ensure applied migrations never drift.
  • Up ⬆ / Down ⬇ parity – every migration pair keeps rollbacks honest.
  • Zero dependencies – a single static binary per database driver.

License

MIT © Bret Comnes 2025

Mascot

gostgrator migratory gopher

Documentation

Overview

Package gostgrator provides database-agnostic schema migration utilities for Go applications. It loads *.sql* migration files, tracks execution state in a table you choose, and moves the database forward or backward to any version you specify.

A thin driver layer (currently PostgreSQL and SQLite) supplies SQL dialect differences. Companion CLI tools live under sub-packages *pg* and *sqlite*; the core logic is here.

Install

go get github.com/bcomnes/gostgrator/v2@latest

Quick start

import (
    "context"
    "database/sql"
    "os"

    _ "github.com/jackc/pgx/v5/stdlib" // or modernc.org/sqlite
    "github.com/bcomnes/gostgrator/v2"
)

func main() {
    db, _ := sql.Open("pgx", os.Getenv("DATABASE_URL"))
    client := gostgrator.NewPostgresClient(gostgrator.Config{}, db)
    cfg := gostgrator.Config{
        Migrations: gostgrator.DiskMigrations{Pattern: "migrations/*.sql"},
    }

    g, _ := gostgrator.NewGostgrator(cfg, client)
    g.Migrate(context.Background(), "max")
}

Configuration

Use Config to tweak behaviour:

  • SchemaTable — pass to the client constructor (default "schemaversion")
  • Migrations — DiskMigrations or FSMigrations for locating migration files
  • Newline — line-ending normalization for migration checksums
  • ValidateChecksums — compare MD5 hashes before running *up* migrations

NewGostgrator accepts a Client. NewPostgresClient and NewSqlite3Client select their dialect and apply client defaults. NewClient offers configuration-driven selection using Config.Driver.

Migration files

A migration *pair* is two files with the same version and name:

001.do.create_users.sql   // apply
001.undo.create_users.sql // roll back

Versions may be plain integers (*001*, *002*, …) or timestamps if you prefer. The CLI’s *new* command scaffolds these files for you.

Programmatic API

NewGostgrator(cfg, client)   → *Gostgrator, error
(*Gostgrator).Migrate(ctx, v) → []Migration, error
(*Gostgrator).Down(ctx, n)    → []Migration, error
(*Gostgrator).GetMigrations() → []Migration, error
(*Gostgrator).GetDatabaseVersion(ctx) → int, error

All operations are context-aware; cancel the context to abort long runs.

CLI helpers

If you prefer shell commands, install driver-specific binaries:

go get -tool github.com/bcomnes/gostgrator/v2/pg@latest      # PostgreSQL
go get -tool github.com/bcomnes/gostgrator/v2/sqlite@latest  # SQLite

See each sub-package’s doc for flags and usage.

Exit codes

The library returns errors; the CLIs exit with non-zero status on any failure. ValidateErrors include version and MD5 info for easy triage.

Versioning

A semantic version string is exposed as:

var Version = "vX.Y.Z"

Embed it in your own commands to surface gostgrator’s build version.

Generated documentation; update whenever public API or CLI flags change.

Index

Examples

Constants

This section is empty.

Variables

View Source
var DefaultConfig = Config{
	SchemaTable:       "schemaversion",
	ValidateChecksums: true,
}

DefaultConfig provides default values for configuration.

View Source
var (
	Version = "2.0.2"
)

Functions

func CreateMigration

func CreateMigration(cfg Config, description string, mode string) error

CreateMigration creates a new pair of migration files (do/undo) on disk. It accepts DiskMigrations or legacy MigrationPattern and rejects FSMigrations. description: a human-readable description that will be kebab-cased for the filename. mode: "int" for integer increment (default) or "timestamp" to use the Unix timestamp.

Types

type Client

type Client interface {
	QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)
	ExecContext(ctx context.Context, script string, args ...any) (sql.Result, error)
	GetDatabaseVersionSql() Statement
	HasVersionTable(ctx context.Context) (bool, error)
	EnsureTable(ctx context.Context) error
	GetMd5Sql(m Migration) Statement
	PersistActionSql(m Migration) Statement
}

Client defines SQL execution and database-specific migration bookkeeping. Implementations can be supplied to NewGostgrator to support custom dialects or wrap an existing client. The client is responsible for its tracking table configuration; Gostgrator does not configure or close an injected client. SQL builders return statements with bound bookkeeping values. Execution methods must forward args to the driver; migration scripts are executed without args.

Example

This example implements every Client method directly against database/sql. A new dialect supplies its own tracking-table DDL and bookkeeping queries.

package main

import (
	"context"
	"database/sql"
	"fmt"
	"testing/fstest"

	"github.com/bcomnes/gostgrator/v2"
	_ "modernc.org/sqlite"
)

// sqliteClient implements its own bookkeeping rather than wrapping a built-in client.
// Identifiers are fixed, quoted SQL; all variable values are bound parameters.
// It owns a fixed tracking table; it does not upgrade legacy tracking schemas.
type sqliteClient struct {
	db *sql.DB
}

var _ gostgrator.Client = (*sqliteClient)(nil)

func (c *sqliteClient) QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error) {
	return c.db.QueryContext(ctx, query, args...)
}

func (c *sqliteClient) ExecContext(ctx context.Context, script string, args ...any) (sql.Result, error) {
	return c.db.ExecContext(ctx, script, args...)
}

func (c *sqliteClient) GetDatabaseVersionSql() gostgrator.Statement {
	return gostgrator.Statement{SQL: `SELECT COALESCE(MAX(version), 0) FROM "example_migrations"`}
}

func (c *sqliteClient) HasVersionTable(ctx context.Context) (bool, error) {
	var exists bool
	err := c.db.QueryRowContext(ctx, `
		SELECT EXISTS (
			SELECT 1 FROM sqlite_master
			WHERE type = ? AND name = ?
		)
	`, "table", "example_migrations").Scan(&exists)
	return exists, err
}

func (c *sqliteClient) EnsureTable(ctx context.Context) error {
	_, err := c.db.ExecContext(ctx, `
		CREATE TABLE IF NOT EXISTS "example_migrations" (
			version INTEGER PRIMARY KEY,
			name TEXT NOT NULL,
			md5 TEXT NOT NULL,
			run_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
		)
	`)
	return err
}

func (c *sqliteClient) GetMd5Sql(m gostgrator.Migration) gostgrator.Statement {
	return gostgrator.Statement{SQL: `SELECT md5 FROM "example_migrations" WHERE version = ?`, Args: []any{m.Version}}
}

func (c *sqliteClient) PersistActionSql(m gostgrator.Migration) gostgrator.Statement {
	if m.Action == "undo" {
		return gostgrator.Statement{SQL: `DELETE FROM "example_migrations" WHERE version = ?`, Args: []any{m.Version}}
	}
	return gostgrator.Statement{
		SQL:  `INSERT INTO "example_migrations" (version, name, md5) VALUES (?, ?, ?)`,
		Args: []any{m.Version, m.Name, m.Md5},
	}
}

// This example implements every Client method directly against database/sql.
// A new dialect supplies its own tracking-table DDL and bookkeeping queries.
func main() {
	db, err := sql.Open("sqlite", ":memory:")
	if err != nil {
		panic(err)
	}
	defer db.Close() // The caller owns the connection, not Gostgrator.
	db.SetMaxOpenConns(1)

	client := &sqliteClient{db: db}
	g, err := gostgrator.NewGostgrator(gostgrator.Config{
		Migrations: gostgrator.FSMigrations{
			FS: fstest.MapFS{
				"001.do.create-items.sql":   {Data: []byte("CREATE TABLE items (id INTEGER PRIMARY KEY);")},
				"001.undo.create-items.sql": {Data: []byte("DROP TABLE items;")},
			},
			Pattern: "*.sql",
		},
		ValidateChecksums: true,
	}, client)
	if err != nil {
		panic(err)
	}

	ctx := context.Background()
	applied, err := g.Migrate(ctx, "max")
	if err != nil {
		panic(err)
	}
	fmt.Printf("Applied %d migration\n", len(applied))

	// Re-running validates the stored checksum without applying anything again.
	applied, err = g.Migrate(ctx, "max")
	if err != nil {
		panic(err)
	}
	fmt.Printf("Applied %d migrations on rerun\n", len(applied))

	undone, err := g.Migrate(ctx, "0")
	if err != nil {
		panic(err)
	}
	fmt.Printf("Undid %d migration\n", len(undone))
	version, err := g.GetDatabaseVersion(ctx)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Database version: %d\n", version)

}
Output:
Applied 1 migration
Applied 0 migrations on rerun
Undid 1 migration
Database version: 0

func NewClient

func NewClient(cfg Config, db *sql.DB) (Client, error)

NewClient creates a new Client based on the provided configuration and database connection.

func NewPostgresClient

func NewPostgresClient(cfg Config, db *sql.DB) Client

NewPostgresClient creates a PostgreSQL client using the supplied database. SchemaTable defaults to DefaultConfig.SchemaTable. The constructor selects the PostgreSQL dialect regardless of Config.Driver; the caller owns db. SchemaTable is an unquoted table or schema.table name. Each dot-separated component is quoted independently; embedded double quotes are escaped.

func NewSqlite3Client

func NewSqlite3Client(cfg Config, db *sql.DB) Client

NewSqlite3Client creates a SQLite client using the supplied database. SchemaTable defaults to DefaultConfig.SchemaTable. The constructor selects the SQLite dialect regardless of Config.Driver; the caller owns db. SchemaTable is quoted as one literal table name, including any dots; embedded double quotes are escaped. It is not an attached-database qualifier.

type Config

type Config struct {
	// Driver selects the SQL dialect: "pg", "sqlite", or the legacy alias "sqlite3".
	// It is used by NewClient, not by NewGostgrator or dialect-specific constructors.
	Driver string `json:"driver,omitempty"`
	// SchemaTable is the name of the migration table for built-in clients.
	// Pass it to the client constructor; NewGostgrator does not configure the client.
	SchemaTable string `json:"schemaTable,omitempty"`
	// Migrations selects an explicit disk or filesystem migration source.
	// It must not be combined with MigrationPattern.
	Migrations MigrationSource `json:"-"`
	// MigrationPattern is the legacy disk glob pattern (e.g. "./migrations/*.sql").
	// It is used when Migrations is nil by the legacy APIs.
	// NewGostgrator rejects this field; use Migrations instead.
	MigrationPattern string `json:"migrationPattern,omitempty"`
	// Newline is the desired newline style ("LF", "CR", or "CRLF").
	Newline string `json:"newline,omitempty"`
	// ValidateChecksums indicates if the tool should validate migration checksums.
	ValidateChecksums bool `json:"validateChecksums,omitempty"`
	// Conn is the connection string used by the CLI.
	// Library constructors do not open a connection or use this field.
	Conn string `json:"conn,omitempty"`
}

Config holds settings for migrations.

type DiskMigrations

type DiskMigrations struct {
	// Pattern is a filepath.Glob pattern, which may be absolute or relative.
	Pattern string
}

DiskMigrations selects migration files using OS filesystem paths.

type FSMigrations

type FSMigrations struct {
	// FS contains the migration files and must not be nil.
	FS fs.FS
	// Pattern is an fs.Glob pattern relative to FS, using forward slashes.
	// It must not contain empty, dot, or dot-dot path components.
	Pattern string
}

FSMigrations selects migration files from a filesystem, including embed.FS. The filesystem is only read; migration creation is not supported.

type Gostgrator

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

Gostgrator is the main orchestrator for running database migrations.

It loads migration files, determines the current database version, validates checksums (if enabled), and runs the necessary migrations to reach a target version.

func NewGostgrator

func NewGostgrator(cfg Config, client Client) (*Gostgrator, error)

NewGostgrator creates a Gostgrator using a caller-provided Client. Use NewPostgresClient or NewSqlite3Client for built-in database support, or supply a custom implementation. The client owns SQL execution and migration tracking-table behavior; the caller is responsible for configuring it and managing its database resources. Config.Driver, Config.Conn, and Config.SchemaTable do not configure or override the supplied client. Use Config.Migrations to select migration files; a nonempty Config.MigrationPattern is rejected. A nil client, including a typed nil implementation, is rejected.

Example (CustomClient)
package main

import (
	"context"
	"database/sql"
	"fmt"
	"log"
	"os"
	"testing/fstest"

	"github.com/bcomnes/gostgrator/v2"
	_ "modernc.org/sqlite"
)

type loggingClient struct {
	gostgrator.Client
	logger *log.Logger
}

var _ gostgrator.Client = (*loggingClient)(nil)

func (c *loggingClient) ExecContext(ctx context.Context, script string, args ...any) (sql.Result, error) {
	result, err := c.Client.ExecContext(ctx, script, args...)
	c.logger.Printf("SQL execution succeeded: %t", err == nil)
	return result, err
}

func main() {
	db, err := sql.Open("sqlite", ":memory:")
	if err != nil {
		panic(err)
	}
	defer db.Close()
	db.SetMaxOpenConns(1)

	client := &loggingClient{
		Client: gostgrator.NewSqlite3Client(gostgrator.Config{
			SchemaTable: "app_migrations",
		}, db),
		logger: log.New(os.Stdout, "", 0),
	}
	// Use an in-memory filesystem to make the example self-contained.
	// DiskMigrations or an embed.FS can be used with the same client.
	migrations := fstest.MapFS{
		"001.do.create-items.sql": {Data: []byte("CREATE TABLE items (id INTEGER PRIMARY KEY);")},
	}
	g, err := gostgrator.NewGostgrator(gostgrator.Config{
		Migrations: gostgrator.FSMigrations{FS: migrations, Pattern: "*.sql"},
	}, client)
	if err != nil {
		panic(err)
	}
	applied, err := g.Migrate(context.Background(), "max")
	if err != nil {
		panic(err)
	}
	fmt.Printf("Applied %d migration\n", len(applied))

}
Output:
SQL execution succeeded: true
SQL execution succeeded: true
Applied 1 migration

func (*Gostgrator) CreateMigration

func (g *Gostgrator) CreateMigration(description, mode string) error

(Optional) If you prefer to expose this functionality as a method on Gostgrator, you can add the following method.

func (*Gostgrator) Down

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

Down rolls back the migrations by the given number of steps. It computes the target version as the current version minus steps (not going below zero), and then calls Migrate to perform the undo operations.

func (*Gostgrator) GetDatabaseVersion

func (g *Gostgrator) GetDatabaseVersion(ctx context.Context) (int, error)

GetDatabaseVersion returns the current database version. If the migration table is not initialized, it returns 0.

func (*Gostgrator) GetMaxVersion

func (g *Gostgrator) GetMaxVersion() (int, error)

GetMaxVersion returns the highest migration version available.

func (*Gostgrator) GetMigrations

func (g *Gostgrator) GetMigrations() ([]Migration, error)

func (*Gostgrator) GetRunnableMigrations

func (g *Gostgrator) GetRunnableMigrations(databaseVersion, targetVersion int) ([]Migration, error)

func (*Gostgrator) Migrate

func (g *Gostgrator) Migrate(ctx context.Context, target string) ([]Migration, error)

Migrate moves the schema to the target version. If target is "max" or empty, it migrates to the highest available version.

func (*Gostgrator) QueryContext

func (g *Gostgrator) QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)

QueryContext executes a query with bound args using the underlying client.

func (*Gostgrator) RunMigrations

func (g *Gostgrator) RunMigrations(ctx context.Context, migrations []Migration) ([]Migration, error)

RunMigrations applies the provided migrations in sequence.

func (*Gostgrator) ValidateMigrations

func (g *Gostgrator) ValidateMigrations(ctx context.Context, databaseVersion int) error

ValidateMigrations verifies that applied migrations have not changed by comparing MD5 checksums.

type Migration

type Migration struct {
	// Version of the migration.
	Version int

	// Action, e.g., "do" or "undo".
	Action string

	// Filename is the OS path, or the path relative to the source filesystem.
	Filename string

	// Name is an optional descriptive name of the migration.
	Name string

	// Md5 is the MD5 checksum of the migration file.
	Md5 string
	// contains filtered or unexported fields
}

Migration represents a single migration file. Values are comparable, but equality includes the identity of the retained filesystem source. Independently loaded filesystem migrations may compare unequal despite identical metadata; compare the relevant public fields instead when checking migration identity or content.

type MigrationSource

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

MigrationSource selects a disk or filesystem migration source. Use DiskMigrations or FSMigrations, as a value or a non-nil pointer.

type PostgresClient

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

PostgresClient implements the Client interface for PostgreSQL.

func (*PostgresClient) EnsureTable

func (c *PostgresClient) EnsureTable(ctx context.Context) error

EnsureTable creates the migration table if it does not exist and adds missing columns.

func (*PostgresClient) ExecContext

func (c *PostgresClient) ExecContext(ctx context.Context, script string, args ...any) (sql.Result, error)

Exposing ExecContext from the configured db connection.

func (*PostgresClient) GetDatabaseVersionSql

func (c *PostgresClient) GetDatabaseVersionSql() Statement

GetDatabaseVersionSql returns a statement to fetch the highest applied version.

func (*PostgresClient) GetMd5Sql

func (c *PostgresClient) GetMd5Sql(m Migration) Statement

GetMd5Sql returns a statement to fetch the checksum for a bound migration version.

func (*PostgresClient) HasVersionTable

func (c *PostgresClient) HasVersionTable(ctx context.Context) (bool, error)

HasVersionTable checks for the existence of the migration table.

func (*PostgresClient) PersistActionSql

func (c *PostgresClient) PersistActionSql(m Migration) Statement

PersistActionSql generates a statement to record a migration action.

func (*PostgresClient) QueryContext

func (c *PostgresClient) QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)

Exposes the QueryContext method from the configured db connection.

type Sqlite3Client

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

Sqlite3Client implements the Client interface for SQLite.

func (*Sqlite3Client) EnsureTable

func (c *Sqlite3Client) EnsureTable(ctx context.Context) error

EnsureTable creates the migration table if it does not exist and adds missing columns.

func (*Sqlite3Client) ExecContext

func (c *Sqlite3Client) ExecContext(ctx context.Context, script string, args ...any) (sql.Result, error)

Exposing ExecContext from the configured db connection.

func (*Sqlite3Client) GetDatabaseVersionSql

func (c *Sqlite3Client) GetDatabaseVersionSql() Statement

GetDatabaseVersionSql returns a statement to fetch the highest applied version.

func (*Sqlite3Client) GetMd5Sql

func (c *Sqlite3Client) GetMd5Sql(m Migration) Statement

GetMd5Sql returns a statement to fetch the checksum for a bound migration version.

func (*Sqlite3Client) HasVersionTable

func (c *Sqlite3Client) HasVersionTable(ctx context.Context) (bool, error)

HasVersionTable checks for the existence of the migration table.

func (*Sqlite3Client) PersistActionSql

func (c *Sqlite3Client) PersistActionSql(m Migration) Statement

PersistActionSql generates a statement to record a migration action.

func (*Sqlite3Client) QueryContext

func (c *Sqlite3Client) QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)

Exposes the QueryContext method from the configured db connection.

type Statement added in v2.0.2

type Statement struct {
	// SQL is the query text, with placeholders matching the client's dialect.
	SQL string
	// Args contains values forwarded to QueryContext or ExecContext.
	// It may be nil when SQL requires no bound arguments.
	Args []any
}

Statement contains SQL text and the arguments to bind when executing it. It is not a prepared statement (sql.Stmt) and holds no database resources. Placeholders are dialect-specific; identifiers must be quoted by the client, not passed in Args.

Directories

Path Synopsis
Package main provides gostgrator‑pg, a PostgreSQL‑specific command‑line interface for the gostgrator migration library.
Package main provides gostgrator‑pg, a PostgreSQL‑specific command‑line interface for the gostgrator migration library.
Package main provides gostgrator‑sqlite, a SQLite‑specific command‑line interface for the gostgrator migration library.
Package main provides gostgrator‑sqlite, a SQLite‑specific command‑line interface for the gostgrator migration library.

Jump to

Keyboard shortcuts

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