Newer
Older
"github.com/jmoiron/sqlx"
//go:embed migrations/*.sql
var migrationFS embed.FS
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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
// 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 {
tx.Rollback()
return
}
tx.Commit()
}()
_, 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
}