• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

golang-migrate / migrate / 5186575159

pending completion
5186575159

Pull #932

github

longit644
Add Golang function as a source of migration
Pull Request #932: Add Golang function as a source of migration

247 of 367 new or added lines in 32 files covered. (67.3%)

44 existing lines in 5 files now uncovered.

4217 of 7234 relevant lines covered (58.29%)

61.49 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

65.0
/database/sqlcipher/sqlcipher.go
1
package sqlcipher
2

3
import (
4
        "database/sql"
5
        "fmt"
6
        "io"
7
        nurl "net/url"
8
        "strconv"
9
        "strings"
10

11
        "go.uber.org/atomic"
12

13
        "github.com/golang-migrate/migrate/v4"
14
        "github.com/golang-migrate/migrate/v4/database"
15
        "github.com/golang-migrate/migrate/v4/source"
16
        "github.com/hashicorp/go-multierror"
17
        _ "github.com/mutecomm/go-sqlcipher/v4"
18
)
19

20
func init() {
2✔
21
        database.Register("sqlcipher", &Sqlite{})
2✔
22
}
2✔
23

24
var DefaultMigrationsTable = "schema_migrations"
25
var (
26
        ErrDatabaseDirty  = fmt.Errorf("database is dirty")
27
        ErrNilConfig      = fmt.Errorf("no config")
28
        ErrNoDatabaseName = fmt.Errorf("no database name")
29
)
30

31
type Config struct {
32
        MigrationsTable string
33
        DatabaseName    string
34
        NoTxWrap        bool
35
}
36

37
type Sqlite struct {
38
        db       *sql.DB
39
        isLocked atomic.Bool
40

41
        config *Config
42
}
43

44
func WithInstance(instance *sql.DB, config *Config) (database.Driver, error) {
8✔
45
        if config == nil {
8✔
46
                return nil, ErrNilConfig
×
47
        }
×
48

49
        if err := instance.Ping(); err != nil {
8✔
50
                return nil, err
×
51
        }
×
52

53
        if len(config.MigrationsTable) == 0 {
10✔
54
                config.MigrationsTable = DefaultMigrationsTable
2✔
55
        }
2✔
56

57
        mx := &Sqlite{
8✔
58
                db:     instance,
8✔
59
                config: config,
8✔
60
        }
8✔
61
        if err := mx.ensureVersionTable(); err != nil {
8✔
62
                return nil, err
×
63
        }
×
64
        return mx, nil
8✔
65
}
66

67
// ensureVersionTable checks if versions table exists and, if not, creates it.
68
// Note that this function locks the database, which deviates from the usual
69
// convention of "caller locks" in the Sqlite type.
70
func (s *Sqlite) ensureVersionTable() (err error) {
8✔
71
        if err = s.Lock(); err != nil {
8✔
72
                return err
×
73
        }
×
74

75
        defer func() {
16✔
76
                if e := s.Unlock(); e != nil {
8✔
77
                        if err == nil {
×
78
                                err = e
×
79
                        } else {
×
80
                                err = multierror.Append(err, e)
×
81
                        }
×
82
                }
83
        }()
84

85
        query := fmt.Sprintf(`
8✔
86
        CREATE TABLE IF NOT EXISTS %s (version uint64,dirty bool);
8✔
87
  CREATE UNIQUE INDEX IF NOT EXISTS version_unique ON %s (version);
8✔
88
  `, s.config.MigrationsTable, s.config.MigrationsTable)
8✔
89

8✔
90
        if _, err := s.db.Exec(query); err != nil {
8✔
91
                return err
×
92
        }
×
93
        return nil
8✔
94
}
95

96
func (s *Sqlite) Open(url string) (database.Driver, error) {
6✔
97
        purl, err := nurl.Parse(url)
6✔
98
        if err != nil {
6✔
99
                return nil, err
×
100
        }
×
101
        dbfile := strings.Replace(migrate.FilterCustomQuery(purl).String(), "sqlite3://", "", 1)
6✔
102
        db, err := sql.Open("sqlite3", dbfile)
6✔
103
        if err != nil {
6✔
104
                return nil, err
×
105
        }
×
106

107
        qv := purl.Query()
6✔
108

6✔
109
        migrationsTable := qv.Get("x-migrations-table")
6✔
110
        if len(migrationsTable) == 0 {
12✔
111
                migrationsTable = DefaultMigrationsTable
6✔
112
        }
6✔
113

114
        noTxWrap := false
6✔
115
        if v := qv.Get("x-no-tx-wrap"); v != "" {
10✔
116
                noTxWrap, err = strconv.ParseBool(v)
4✔
117
                if err != nil {
6✔
118
                        return nil, fmt.Errorf("x-no-tx-wrap: %s", err)
2✔
119
                }
2✔
120
        }
121

122
        mx, err := WithInstance(db, &Config{
4✔
123
                DatabaseName:    purl.Path,
4✔
124
                MigrationsTable: migrationsTable,
4✔
125
                NoTxWrap:        noTxWrap,
4✔
126
        })
4✔
127
        if err != nil {
4✔
128
                return nil, err
×
129
        }
×
130
        return mx, nil
4✔
131
}
132

NEW
133
func (s *Sqlite) Close() error {
×
NEW
134
        return s.db.Close()
×
UNCOV
135
}
×
136

