migrator

package
v0.19.0 Latest Latest
Warning

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

Go to latest
Published: Jun 16, 2026 License: AGPL-3.0 Imports: 12 Imported by: 0

README

# migrator Package

The migrator package provides a clean API for managing database migrations with automatic schema injection and tracking.

Overview

The migrator package offers a simplified migration management system that:

  • Automatic Schema Injection: No need to manually register migrations with schema
  • Clean Interface-based API: Uses MigratorInterface and Migrator pattern
  • Context Support: All operations support context for cancellation and timeout handling
  • Migration Tracking: Automatically tracks executed migrations in a migration_tracker table
  • Flexible Rollback: Support for rolling back by steps or by batch

Installation

import "github.com/dracory/neat/database/migrator"

Quick Start

package main

import (
    "context"
    "github.com/dracory/neat"
    contractsschema "github.com/dracory/neat/contracts/database/schema"
    "github.com/dracory/neat/database/migrator"
)

func main() {
    db, _ := neat.NewFromDSN("sqlite://./app.db")
    defer db.Close()

    // Create migrator instance
    migrator := migrator.NewMigrator(db)

    // Add migrations
    migrator.AddMigration(&CreateUsersTable{})
    migrator.AddMigration(&CreatePostsTable{})

    // Run migrations
    ctx := context.Background()
    if err := migrator.Up(ctx); err != nil {
        log.Fatal(err)
    }
}

API Reference

MigratorInterface

type MigratorInterface interface {
    AddMigration(migration migrator.MigrationInterface) error
    AddMigrations(migrations []migrator.MigrationInterface) error
    Up(ctx context.Context) error
    Down(ctx context.Context) error
    RollbackSteps(ctx context.Context, steps int) error
    RollbackToBatch(ctx context.Context, batch int) error
    Status() ([]MigrationStatus, error)
    Fresh(ctx context.Context) error
    Reset(ctx context.Context) error
    SetTransactionsEnabled(enabled bool)
    SetTransactionIsolationLevel(level string)
    SetTableName(name string) error
    SetSignatureValidation(enabled bool, format SignatureFormat)
    SetLexicographicalOrdering(enabled bool)
}

Constructors

NewMigrator

Creates a migrator with sensible defaults.

migrator := migrator.NewMigrator(db)
NewMigratorWithOptions

Creates a migrator with custom options. Useful for reducing boilerplate when multiple options need to be set.

migrator, err := migrator.NewMigratorWithOptions(db, &migrator.Options{
    TableName:                  "my_migrations",
    TransactionIsolationLevel:  "READ COMMITTED",
    LexicographicalOrdering:    true,
    SignatureValidationEnabled: true,
    SignatureValidationFormat:  migrator.SignatureFormatDateTime,
})
if err != nil {
    log.Fatal(err)
}

Methods

AddMigration

Adds a single migration to the migrator instance. Returns an error if the signature is empty or already registered.

if err := migrator.AddMigration(&CreateUsersTable{}); err != nil {
    log.Fatal(err)
}
AddMigrations

Adds multiple migrations at once. Returns an error if any signature is empty or duplicate.

if err := migrator.AddMigrations([]migrator.MigrationInterface{
    &CreateUsersTable{},
    &CreatePostsTable{},
}); err != nil {
    log.Fatal(err)
}
Up

Runs all pending migrations. Automatically creates the migration_tracker table if it doesn't exist.

ctx := context.Background()
err := migrator.Up(ctx)
Down

Rolls back the last migration.

ctx := context.Background()
err := migrator.Down(ctx)
RollbackSteps

Rolls back the specified number of migrations.

ctx := context.Background()
err := migrator.RollbackSteps(ctx, 3) // Rollback last 3 migrations
RollbackToBatch

Rolls back all migrations to the specified batch.

ctx := context.Background()
err := migrator.RollbackToBatch(ctx, 20240615120000)
Status

Returns the current status of all migrations, including both completed (from the tracker table) and pending (registered but not yet run).

status, err := migrator.Status()
for _, s := range status {
    fmt.Printf("Migration: %s - State: %s\n", s.ID, s.State)
}
Fresh

Drops all tables except migration_tracker and clears the tracker.

