+55
appview/db/db.go
+55
appview/db/db.go
···
3
3
import (
4
4
"context"
5
5
"database/sql"
6
+
"log"
6
7
7
8
_ "github.com/mattn/go-sqlite3"
8
9
)
···
122
123
unique(starred_by_did, repo_at)
123
124
);
124
125
126
+
create table if not exists migrations (
127
+
id integer primary key autoincrement,
128
+
name text unique
129
+
)
125
130
`)
126
131
if err != nil {
127
132
return nil, err
128
133
}
134
+
135
+
// run migrations
136
+
runMigration(db, "add-description-to-repos", func(tx *sql.Tx) error {
137
+
tx.Exec(`
138
+
alter table repos add column description text check (length(description) <= 200);
139
+
`)
140
+
return nil
141
+
})
142
+
129
143
return &DB{db}, nil
130
144
}
145
+
146
+
type migrationFn = func(*sql.Tx) error
147
+
148
+
func runMigration(d *sql.DB, name string, migrationFn migrationFn) error {
149
+
tx, err := d.Begin()
150
+
if err != nil {
151
+
return err
152
+
}
153
+
defer tx.Rollback()
154
+
155
+
var exists bool
156
+
err = tx.QueryRow("select exists (select 1 from migrations where name = ?)", name).Scan(&exists)
157
+
if err != nil {
158
+
return err
159
+
}
160
+
161
+
if !exists {
162
+
// run migration
163
+
err = migrationFn(tx)
164
+
if err != nil {
165
+
log.Printf("Failed to run migration %s: %v", name, err)
166
+
return err
167
+
}
168
+
169
+
// mark migration as complete
170
+
_, err = tx.Exec("insert into migrations (name) values (?)", name)
171
+
if err != nil {
172
+
log.Printf("Failed to mark migration %s as complete: %v", name, err)
173
+
return err
174
+
}
175
+
176
+
// commit the transaction
177
+
if err := tx.Commit(); err != nil {
178
+
return err
179
+
}
180
+
} else {
181
+
log.Printf("skipped migration %s, already applied", name)
182
+
}
183
+
184
+
return nil
185
+
}