go-sql-migrate
Retired. This module is no longer maintained and will not get fixes. For PostgreSQL, use
go-pgx-kit. There is no Go replacement here for its
MySQL and SQLite support.
A lightweight, reflection-based SQL schema migration library for Go. Supports PostgreSQL, MySQL, and SQLite.
Installation
go get codeberg.org/obadness/go-sql-migrate
Dependencies
This module uses sqlx for database interaction. A *sqlx.DB instance is required to use Migrate and GenerateSchemaDiffs.
go get github.com/jmoiron/sqlx
You will also need a driver for your database:
# PostgreSQL
go get github.com/jackc/pgx/v5/stdlib
# MySQL
go get github.com/go-sql-driver/mysql
# SQLite
go get github.com/mattn/go-sqlite3
Register the driver with sqlx before passing the *sqlx.DB to this module:
// PostgreSQL example
import (
"github.com/jmoiron/sqlx"
_ "github.com/jackc/pgx/v5/stdlib"
)
db, err := sqlx.Open("pgx", "postgres://user:pass@localhost:5432/mydb")
if err != nil {
log.Fatal(err)
}
Usage
Define a schema
Implement the Schema interface by adding a TableName() method to your struct, then annotate fields with struct tags:
type User struct {
ID uuid.UUID `db:"id" sql:"UUID" primary:"true"`
Email string `db:"email" unique:"true"`
Name string `db:"name"`
Active bool `db:"active" default:"true"`
CreatedAt time.Time `db:"created_at"`
}
func (u User) TableName() string { return "users" }
Run a migration
import migrate "codeberg.org/obadness/go-sql-migrate"
schemas := []migrate.Schema{
User{},
}
err := migrate.Migrate(ctx, db, migrate.ActionCreate, migrate.PostgreSQL, schemas)
if err != nil {
log.Fatal(err)
}
Pass multiple schemas in one call:
schemas := []migrate.Schema{
User{},
Post{},
Comment{},
}
err := migrate.Migrate(ctx, db, migrate.ActionCreate, migrate.PostgreSQL, schemas)
if err != nil {
log.Fatal(err)
}
Actions
| Action |
Constant |
Description |
| Create |
migrate.ActionCreate |
Runs CREATE TABLE IF NOT EXISTS |
| Drop |
migrate.ActionDrop |
Runs DROP TABLE IF EXISTS |
| Alter |
migrate.ActionAlter |
Adds missing columns and updates changed column types |
Use the constants directly or construct an Action from a string using NewAction — useful when the action comes from a CLI flag, environment variable, or config file:
// From a constant
action := migrate.ActionCreate
// From a string (e.g. CLI flag or env var)
action := migrate.NewAction(os.Getenv("MIGRATE_ACTION"))
err := migrate.Migrate(ctx, db, action, migrate.PostgreSQL, schemas)
if err != nil {
log.Fatal(err)
}
An unrecognised value returns an empty Action which fails validation inside Migrate with a clear error — no silent no-ops.
Dialects
| Dialect |
Constant |
| PostgreSQL |
migrate.PostgreSQL |
| MySQL |
migrate.MySQL |
| SQLite |
migrate.SQLite |
| Tag |
Description |
Example |
db |
Column name. Omit or set to - to skip the field |
db:"email" |
sql |
Override the SQL type |
sql:"VARCHAR(100)" |
primary |
Mark as primary key |
primary:"true" |
unique |
Add UNIQUE constraint |
unique:"true" |
nullable |
Allow NULL values |
nullable:"true" |
default |
Set a DEFAULT value |
default:"true" |
fk |
Add a FOREIGN KEY reference |
fk:"other_table(id)" |
Go to SQL type mapping
| Go type |
PostgreSQL |
MySQL |
SQLite |
string |
VARCHAR(255) |
VARCHAR(255) |
TEXT |
int, int32 |
INTEGER |
INTEGER |
INTEGER |
int64 |
INTEGER |
BIGINT |
INTEGER |
float32, float64 |
NUMERIC |
NUMERIC |
NUMERIC |
bool |
BOOLEAN |
TINYINT(1) |
BOOLEAN |
time.Time |
TIMESTAMPTZ |
DATETIME |
DATETIME |
uuid.UUID |
UUID |
CHAR(36) |
TEXT |
json.RawMessage |
JSONB |
JSON |
TEXT |
Override any mapping with the sql tag.
Alter behaviour
ActionAlter compares your struct definition against the live table and generates statements for:
- Columns present in the struct but missing from the table →
ADD COLUMN
- Columns where the Go-mapped type differs from the actual column type →
ALTER COLUMN ... TYPE
Columns present in the table but absent from the struct are left untouched.
Note: ALTER COLUMN ... TYPE can fail if existing data is incompatible with the new type. Review generated statements before running against production data.
Generating SQL without executing
Use the underlying functions directly to inspect generated SQL before running it:
// Create
sql, err := migrate.GenerateCreateTableSQL(User{}, migrate.PostgreSQL)
// Alter / diff
stmts, err := migrate.GenerateSchemaDiffs(db, User{}, migrate.PostgreSQL)
for _, s := range stmts {
fmt.Println(s)
}
// Drop
sql := migrate.DropTableSQL(User{})
License
MIT