ctx := context.Background()
err := migrator.Fresh(ctx)
Reset

Rolls back all migrations.

ctx := context.Background()
err := migrator.Reset(ctx)
SetTransactionsEnabled

Enables or disables transaction wrapping for migration operations. Transactions are enabled by default for safety.

migrator.SetTransactionsEnabled(true)  // Enable transactions (default)
migrator.SetTransactionsEnabled(false) // Disable transactions for large migrations
SetTransactionIsolationLevel

Sets the transaction isolation level for migration operations.

migrator.SetTransactionIsolationLevel("SERIALIZABLE")
migrator.SetTransactionIsolationLevel("READ COMMITTED")

Supported isolation levels:

  • READ UNCOMMITTED
  • READ COMMITTED
  • REPEATABLE READ
  • SERIALIZABLE
  • SNAPSHOT

Migration Implementation

Migrations must implement the MigrationInterface from the migrator package:

import (
    contractsschema "github.com/dracory/neat/contracts/database/schema"
    "github.com/dracory/neat/database/schema"
)

type CreateUsersTable struct {
    migrator.BaseMigration
}

func (m *CreateUsersTable) Signature() string {
    return "2024_06_15_120000_create_users_table"
}

func (m *CreateUsersTable) Description() string {
    return "Creates users table"
}

func (m *CreateUsersTable) Up() error {
    return m.GetSchema().Create("users", func(blueprint contractsschema.Blueprint) {
        blueprint.ID()
        blueprint.String("name")
        blueprint.String("email")
        blueprint.Timestamps()
    })
}

func (m *CreateUsersTable) Down() error {
    return m.GetSchema().DropIfExists("users")
}

Migration Tracking

The migrator automatically tracks migrations in a migration_tracker table with the following structure:

type MigrationTracker struct {
    ID          string    // Migration signature
    Batch       int       // Batch number (timestamp)
    Description string    // Migration description
    StartedAt   time.Time // When migration started
    CompletedAt time.Time // When migration finished
}

Transaction Support

The migrator package supports transaction wrapping for safe migration execution. Transactions are enabled by default to ensure atomic execution.

Enabling/Disabling Transactions

migrator := migrator.NewMigrator(db)

// Transactions are enabled by default
migrator.SetTransactionsEnabled(true)

// Disable for large migrations or specific needs
migrator.SetTransactionsEnabled(false)

Transaction Isolation Levels

migrator.SetTransactionIsolationLevel("SERIALIZABLE")

Supported isolation levels:

  • READ UNCOMMITTED
  • READ COMMITTED
  • REPEATABLE READ
  • SERIALIZABLE
  • SNAPSHOT

Note on Current Implementation

Transaction wrapping is currently disabled by default pending verification of schema transaction detection. The infrastructure is in place and can be enabled once schema transaction behavior is properly tested.

See examples/migrator-transactions for a complete example of transaction control usage.

Migration Status

The Status() method returns MigrationStatus objects:

type MigrationStatus struct {
    ID          string    `json:"id"`
    Description string    `json:"description"`
    Batch       int       `json:"batch"`
    StartedAt   time.Time `json:"started_at"`
    CompletedAt time.Time `json:"completed_at"`
    State       string    `json:"state"` // "completed" or "pending"
}

Best Practices

  1. Migration Naming: Use timestamp-based signatures for ordering

    "2024_06_15_120000_create_users_table"
    
  2. Idempotent Up Methods: Check if resources exist before creating

    func (m *CreateUsersTable) Up() error {
        if m.GetSchema().HasTable("users") {
            return nil
        }
        // Create table
    }
    
  3. Context Usage: Always use context for production applications

    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()
    err := migrator.Up(ctx)
    
  4. Error Handling: Handle migration errors appropriately

    if err := migrator.Up(ctx); err != nil {
        log.Fatalf("Migration failed: %v", err)
    }
    

Migration from Old System

If you're migrating from the old schema.NewMigrationManager:

Before:

schema := db.Schema()
schema.Register(migrations)
manager := schema.NewMigrationManager(db)
manager.Run(migrations)

After:

migrator := migrator.NewMigrator(db)
migrator.AddMigrations(migrations)
migrator.Up(context.Background())

