90 lines
1.7 KiB
Go
90 lines
1.7 KiB
Go
package store
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"embed"
|
||
|
|
"sort"
|
||
|
|
"strconv"
|
||
|
|
"strings"
|
||
|
|
)
|
||
|
|
|
||
|
|
//go:embed migrations/*.sql
|
||
|
|
var migrationFiles embed.FS
|
||
|
|
|
||
|
|
// Migrate applies any pending SQL migrations in order within a single transaction.
|
||
|
|
func (s *Store) Migrate(ctx context.Context) error {
|
||
|
|
conn, err := s.pool.Acquire(ctx)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
defer conn.Release()
|
||
|
|
|
||
|
|
tx, err := conn.Begin(ctx)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
defer func() {
|
||
|
|
if tx != nil {
|
||
|
|
tx.Rollback(ctx)
|
||
|
|
}
|
||
|
|
}()
|
||
|
|
|
||
|
|
_, err = tx.Exec(ctx, `CREATE TABLE IF NOT EXISTS schema_migrations (
|
||
|
|
version INT PRIMARY KEY
|
||
|
|
)`)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
entries, err := migrationFiles.ReadDir("migrations")
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
type mig struct {
|
||
|
|
ver int
|
||
|
|
name string
|
||
|
|
}
|
||
|
|
var migs []mig
|
||
|
|
for _, e := range entries {
|
||
|
|
name := e.Name()
|
||
|
|
if !strings.HasSuffix(name, ".sql") {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
parts := strings.SplitN(name, "_", 2)
|
||
|
|
v, err := strconv.Atoi(parts[0])
|
||
|
|
if err != nil {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
migs = append(migs, mig{ver: v, name: name})
|
||
|
|
}
|
||
|
|
sort.Slice(migs, func(i, j int) bool { return migs[i].ver < migs[j].ver })
|
||
|
|
|
||
|
|
for _, m := range migs {
|
||
|
|
var exists bool
|
||
|
|
err = tx.QueryRow(ctx, "SELECT EXISTS(SELECT 1 FROM schema_migrations WHERE version=$1)", m.ver).Scan(&exists)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
if exists {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
data, err := migrationFiles.ReadFile("migrations/" + m.name)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
if _, err = tx.Exec(ctx, string(data)); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
if _, err = tx.Exec(ctx, "INSERT INTO schema_migrations(version) VALUES($1)", m.ver); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if err = tx.Commit(ctx); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
tx = nil
|
||
|
|
return nil
|
||
|
|
}
|