arche / internal/store/migrate/migrate.go

commit a22ffc45
  1package migrate
  2
  3import (
  4	"database/sql"
  5	_ "embed"
  6	"fmt"
  7	"time"
  8)
  9
 10//go:embed sql/001_initial.sql
 11var sql001 string
 12
 13//go:embed sql/002_conflicts.sql
 14var sql002 string
 15
 16//go:embed sql/003_wcache_mode.sql
 17var sql003 string
 18
 19//go:embed sql/004_pack_delta.sql
 20var sql004 string
 21
 22//go:embed sql/005_zstd_dict.sql
 23var sql005 string
 24
 25//go:embed sql/006_file_locks.sql
 26var sql006 string
 27
 28//go:embed sql/007_wcache_dirty.sql
 29var sql007 string
 30
 31//go:embed sql/008_shelves.sql
 32var sql008 string
 33
 34//go:embed sql/009_watcher_pid.sql
 35var sql009 string
 36
 37type migration struct {
 38	version int
 39	sql     string
 40}
 41
 42var all = []migration{
 43	{1, sql001},
 44	{2, sql002},
 45	{3, sql003},
 46	{4, sql004},
 47	{5, sql005},
 48	{6, sql006},
 49	{7, sql007},
 50	{8, sql008},
 51	{9, sql009},
 52}
 53
 54func Run(db *sql.DB) error {
 55	if err := ensureMigrationsTable(db); err != nil {
 56		return err
 57	}
 58
 59	applied, err := appliedVersions(db)
 60	if err != nil {
 61		return err
 62	}
 63
 64	for _, m := range all {
 65		if applied[m.version] {
 66			continue
 67		}
 68		if err := applyMigration(db, m); err != nil {
 69			return fmt.Errorf("migrate v%d: %w", m.version, err)
 70		}
 71	}
 72	return nil
 73}
 74
 75func ensureMigrationsTable(db *sql.DB) error {
 76	_, err := db.Exec(`CREATE TABLE IF NOT EXISTS schema_migrations (
 77		version    INTEGER PRIMARY KEY,
 78		applied_at INTEGER NOT NULL
 79	)`)
 80	return err
 81}
 82
 83func appliedVersions(db *sql.DB) (map[int]bool, error) {
 84	rows, err := db.Query("SELECT version FROM schema_migrations")
 85	if err != nil {
 86		return nil, err
 87	}
 88	defer rows.Close()
 89
 90	m := make(map[int]bool)
 91	for rows.Next() {
 92		var v int
 93		if err := rows.Scan(&v); err != nil {
 94			return nil, err
 95		}
 96		m[v] = true
 97	}
 98	return m, rows.Err()
 99}
100
101func applyMigration(db *sql.DB, m migration) error {
102	tx, err := db.Begin()
103	if err != nil {
104		return err
105	}
106
107	if _, err := tx.Exec(m.sql); err != nil {
108		tx.Rollback()
109		return fmt.Errorf("exec: %w", err)
110	}
111
112	if _, err := tx.Exec(
113		"INSERT INTO schema_migrations (version, applied_at) VALUES (?, ?)",
114		m.version, time.Now().Unix(),
115	); err != nil {
116		tx.Rollback()
117		return fmt.Errorf("record: %w", err)
118	}
119
120	return tx.Commit()
121}