mirror of
https://github.com/therootcompany/golib.git
synced 2026-11-04 06:55:00 +00:00
feat(mymigrate/examples): add minimal migration runner example
Demonstrates mymigrate with embedded SQL migrations: up, down, status, and reset commands. Two migrations (init + create-todos) show the minimum viable pattern for using sqlmigrate with MySQL/MariaDB. Verified against MariaDB 11.4.9 via sclient --alpn mysql.
This commit is contained in:
parent
575617e8b8
commit
818871ed76
16
database/sqlmigrate/mymigrate/examples/todos/go.mod
Normal file
16
database/sqlmigrate/mymigrate/examples/todos/go.mod
Normal file
@ -0,0 +1,16 @@
|
||||
module github.com/therootcompany/golib/database/sqlmigrate/mymigrate/examples/todos
|
||||
|
||||
go 1.26.1
|
||||
|
||||
require (
|
||||
github.com/go-sql-driver/mysql v1.9.3
|
||||
github.com/therootcompany/golib/database/sqlmigrate v1.0.0
|
||||
github.com/therootcompany/golib/database/sqlmigrate/mymigrate v0.0.0
|
||||
)
|
||||
|
||||
require filippo.io/edwards25519 v1.1.0 // indirect
|
||||
|
||||
replace (
|
||||
github.com/therootcompany/golib/database/sqlmigrate => ../../..
|
||||
github.com/therootcompany/golib/database/sqlmigrate/mymigrate => ../..
|
||||
)
|
||||
4
database/sqlmigrate/mymigrate/examples/todos/go.sum
Normal file
4
database/sqlmigrate/mymigrate/examples/todos/go.sum
Normal file
@ -0,0 +1,4 @@
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
|
||||
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
||||
149
database/sqlmigrate/mymigrate/examples/todos/main.go
Normal file
149
database/sqlmigrate/mymigrate/examples/todos/main.go
Normal file
@ -0,0 +1,149 @@
|
||||
// Command todos demonstrates mymigrate with embedded SQL migrations.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// export MY_URL='user:pass@tcp(host:3306)/dbname?multiStatements=true&parseTime=true'
|
||||
// go run . up # apply all pending migrations
|
||||
// go run . up 1 # apply next 1 migration
|
||||
// go run . down # roll back last migration
|
||||
// go run . down 2 # roll back last 2 migrations
|
||||
// go run . status # show applied and pending
|
||||
// go run . reset # roll back all migrations
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"embed"
|
||||
"fmt"
|
||||
stdfs "io/fs"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
|
||||
"github.com/therootcompany/golib/database/sqlmigrate"
|
||||
"github.com/therootcompany/golib/database/sqlmigrate/mymigrate"
|
||||
)
|
||||
|
||||
//go:embed migrations/*.sql
|
||||
var migrationsFS embed.FS
|
||||
|
||||
func main() {
|
||||
if err := run(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func run() error {
|
||||
ctx := context.Background()
|
||||
|
||||
myURL := os.Getenv("MY_URL")
|
||||
if myURL == "" {
|
||||
return fmt.Errorf("MY_URL environment variable is required\n Format: user:pass@tcp(host:port)/dbname?multiStatements=true&parseTime=true")
|
||||
}
|
||||
|
||||
db, err := sql.Open("mysql", myURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("opening database: %w", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
if err := db.PingContext(ctx); err != nil {
|
||||
return fmt.Errorf("connecting: %w", err)
|
||||
}
|
||||
|
||||
runner, err := mymigrate.New(db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
subFS, err := stdfs.Sub(migrationsFS, "migrations")
|
||||
if err != nil {
|
||||
return fmt.Errorf("reading migrations: %w", err)
|
||||
}
|
||||
migrations, err := sqlmigrate.Collect(subFS)
|
||||
if err != nil {
|
||||
return fmt.Errorf("collecting migrations: %w", err)
|
||||
}
|
||||
|
||||
args := os.Args[1:]
|
||||
if len(args) == 0 {
|
||||
args = []string{"status"}
|
||||
}
|
||||
|
||||
switch args[0] {
|
||||
case "up":
|
||||
n := -1
|
||||
if len(args) > 1 {
|
||||
n, err = strconv.Atoi(args[1])
|
||||
if err != nil || n < 1 {
|
||||
return fmt.Errorf("up count must be a positive integer")
|
||||
}
|
||||
}
|
||||
applied, err := sqlmigrate.Up(ctx, runner, migrations, n)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(applied) == 0 {
|
||||
fmt.Println("Already up-to-date.")
|
||||
} else {
|
||||
for _, name := range applied {
|
||||
fmt.Printf(" applied: %s\n", name)
|
||||
}
|
||||
}
|
||||
|
||||
case "down":
|
||||
n := 1
|
||||
if len(args) > 1 {
|
||||
n, err = strconv.Atoi(args[1])
|
||||
if err != nil || n < 1 {
|
||||
return fmt.Errorf("down count must be a positive integer")
|
||||
}
|
||||
}
|
||||
rolled, err := sqlmigrate.Down(ctx, runner, migrations, n)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(rolled) == 0 {
|
||||
fmt.Println("Nothing to roll back.")
|
||||
} else {
|
||||
for _, name := range rolled {
|
||||
fmt.Printf(" rolled back: %s\n", name)
|
||||
}
|
||||
}
|
||||
|
||||
case "status":
|
||||
status, err := sqlmigrate.GetStatus(ctx, runner, migrations)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Applied: %d\n", len(status.Applied))
|
||||
for _, name := range status.Applied {
|
||||
fmt.Printf(" %s\n", name)
|
||||
}
|
||||
fmt.Printf("Pending: %d\n", len(status.Pending))
|
||||
for _, name := range status.Pending {
|
||||
fmt.Printf(" %s\n", name)
|
||||
}
|
||||
|
||||
case "reset":
|
||||
rolled, err := sqlmigrate.Down(ctx, runner, migrations, -1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(rolled) == 0 {
|
||||
fmt.Println("Nothing to reset.")
|
||||
} else {
|
||||
for _, name := range rolled {
|
||||
fmt.Printf(" rolled back: %s\n", name)
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unknown command %q (use: up, down, status, reset)", args[0])
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
DELETE FROM _migrations WHERE id = '00000001';
|
||||
DROP TABLE IF EXISTS _migrations;
|
||||
@ -0,0 +1,7 @@
|
||||
CREATE TABLE IF NOT EXISTS _migrations (
|
||||
id CHAR(8) PRIMARY KEY,
|
||||
name VARCHAR(80) NULL UNIQUE,
|
||||
applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
INSERT INTO _migrations (name, id) VALUES ('0001_init-migrations', '00000001');
|
||||
@ -0,0 +1,2 @@
|
||||
DROP TABLE IF EXISTS todos;
|
||||
DELETE FROM _migrations WHERE id = 'a1b2c3d4';
|
||||
@ -0,0 +1,8 @@
|
||||
CREATE TABLE todos (
|
||||
id CHAR(8) PRIMARY KEY,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
done BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
INSERT INTO _migrations (name, id) VALUES ('0002_create-todos', 'a1b2c3d4');
|
||||
Loading…
x
Reference in New Issue
Block a user