Skip to content

Commit

Permalink
.
Browse files Browse the repository at this point in the history
  • Loading branch information
hzarka committed Feb 8, 2016
0 parents commit 19c9bf7
Show file tree
Hide file tree
Showing 6 changed files with 249 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
my.cnf
mysqlcsvdump
.*.swp
7 changes: 7 additions & 0 deletions Makefile
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

85 changes: 85 additions & 0 deletions mysqlbqdump.go
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
}
31 changes: 31 additions & 0 deletions util.go
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)
}
}
67 changes: 67 additions & 0 deletions write_csv.go
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)
}
}
56 changes: 56 additions & 0 deletions write_json.go
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)
}
}

0 comments on commit 19c9bf7

Please sign in to comment.