Note: schema.Register(), schema.Migrations(), and schema.NewMigrationManager() have been removed. Use the migrator package as shown above.

Examples

See the examples/migrator-migrations directory for complete examples of using the migrator package.

Testing

The migrator package includes comprehensive tests. Run them with:

go test ./database/migrator/...

Notes

  • The migrator automatically creates the migration_tracker table on first run
  • Migrations are executed in the order they are added
  • Already-run migrations are automatically skipped
  • Schema is automatically injected into each migration before execution

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ValidateMigrationSignature

func ValidateMigrationSignature(signature string, format SignatureFormat) error

ValidateMigrationSignature validates that a migration signature follows the specified format.

Supported formats:

  • YYYY_MM_DD_HHMM_description (for datetime format)
  • YYYY_MM_DD_NNN_description (for date format)
  • unix_timestamp_description (for unix format)
  • custom (no prefix format restriction, only length and non-empty)

Business Logic:

  • Enforces maximum length of 255 characters
  • Rejects empty signatures
  • For "custom" format: only validates length and non-empty
  • For other formats: requires at least 5 underscore-separated parts (or 2 for unix)
  • Validates date part (YYYY_MM_DD) is a valid calendar date
  • Validates time part (HHMM) or sequence part (NNN) based on format
  • Validates description exists and is within length limits

func ValidateTableName

func ValidateTableName(name string) error

ValidateTableName ensures the table name contains only safe characters. Exported to allow external validation of table names before creating a migrator instance.

Types

type BaseMigration

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

BaseMigration provides common functionality for all migrations.

func (*BaseMigration) Description

func (b *BaseMigration) Description() string

Description Get a human-readable description of what this migration does.

func (*BaseMigration) Down

func (b *BaseMigration) Down() error

Down Reverse the migrations.

func (*BaseMigration) GetSchema

func (b *BaseMigration) GetSchema() contractsschema.Schema

GetSchema returns the schema for this migration.

func (*BaseMigration) SetSchema

func (b *BaseMigration) SetSchema(schema contractsschema.Schema)

SetSchema sets the schema for this migration.

func (*BaseMigration) Signature

func (b *BaseMigration) Signature() string

Signature Get the migration signature.

func (*BaseMigration) Up

func (b *BaseMigration) Up() error

Up Run the migrations.

type MigrationInterface

type MigrationInterface interface {
	// Signature Get the migration signature.
	Signature() string

	// Description Get a human-readable description of what this migration does.
	Description() string

	// Up Run the migrations.
	Up() error

	// Down Reverse the migrations.
	Down() error

	// SetSchema sets the schema for this migration.
	SetSchema(schema contractsschema.Schema)

	// GetSchema returns the schema for this migration.
	GetSchema() contractsschema.Schema
}

MigrationInterface defines the contract for a single migration.

type MigrationStatus

type MigrationStatus struct {
	ID          string    `json:"id"`
	Description string    `json:"description"`
	Batch       int       `json:"batch"`
	StartedAt   time.Time `json:"started_at"`
	CompletedAt time.Time `json:"completed_at"`
	State       string    `json:"state"` // "pending", "completed", "failed"
}

MigrationStatus represents the status of a migration returned to users This is a DTO/response type derived from MigrationTracker data

type MigrationTracker

type MigrationTracker struct {
	ID          string    // The migration signature (e.g., "2024_06_15_120000_create_users_table")
	Batch       int       // Timestamp ID (YYYYMMDDHHMMSS). Groups the run
	Description string    // The migration description from Description() method
	StartedAt   time.Time // When the migration started
	CompletedAt time.Time // When the migration finished
}

MigrationTracker represents a migration record stored in the migration_tracker table This is the database model/entity used for persistence

type Migrator

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

Migrator handles execution and tracking of interface-based migrations

func (*Migrator) AddMigration

func (s *Migrator) AddMigration(migration MigrationInterface) error

AddMigration adds a new migration to the list. Returns an error if the migration signature is empty or already registered.

func (*Migrator) AddMigrations

func (s *Migrator) AddMigrations(migrations []MigrationInterface) error

AddMigrations adds multiple migrations to the runner. Returns an error if any migration has an empty or duplicate signature.

func (*Migrator) Down