137
func (s *Sqlite) Drop() (err error) {
6✔
138
        query := `SELECT name FROM sqlite_master WHERE type = 'table';`
6✔
139
        tables, err := s.db.Query(query)
6✔
140
        if err != nil {
6✔
141
                return &database.Error{OrigErr: err, Query: []byte(query)}
×
142
        }
×
143
        defer func() {
12✔
144
                if errClose := tables.Close(); errClose != nil {
6✔
145
                        err = multierror.Append(err, errClose)
×
146
                }
×
147
        }()
148

149
        tableNames := make([]string, 0)
6✔
150
        for tables.Next() {
18✔
151
                var tableName string
12✔
152
                if err := tables.Scan(&tableName); err != nil {
12✔
153
                        return err
×
154
                }
×
155
                if len(tableName) > 0 {
24✔
156
                        tableNames = append(tableNames, tableName)
12✔
157
                }
12✔
158
        }
159
        if err := tables.Err(); err != nil {
6✔
160
                return &database.Error{OrigErr: err, Query: []byte(query)}
×
161
        }
×
162

163
        if len(tableNames) > 0 {
12✔
164
                for _, t := range tableNames {
18✔
165
                        query := "DROP TABLE " + t
12✔
166
                        err = s.executeQuery(query)
12✔
167
                        if err != nil {
12✔
168
                                return &database.Error{OrigErr: err, Query: []byte(query)}
×
169
                        }
×
170
                }
171
                query := "VACUUM"
6✔
172
                _, err = s.db.Query(query)
6✔
173
                if err != nil {
6✔
174
                        return &database.Error{OrigErr: err, Query: []byte(query)}
×
175
                }
×
176
        }
177

178
        return nil
6✔
179
}
180

181
func (s *Sqlite) Lock() error {
26✔
182
        if !s.isLocked.CAS(false, true) {
30✔
183
                return database.ErrLocked
4✔
184
        }
4✔
185
        return nil
22✔
186
}
187

188
func (s *Sqlite) Unlock() error {
22✔
189
        if !s.isLocked.CAS(true, false) {
22✔
190
                return database.ErrNotLocked
×
191
        }
×
192
        return nil
22✔
193
}
194

195
func (s *Sqlite) Run(migration io.Reader) error {
12✔
196
        migr, err := io.ReadAll(migration)
12✔
197
        if err != nil {
12✔
198
                return err
×
199
        }
×
200
        query := string(migr[:])
12✔
201

12✔
202
        if s.config.NoTxWrap {
14✔
203
                return s.executeQueryNoTx(query)
2✔
204
        }
2✔
205
        return s.executeQuery(query)
10✔
206
}
207

208
func (s *Sqlite) executeQuery(query string) error {
22✔
209
        tx, err := s.db.Begin()
22✔
210
        if err != nil {
22✔
211
                return &database.Error{OrigErr: err, Err: "transaction start failed"}
×
212
        }
×
213
        if _, err := tx.Exec(query); err != nil {
22✔
214
                if errRollback := tx.Rollback(); errRollback != nil {
×
215
                        err = multierror.Append(err, errRollback)
×
216
                }
×
217
                return &database.Error{OrigErr: err, Query: []byte(query)}
×
218
        }
219
        if err := tx.Commit(); err != nil {
22✔
220
                return &database.Error{OrigErr: err, Err: "transaction commit failed"}
×
221
        }
×
222
        return nil
22✔
223
}
224

225
func (s *Sqlite) executeQueryNoTx(query string) error {
2✔
226
        if _, err := s.db.Exec(query); err != nil {
2✔
227
                return &database.Error{OrigErr: err, Query: []byte(query)}
×
228
        }
×
229
        return nil
2✔
230
}
231

232
func (s *Sqlite) SetVersion(version int, dirty bool) error {
40✔
233
        tx, err := s.db.Begin()
40✔
234
        if err != nil {
40✔
235
                return &database.Error{OrigErr: err, Err: "transaction start failed"}
×
236
        }
×
237

238
        query := "DELETE FROM " + s.config.MigrationsTable
40✔
239
        if _, err := tx.Exec(query); err != nil {
40✔
240
                return &database.Error{OrigErr: err, Query: []byte(query)}
×
241
        }
×
242

243
        // Also re-write the schema version for nil dirty versions to prevent
244
        // empty schema version for failed down migration on the first migration
245
        // See: https://github.com/golang-migrate/migrate/issues/330
246
        if version >= 0 || (version == database.NilVersion && dirty) {
76✔
247
                query := fmt.Sprintf(`INSERT INTO %s (version, dirty) VALUES (?, ?)`, s.config.MigrationsTable)
36✔
248
                if _, err := tx.Exec(query, version, dirty); err != nil {
36✔
249
                        if errRollback := tx.Rollback(); errRollback != nil {
×
250
                                err = multierror.Append(err, errRollback)
×
251
                        }
×
252
                        return &database.Error{OrigErr: err, Query: []byte(query)}
×
253
                }
254
        }
255

256
        if err := tx.Commit(); err != nil {
40✔
257
                return &database.Error{OrigErr: err, Err: "transaction commit failed"}
×
258
        }
×
259

260
        return nil
40✔
261
}
262

263
func (s *Sqlite) Version() (version int, dirty bool, err error) {
32✔
264
        query := "SELECT version, dirty FROM " + s.config.MigrationsTable + " LIMIT 1"
32✔
265
        err = s.db.QueryRow(query).Scan(&version, &dirty)
32✔
266
        if err != nil {
44✔
267
                return database.NilVersion, false, nil
12✔
268
        }
12✔
269
        return version, dirty, nil
20✔
270
}
271

NEW
272
func (s *Sqlite) Exec(e source.Executor) error {
×
NEW
273
        return e.Execute(s.db)
×
NEW
274
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc