7
7
"io"
8
8
"reflect"
9
9
"strings"
10
- "sync"
11
10
"text/template"
12
11
"time"
13
12
)
@@ -27,15 +26,17 @@ type Data struct {
27
26
headerTmpl * template.Template
28
27
tableTmpl * template.Template
29
28
footerTmpl * template.Template
30
- mux sync.Mutex
31
- wg sync.WaitGroup
32
29
err error
33
30
}
34
31
35
32
type table struct {
36
- Name string
37
- SQL string
38
- Values []string
33
+ Name string
34
+ Err error
35
+
36
+ data * Data
37
+ rows * sql.Rows
38
+ types []reflect.Type
39
+ values []interface {}
39
40
}
40
41
41
42
type metaData struct {
@@ -44,8 +45,9 @@ type metaData struct {
44
45
CompleteTime string
45
46
}
46
47
47
- const version = "0.3.5 "
48
+ const version = "0.4.0 "
48
49
50
+ // takes a *metaData
49
51
const headerTmpl = `-- Go SQL Dump {{ .DumpVersion }}
50
52
--
51
53
-- ------------------------------------------------------
@@ -63,46 +65,46 @@ const headerTmpl = `-- Go SQL Dump {{ .DumpVersion }}
63
65
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
64
66
`
65
67
68
+ // takes a *metaData
69
+ const footerTmpl = `/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
70
+
71
+ /*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
72
+ /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
73
+ /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
74
+ /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
75
+ /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
76
+ /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
77
+ /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
78
+
79
+ -- Dump completed on {{ .CompleteTime }}
80
+ `
81
+
82
+ // Takes a *table
66
83
const tableTmpl = `
67
84
--
68
- -- Table structure for table {{ .Name }}
85
+ -- Table structure for table {{ .NameEsc }}
69
86
--
70
87
71
- DROP TABLE IF EXISTS {{ .Name }};
88
+ DROP TABLE IF EXISTS {{ .NameEsc }};
72
89
/*!40101 SET @saved_cs_client = @@character_set_client */;
73
90
SET character_set_client = utf8mb4 ;
74
- {{ .SQL }};
91
+ {{ .CreateSQL }};
75
92
/*!40101 SET character_set_client = @saved_cs_client */;
76
93
77
94
--
78
- -- Dumping data for table {{ .Name }}
95
+ -- Dumping data for table {{ .NameEsc }}
79
96
--
80
97
81
- LOCK TABLES {{ .Name }} WRITE;
82
- /*!40000 ALTER TABLE {{ .Name }} DISABLE KEYS */;
83
- {{- if .Values }}
84
- INSERT INTO {{ .Name }} VALUES
85
- {{- range $index, $element := .Values -}}
86
- {{- if $index }},{{ else }} {{ end -}}{{ $element }}
87
- {{- end -}};
98
+ LOCK TABLES {{ .NameEsc }} WRITE;
99
+ /*!40000 ALTER TABLE {{ .NameEsc }} DISABLE KEYS */;
100
+ {{- if .Next }}
101
+ INSERT INTO {{ .NameEsc }} VALUES {{ .RowValues }}
102
+ {{- range $value := .Stream }},{{ $value }}{{ end -}};
88
103
{{- end }}
89
- /*!40000 ALTER TABLE {{ .Name }} ENABLE KEYS */;
104
+ /*!40000 ALTER TABLE {{ .NameEsc }} ENABLE KEYS */;
90
105
UNLOCK TABLES;
91
106
`
92
107
93
- const footerTmpl = `/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
94
-
95
- /*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
96
- /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
97
- /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
98
- /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
99
- /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
100
- /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
101
- /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
102
-
103
- -- Dump completed on {{ .CompleteTime }}
104
- `
105
-
106
108
const nullType = "NULL"
107
109
108
110
// Dump data using struct
@@ -128,13 +130,11 @@ func (data *Data) Dump() error {
128
130
return err
129
131
}
130
132
131
- data .wg .Add (len (tables ))
132
133
for _ , name := range tables {
133
134
if err := data .dumpTable (name ); err != nil {
134
135
return err
135
136
}
136
137
}
137
- data .wg .Wait ()
138
138
if data .err != nil {
139
139
return data .err
140
140
}
@@ -156,24 +156,14 @@ func (data *Data) dumpTable(name string) error {
156
156
return err
157
157
}
158
158
159
- go data .writeTable (table )
160
- return nil
159
+ return data .writeTable (table )
161
160
}
162
161
163
- func (data * Data ) writeTable (table * table ) {
164
- // Keep a counter of how many tables have been written
165
- defer data .wg .Done ()
166
-
167
- // Force this method into serial
168
- data .mux .Lock ()
169
- defer data .mux .Unlock ()
170
-
171
- if data .err != nil {
172
- return
173
- } else if err := data .tableTmpl .Execute (data .Out , table ); err != nil {
174
- data .err = err
162
+ func (data * Data ) writeTable (table * table ) error {
163
+ if err := data .tableTmpl .Execute (data .Out , table ); err != nil {
164
+ return err
175
165
}
176
- return
166
+ return table . Err
177
167
}
178
168
179
169
// MARK: get methods
@@ -237,112 +227,141 @@ func (data *metaData) updateServerVersion(db *sql.DB) (err error) {
237
227
// MARK: create methods
238
228
239
229
func (data * Data ) createTable (name string ) (* table , error ) {
240
- var err error
241
- t := & table {Name : "`" + name + "`" }
242
-
243
- if t .SQL , err = data .createTableSQL (name ); err != nil {
244
- return nil , err
245
- }
246
-
247
- if t .Values , err = data .createTableValues (name ); err != nil {
248
- return nil , err
230
+ t := & table {
231
+ Name : name ,
232
+ data : data ,
249
233
}
250
234
251
235
return t , nil
252
236
}
253
237
254
- func (data * Data ) createTableSQL ( name string ) ( string , error ) {
255
- var tableReturn , tableSQL sql. NullString
256
- err := data . Connection . QueryRow ( "SHOW CREATE TABLE `" + name + "`" ). Scan ( & tableReturn , & tableSQL )
238
+ func (table * table ) NameEsc () string {
239
+ return "`" + table . Name + "`"
240
+ }
257
241
258
- if err != nil {
242
+ func (table * table ) CreateSQL () (string , error ) {
243
+ var tableReturn , tableSQL sql.NullString
244
+ if err := table .data .Connection .QueryRow ("SHOW CREATE TABLE " + table .NameEsc ()).Scan (& tableReturn , & tableSQL ); err != nil {
259
245
return "" , err
260
246
}
261
- if tableReturn .String != name {
247
+
248
+ if tableReturn .String != table .Name {
262
249
return "" , errors .New ("Returned table is not the same as requested table" )
263
250
}
264
251
265
252
return tableSQL .String , nil
266
253
}
267
254
268
- func (data * Data ) createTableValues (name string ) ([]string , error ) {
269
- rows , err := data .Connection .Query ("SELECT * FROM `" + name + "`" )
255
+ // defer rows.Close()
256
+ func (table * table ) Init () (err error ) {
257
+ if len (table .types ) != 0 {
258
+ return errors .New ("can't init twice" )
259
+ }
260
+
261
+ table .rows , err = table .data .Connection .Query ("SELECT * FROM " + table .NameEsc ())
270
262
if err != nil {
271
- return nil , err
263
+ return err
272
264
}
273
- defer rows .Close ()
274
265
275
- columns , err := rows .Columns ()
266
+ columns , err := table . rows .Columns ()
276
267
if err != nil {
277
- return nil , err
268
+ return err
278
269
}
279
270
if len (columns ) == 0 {
280
- return nil , errors .New ("No columns in table " + name + "." )
271
+ return errors .New ("No columns in table " + table . Name + "." )
281
272
}
282
273
283
- dataText := make ([]string , 0 )
284
- tt , err := rows .ColumnTypes ()
274
+ tt , err := table .rows .ColumnTypes ()
285
275
if err != nil {
286
- return nil , err
276
+ return err
287
277
}
288
278
289
- types : = make ([]reflect.Type , len (tt ))
279
+ table . types = make ([]reflect.Type , len (tt ))
290
280
for i , tp := range tt {
291
281
st := tp .ScanType ()
292
282
if tp .DatabaseTypeName () == "BLOB" {
293
- types [i ] = reflect .TypeOf (sql.RawBytes {})
283
+ table . types [i ] = reflect .TypeOf (sql.RawBytes {})
294
284
} else if st != nil && (st .Kind () == reflect .Int ||
295
285
st .Kind () == reflect .Int8 ||
296
286
st .Kind () == reflect .Int16 ||
297
287
st .Kind () == reflect .Int32 ||
298
288
st .Kind () == reflect .Int64 ) {
299
- types [i ] = reflect .TypeOf (sql.NullInt64 {})
289
+ table . types [i ] = reflect .TypeOf (sql.NullInt64 {})
300
290
} else {
301
- types [i ] = reflect .TypeOf (sql.NullString {})
291
+ table . types [i ] = reflect .TypeOf (sql.NullString {})
302
292
}
303
293
}
304
- values : = make ([]interface {}, len (tt ))
305
- for i := range values {
306
- values [i ] = reflect .New (types [i ]).Interface ()
294
+ table . values = make ([]interface {}, len (tt ))
295
+ for i := range table . values {
296
+ table . values [i ] = reflect .New (table . types [i ]).Interface ()
307
297
}
308
- for rows .Next () {
309
- if err := rows .Scan (values ... ); err != nil {
310
- return dataText , err
298
+ return nil
299
+ }
300
+
301
+ func (table * table ) Next () bool {
302
+ if table .rows == nil {
303
+ if err := table .Init (); err != nil {
304
+ table .Err = err
305
+ return false
306
+ }
307
+ }
308
+ // Fallthrough
309
+ if table .rows .Next () {
310
+ if err := table .rows .Scan (table .values ... ); err != nil {
311
+ table .Err = err
312
+ return false
313
+ } else if err := table .rows .Err (); err != nil {
314
+ table .Err = err
315
+ return false
311
316
}
317
+ } else {
318
+ table .rows .Close ()
319
+ table .rows = nil
320
+ return false
321
+ }
322
+ return true
323
+ }
312
324
313
- dataStrings := make ([]string , len (columns ))
325
+ func (table * table ) RowValues () string {
326
+ dataStrings := make ([]string , len (table .values ))
314
327
315
- for key , value := range values {
316
- if value == nil {
328
+ for key , value := range table .values {
329
+ switch s := value .(type ) {
330
+ case nil :
331
+ dataStrings [key ] = nullType
332
+ case * sql.NullString :
333
+ if s .Valid {
334
+ dataStrings [key ] = "'" + sanitize (s .String ) + "'"
335
+ } else {
336
+ dataStrings [key ] = nullType
337
+ }
338
+ case * sql.NullInt64 :
339
+ if s .Valid {
340
+ dataStrings [key ] = fmt .Sprintf ("%d" , s .Int64 )
341
+ } else {
342
+ dataStrings [key ] = nullType
343
+ }
344
+ case * sql.RawBytes :
345
+ if len (* s ) == 0 {
317
346
dataStrings [key ] = nullType
318
347
} else {
319
- switch s := value .(type ) {
320
- case * sql.NullString :
321
- if s .Valid {
322
- dataStrings [key ] = "'" + sanitize (s .String ) + "'"
323
- } else {
324
- dataStrings [key ] = nullType
325
- }
326
- case * sql.NullInt64 :
327
- if s .Valid {
328
- dataStrings [key ] = fmt .Sprintf ("%d" , s .Int64 )
329
- } else {
330
- dataStrings [key ] = nullType
331
- }
332
- case * sql.RawBytes :
333
- if len (* s ) == 0 {
334
- dataStrings [key ] = nullType
335
- } else {
336
- dataStrings [key ] = "_binary '" + sanitize (string (* s )) + "'"
337
- }
338
- default :
339
- dataStrings [key ] = fmt .Sprint ("'" , value , "'" )
340
- }
348
+ dataStrings [key ] = "_binary '" + sanitize (string (* s )) + "'"
341
349
}
350
+ default :
351
+ dataStrings [key ] = fmt .Sprint ("'" , value , "'" )
342
352
}
343
-
344
- dataText = append (dataText , "(" + strings .Join (dataStrings , "," )+ ")" )
345
353
}
346
354
347
- return dataText , rows .Err ()
355
+ return "(" + strings .Join (dataStrings , "," ) + ")"
356
+ }
357
+
358
+ func (table * table ) Stream () <- chan string {
359
+ valueOut := make (chan string , 1 )
360
+ go func (out chan string ) {
361
+ defer close (out )
362
+ for table .Next () {
363
+ out <- table .RowValues ()
364
+ }
365
+ }(valueOut )
366
+ return valueOut
348
367
}
0 commit comments