func (s *Migrator) Down(ctx context.Context) error

Down rolls back the last migration

func (*Migrator) Fresh

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

Fresh drops all tables and re-runs migrations

func (*Migrator) Reset

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

Reset rolls back and re-runs all migrations

func (*Migrator) RollbackSteps

func (s *Migrator) RollbackSteps(ctx context.Context, steps int) error

RollbackSteps rolls back the specified number of migrations

func (*Migrator) RollbackToBatch

func (s *Migrator) RollbackToBatch(ctx context.Context, batch int) error

RollbackToBatch rolls back all migrations to the specified batch

func (*Migrator) SetLexicographicalOrdering added in v0.18.0

func (s *Migrator) SetLexicographicalOrdering(enabled bool)

SetLexicographicalOrdering enables or disables lexicographical ordering of migrations. When enabled, migrations are sorted by signature before execution. Default is enabled.

func (*Migrator) SetSignatureValidation

func (s *Migrator) SetSignatureValidation(enabled bool, format SignatureFormat)

SetSignatureValidation enables or disables signature format validation. When enabled, each migration signature is validated against the specified format before execution. Default is disabled.

func (*Migrator) SetTableName

func (s *Migrator) SetTableName(name string) error

SetTableName sets the name of the migration tracking table. The name is validated to prevent SQL injection.

func (*Migrator) SetTransactionIsolationLevel

func (s *Migrator) SetTransactionIsolationLevel(level string)

SetTransactionIsolationLevel sets the transaction isolation level for migration operations

func (*Migrator) SetTransactionsEnabled

func (s *Migrator) SetTransactionsEnabled(enabled bool)

SetTransactionsEnabled enables or disables transaction wrapping for migration operations

func (*Migrator) Status

func (s *Migrator) Status() ([]MigrationStatus, error)

Status returns migration status for all registered migrations. Includes both completed migrations (from the tracker table) and pending migrations (registered but not yet run).

func (*Migrator) Up

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

Up runs all pending migrations Automatically injects schema into each migration before execution

type MigratorInterface

type MigratorInterface interface {
	AddMigration(migration MigrationInterface) error
	AddMigrations(migrations []MigrationInterface) error
	Up(ctx context.Context) error
	Down(ctx context.Context) error
	RollbackSteps(ctx context.Context, steps int) error
	RollbackToBatch(ctx context.Context, batch int) error
	Status() ([]MigrationStatus, error)
	Fresh(ctx context.Context) error
	Reset(ctx context.Context) error
	SetTransactionsEnabled(enabled bool)
	SetTransactionIsolationLevel(level string)
	SetTableName(name string) error
	SetSignatureValidation(enabled bool, format SignatureFormat)
	SetLexicographicalOrdering(enabled bool)
}

MigratorInterface defines the contract for migration management

func NewMigrator

func NewMigrator(db *database.Database) MigratorInterface

NewMigrator creates a new Migrator instance Takes neat db instance as dependency, extracts schema and orm internally

func NewMigratorWithOptions added in v0.19.0

func NewMigratorWithOptions(db *database.Database, opts *Options) (MigratorInterface, error)

NewMigratorWithOptions creates a Migrator with the given options.

type Options added in v0.19.0

type Options struct {
	TableName                  string
	TransactionIsolationLevel  string
	LexicographicalOrdering    bool
	SignatureValidationEnabled bool
	SignatureValidationFormat  SignatureFormat
}

Options configures the Migrator at construction time.

type SignatureFormat

type SignatureFormat string

SignatureFormat defines the format for migration signatures

const (
	// SignatureFormatDateTime uses timestamp-based format (default)
	// Example: 2026_06_14_1200_create_users_table
	SignatureFormatDateTime SignatureFormat = "datetime"

	// SignatureFormatDate uses sequence-based format
	// Example: 2026_06_14_001_create_users_table
	SignatureFormatDate SignatureFormat = "date"

	// SignatureFormatUnix uses unix timestamp format (legacy)
	// Example: 1717080000_create_users_table
	SignatureFormatUnix SignatureFormat = "unix"

	// SignatureFormatCustom uses no prefix format restriction
	SignatureFormatCustom SignatureFormat = "custom"
)

Jump to

Keyboard shortcuts

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