@@ -2,6 +2,7 @@ package mysqldump
22
33import (
44 "bytes"
5+ "context"
56 "database/sql"
67 "errors"
78 "fmt"
@@ -14,9 +15,11 @@ import (
1415/*
1516Data struct to configure dump behavior
1617
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
2023*/
2124type Data struct {
2225 Out io.Writer
@@ -25,6 +28,7 @@ type Data struct {
2528 MaxAllowedPacket int
2629 LockTables bool
2730
31+ tx * sql.Tx
2832 headerTmpl * template.Template
2933 tableTmpl * template.Template
3034 footerTmpl * template.Template
@@ -123,11 +127,19 @@ func (data *Data) Dump() error {
123127 data .MaxAllowedPacket = defaultMaxAllowedPacket
124128 }
125129
126- if err := meta . updateServerVersion ( data .Connection ); err != nil {
130+ if err := data .getTemplates ( ); err != nil {
127131 return err
128132 }
129133
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 {
131143 return err
132144 }
133145
@@ -173,6 +185,21 @@ func (data *Data) Dump() error {
173185
174186// MARK: - Private methods
175187
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+
176203// MARK: writter methods
177204
178205func (data * Data ) dumpTable (name string ) error {
@@ -214,7 +241,7 @@ func (data *Data) getTemplates() (err error) {
214241func (data * Data ) getTables () ([]string , error ) {
215242 tables := make ([]string , 0 )
216243
217- rows , err := data .Connection .Query ("SHOW TABLES" )
244+ rows , err := data .tx .Query ("SHOW TABLES" )
218245 if err != nil {
219246 return tables , err
220247 }
@@ -241,10 +268,10 @@ func (data *Data) isIgnoredTable(name string) bool {
241268 return false
242269}
243270
244- func (data * metaData ) updateServerVersion (db * sql. DB ) (err error ) {
271+ func (meta * metaData ) updateServerVersion (data * Data ) (err error ) {
245272 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
248275 return
249276}
250277
@@ -263,7 +290,7 @@ func (table *table) NameEsc() string {
263290
264291func (table * table ) CreateSQL () (string , error ) {
265292 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 {
267294 return "" , err
268295 }
269296
@@ -274,13 +301,12 @@ func (table *table) CreateSQL() (string, error) {
274301 return tableSQL .String , nil
275302}
276303
277- // defer rows.Close()
278304func (table * table ) Init () (err error ) {
279305 if len (table .types ) != 0 {
280306 return errors .New ("can't init twice" )
281307 }
282308
283- table .rows , err = table .data .Connection .Query ("SELECT * FROM " + table .NameEsc ())
309+ table .rows , err = table .data .tx .Query ("SELECT * FROM " + table .NameEsc ())
284310 if err != nil {
285311 return err
286312 }
@@ -299,6 +325,7 @@ func (table *table) Init() (err error) {
299325 }
300326
301327 table .types = make ([]reflect.Type , len (tt ))
328+ table .values = make ([]interface {}, len (tt ))
302329 for i , tp := range tt {
303330 st := tp .ScanType ()
304331 if tp .DatabaseTypeName () == "BLOB" {
@@ -312,9 +339,6 @@ func (table *table) Init() (err error) {
312339 } else {
313340 table .types [i ] = reflect .TypeOf (sql.NullString {})
314341 }
315- }
316- table .values = make ([]interface {}, len (tt ))
317- for i := range table .values {
318342 table .values [i ] = reflect .New (table .types [i ]).Interface ()
319343 }
320344 return nil
0 commit comments