/
migrate.go
133 lines (106 loc) · 2.82 KB
/
migrate.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
package migrate
import (
"database/sql"
"github.com/dnote/cli/infra"
"github.com/dnote/cli/log"
"github.com/pkg/errors"
)
const (
// LocalMode is a local migration mode
LocalMode = iota
// RemoteMode is a remote migration mode
RemoteMode
)
// LocalSequence is a list of local migrations to be run
var LocalSequence = []migration{
lm1,
lm2,
lm3,
lm4,
lm5,
lm6,
lm7,
lm8,
lm9,
}
// RemoteSequence is a list of remote migrations to be run
var RemoteSequence = []migration{
rm1,
}
func initSchema(ctx infra.DnoteCtx, schemaKey string) (int, error) {
// schemaVersion is the index of the latest run migration in the sequence
schemaVersion := 0
db := ctx.DB
_, err := db.Exec("INSERT INTO system (key, value) VALUES (?, ?)", schemaKey, schemaVersion)
if err != nil {
return schemaVersion, errors.Wrap(err, "inserting schema")
}
return schemaVersion, nil
}
func getSchemaKey(mode int) (string, error) {
if mode == LocalMode {
return infra.SystemSchema, nil
}
if mode == RemoteMode {
return infra.SystemRemoteSchema, nil
}
return "", errors.Errorf("unsupported migration type '%d'", mode)
}
func getSchema(ctx infra.DnoteCtx, schemaKey string) (int, error) {
var ret int
db := ctx.DB
err := db.QueryRow("SELECT value FROM system where key = ?", schemaKey).Scan(&ret)
if err == sql.ErrNoRows {
ret, err = initSchema(ctx, schemaKey)
if err != nil {
return ret, errors.Wrap(err, "initializing schema")
}
} else if err != nil {
return ret, errors.Wrap(err, "querying schema")
}
return ret, nil
}
func execute(ctx infra.DnoteCtx, m migration, schemaKey string) error {
log.Debug("running migration %s\n", m.name)
tx, err := ctx.DB.Begin()
if err != nil {
return errors.Wrap(err, "beginning a transaction")
}
err = m.run(ctx, tx)
if err != nil {
tx.Rollback()
return errors.Wrapf(err, "running '%s'", m.name)
}
var currentSchema int
err = tx.QueryRow("SELECT value FROM system WHERE key = ?", schemaKey).Scan(¤tSchema)
if err != nil {
tx.Rollback()
return errors.Wrap(err, "getting current schema")
}
_, err = tx.Exec("UPDATE system SET value = value + 1 WHERE key = ?", schemaKey)
if err != nil {
tx.Rollback()
return errors.Wrap(err, "incrementing schema")
}
tx.Commit()
return nil
}
// Run performs unrun migrations
func Run(ctx infra.DnoteCtx, migrations []migration, mode int) error {
schemaKey, err := getSchemaKey(mode)
if err != nil {
return errors.Wrap(err, "getting schema key")
}
schema, err := getSchema(ctx, schemaKey)
if err != nil {
return errors.Wrap(err, "getting the current schema")
}
log.Debug("current schema: %s %d of %d\n", infra.SystemSchema, schema, len(migrations))
toRun := migrations[schema:]
for _, m := range toRun {
if err := execute(ctx, m, schemaKey); err != nil {
return errors.Wrap(err, "running migration")
}
}
return nil
}