· 5 years ago · Mar 05, 2020, 12:14 PM
1package database
2
3import (
4 "context"
5 "fmt"
6 "github.com/jackc/pgx/v4"
7)
8
9type Migration struct {
10 Version int
11 Sql string
12 Arguments []interface{}
13}
14
15type Migrator struct {
16 migrations []*Migration
17 conn *pgx.Conn
18 tableName string
19}
20
21func CreateMigrator(conn *pgx.Conn) (*Migrator, error) {
22 migrator := &Migrator{
23 migrations: []*Migration{},
24 conn: conn,
25 tableName: "migrator",
26 }
27
28 err := migrator.CreateTable()
29 if err != nil {
30 return nil, err
31 }
32
33 return migrator, nil
34}
35
36func (migrator *Migrator) AddMigration(sql string, arguments ...interface{}) {
37 migration := &Migration{
38 Version: len(migrator.migrations) + 1,
39 Sql: sql,
40 Arguments: arguments,
41 }
42
43 migrator.migrations = append(migrator.migrations, migration)
44}
45
46func (migrator *Migrator) Run() error {
47 lastVersion, err := migrator.getLastVersion()
48 if err != nil {
49 return err
50 }
51
52 for _, migration := range migrator.migrations {
53 if migration.Version > lastVersion {
54 err = migrator.execute(migration)
55 if err != nil {
56 return err
57 }
58 }
59 }
60
61 return nil
62}
63
64func (migrator *Migrator) getLastVersion() (int, error) {
65 var version int
66 err := migrator.conn.QueryRow(context.Background(), fmt.Sprintf("select max(version) version from %s", migrator.tableName)).Scan(&version)
67 if err != nil {
68 return 0, err
69 }
70
71 return version, nil
72}
73
74func (migrator *Migrator) execute(migration *Migration) error {
75 _, err := migrator.conn.Exec(context.Background(), migration.Sql, migration.Arguments...)
76 if err != nil {
77 return err
78 }
79
80 _, err = migrator.conn.Exec(context.Background(), fmt.Sprintf(`
81 insert into %s (version)
82 values (%d)`, migrator.tableName, migration.Version), migration.Arguments...)
83 if err != nil {
84 return err
85 }
86
87 return nil
88}
89
90func (migrator *Migrator) CreateTable() error {
91 _, err := migrator.conn.Exec(context.Background(), fmt.Sprintf(`
92 create table if not exists %s
93 (
94 version int constraint migrator_pk primary key
95 );
96 `, migrator.tableName))
97 if err != nil {
98 return err
99 }
100
101 return nil
102}