@@ -2,6 +2,7 @@ package mysqldump
2
2
3
3
import (
4
4
"bytes"
5
+ "context"
5
6
"database/sql"
6
7
"errors"
7
8
"fmt"
@@ -14,9 +15,11 @@ import (
14
15
/*
15
16
Data struct to configure dump behavior
16
17
17
- Out: Stream to wite to
18
- Connection: Database connection to dump
19
- IgnoreTables: Mark sensitive tables to ignore
18
+ Out: Stream to wite to
19
+ Connection: Database connection to dump
20
+ IgnoreTables: Mark sensitive tables to ignore
21
+ MaxAllowedPacket: Sets the largest packet size to use in backups
22
+ LockTables: Lock all tables for the duration of the dump
20
23
*/
21
24
type Data struct {
22
25
Out io.Writer
@@ -25,6 +28,7 @@ type Data struct {
25
28
MaxAllowedPacket int
26
29
LockTables bool
27
30
31
+ tx * sql.Tx
28
32
headerTmpl * template.Template
29
33
tableTmpl * template.Template
30
34
footerTmpl * template.Template
@@ -123,11 +127,19 @@ func (data *Data) Dump() error {
123
127
data .MaxAllowedPacket = defaultMaxAllowedPacket
124
128
}
125
129
126
- if err := meta . updateServerVersion ( data .Connection ); err != nil {
130
+ if err := data .getTemplates ( ); err != nil {
127
131
return err
128
132
}
129
133
130
- if err := data .getTemplates (); err != nil {
134
+ // Start the read only transaction and defer the rollback until the end
135
+ // This way the database will have the exact state it did at the begining of
136
+ // the backup and nothing can be accidentally committed
137
+ if err := data .begin (); err != nil {
138
+ return err
139
+ }
140
+ defer data .rollback ()
141
+
142
+ if err := meta .updateServerVersion (data ); err != nil {
131
143
return err
132
144
}
133
145
@@ -173,6 +185,21 @@ func (data *Data) Dump() error {
173
185
174
186
// MARK: - Private methods
175
187
188
+ // begin starts a read only transaction that will be whatever the database was
189
+ // when it was called
190
+ func (data * Data ) begin () (err error ) {
191
+ data .tx , err = data .Connection .BeginTx (context .Background (), & sql.TxOptions {
192
+ Isolation : sql .LevelRepeatableRead ,
193
+ ReadOnly : true ,
194
+ })
195
+ return
196
+ }
197
+
198
+ // rollback cancels the transaction
199
+ func (data * Data ) rollback () error {
200
+ return data .tx .Rollback ()
201
+ }
202
+
176
203
// MARK: writter methods
177
204
178
205
func (data * Data ) dumpTable (name string ) error {
@@ -214,7 +241,7 @@ func (data *Data) getTemplates() (err error) {
214
241
func (data * Data ) getTables () ([]string , error ) {
215
242
tables := make ([]string , 0 )
216
243
217
- rows , err := data .Connection .Query ("SHOW TABLES" )
244
+ rows , err := data .tx .Query ("SHOW TABLES" )
218
245
if err != nil {
219
246
return tables , err
220
247
}
@@ -241,10 +268,10 @@ func (data *Data) isIgnoredTable(name string) bool {
241
268
return false
242
269
}
243
270
244
- func (data * metaData ) updateServerVersion (db * sql. DB ) (err error ) {
271
+ func (meta * metaData ) updateServerVersion (data * Data ) (err error ) {
245
272
var serverVersion sql.NullString
246
- err = db .QueryRow ("SELECT version()" ).Scan (& serverVersion )
247
- data .ServerVersion = serverVersion .String
273
+ err = data . tx .QueryRow ("SELECT version()" ).Scan (& serverVersion )
274
+ meta .ServerVersion = serverVersion .String
248
275
return
249
276
}
250
277
@@ -263,7 +290,7 @@ func (table *table) NameEsc() string {
263
290
264
291
func (table * table ) CreateSQL () (string , error ) {
265
292
var tableReturn , tableSQL sql.NullString
266
- if err := table .data .Connection .QueryRow ("SHOW CREATE TABLE " + table .NameEsc ()).Scan (& tableReturn , & tableSQL ); err != nil {
293
+ if err := table .data .tx .QueryRow ("SHOW CREATE TABLE " + table .NameEsc ()).Scan (& tableReturn , & tableSQL ); err != nil {
267
294
return "" , err
268
295
}
269
296
@@ -274,13 +301,12 @@ func (table *table) CreateSQL() (string, error) {
274
301
return tableSQL .String , nil
275
302
}
276
303
277
- // defer rows.Close()
278
304
func (table * table ) Init () (err error ) {
279
305
if len (table .types ) != 0 {
280
306
return errors .New ("can't init twice" )
281
307
}
282
308
283
- table .rows , err = table .data .Connection .Query ("SELECT * FROM " + table .NameEsc ())
309
+ table .rows , err = table .data .tx .Query ("SELECT * FROM " + table .NameEsc ())
284
310
if err != nil {
285
311
return err
286
312
}
@@ -299,6 +325,7 @@ func (table *table) Init() (err error) {
299
325
}
300
326
301
327
table .types = make ([]reflect.Type , len (tt ))
328
+ table .values = make ([]interface {}, len (tt ))
302
329
for i , tp := range tt {
303
330
st := tp .ScanType ()
304
331
if tp .DatabaseTypeName () == "BLOB" {
@@ -312,9 +339,6 @@ func (table *table) Init() (err error) {
312
339
} else {
313
340
table .types [i ] = reflect .TypeOf (sql.NullString {})
314
341
}
315
- }
316
- table .values = make ([]interface {}, len (tt ))
317
- for i := range table .values {
318
342
table .values [i ] = reflect .New (table .types [i ]).Interface ()
319
343
}
320
344
return nil
0 commit comments