-
Notifications
You must be signed in to change notification settings - Fork 0
/
dbmigrate.go
135 lines (115 loc) · 3.52 KB
/
dbmigrate.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package dbmigrate
import (
"fmt"
"sort"
)
// VendorAdapter is the set of functionality needed to implement a compatibility with a database
// vendor
type VendorAdapter interface {
GetAppliedMigrationsOrderedAsc() ([]string, error)
ApplyMigration(pair MigrationPair) error
RollbackMigration(pair MigrationPair) error
}
// Storage is an interface for where the migrations are stored
type Storage interface {
GetMigrationPairs() ([]MigrationPair, error)
}
// MigrationPair stores a pair of apply/rollback migrations
type MigrationPair struct {
Version string
Name string
ApplyBody string
RollbackBody string
}
// DBMigrate stores all the configuration needed to run migrations
type DBMigrate struct {
sortedMigrationPairs []MigrationPair
migrationPairs map[string]MigrationPair
allVersions []string
adapter VendorAdapter
storage Storage
verbose bool // TODO
}
type migrationPairs []MigrationPair
func (a migrationPairs) Len() int { return len(a) }
func (a migrationPairs) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a migrationPairs) Less(i, j int) bool { return a[i].Version < a[j].Version }
// New returns an initialized DBMigrate instance.
func New(adapter VendorAdapter, storage Storage) (*DBMigrate, error) {
dbMigrate := DBMigrate{
adapter: adapter,
storage: storage,
}
if migrationPairs, err := storage.GetMigrationPairs(); err == nil {
dbMigrate.sortedMigrationPairs = migrationPairs
} else {
return nil, err
}
sort.Sort(migrationPairs(dbMigrate.sortedMigrationPairs))
dbMigrate.migrationPairs = map[string]MigrationPair{}
for _, m := range dbMigrate.sortedMigrationPairs {
dbMigrate.migrationPairs[m.Version] = m
}
return &dbMigrate, nil
}
// ApplyAll applies all migrations versions not yet applied in ascending
// alphanumeric order
func (dbMigrate *DBMigrate) ApplyAll() error {
return dbMigrate.apply(true)
}
// ApplyOne applies the next not yet applied migrations going in ascending
// alphenermic order
func (dbMigrate *DBMigrate) ApplyOne() error {
return dbMigrate.apply(false)
}
func (dbMigrate *DBMigrate) apply(all bool) error {
appliedVersions, err := dbMigrate.adapter.GetAppliedMigrationsOrderedAsc()
if err != nil {
return err
}
// TODO this could be more efficient
versionsToApply := []string{}
for version := range dbMigrate.migrationPairs {
if !stringInSlice(version, appliedVersions) {
versionsToApply = append(versionsToApply, version)
}
}
sort.Sort(sort.StringSlice(versionsToApply))
for _, version := range versionsToApply {
fmt.Println("Applying migration", dbMigrate.migrationPairs[version].Name)
err = dbMigrate.adapter.ApplyMigration(dbMigrate.migrationPairs[version])
if err != nil {
return err
}
if !all {
return nil
}
}
return nil
}
// RollbackLatest rolls back the latest applied migration
func (dbMigrate *DBMigrate) RollbackLatest() error {
appliedVersions, err := dbMigrate.adapter.GetAppliedMigrationsOrderedAsc()
if err != nil {
return err
}
for i := len(dbMigrate.sortedMigrationPairs) - 1; i >= 0; i-- {
if stringInSlice(dbMigrate.sortedMigrationPairs[i].Version, appliedVersions) {
fmt.Println("rolling back migration", dbMigrate.sortedMigrationPairs[i].Name)
err = dbMigrate.adapter.RollbackMigration(dbMigrate.sortedMigrationPairs[i])
if err != nil {
return err
}
return nil
}
}
return nil
}
func stringInSlice(needle string, haystack []string) bool {
for _, s := range haystack {
if s == needle {
return true
}
}
return false
}