Skip to content

Commit

Permalink
Separation of input / output source code.
Browse files Browse the repository at this point in the history
  • Loading branch information
noborus committed May 20, 2019
1 parent 1b947d1 commit ab49098
Show file tree
Hide file tree
Showing 19 changed files with 301 additions and 265 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ require (
github.com/mattn/go-sqlite3 v1.10.0
github.com/noborus/tbln v0.0.0-20190320060533-493278614684
github.com/olekukonko/tablewriter v0.0.1
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
)
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25 h1:jsG6UpNLt9iAsb0S2AGW28DveNzzgmbXR+ENoPjUeIU=
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
Expand Down
98 changes: 0 additions & 98 deletions inout.go → input.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,13 @@ package main

import (
"compress/gzip"
"encoding/hex"
"fmt"
"io"
"log"
"os"
"path/filepath"
"regexp"
"strings"
"time"
"unicode/utf8"
)

// Input is wrap the reader.
Expand Down Expand Up @@ -280,81 +277,6 @@ func tableFileOpen(filename string) (io.ReadCloser, error) {
return extFileReader(filename, file), nil
}

// Output is database export
type Output interface {
First([]string, []string) error
RowWrite([]interface{}, []string) error
Last() error
}

// Export is execute SQL and output the result.
func (trdsql *TRDSQL) Export(db *DDB, sqlstr string, output Output) error {
rows, err := db.Select(sqlstr)
if err != nil {
return err
}

columns, err := rows.Columns()
if err != nil {
return err
}
defer func() {
err = rows.Close()
if err != nil {
log.Printf("ERROR: close:%s", err)
}
}()
values := make([]interface{}, len(columns))
scanArgs := make([]interface{}, len(columns))
for i := range values {
scanArgs[i] = &values[i]
}

columnTypes, err := rows.ColumnTypes()
if err != nil {
return err
}
types := make([]string, len(columns))
for i, ct := range columnTypes {
types[i] = convertType(ct.DatabaseTypeName())
}

err = output.First(columns, types)
if err != nil {
return err
}
for rows.Next() {
err = rows.Scan(scanArgs...)
if err != nil {
return err
}
err = output.RowWrite(values, columns)
if err != nil {
return err
}
}
return output.Last()
}

func convertType(dbtype string) string {
switch strings.ToLower(dbtype) {
case "smallint", "integer", "int", "int2", "int4", "smallserial", "serial":
return "int"
case "bigint", "int8", "bigserial":
return "bigint"
case "float", "decimal", "numeric", "real", "double precision":
return "numeric"
case "bool":
return "bool"
case "timestamp", "timestamptz", "date", "time":
return "timestamp"
case "string", "text", "char", "varchar":
return "text"
default:
return "text"
}
}

func guessExtension(tablename string) int {
if strings.HasSuffix(tablename, ".gz") {
tablename = tablename[0 : len(tablename)-3]
Expand All @@ -373,23 +295,3 @@ func guessExtension(tablename string) int {
debug.Printf("Guess file type as CSV: [%s]", tablename)
return CSV
}

func valString(v interface{}) string {
var str string
switch t := v.(type) {
case nil:
str = ""
case time.Time:
str = t.Format(time.RFC3339)
case []byte:
if ok := utf8.Valid(t); ok {
str = string(t)
} else {
str = `\x` + hex.EncodeToString(t)
}
default:
str = fmt.Sprint(v)
str = strings.ReplaceAll(str, "\n", "\\n")
}
return str
}
39 changes: 0 additions & 39 deletions csv.go → input_csv.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,42 +123,3 @@ func (cr *CSVIn) ReadRow(row []interface{}) ([]interface{}, error) {
}
return row, nil
}

func (trdsql *TRDSQL) csvOutNew() Output {
var err error
c := &CSVOut{}
c.writer = csv.NewWriter(trdsql.outStream)
c.writer.Comma, err = delimiter(trdsql.outDelimiter)
if err != nil {
debug.Printf("%s\n", err)
}
c.outHeader = trdsql.outHeader
return c
}

// First is output of header and preparation
func (c *CSVOut) First(columns []string, types []string) error {
if c.outHeader {
err := c.writer.Write(columns)
if err != nil {
return err
}
}
c.results = make([]string, len(columns))
return nil
}

// RowWrite is row output
func (c *CSVOut) RowWrite(values []interface{}, columns []string) error {
for i, col := range values {
c.results[i] = valString(col)
}
err := c.writer.Write(c.results)
return err
}

// Last is flush
func (c *CSVOut) Last() error {
c.writer.Flush()
return nil
}
34 changes: 0 additions & 34 deletions json.go → input_json.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,6 @@ type JSONIn struct {
count int
}

// JSONOut provides methods of the Output interface
type JSONOut struct {
writer *json.Encoder
results []map[string]string
}

func (trdsql *TRDSQL) jsonInputNew(r io.Reader) (Input, error) {
jr := &JSONIn{}
jr.reader = json.NewDecoder(r)
Expand Down Expand Up @@ -200,31 +194,3 @@ func (jr *JSONIn) rowParse(row []interface{}, jsonRow interface{}) []interface{}
}
return row
}

