-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 19c9bf7
Showing
6 changed files
with
249 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
my.cnf | ||
mysqlcsvdump | ||
.*.swp |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
|
||
test: | ||
go build && ./mysqlbqdump --epoch=false --format=json test test | ||
|
||
build: | ||
CGO_ENABLED=0 go build -a -installsuffix cgo | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package main | ||
|
||
import "gopkg.in/ini.v1" | ||
import "log" | ||
import "os" | ||
import "fmt" | ||
import "io/ioutil" | ||
import "database/sql" | ||
import flag "github.com/ogier/pflag" | ||
import _ "github.com/go-sql-driver/mysql" | ||
|
||
type Writer interface { | ||
WriteRows(*sql.Rows) | ||
} | ||
|
||
type Config struct { | ||
FieldSep string | ||
RowSep string | ||
NullString string | ||
DateEpoch bool | ||
} | ||
|
||
func getDSN(filename string, section string, database string) string { | ||
debug("importing", filename, section) | ||
cfg, err := ini.Load(filename) | ||
if err != nil { | ||
log.Fatalln(err) | ||
} | ||
sec := cfg.Section(section) | ||
host := sec.Key("host").MustString("127.0.0.1") | ||
port := sec.Key("port").MustString("3306") | ||
user := sec.Key("user").String() | ||
password := sec.Key("password").String() | ||
return fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?parseTime=true", user, password, host, port, database) | ||
} | ||
|
||
func main() { | ||
var config Config | ||
flag.BoolVar(&DEBUG, "debug", false, "enable debug logging") | ||
flag.BoolVar(&QUIET, "quiet", false, "disable output") | ||
flag.StringVar(&config.FieldSep, "csv-fields-terminated-by", "\t", "field separator") | ||
flag.StringVar(&config.RowSep, "csv-records-terminated-by", "\n", "row separator") | ||
flag.StringVar(&config.NullString, "csv-null-string", "\\N", "output string for NULL values") | ||
flag.BoolVar(&config.DateEpoch, "epoch", true, "output datetime as epoch instead of RFC3339") | ||
defaults_file := flag.String("defaults-file", "my.cnf", "defaults file") | ||
defaults_group_suffix := flag.String("defaults-group-suffix", "", "defaults group suffix") | ||
format := flag.String("format", "json", "output format 'json' or 'csv'") | ||
flag.Usage = func() { | ||
fmt.Fprintf(os.Stderr, "Usage: mysqlcsvdump [options] database table > output.json\n\n") | ||
fmt.Fprintf(os.Stderr, "Reads connection info from ./my.cnf. Use '-' for table to send query in stdin\n\n") | ||
flag.PrintDefaults() | ||
} | ||
flag.Parse() | ||
|
||
args := flag.Args() | ||
if len(args) < 2 { | ||
flag.Usage() | ||
os.Exit(1) | ||
} | ||
dsn := getDSN(*defaults_file, "client"+*defaults_group_suffix, args[0]) | ||
rows := getRows(dsn, args[1]) | ||
if *format == "json" { | ||
NewJsonWriter(&config).WriteRows(rows) | ||
} else { | ||
NewCsvWriter(&config).WriteRows(rows) | ||
} | ||
} | ||
|
||
func getRows(dsn string, table string) *sql.Rows { | ||
db, err := sql.Open("mysql", dsn) | ||
handleError(err) | ||
defer db.Close() | ||
query := fmt.Sprintf("SELECT * FROM `%s`", table) | ||
if table == "-" { | ||
bytes, err := ioutil.ReadAll(os.Stdin) | ||
handleError(err) | ||
query = string(bytes) | ||
} | ||
stmt, err := db.Prepare(query) | ||
handleError(err) | ||
defer stmt.Close() | ||
rows, err := stmt.Query() | ||
handleError(err) | ||
return rows | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package main | ||
|
||
import "log" | ||
import "os" | ||
|
||
var logger = log.New(os.Stderr, "mysqlcsvdump:", log.LstdFlags) | ||
|
||
var DEBUG bool | ||
var QUIET bool | ||
|
||
func debug(params ...interface{}) { | ||
if DEBUG { | ||
logger.Println(params...) | ||
} | ||
} | ||
|
||
func info(params ...interface{}) { | ||
if !QUIET { | ||
logger.Println(params...) | ||
} | ||
} | ||
|
||
func fatal(params ...interface{}) { | ||
logger.Fatalln(params...) | ||
} | ||
|
||
func handleError(err error) { | ||
if err != nil { | ||
fatal(err) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package main | ||
|
||
import "fmt" | ||
import "time" | ||
import "database/sql" | ||
|
||
type CsvWriter struct { | ||
*Config | ||
} | ||
|
||
func NewCsvWriter(config *Config) *CsvWriter { | ||
return &CsvWriter{config} | ||
} | ||
|
||
func (w *CsvWriter) WriteRow(row []interface{}) { | ||
for i, f := range row { | ||
if i != 0 { | ||
fmt.Print(w.FieldSep) | ||
} | ||
switch v := (f).(type) { | ||
case nil: | ||
fmt.Print(w.NullString) | ||
case bool: | ||
if v { | ||
fmt.Print("1") | ||
} else { | ||
fmt.Print("0") | ||
} | ||
case []byte: | ||
fmt.Print(string(v)) | ||
case time.Time: | ||
if w.DateEpoch { | ||
fmt.Print(v.Unix()) | ||
} else { | ||
fmt.Print(v.Format(time.RFC3339)) | ||
} | ||
default: | ||
fmt.Print(v) | ||
} | ||
} | ||
fmt.Print(w.RowSep) | ||
} | ||
|
||
func (w *CsvWriter) WriteRows(rows *sql.Rows) { | ||
columnNames, err := rows.Columns() | ||
handleError(err) | ||
for i, c := range columnNames { | ||
if i != 0 { | ||
fmt.Print(w.FieldSep) | ||
} | ||
fmt.Print(c) | ||
} | ||
fmt.Print(w.RowSep) | ||
|
||
vals := make([]interface{}, len(columnNames)) | ||
scanArgs := make([]interface{}, len(columnNames)) | ||
for i := 0; i < len(columnNames); i++ { | ||
scanArgs[i] = &vals[i] | ||
} | ||
for rows.Next() { | ||
err = rows.Scan(scanArgs...) | ||
if err != nil { | ||
fatal(err) | ||
} | ||
w.WriteRow(vals) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package main | ||
|
||
import "database/sql" | ||
import "encoding/json" | ||
import "time" | ||
import "os" | ||
|
||
type JsonWriter struct { | ||
*Config | ||
enc *json.Encoder | ||
row map[string]interface{} | ||
} | ||
|
||
func NewJsonWriter(config *Config) *JsonWriter { | ||
return &JsonWriter{config, json.NewEncoder(os.Stdout), make(map[string]interface{})} | ||
} | ||
|
||
func (w *JsonWriter) WriteRow(columns []string, row []interface{}) { | ||
for i, c := range columns { | ||
switch v := (row[i]).(type) { | ||
case nil: | ||
w.row[c] = nil | ||
case bool: | ||
w.row[c] = v | ||
case []byte: | ||
w.row[c] = string(v) | ||
case time.Time: | ||
if w.DateEpoch { | ||
w.row[c] = v.Unix() | ||
} else { | ||
w.row[c] = v.Format(time.RFC3339) | ||
} | ||
default: | ||
w.row[c] = v | ||
} | ||
} | ||
w.enc.Encode(w.row) | ||
} | ||
|
||
func (w *JsonWriter) WriteRows(rows *sql.Rows) { | ||
columnNames, err := rows.Columns() | ||
handleError(err) | ||
|
||
vals := make([]interface{}, len(columnNames)) | ||
scanArgs := make([]interface{}, len(columnNames)) | ||
for i := 0; i < len(columnNames); i++ { | ||
scanArgs[i] = &vals[i] | ||
} | ||
for rows.Next() { | ||
err = rows.Scan(scanArgs...) | ||
if err != nil { | ||
fatal(err) | ||
} | ||
w.WriteRow(columnNames, vals) | ||
} | ||
} |