Newer
Older
"github.com/jmoiron/sqlx"
//go:embed migrations/*.sql
var migrationFS embed.FS
// Make sure that we have a table for the migrations
tx, err := db.BeginTxx(ctx, nil)
if err != nil {
return fmt.Errorf("creating tx: %w", err)
}
defer func() {
if err != nil {
rerr := tx.Rollback()
if rerr != nil {
log.WithError(rerr).
Error("can't roll back transaction")
}
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
}()
_, err = tx.ExecContext(ctx, `CREATE TABLE IF NOT EXISTS migrations (name string)`)
if err != nil {
return fmt.Errorf("creating migrations table: %w", err)
}
_, err = tx.ExecContext(ctx, `CREATE UNIQUE INDEX IF NOT EXISTS migrations_name ON migrations (name)`)
if err != nil {
return fmt.Errorf("creating migrations index: %w", err)
}
entries, err := migrationFS.ReadDir("migrations")
if err != nil {
return fmt.Errorf("reading embedded migration FS: %w", err)
}
sort.Slice(entries, func(i, j int) bool {
return entries[i].Name() < entries[j].Name()
})
for _, e := range entries {
// Check if we need to apply that migration
var count int
err = tx.GetContext(ctx, &count, `SELECT count(*) FROM migrations WHERE name = ?1`, e.Name())
if err != nil {
return fmt.Errorf("checking for migration %q: %w", e.Name(), err)
}
if count > 1 {
return fmt.Errorf("migration %q applied more than once", e.Name())
}
if count == 1 {
continue
}
content, err := fs.ReadFile(migrationFS, "migrations/"+e.Name())
if err != nil {
return fmt.Errorf("reading migration %q: %w", e.Name(), err)
}
_, err = tx.ExecContext(ctx, string(content))
if err != nil {
return fmt.Errorf("applying migration %q: %w", e.Name(), err)
}
// Mark migration as applied
_, err = tx.ExecContext(ctx, `INSERT INTO migrations (name) VALUES (?1)`, e.Name())
if err != nil {
return fmt.Errorf("marking migration %q as applied: %w", e.Name(), err)
}
}
return nil
}