func (trdsql *TRDSQL) jsonOutNew() Output {
js := &JSONOut{}
js.writer = json.NewEncoder(trdsql.outStream)
js.writer.SetIndent("", " ")
return js
}

// First is preparation
func (js *JSONOut) First(columns []string, types []string) error {
js.results = make([]map[string]string, 0)
return nil
}

// RowWrite is Addition to array
func (js *JSONOut) RowWrite(values []interface{}, columns []string) error {
m := make(map[string]string, len(columns))
for i, col := range values {
m[columns[i]] = valString(col)
}
js.results = append(js.results, m)
return nil
}

// Last is Actual output
func (js *JSONOut) Last() error {
return js.writer.Encode(js.results)
}
36 changes: 0 additions & 36 deletions ltsv.go → input_ltsv.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,6 @@ type LTSVIn struct {
types []string
}

// LTSVOut provides methods of the Output interface
type LTSVOut struct {
writer *bufio.Writer
delimiter string
results map[string]string
}

func (trdsql *TRDSQL) ltsvInputNew(r io.Reader) (Input, error) {
lr := &LTSVIn{}
lr.reader = bufio.NewReader(r)
Expand Down Expand Up @@ -116,32 +109,3 @@ func (lr *LTSVIn) readline() (string, error) {
}
}
}

func (trdsql *TRDSQL) ltsvOutNew() Output {
lw := &LTSVOut{}
lw.delimiter = "\t"
lw.writer = bufio.NewWriter(trdsql.outStream)
return lw
}

// First is preparation
func (lw *LTSVOut) First(columns []string, types []string) error {
lw.results = make(map[string]string, len(columns))
return nil
}

// RowWrite is Actual output
func (lw *LTSVOut) RowWrite(values []interface{}, columns []string) error {
results := make([]string, len(values))
for i, col := range values {
results[i] = columns[i] + ":" + valString(col)
}
str := strings.Join(results, lw.delimiter) + "\n"
_, err := lw.writer.Write([]byte(str))
return err
}

// Last is flush
func (lw *LTSVOut) Last() error {
return lw.writer.Flush()
}
44 changes: 0 additions & 44 deletions tbln.go → input_tbln.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,6 @@ type TBLNIn struct {
preRead [][]interface{}
}

// TBLNOut provides methods of the Output interface
type TBLNOut struct {
writer *tbln.Writer
results []string
}

func (trdsql *TRDSQL) tblnInputNew(r io.Reader) (Input, error) {
tr := &TBLNIn{}
tr.reader = tbln.NewReader(r)
Expand Down Expand Up @@ -65,41 +59,3 @@ func (tr *TBLNIn) ReadRow([]interface{}) ([]interface{}, error) {
}
return row, err
}

func (trdsql *TRDSQL) tblnOutNew() Output {
tw := &TBLNOut{}
tw.writer = tbln.NewWriter(trdsql.outStream)
return tw
}

// First is preparation
func (tw *TBLNOut) First(columns []string, types []string) error {
d := tbln.NewDefinition()
err := d.SetNames(columns)
if err != nil {
return err
}
err = d.SetTypes(types)
if err != nil {
return err
}
err = tw.writer.WriteDefinition(d)
if err != nil {
return err
}
tw.results = make([]string, len(columns))
return nil
}

// RowWrite is Addition to array
func (tw *TBLNOut) RowWrite(values []interface{}, columns []string) error {
for i, col := range values {
tw.results[i] = valString(col)
}
return tw.writer.WriteRow(tw.results)
}

// Last is Actual output
func (tw *TBLNOut) Last() error {
return nil
}
16 changes: 3 additions & 13 deletions inout_test.go → input_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,6 @@ import (
"testing"
)

func TestValString(t *testing.T) {
str := "test"
if valString(str) != str {
t.Errorf("valString error.")
}
if valString(nil) != "" {
t.Errorf("valString error.")
}
}

func TestSqlFields(t *testing.T) {
str := "SELECT * FROM \"C:\\file with a space.csv\""
w := sqlFields(str)
Expand All @@ -31,21 +21,21 @@ func TestTableFileOpen(t *testing.T) {
if err != nil && stdin == os.Stdin {
t.Error(err)
}
f, err := tableFileOpen("inout_test.go")
f, err := tableFileOpen("input_test.go")
if err != nil {
t.Error(err)
}
f.Close()

// SQLite3 & MySQL escape
f, err = tableFileOpen("`inout_test.go`")
f, err = tableFileOpen("`input_test.go`")
if err != nil {
t.Error(err)
}
f.Close()

// PostgreSQL escape
f, err = tableFileOpen("\"inout_test.go\"")
f, err = tableFileOpen("\"input_test.go\"")
if err != nil {
t.Error(err)
}
Expand Down
Loading

0 comments on commit ab49098

Please sign in to comment.