proxy-pool/internal/adapters/postgresadmin/migrations.go
2026-07-29 19:55:33 +08:00

70 lines
1.8 KiB
Go

package postgresadmin
import (
"embed"
"fmt"
"path/filepath"
"regexp"
"sort"
"strconv"
"strings"
)
type Migration struct {
Version int
Name string
SQL string
}
//go:embed migrations/*.sql
var migrationFiles embed.FS
var loadedMigrations = mustLoadMigrations()
var migrationNamePattern = regexp.MustCompile(`^(\d{4})_([a-z][a-z0-9_]*)\.sql$`)
func Migrations() []Migration {
return append([]Migration(nil), loadedMigrations...)
}
func mustLoadMigrations() []Migration {
entries, err := migrationFiles.ReadDir("migrations")
if err != nil {
panic(fmt.Sprintf("postgresadmin: read embedded migrations: %v", err))
}
result := make([]Migration, 0, len(entries))
for _, entry := range entries {
if entry.IsDir() {
continue
}
matches := migrationNamePattern.FindStringSubmatch(entry.Name())
if len(matches) != 3 {
panic("postgresadmin: invalid migration filename " + entry.Name())
}
version, err := strconv.Atoi(matches[1])
if err != nil || version <= 0 {
panic("postgresadmin: invalid migration version " + entry.Name())
}
payload, err := migrationFiles.ReadFile(filepath.ToSlash("migrations/" + entry.Name()))
if err != nil {
panic(fmt.Sprintf("postgresadmin: read migration %s: %v", entry.Name(), err))
}
if strings.TrimSpace(string(payload)) == "" {
panic("postgresadmin: empty migration " + entry.Name())
}
result = append(result, Migration{Version: version, Name: matches[2], SQL: string(payload)})
}
sort.Slice(result, func(left, right int) bool {
return result[left].Version < result[right].Version
})
for index := 1; index < len(result); index++ {
if result[index-1].Version == result[index].Version {
panic(fmt.Sprintf("postgresadmin: duplicate migration version %d", result[index].Version))
}
}
if len(result) == 0 {
panic("postgresadmin: no embedded migrations")
}
return result
}