Skip to content

Commit 298b70a

Browse files
authored
Merge pull request #2 from BrandonRoehl/master
Switch to use go channels
2 parents f61ba94 + db86117 commit 298b70a

File tree

3 files changed

+262
-282
lines changed

3 files changed

+262
-282
lines changed

dump.go

Lines changed: 130 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"io"
88
"reflect"
99
"strings"
10-
"sync"
1110
"text/template"
1211
"time"
1312
)
@@ -27,15 +26,17 @@ type Data struct {
2726
headerTmpl *template.Template
2827
tableTmpl *template.Template
2928
footerTmpl *template.Template
30-
mux sync.Mutex
31-
wg sync.WaitGroup
3229
err error
3330
}
3431

3532
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{}
3940
}
4041

4142
type metaData struct {
@@ -44,8 +45,9 @@ type metaData struct {
4445
CompleteTime string
4546
}
4647

47-
const version = "0.3.5"
48+
const version = "0.4.0"
4849

50+
// takes a *metaData
4951
const headerTmpl = `-- Go SQL Dump {{ .DumpVersion }}
5052
--
5153
-- ------------------------------------------------------
@@ -63,46 +65,46 @@ const headerTmpl = `-- Go SQL Dump {{ .DumpVersion }}
6365
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
6466
`
6567

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
6683
const tableTmpl = `
6784
--
68-
-- Table structure for table {{ .Name }}
85+
-- Table structure for table {{ .NameEsc }}
6986
--
7087
71-
DROP TABLE IF EXISTS {{ .Name }};
88+
DROP TABLE IF EXISTS {{ .NameEsc }};
7289
/*!40101 SET @saved_cs_client = @@character_set_client */;
7390
SET character_set_client = utf8mb4 ;
74-
{{ .SQL }};
91+
{{ .CreateSQL }};
7592
/*!40101 SET character_set_client = @saved_cs_client */;
7693
7794
--
78-
-- Dumping data for table {{ .Name }}
95+
-- Dumping data for table {{ .NameEsc }}
7996
--
8097
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 -}};
88103
{{- end }}
89-
/*!40000 ALTER TABLE {{ .Name }} ENABLE KEYS */;
104+
/*!40000 ALTER TABLE {{ .NameEsc }} ENABLE KEYS */;
90105
UNLOCK TABLES;
91106
`
92107

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-
106108
const nullType = "NULL"
107109

108110
// Dump data using struct
@@ -128,13 +130,11 @@ func (data *Data) Dump() error {
128130
return err
129131
}
130132

131-
data.wg.Add(len(tables))
132133
for _, name := range tables {
133134
if err := data.dumpTable(name); err != nil {
134135
return err
135136
}
136137
}
137-
data.wg.Wait()
138138
if data.err != nil {
139139
return data.err
140140
}
@@ -156,24 +156,14 @@ func (data *Data) dumpTable(name string) error {
156156
return err
157157
}
158158

159-
go data.writeTable(table)
160-
return nil
159+
return data.writeTable(table)
161160
}
162161

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
175165
}
176-
return
166+
return table.Err
177167
}
178168

179169
// MARK: get methods
@@ -237,112 +227,141 @@ func (data *metaData) updateServerVersion(db *sql.DB) (err error) {
237227
// MARK: create methods
238228

239229
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,
249233
}
250234

251235
return t, nil
252236
}
253237

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+
}
257241

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 {
259245
return "", err
260246
}
261-
if tableReturn.String != name {
247+
248+
if tableReturn.String != table.Name {
262249
return "", errors.New("Returned table is not the same as requested table")
263250
}
264251

265252
return tableSQL.String, nil
266253
}
267254

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())
270262
if err != nil {
271-
return nil, err
263+
return err
272264
}
273-
defer rows.Close()
274265

275-
columns, err := rows.Columns()
266+
columns, err := table.rows.Columns()
276267
if err != nil {
277-
return nil, err
268+
return err
278269
}
279270
if len(columns) == 0 {
280-
return nil, errors.New("No columns in table " + name + ".")
271+
return errors.New("No columns in table " + table.Name + ".")
281272
}
282273

283-
dataText := make([]string, 0)
284-
tt, err := rows.ColumnTypes()
274+
tt, err := table.rows.ColumnTypes()
285275
if err != nil {
286-
return nil, err
276+
return err
287277
}
288278

289-
types := make([]reflect.Type, len(tt))
279+
table.types = make([]reflect.Type, len(tt))
290280
for i, tp := range tt {
291281
st := tp.ScanType()
292282
if tp.DatabaseTypeName() == "BLOB" {
293-
types[i] = reflect.TypeOf(sql.RawBytes{})
283+
table.types[i] = reflect.TypeOf(sql.RawBytes{})
294284
} else if st != nil && (st.Kind() == reflect.Int ||
295285
st.Kind() == reflect.Int8 ||
296286
st.Kind() == reflect.Int16 ||
297287
st.Kind() == reflect.Int32 ||
298288
st.Kind() == reflect.Int64) {
299-
types[i] = reflect.TypeOf(sql.NullInt64{})
289+
table.types[i] = reflect.TypeOf(sql.NullInt64{})
300290
} else {
301-
types[i] = reflect.TypeOf(sql.NullString{})
291+
table.types[i] = reflect.TypeOf(sql.NullString{})
302292
}
303293
}
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()
307297
}
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
311316
}
317+
} else {
318+
table.rows.Close()
319+
table.rows = nil
320+
return false
321+
}
322+
return true
323+
}
312324

313-
dataStrings := make([]string, len(columns))
325+
func (table *table) RowValues() string {
326+
dataStrings := make([]string, len(table.values))
314327

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 {
317346
dataStrings[key] = nullType
318347
} 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)) + "'"
341349
}
350+
default:
351+
dataStrings[key] = fmt.Sprint("'", value, "'")
342352
}
343-
344-
dataText = append(dataText, "("+strings.Join(dataStrings, ",")+")")
345353
}
346354

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
348367
}

0 commit comments

Comments
 (0)