Skip to content

Commit eeb1b86

Browse files
authored
Merge pull request #200 from noborus/analyze-advice
Improved analyze additional advice
2 parents 67447d8 + fd657d9 commit eeb1b86

File tree

4 files changed

+116
-51
lines changed

4 files changed

+116
-51
lines changed

analyze.go

+111-46
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ type AnalyzeOpts struct {
2424
OutStream io.Writer
2525
}
2626

27+
// Defined to wrap string styling.
28+
var (
29+
colorTable = gchalk.Yellow
30+
colorFileType = gchalk.Red
31+
colorCaption = gchalk.Cyan
32+
colorNotes = gchalk.Magenta
33+
)
34+
2735
// NewAnalyzeOpts returns AnalyzeOpts.
2836
func NewAnalyzeOpts() *AnalyzeOpts {
2937
return &AnalyzeOpts{
@@ -37,6 +45,7 @@ func NewAnalyzeOpts() *AnalyzeOpts {
3745
// Analyze analyzes the file and outputs the table information.
3846
// In addition, SQL execution examples are output.
3947
func Analyze(fileName string, opts *AnalyzeOpts, readOpts *ReadOpts) error {
48+
w := opts.OutStream
4049
rOpts, fileName := GuessOpts(readOpts, fileName)
4150
file, err := importFileOpen(fileName)
4251
if err != nil {
@@ -61,76 +70,106 @@ func Analyze(fileName string, opts *AnalyzeOpts, readOpts *ReadOpts) error {
6170
if err != nil {
6271
return err
6372
}
73+
names := quoteNames(columnNames, opts.Quote)
74+
6475
columnTypes, err := reader.Types()
6576
if err != nil {
6677
return err
6778
}
68-
names := make([]string, len(columnNames))
69-
for i := range columnNames {
70-
names[i] = quoted(columnNames[i], opts.Quote)
71-
}
72-
results := make([][]string, 0)
73-
for _, row := range reader.PreReadRow() {
74-
resultRow := make([]string, len(names))
75-
for j, col := range row {
76-
resultRow[j] = ValString(col)
79+
80+
results := getResults(reader, len(names))
81+
82+
if opts.Detail {
83+
fmt.Fprintf(w, "The table name is %s.\n", colorTable(tableName))
84+
fmt.Fprintf(w, "The file type is %s.\n", colorFileType(rOpts.realFormat.String()))
85+
if len(names) <= 1 && len(results) != 0 {
86+
additionalAdvice(w, rOpts, columnNames[0], results[0][0])
7787
}
78-
results = append(results, resultRow)
88+
89+
fmt.Fprintln(w, colorCaption("\nData types:"))
90+
typeTableRender(w, names, columnTypes)
91+
92+
fmt.Fprintln(w, colorCaption("\nData samples:"))
93+
sampleTableRender(w, names, results)
94+
95+
fmt.Fprintln(w, colorCaption("\nExamples:"))
96+
}
97+
98+
if len(results) == 0 {
99+
return nil
79100
}
80-
typeTable := tablewriter.NewWriter(opts.OutStream)
101+
queries := examples(tableName, names, results[0])
102+
for _, query := range queries {
103+
fmt.Fprintf(w, "%s %s\n", opts.Command, `"`+query+`"`)
104+
}
105+
return nil
106+
}
107+
108+
func typeTableRender(w io.Writer, names []string, columnTypes []string) {
109+
typeTable := tablewriter.NewWriter(w)
81110
typeTable.SetAutoFormatHeaders(false)
82111
typeTable.SetHeader([]string{"column name", "type"})
83-
for i := range columnNames {
112+
for i := range names {
84113
typeTable.Append([]string{names[i], columnTypes[i]})
85114
}
86-
sampleTable := tablewriter.NewWriter(opts.OutStream)
115+
typeTable.Render()
116+
}
117+
118+
func sampleTableRender(w io.Writer, names []string, results [][]string) {
119+
sampleTable := tablewriter.NewWriter(w)
87120
sampleTable.SetAutoFormatHeaders(false)
88121
sampleTable.SetHeader(names)
89122
for _, row := range results {
90123
sampleTable.Append(row)
91124
}
125+
sampleTable.Render()
126+
}
92127

93-
yellow := gchalk.Yellow
94-
red := gchalk.Red
95-
magenta := gchalk.Magenta
96-
cyan := gchalk.Cyan
97-
if opts.Detail {
98-
fmt.Fprintf(opts.OutStream, "The table name is %s.\n", yellow(tableName))
99-
fmt.Fprintf(opts.OutStream, "The file type is %s.\n", red(rOpts.realFormat.String()))
100-
if len(names) <= 1 && rOpts.realFormat == CSV {
101-
fmt.Fprintln(opts.OutStream, magenta("Is the delimiter different?"))
102-
fmt.Fprintln(opts.OutStream, magenta(`Please try again with -id "\t" or -id " ".`))
103-
}
104-
fmt.Fprintln(opts.OutStream, cyan("\nData types:"))
105-
typeTable.Render()
106-
fmt.Fprintln(opts.OutStream, cyan("\nData samples:"))
107-
sampleTable.Render()
108-
fmt.Fprintln(opts.OutStream, cyan("\nExamples:"))
128+
func additionalAdvice(w io.Writer, rOpts *ReadOpts, name string, value string) {
129+
switch rOpts.realFormat {
130+
case CSV:
131+
checkCSV(w, value)
132+
case JSON:
133+
checkJSON(w, rOpts.InJQuery, name)
109134
}
135+
}
110136

111-
if len(results) == 0 {
112-
return nil
137+
func checkCSV(w io.Writer, value string) {
138+
if value == "[" || value == "{" {
139+
fmt.Fprintln(w, colorNotes("Is it a JSON file?"))
140+
fmt.Fprintln(w, colorNotes("Please try again with -ijson."))
141+
return
113142
}
143+
fmt.Fprintln(w, colorNotes("Is the delimiter different?"))
144+
delimiter := " "
145+
if strings.Count(value, ";") > 1 {
146+
delimiter = ";"
147+
}
148+
if strings.Count(value, "\t") > 1 {
149+
delimiter = "\\t"
150+
}
151+
fmt.Fprintf(w, colorNotes("Please try again with -id \"%s\" or other character.\n"), delimiter)
152+
if strings.Contains(value, ":") {
153+
fmt.Fprintln(w, colorNotes("Is it a LTSV file?"))
154+
fmt.Fprintln(w, colorNotes("Please try again with -iltsv."))
155+
}
156+
}
114157

115-
queries := examples(tableName, names, results[0])
116-
for _, query := range queries {
117-
fmt.Fprintf(opts.OutStream, "%s %s\n", opts.Command, `"`+query+`"`)
158+
func checkJSON(w io.Writer, jquery string, name string) {
159+
fmt.Fprintln(w, colorNotes("Is it for internal objects?"))
160+
jq := "." + name
161+
if jquery != "" {
162+
jq = jquery + jq
118163
}
119-
return nil
164+
fmt.Fprintf(w, colorNotes("Please try again with -ijq \"%s\".\n"), jq)
120165
}
121166

122-
func examples(tableName string, names []string, results []string) []string {
123-
queries := []string{
124-
// #nosec G201
125-
fmt.Sprintf("SELECT %s FROM %s", strings.Join(names, ", "), tableName),
126-
// #nosec G201
127-
fmt.Sprintf("SELECT %s FROM %s WHERE %s = '%s'", strings.Join(names, ", "), tableName, names[0], results[0]),
128-
// #nosec G201
129-
fmt.Sprintf("SELECT %s, count(%s) FROM %s GROUP BY %s", names[0], names[0], tableName, names[0]),
130-
// #nosec G201
131-
fmt.Sprintf("SELECT %s FROM %s ORDER BY %s LIMIT 10", strings.Join(names, ", "), tableName, names[0]),
167+
func quoteNames(names []string, quote string) []string {
168+
qnames := make([]string, len(names))
169+
for i := range names {
170+
qnames[i] = quoted(names[i], quote)
132171
}
133-
return queries
172+
return qnames
134173
}
135174

136175
var noQuoteRegexp = regexp.MustCompile(`^[a-z0-9_]+$`)
@@ -144,3 +183,29 @@ func quoted(name string, quote string) string {
144183
}
145184
return quote + name + quote
146185
}
186+
187+
func getResults(reader Reader, colNum int) [][]string {
188+
results := make([][]string, 0)
189+
for _, row := range reader.PreReadRow() {
190+
resultRow := make([]string, colNum)
191+
for j, col := range row {
192+
resultRow[j] = ValString(col)
193+
}
194+
results = append(results, resultRow)
195+
}
196+
return results
197+
}
198+
199+
func examples(tableName string, names []string, results []string) []string {
200+
queries := []string{
201+
// #nosec G201
202+
fmt.Sprintf("SELECT %s FROM %s", strings.Join(names, ", "), tableName),
203+
// #nosec G201
204+
fmt.Sprintf("SELECT %s FROM %s WHERE %s = '%s'", strings.Join(names, ", "), tableName, names[0], results[0]),
205+
// #nosec G201
206+
fmt.Sprintf("SELECT %s, count(%s) FROM %s GROUP BY %s", names[0], names[0], tableName, names[0]),
207+
// #nosec G201
208+
fmt.Sprintf("SELECT %s FROM %s ORDER BY %s LIMIT 10", strings.Join(names, ", "), tableName, names[0]),
209+
}
210+
return queries
211+
}

cmd/cmd.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ func (cli Cli) Run(args []string) int {
193193
trdsql.InPreRead(inPreRead),
194194
trdsql.InJQ(inJQuery),
195195
)
196-
if err := trdsql.Analyze(analyze, opts, readOpts); err != nil {
196+
if err = trdsql.Analyze(analyze, opts, readOpts); err != nil {
197197
log.Printf("ERROR: %s", err)
198198
return 1
199199
}
@@ -291,7 +291,7 @@ func (cli Cli) Run(args []string) int {
291291

292292
ctx := context.Background()
293293

294-
if err := trd.ExecContext(ctx, query); err != nil {
294+
if err = trd.ExecContext(ctx, query); err != nil {
295295
log.Printf("%s", err)
296296
return 1
297297
}

database.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ func (db *DB) copyImport(ctx context.Context, table *importTable, reader Reader)
195195
if row == nil {
196196
break
197197
}
198-
if _, err := stmt.ExecContext(ctx, row...); err != nil {
198+
if _, err = stmt.ExecContext(ctx, row...); err != nil {
199199
return err
200200
}
201201
}
@@ -212,7 +212,7 @@ func (db *DB) copyImport(ctx context.Context, table *importTable, reader Reader)
212212
if len(table.row) == 0 {
213213
continue
214214
}
215-
if _, err := stmt.ExecContext(ctx, table.row...); err != nil {
215+
if _, err = stmt.ExecContext(ctx, table.row...); err != nil {
216216
return err
217217
}
218218
}

exporter.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ func (e *WriteFormat) ExportContext(ctx context.Context, db *DB, query string) e
4545
}
4646

4747
defer func() {
48-
if err := rows.Close(); err != nil {
48+
if err = rows.Close(); err != nil {
4949
log.Printf("ERROR: close:%s", err)
5050
}
5151
}()

0 commit comments

Comments
 (0)