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

golang-migrate / migrate / 17178144630

23 Aug 2025 05:20PM UTC coverage: 56.309% (-0.005%) from 56.314%
17178144630

Pull #1308

github

chandrakant-cohesity
Fixed defer block
Pull Request #1308: Ensure bufferWriter is always closed in Migration.Buffer and propagate close errors

7 of 11 new or added lines in 1 file covered. (63.64%)

557 existing lines in 16 files now uncovered.

4561 of 8100 relevant lines covered (56.31%)

50.12 hits per line

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

66.1
/database/sqlite3/sqlite3.go
1
package sqlite3
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/hashicorp/go-multierror"
16
        _ "github.com/mattn/go-sqlite3"
17
)
18

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

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

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

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

40
        config *Config
41
}
42

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

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

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

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

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

20✔
74
        defer func() {
10✔
UNCOV
75
                if e := m.Unlock(); e != nil {
×
76
                        if err == nil {
×
77
                                err = e
×
78
                        } else {
×
79
                                err = multierror.Append(err, e)
×
80
                        }
81
                }
82
        }()
83

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

10✔
UNCOV
89
        if _, err := m.db.Exec(query); err != nil {
×
90
                return err
×
91
        }
10✔
92
        return nil
93
}
94

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

8✔
106
        qv := purl.Query()
8✔
107

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

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

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

×
132
func (m *Sqlite) Close() error {
×
133
        return m.db.Close()
×
134
}
135

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

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

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

8✔
177
        return nil
178
}
179

34✔
180
func (m *Sqlite) Lock() error {
40✔
181
        if !m.isLocked.CAS(false, true) {
6✔
182
                return database.ErrLocked
6✔
183
        }
28✔
184
        return nil
185
}
186

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

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

16✔
201
        if m.config.NoTxWrap {
2✔
202
                return m.executeQueryNoTx(query)
2✔
203
        }
12✔
204
        return m.executeQuery(query)
205
}
206

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

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

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

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

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

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

52✔
259
        return nil
260
}
261

46✔
262
func (m *Sqlite) Version() (version int, dirty bool, err error) {
46✔
263
        query := "SELECT version, dirty FROM " + m.config.MigrationsTable + " LIMIT 1"
46✔
264
        err = m.db.QueryRow(query).Scan(&version, &dirty)
62✔
265
        if err != nil {
16✔
266
                return database.NilVersion, false, nil
16✔
267
        }
30✔
268
        return version, dirty, nil
269
}
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

© 2026 Coveralls, Inc