Documentation
¶
Overview ¶
Package queen provides a lightweight database migration library for Go.
Queen follows the principle "migrations are code, not files". Instead of managing separate .sql files, you define migrations directly in Go code. This approach provides type safety, better IDE support, and easier testing compared to traditional file-based migration tools.
Basic Usage ¶
Create a Queen instance with a database driver and register migrations:
db, _ := sql.Open("pgx", "postgres://localhost/myapp?sslmode=disable")
driver := postgres.New(db)
q := queen.New(driver)
defer q.Close()
q.MustAdd(queen.M{
Version: "001",
Name: "create_users",
UpSQL: "CREATE TABLE users (id SERIAL PRIMARY KEY, email VARCHAR(255))",
DownSQL: "DROP TABLE users",
})
q.MustAdd(queen.M{
Version: "002",
Name: "add_users_name",
UpSQL: "ALTER TABLE users ADD COLUMN name VARCHAR(255)",
DownSQL: "ALTER TABLE users DROP COLUMN name",
})
if err := q.Up(context.Background()); err != nil {
log.Fatal(err)
}
SQL Migrations ¶
SQL migrations use UpSQL and DownSQL fields:
q.Add(queen.M{
Version: "001",
Name: "create_users",
UpSQL: "CREATE TABLE users (id INT, email VARCHAR(255))",
DownSQL: "DROP TABLE users",
})
Go Function Migrations ¶
For complex logic that can't be expressed in SQL, use UpFunc and DownFunc:
q.Add(queen.M{
Version: "002",
Name: "normalize_emails",
ManualChecksum: "v1",
UpFunc: func(ctx context.Context, tx *sql.Tx) error {
rows, err := tx.QueryContext(ctx, "SELECT id, email FROM users")
if err != nil {
return err
}
defer rows.Close()
for rows.Next() {
var id int
var email string
rows.Scan(&id, &email)
normalized := strings.ToLower(strings.TrimSpace(email))
tx.ExecContext(ctx, "UPDATE users SET email = $1 WHERE id = $2",
normalized, id)
}
return rows.Err()
},
})
When using Go functions, always set ManualChecksum to track changes. Update it whenever you modify the function (e.g., "v1" -> "v2").
Testing ¶
Queen provides built-in testing helpers:
func TestMigrations(t *testing.T) {
db := setupTestDB(t)
driver := postgres.New(db)
q := queen.NewTest(t, driver) // Auto-cleanup on test end
q.MustAdd(queen.M{
Version: "001",
Name: "create_users",
UpSQL: "CREATE TABLE users (id INT)",
DownSQL: "DROP TABLE users",
})
q.TestUpDown() // Tests both up and down migrations
}
Natural Sorting ¶
Queen uses natural sorting for migration versions, so "1" < "2" < "10" < "100". You can use any versioning scheme: sequential numbers ("001", "002"), prefixes ("users_001", "posts_001"), or semantic versions ("v1.0.0").
Migration Operations ¶
Common operations include:
q.Up(ctx) // Apply all pending migrations q.UpSteps(ctx, 3) // Apply next 3 migrations q.Down(ctx, 1) // Rollback last migration q.Reset(ctx) // Rollback all migrations statuses, _ := q.Status(ctx) // Get migration status q.Validate(ctx) // Validate migrations
Example ¶
Example demonstrates basic usage of Queen migrations.
package main
import (
"context"
"fmt"
"log"
"github.com/honeynil/queen"
"github.com/honeynil/queen/drivers/mock"
)
func main() {
driver := mock.New()
q := queen.New(driver)
defer q.Close()
// Register migrations
q.MustAdd(queen.M{
Version: "001",
Name: "create_users",
UpSQL: `CREATE TABLE users (id SERIAL PRIMARY KEY, email VARCHAR(255))`,
DownSQL: `DROP TABLE users`,
})
q.MustAdd(queen.M{
Version: "002",
Name: "add_users_name",
UpSQL: `ALTER TABLE users ADD COLUMN name VARCHAR(255)`,
DownSQL: `ALTER TABLE users DROP COLUMN name`,
})
// Apply all pending migrations
ctx := context.Background()
if err := q.Up(ctx); err != nil {
log.Fatal(err)
}
fmt.Println("Migrations applied successfully!")
}
Example (Configuration) ¶
Example_configuration demonstrates custom configuration.
package main
import (
"context"
"time"
"github.com/honeynil/queen"
"github.com/honeynil/queen/drivers/mock"
)
func main() {
driver := mock.New()
config := &queen.Config{
TableName: "custom_migrations", // Custom table name
LockTimeout: 30 * time.Minute,
SkipLock: false, // Enable lock protection
}
q := queen.NewWithConfig(driver, config)
defer q.Close()
q.Up(context.Background())
}
Example (GoFunctionMigration) ¶
Example_goFunctionMigration demonstrates using Go functions for complex migrations.
package main
import (
"context"
"database/sql"
"strings"
"github.com/honeynil/queen"
"github.com/honeynil/queen/drivers/mock"
)
func main() {
driver := mock.New()
q := queen.New(driver)
defer q.Close()
// SQL migration to create table
q.MustAdd(queen.M{
Version: "001",
Name: "create_users",
UpSQL: `CREATE TABLE users (id SERIAL PRIMARY KEY, email VARCHAR(255))`,
DownSQL: `DROP TABLE users`,
})
// Go function migration for complex data transformation
q.MustAdd(queen.M{
Version: "002",
Name: "normalize_emails",
ManualChecksum: "v1", // Track function changes
UpFunc: func(ctx context.Context, tx *sql.Tx) error {
rows, err := tx.QueryContext(ctx, "SELECT id, email FROM users")
if err != nil {
return err
}
defer rows.Close()
for rows.Next() {
var id int
var email string
if err := rows.Scan(&id, &email); err != nil {
return err
}
normalized := strings.ToLower(strings.TrimSpace(email))
_, err = tx.ExecContext(ctx,
"UPDATE users SET email = $1 WHERE id = $2",
normalized, id)
if err != nil {
return err
}
}
return rows.Err()
},
})
q.Up(context.Background())
}
Example (ModularMigrations) ¶
Example_modularMigrations demonstrates organizing migrations by domain.
package main
import (
"context"
"github.com/honeynil/queen"
"github.com/honeynil/queen/drivers/mock"
)
func main() {
driver := mock.New()
q := queen.New(driver)
defer q.Close()
// Register migrations from different modules
registerUserMigrations(q)
registerPostMigrations(q)
q.Up(context.Background())
}
func registerUserMigrations(q *queen.Queen) {
q.MustAdd(queen.M{
Version: "users_001",
Name: "create_users",
UpSQL: `CREATE TABLE users (id SERIAL PRIMARY KEY)`,
DownSQL: `DROP TABLE users`,
})
}
func registerPostMigrations(q *queen.Queen) {
q.MustAdd(queen.M{
Version: "posts_001",
Name: "create_posts",
UpSQL: `CREATE TABLE posts (id SERIAL PRIMARY KEY)`,
DownSQL: `DROP TABLE posts`,
})
}
Example (Status) ¶
Example_status demonstrates checking migration status.
package main
import (
"context"
"fmt"
"log"
"github.com/honeynil/queen"
"github.com/honeynil/queen/drivers/mock"
)
func main() {
driver := mock.New()
q := queen.New(driver)
defer q.Close()
q.MustAdd(queen.M{
Version: "001",
Name: "create_users",
UpSQL: `CREATE TABLE users (id INT)`,
DownSQL: `DROP TABLE users`,
})
ctx := context.Background()
// Check status of all migrations
statuses, err := q.Status(ctx)
if err != nil {
log.Fatal(err)
}
for _, s := range statuses {
fmt.Printf("Version: %s, Name: %s, Status: %s\n",
s.Version, s.Name, s.Status)
}
}
Example (Testing) ¶
Example_testing demonstrates testing migrations.
package main
import (
"testing"
"github.com/honeynil/queen"
"github.com/honeynil/queen/drivers/mock"
)
func main() {
// In your test
testFunc := func(t *testing.T) {
driver := setupTestDB(t) // Your test DB setup
q := queen.NewTest(t, driver) // Auto-cleanup on test end
q.MustAdd(queen.M{
Version: "001",
Name: "create_users",
UpSQL: `CREATE TABLE users (id INT)`,
DownSQL: `DROP TABLE users`,
})
// Test both up and down migrations
q.TestUpDown()
}
// Run the test (in real code, use go test)
t := &testing.T{}
testFunc(t)
}
func setupTestDB(_ *testing.T) queen.Driver {
return mock.New()
}
Index ¶
- Variables
- func IsValidMigrationName(name string) bool
- type Applied
- type Config
- type Driver
- type Logger
- type M
- type Migration
- type MigrationError
- type MigrationFunc
- type MigrationPlan
- type MigrationStatus
- type MigrationType
- type NamingConfig
- type NamingPattern
- type Option
- type Queen
- func (q *Queen) Add(m M) error
- func (q *Queen) Close() error
- func (q *Queen) Down(ctx context.Context, n int) error
- func (q *Queen) DryRun(ctx context.Context, direction string, limit int) ([]MigrationPlan, error)
- func (q *Queen) Explain(ctx context.Context, version string) (*MigrationPlan, error)
- func (q *Queen) MustAdd(m M)
- func (q *Queen) Reset(ctx context.Context) error
- func (q *Queen) Status(ctx context.Context) ([]MigrationStatus, error)
- func (q *Queen) Up(ctx context.Context) error
- func (q *Queen) UpSteps(ctx context.Context, n int) error
- func (q *Queen) Validate(ctx context.Context) error
- type Status
- type TestHelper
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( ErrNoMigrations = errors.New("no migrations registered") ErrVersionConflict = errors.New("version conflict") ErrMigrationNotFound = errors.New("migration not found") ErrChecksumMismatch = errors.New("checksum mismatch") ErrLockTimeout = errors.New("lock timeout") ErrNoDriver = errors.New("driver not initialized") ErrInvalidMigration = errors.New("invalid migration") ErrNameTooLong = errors.New("migration name exceeds 63 characters") ErrInvalidMigrationName = errors.New("invalid migration name") ErrAlreadyApplied = errors.New("migration already applied") )
Common errors returned by Queen operations.
Functions ¶
func IsValidMigrationName ¶ added in v0.2.0
IsValidMigrationName checks if a migration name is valid.
Types ¶
type Applied ¶
type Applied struct {
// Version is the unique version identifier of the migration.
Version string
// Name is the human-readable name of the migration.
Name string
// AppliedAt is when the migration was applied.
AppliedAt time.Time
// Checksum is the hash of the migration content at the time it was applied.
Checksum string
}
Applied represents a migration that has been applied to the database. This is returned by Driver.GetApplied().
type Config ¶
type Config struct {
// TableName for migration tracking. Default: "queen_migrations"
TableName string
// LockTimeout for acquiring migration lock. Default: 30 minutes
LockTimeout time.Duration
// SkipLock disables locking (not recommended for production). Default: false
SkipLock bool
// Naming configures migration version naming validation.
// Default: nil (no validation, for backward compatibility)
Naming *NamingConfig
// IsolationLevel sets the default transaction isolation level for all migrations.
// Default: sql.LevelDefault (uses database default)
//
// Supported levels:
// - sql.LevelDefault: use database default
// - sql.LevelReadUncommitted: allow dirty reads
// - sql.LevelReadCommitted: prevent dirty reads (PostgreSQL default)
// - sql.LevelRepeatableRead: prevent non-repeatable reads (MySQL default)
// - sql.LevelSerializable: full isolation (SQLite default)
//
// Individual migrations can override this with their own IsolationLevel.
IsolationLevel sql.IsolationLevel
}
Config configures Queen behavior.
func DefaultConfig ¶
func DefaultConfig() *Config
DefaultConfig returns default settings: "queen_migrations" table, 30min lock timeout.
type Driver ¶
type Driver interface {
// Init initializes the driver and creates the migrations tracking table if needed.
// This should be called before any other operations.
Init(ctx context.Context) error
// GetApplied returns all migrations that have been applied to the database.
// The returned slice should be sorted by applied time in ascending order.
GetApplied(ctx context.Context) ([]Applied, error)
// Record marks a migration as applied in the database.
// This should be called after successfully executing a migration.
Record(ctx context.Context, m *Migration) error
// Remove removes a migration record from the database.
// This should be called after successfully rolling back a migration.
Remove(ctx context.Context, version string) error
// Lock acquires an exclusive lock to prevent concurrent migrations.
//
// If the lock cannot be acquired within the specified timeout, it returns
// ErrLockTimeout. The lock must be held until Unlock() is called.
//
// Implementation notes:
// - Use database-specific locking (PostgreSQL advisory locks, MySQL named locks, etc.)
// - The lock should be exclusive to prevent concurrent migration runs
// - Consider using a unique lock identifier based on the migrations table name
Lock(ctx context.Context, timeout time.Duration) error
// Unlock releases the migration lock.
// This should be called in a defer statement after acquiring the lock.
Unlock(ctx context.Context) error
// Exec executes a function within a transaction with the specified isolation level.
// If the function returns an error, the transaction is rolled back.
// Otherwise, the transaction is committed.
//
// The isolationLevel parameter specifies the transaction isolation level:
// - sql.LevelDefault: use database default isolation level
// - sql.LevelReadUncommitted: allow dirty reads
// - sql.LevelReadCommitted: prevent dirty reads
// - sql.LevelRepeatableRead: prevent non-repeatable reads
// - sql.LevelSerializable: full isolation
//
// Note: Not all databases support all isolation levels. The driver should
// validate compatibility and return an error if unsupported.
Exec(ctx context.Context, isolationLevel sql.IsolationLevel, fn func(*sql.Tx) error) error
// Close closes the database connection.
Close() error
}
Driver is the interface that database-specific drivers must implement.
Driver abstracts database-specific migration tracking, locking, and transaction management. This allows Queen to support multiple databases (PostgreSQL, MySQL, SQLite, etc.) without changing the core library.
Implementing a Driver ¶
To implement a driver for a new database:
- Implement all Driver interface methods
- Create a migrations tracking table in Init()
- Use database-specific locking (advisory locks, named locks, etc.)
- Handle transactions properly in Exec()
See drivers/postgres/postgres.go for a reference implementation.
Thread Safety ¶
Driver implementations must be safe for concurrent use by multiple goroutines. The Queen instance will handle locking to prevent concurrent migrations, but the driver should still be thread-safe for Status() and Validate() operations.
type Logger ¶ added in v0.2.0
type Logger interface {
// InfoContext logs an informational message with structured fields.
// Compatible with slog.Logger.InfoContext.
InfoContext(ctx context.Context, msg string, args ...any)
// WarnContext logs a warning message with structured fields.
// Compatible with slog.Logger.WarnContext.
WarnContext(ctx context.Context, msg string, args ...any)
// ErrorContext logs an error message with structured fields.
// Compatible with slog.Logger.ErrorContext.
ErrorContext(ctx context.Context, msg string, args ...any)
}
Logger defines a structured logging interface compatible with slog.
This interface is intentionally compatible with *slog.Logger from the standard library, allowing direct usage of slog loggers without adapters.
Example using slog:
import "log/slog" logger := slog.Default() q := queen.New(driver, queen.WithLogger(logger))
Example using custom logger:
type MyLogger struct{}
func (l *MyLogger) InfoContext(ctx context.Context, msg string, args ...any) {
// Custom implementation
}
func (l *MyLogger) WarnContext(ctx context.Context, msg string, args ...any) {
// Custom implementation
}
func (l *MyLogger) ErrorContext(ctx context.Context, msg string, args ...any) {
// Custom implementation
}
q := queen.New(driver, queen.WithLogger(&MyLogger{}))
type M ¶
type M = Migration
M is a convenient alias for Migration, used in registration:
q.Add(queen.M{
Version: "001",
Name: "create_users",
UpSQL: "CREATE TABLE users...",
DownSQL: "DROP TABLE users",
})
type Migration ¶
type Migration struct {
// Version uniquely identifies this migration.
// Examples: "001", "002", "user_001", "v1.0.0"
Version string
// Name describes what this migration does.
// Examples: "create_users", "add_email_index"
Name string
// UpSQL applies the migration using SQL.
// Leave empty when using UpFunc.
UpSQL string
// DownSQL rolls back the migration using SQL.
// Optional but recommended for safe rollbacks.
DownSQL string
// UpFunc applies the migration using Go code.
// Use for complex logic that can't be expressed in SQL.
UpFunc MigrationFunc
// DownFunc rolls back the migration using Go code.
// Optional but recommended for safe rollbacks.
DownFunc MigrationFunc
// ManualChecksum tracks changes to function migrations.
// Required when using UpFunc/DownFunc for validation.
// Examples: "v1", "v2", "normalize-emails-v1"
// Update this whenever you modify the function.
ManualChecksum string
// IsolationLevel sets the transaction isolation level for this migration.
// Default: sql.LevelDefault (uses Config.IsolationLevel or database default)
//
// This overrides the global Config.IsolationLevel for this specific migration.
//
// Use cases:
// - Critical migrations requiring SERIALIZABLE isolation
// - Bulk data migrations that can use READ COMMITTED for better performance
// - Preventing race conditions during schema changes
//
// Example:
// queen.M{
// Version: "003",
// Name: "critical_update",
// IsolationLevel: sql.LevelSerializable,
// UpSQL: "UPDATE users SET ...",
// }
IsolationLevel sql.IsolationLevel
// contains filtered or unexported fields
}
Migration represents a single database migration.
A migration can be defined using SQL strings (UpSQL/DownSQL) or Go functions (UpFunc/DownFunc). You can also mix both approaches in the same migration.
Version and Name ¶
Version must be unique across all migrations. Queen uses natural sorting, so "1", "2", "10" sort correctly. You can use prefixes for organization: "users_001", "posts_001".
Name should be a human-readable description like "create_users" or "add_email_index".
SQL Migrations ¶
For simple schema changes, use UpSQL and DownSQL:
queen.M{
Version: "001",
Name: "create_users",
UpSQL: "CREATE TABLE users (id INT)",
DownSQL: "DROP TABLE users",
}
Go Function Migrations ¶
For complex logic that can't be expressed in SQL, use UpFunc and DownFunc:
queen.M{
Version: "002",
Name: "migrate_data",
ManualChecksum: "v1",
UpFunc: func(ctx context.Context, tx *sql.Tx) error {
// Your migration logic here
return nil
},
}
IMPORTANT: When using UpFunc/DownFunc, always set ManualChecksum to track changes. Update it whenever you modify the function (e.g., "v1" -> "v2").
Checksums ¶
Queen automatically calculates checksums for SQL migrations. For Go function migrations, you must provide ManualChecksum. This detects when applied migrations have been modified, which can indicate a problem.
func (*Migration) Checksum ¶
Checksum returns a hash for validation. Uses ManualChecksum if set, calculates from SQL otherwise, or returns a marker for Go functions.
func (*Migration) HasRollback ¶
HasRollback checks if DownSQL or DownFunc is defined.
func (*Migration) IsDestructive ¶
IsDestructive checks DownSQL for destructive keywords: DROP TABLE, DROP DATABASE, TRUNCATE, etc. Up migrations are assumed constructive and not checked.
type MigrationError ¶
type MigrationError struct {
Version string // Migration version (e.g., "001", "002")
Name string // Migration name (e.g., "create_users")
Operation string // Operation being performed: "up", "down", "validate"
Driver string // Database driver name (e.g., "postgres", "mysql", "sqlite")
Cause error // The underlying error that occurred
}
MigrationError wraps an error with migration context.
This structured error provides rich context for debugging migration failures, including which migration failed, what operation was being performed, and which database driver was in use.
func (*MigrationError) Error ¶
func (e *MigrationError) Error() string
func (*MigrationError) Unwrap ¶
func (e *MigrationError) Unwrap() error
type MigrationFunc ¶
MigrationFunc is a function that executes a migration using a transaction. It receives a context and a transaction, and should return an error if the migration fails.
type MigrationPlan ¶ added in v0.2.0
type MigrationPlan struct {
// Version is the unique version identifier of the migration.
Version string `json:"version"`
// Name is the human-readable name of the migration.
Name string `json:"name"`
// Direction indicates the migration direction: "up" or "down".
Direction string `json:"direction"`
// Status indicates whether the migration is pending, applied, or modified.
Status string `json:"status"`
// Type indicates the migration type: "sql", "go-func", or "mixed".
Type MigrationType `json:"type"`
// SQL contains the SQL that will be executed (if applicable).
// Empty for Go function-only migrations.
SQL string `json:"sql,omitempty"`
// HasRollback indicates if the migration has a down migration.
HasRollback bool `json:"has_rollback"`
// IsDestructive indicates if the migration contains destructive operations.
// Only applicable for down migrations.
IsDestructive bool `json:"is_destructive"`
// Checksum is the current checksum of the migration.
Checksum string `json:"checksum"`
// Warnings contains any warnings about this migration.
// Examples: "No rollback defined", "Destructive operation", etc.
Warnings []string `json:"warnings,omitempty"`
}
MigrationPlan represents a migration execution plan for dry-run mode. This is returned by Queen.DryRun() and Queen.Explain().
type MigrationStatus ¶
type MigrationStatus struct {
// Version is the unique version identifier of the migration.
Version string
// Name is the human-readable name of the migration.
Name string
// Status indicates whether the migration is pending, applied, or modified.
Status Status
// AppliedAt is when the migration was applied (nil if not applied).
AppliedAt *time.Time
// Checksum is the current checksum of the migration.
Checksum string
// HasRollback indicates if the migration has a down migration.
HasRollback bool
// Destructive indicates if the down migration contains destructive operations.
Destructive bool
}
MigrationStatus contains detailed information about a migration's current state. This is returned by Queen.Status().
type MigrationType ¶ added in v0.2.0
type MigrationType string
MigrationType represents the type of migration implementation.
const ( // MigrationTypeSQL indicates the migration uses SQL (UpSQL/DownSQL). MigrationTypeSQL MigrationType = "sql" // MigrationTypeGoFunc indicates the migration uses Go functions (UpFunc/DownFunc). MigrationTypeGoFunc MigrationType = "go-func" // MigrationTypeMixed indicates the migration uses both SQL and Go functions. MigrationTypeMixed MigrationType = "mixed" )
type NamingConfig ¶ added in v0.2.0
type NamingConfig struct {
// Pattern specifies the naming pattern to enforce.
// Default: NamingPatternNone (no validation).
Pattern NamingPattern
// Padding specifies the number of digits for sequential-padded pattern.
// Only used when Pattern is NamingPatternSequentialPadded.
// Default: 3 (generates 001, 002, 003, ...)
Padding int
// Enforce determines whether to return an error on validation failure.
// If false, validation failures are logged as warnings but don't prevent migration.
// Default: true
Enforce bool
}
NamingConfig configures migration version naming validation.
func DefaultNamingConfig ¶ added in v0.2.0
func DefaultNamingConfig() *NamingConfig
DefaultNamingConfig returns the default naming configuration.
func (*NamingConfig) FindNextVersion ¶ added in v0.2.0
func (nc *NamingConfig) FindNextVersion(existingVersions []string) (string, error)
FindNextVersion finds the next version based on the pattern and existing versions. This is primarily used by CLI tools for auto-generating version numbers.
func (*NamingConfig) Validate ¶ added in v0.2.0
func (nc *NamingConfig) Validate(version string) error
Validate checks if a version string matches the configured naming pattern.
type NamingPattern ¶ added in v0.2.0
type NamingPattern string
NamingPattern defines the migration version naming convention.
const ( // NamingPatternNone disables naming pattern validation (default for backward compatibility). NamingPatternNone NamingPattern = "" // NamingPatternSequential enforces sequential numbering: 1, 2, 3, ... NamingPatternSequential NamingPattern = "sequential" // NamingPatternSequentialPadded enforces padded sequential numbering: 001, 002, 003, ... // This is the recommended default for most projects. NamingPatternSequentialPadded NamingPattern = "sequential-padded" // NamingPatternSemver enforces semantic versioning: 1.0.0, 1.1.0, 2.0.0, ... NamingPatternSemver NamingPattern = "semver" )
type Option ¶ added in v0.2.0
type Option func(*Queen)
Option configures a Queen instance.
func WithLogger ¶ added in v0.2.0
WithLogger sets a custom logger for the Queen instance.
The logger interface is compatible with *slog.Logger from the standard library, so you can pass slog loggers directly:
import "log/slog" logger := slog.Default() q := queen.New(driver, queen.WithLogger(logger))
If no logger is configured, a no-op logger is used (no logging).
type Queen ¶
type Queen struct {
// contains filtered or unexported fields
}
Queen manages database migrations.
func New ¶
New creates a Queen instance with default configuration and optional settings.
Example:
q := queen.New(driver)
With logger:
import "log/slog" q := queen.New(driver, queen.WithLogger(slog.Default()))
func NewWithConfig ¶
NewWithConfig creates a Queen instance with custom settings.
func (*Queen) Add ¶
Add registers a migration after validation. Returns ErrVersionConflict if version already exists.
func (*Queen) Down ¶
Down rolls back the last n migrations. If n <= 0, rolls back only the last migration.
Example ¶
ExampleQueen_Down demonstrates rolling back migrations.
package main
import (
"context"
"fmt"
"log"
"github.com/honeynil/queen"
"github.com/honeynil/queen/drivers/mock"
)
func main() {
driver := mock.New()
q := queen.New(driver)
defer q.Close()
ctx := context.Background()
// Rollback last migration
if err := q.Down(ctx, 1); err != nil {
log.Fatal(err)
}
fmt.Println("Rolled back 1 migration")
}
func (*Queen) DryRun ¶ added in v0.2.0
DryRun returns a migration execution plan without applying migrations.
Direction can be "up" or "down":
- "up": shows pending migrations that would be applied
- "down": shows applied migrations that could be rolled back
This is useful for:
- Previewing what migrations will be applied before running them
- CI/CD validation and checks
- Understanding the current migration state
Example:
plans, err := q.DryRun(ctx, "up")
for _, plan := range plans {
fmt.Printf("Will apply: %s - %s\n", plan.Version, plan.Name)
if len(plan.Warnings) > 0 {
fmt.Printf(" Warnings: %v\n", plan.Warnings)
}
}
func (*Queen) Explain ¶ added in v0.2.0
Explain returns a detailed migration plan for a specific version.
This provides comprehensive information about a single migration, including its SQL, type, warnings, and current status.
Example:
plan, err := q.Explain(ctx, "001")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Migration: %s - %s\n", plan.Version, plan.Name)
fmt.Printf("Status: %s\n", plan.Status)
if plan.SQL != "" {
fmt.Printf("SQL:\n%s\n", plan.SQL)
}
func (*Queen) MustAdd ¶
MustAdd is like Add but panics on error. Use during initialization when registration must succeed.
func (*Queen) Reset ¶
Reset rolls back all applied migrations.
Example ¶
ExampleQueen_Reset demonstrates rolling back all migrations.
package main
import (
"context"
"fmt"
"log"
"github.com/honeynil/queen"
"github.com/honeynil/queen/drivers/mock"
)
func main() {
driver := mock.New()
q := queen.New(driver)
defer q.Close()
ctx := context.Background()
if err := q.Reset(ctx); err != nil {
log.Fatal(err)
}
fmt.Println("All migrations rolled back")
}
func (*Queen) Status ¶
func (q *Queen) Status(ctx context.Context) ([]MigrationStatus, error)
Status returns the status of all registered migrations.
func (*Queen) Up ¶
Up applies all pending migrations. Equivalent to UpSteps(ctx, 0).
Example ¶
ExampleQueen_Up demonstrates applying all pending migrations.
package main
import (
"context"
"fmt"
"log"
"github.com/honeynil/queen"
"github.com/honeynil/queen/drivers/mock"
)
func main() {
driver := mock.New()
q := queen.New(driver)
defer q.Close()
q.MustAdd(queen.M{
Version: "001",
Name: "create_users",
UpSQL: `CREATE TABLE users (id INT)`,
DownSQL: `DROP TABLE users`,
})
ctx := context.Background()
if err := q.Up(ctx); err != nil {
log.Fatal(err)
}
fmt.Println("All migrations applied")
}
func (*Queen) UpSteps ¶
UpSteps applies up to n pending migrations. If n <= 0, applies all pending migrations.
Example ¶
ExampleQueen_UpSteps demonstrates applying a specific number of migrations.
package main
import (
"context"
"fmt"
"log"
"github.com/honeynil/queen"
"github.com/honeynil/queen/drivers/mock"
)
func main() {
driver := mock.New()
q := queen.New(driver)
defer q.Close()
q.MustAdd(queen.M{Version: "001", Name: "migration_1", UpSQL: "..."})
q.MustAdd(queen.M{Version: "002", Name: "migration_2", UpSQL: "..."})
q.MustAdd(queen.M{Version: "003", Name: "migration_3", UpSQL: "..."})
ctx := context.Background()
// Apply only the next 2 migrations
if err := q.UpSteps(ctx, 2); err != nil {
log.Fatal(err)
}
fmt.Println("Applied 2 migrations")
}
func (*Queen) Validate ¶
Validate checks for duplicate versions, invalid migrations, and checksum mismatches.
Example ¶
ExampleQueen_Validate demonstrates validating migrations.
package main
import (
"context"
"fmt"
"log"
"github.com/honeynil/queen"
"github.com/honeynil/queen/drivers/mock"
)
func main() {
driver := mock.New()
q := queen.New(driver)
defer q.Close()
q.MustAdd(queen.M{
Version: "001",
Name: "create_users",
UpSQL: `CREATE TABLE users (id INT)`,
})
ctx := context.Background()
if err := q.Validate(ctx); err != nil {
log.Fatalf("Validation failed: %v", err)
}
fmt.Println("All migrations valid")
}
type Status ¶
type Status int
Status represents the current state of a migration.
const ( // StatusPending indicates the migration has not been applied yet. StatusPending Status = iota // StatusApplied indicates the migration has been successfully applied. StatusApplied // StatusModified indicates the migration has been applied, // but its content has changed (checksum mismatch). StatusModified )
type TestHelper ¶
type TestHelper struct {
*Queen
// contains filtered or unexported fields
}
TestHelper provides testing utilities for migrations.
TestHelper wraps a Queen instance with test-specific helpers that automatically fail tests on errors instead of returning them. This reduces boilerplate in migration tests.
The TestHelper automatically cleans up (closes the Queen instance) when the test ends using t.Cleanup().
Usage ¶
Create a TestHelper with NewTest and use its Must* methods:
func TestMigrations(t *testing.T) {
db := setupTestDB(t)
driver := postgres.New(db)
q := queen.NewTest(t, driver)
q.MustAdd(queen.M{...})
q.MustUp()
q.MustValidate()
}
Or use TestUpDown to test both up and down migrations:
func TestMigrations(t *testing.T) {
q := queen.NewTest(t, driver)
q.MustAdd(queen.M{...})
q.TestUpDown() // Applies then rolls back all migrations
}
For thorough testing of each down migration, use TestRollback:
func TestMigrations(t *testing.T) {
q := queen.NewTest(t, driver)
q.MustAdd(queen.M{...})
q.TestRollback() // Up -> Down (one by one) -> Up
}
func NewTest ¶
func NewTest(t *testing.T, driver Driver) *TestHelper
NewTest creates a Queen instance with automatic cleanup.
Usage:
func TestMigrations(t *testing.T) {
db := setupTestDB(t) // Your test DB setup
driver := postgres.New(db)
q := queen.NewTest(t, driver)
q.MustAdd(queen.M{...})
// Test will automatically clean up
}
Example ¶
ExampleNewTest demonstrates using the testing helper.
package main
import (
"fmt"
"testing"
"github.com/honeynil/queen"
"github.com/honeynil/queen/drivers/mock"
)
func main() {
testFunc := func(t *testing.T) {
driver := setupTestDB(t)
// NewTest automatically cleans up when test ends
q := queen.NewTest(t, driver)
q.MustAdd(queen.M{
Version: "001",
Name: "create_users",
UpSQL: `CREATE TABLE users (id INT)`,
DownSQL: `DROP TABLE users`,
})
// Test migrations
q.MustUp()
q.MustValidate()
fmt.Println("Test passed")
}
t := &testing.T{}
testFunc(t)
}
func setupTestDB(_ *testing.T) queen.Driver {
return mock.New()
}
func (*TestHelper) MustDown ¶
func (th *TestHelper) MustDown(n int)
MustDown is like Down but fails the test on error.
func (*TestHelper) MustReset ¶
func (th *TestHelper) MustReset()
MustReset is like Reset but fails the test on error.
func (*TestHelper) MustUp ¶
func (th *TestHelper) MustUp()
MustUp is like Up but fails the test on error.
func (*TestHelper) MustValidate ¶
func (th *TestHelper) MustValidate()
MustValidate is like Validate but fails the test on error.
func (*TestHelper) TestRollback ¶ added in v0.2.0
func (th *TestHelper) TestRollback()
TestRollback verifies each down migration works correctly.
This test performs a complete migration cycle:
- Applies all migrations (Up)
- Rolls back each migration one by one (Down)
- Reapplies all migrations (Up)
This ensures:
- Each Down migration executes without errors
- Each Down migration properly undoes its Up counterpart
- The database returns to a clean state after rollback
If any step fails, the test reports exactly which migration caused the failure.
Usage:
func TestMigrations(t *testing.T) {
q := queen.NewTest(t, driver)
q.MustAdd(queen.M{...})
q.TestRollback() // Tests full Up -> Down -> Up cycle
}
Example ¶
ExampleTestHelper_TestRollback demonstrates thorough testing of down migrations.
package main
import (
"fmt"
"testing"
"github.com/honeynil/queen"
"github.com/honeynil/queen/drivers/mock"
)
func main() {
testFunc := func(t *testing.T) {
driver := setupTestDB(t)
q := queen.NewTest(t, driver)
q.MustAdd(queen.M{
Version: "001",
Name: "create_users",
UpSQL: `CREATE TABLE users (id INT)`,
DownSQL: `DROP TABLE users`,
})
q.MustAdd(queen.M{
Version: "002",
Name: "add_email",
UpSQL: `ALTER TABLE users ADD COLUMN email VARCHAR(255)`,
DownSQL: `ALTER TABLE users DROP COLUMN email`,
})
// TestRollback performs a full cycle: Up -> Down (one by one) -> Up
// This catches broken Down migrations that don't properly undo their Up
q.TestRollback()
fmt.Println("All down migrations work correctly")
}
t := &testing.T{}
testFunc(t)
}
func setupTestDB(_ *testing.T) queen.Driver {
return mock.New()
}
func (*TestHelper) TestUpDown ¶
func (th *TestHelper) TestUpDown()
TestUpDown verifies migrations can be applied and rolled back.
Recommended for testing because it ensures: - Up migrations execute without errors - Down migrations execute without errors - Database returns to original state after rollback
Usage:
func TestMigrations(t *testing.T) {
q := queen.NewTest(t, driver)
q.MustAdd(queen.M{
Version: "001",
Name: "create_users",
UpSQL: "CREATE TABLE users (id INT)",
DownSQL: "DROP TABLE users",
})
q.TestUpDown() // Tests both up and down
}
Example ¶
ExampleTestHelper_TestUpDown demonstrates testing up and down migrations.
package main
import (
"fmt"
"testing"
"github.com/honeynil/queen"
"github.com/honeynil/queen/drivers/mock"
)
func main() {
testFunc := func(t *testing.T) {
driver := setupTestDB(t)
q := queen.NewTest(t, driver)
q.MustAdd(queen.M{
Version: "001",
Name: "create_users",
UpSQL: `CREATE TABLE users (id INT)`,
DownSQL: `DROP TABLE users`,
})
// TestUpDown applies all migrations, then rolls them back
q.TestUpDown()
fmt.Println("Up and down migrations work correctly")
}
t := &testing.T{}
testFunc(t)
}
func setupTestDB(_ *testing.T) queen.Driver {
return mock.New()
}
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package cli provides a command-line interface for Queen migrations.
|
Package cli provides a command-line interface for Queen migrations. |
|
drivers
|
|
|
base
Package base provides common functionality for Queen database drivers.
|
Package base provides common functionality for Queen database drivers. |
|
clickhouse
Package clickhouse provides a ClickHouse driver for Queen migrations.
|
Package clickhouse provides a ClickHouse driver for Queen migrations. |
|
cockroachdb
Package cockroachdb provides a CockroachDB driver for Queen migrations.
|
Package cockroachdb provides a CockroachDB driver for Queen migrations. |
|
mock
Package mock provides an in-memory mock driver for testing Queen without a real database.
|
Package mock provides an in-memory mock driver for testing Queen without a real database. |
|
mssql
Package mssql provides a MS SQL Server driver for Queen migrations.
|
Package mssql provides a MS SQL Server driver for Queen migrations. |
|
mysql
Package mysql provides a MySQL driver for Queen migrations.
|
Package mysql provides a MySQL driver for Queen migrations. |
|
postgres
Package postgres provides a PostgreSQL driver for Queen migrations.
|
Package postgres provides a PostgreSQL driver for Queen migrations. |
|
sqlite
Package sqlite provides a SQLite driver for Queen migrations.
|
Package sqlite provides a SQLite driver for Queen migrations. |
|
ydb
Package ydb provides a YandexDB (YDB) driver for Queen migrations.
|
Package ydb provides a YandexDB (YDB) driver for Queen migrations. |
|
internal
|
|
|
checksum
Package checksum provides checksum calculation for migrations.
|
Package checksum provides checksum calculation for migrations. |
|
sort
Package sort provides natural sorting for migration versions.
|
Package sort provides natural sorting for migration versions. |