From c5520f86fc46b8f6c0295f84e3dc6bb59fd8d834 Mon Sep 17 00:00:00 2001 From: Mithrandie Date: Fri, 23 Jun 2017 01:49:32 +0900 Subject: [PATCH 1/2] Fix a flags bug. --- docs/_posts/2006-01-02-flag.md | 4 ++-- lib/query/query.go | 6 +++--- lib/query/query_test.go | 12 ++++++------ 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/_posts/2006-01-02-flag.md b/docs/_posts/2006-01-02-flag.md index aac6239e..4651e84c 100644 --- a/docs/_posts/2006-01-02-flag.md +++ b/docs/_posts/2006-01-02-flag.md @@ -15,8 +15,8 @@ A flag is a representation of a [command option]({{ '/reference/command.html#glo | @@DELIMITER | string | Field delimiter | | @@ENCODING | string | File encoding | | @@REPOSITORY | string | Directory path where files are located | -| @@NO-HEADER | boolean | Import first line as a record | -| @@WITHOUT-NULL | boolean | Parse empty field as empty string | +| @@NO_HEADER | boolean | Import first line as a record | +| @@WITHOUT_NULL | boolean | Parse empty field as empty string | ## Set Flag diff --git a/lib/query/query.go b/lib/query/query.go index 503c3948..1fd590c7 100644 --- a/lib/query/query.go +++ b/lib/query/query.go @@ -875,7 +875,7 @@ func SetFlag(stmt parser.SetFlag) error { switch strings.ToUpper(stmt.Name) { case "@@DELIMITER", "@@ENCODING", "@@REPOSITORY": p = parser.PrimaryToString(stmt.Value) - case "@@NO-HEADER", "@@WITHOUT-NULL": + case "@@NO_HEADER", "@@WITHOUT_NULL": p = parser.PrimaryToBoolean(stmt.Value) } if parser.IsNull(p) { @@ -889,9 +889,9 @@ func SetFlag(stmt parser.SetFlag) error { err = cmd.SetEncoding(p.(parser.String).Value()) case "@@REPOSITORY": err = cmd.SetRepository(p.(parser.String).Value()) - case "@@NO-HEADER": + case "@@NO_HEADER": err = cmd.SetNoHeader(p.(parser.Boolean).Bool()) - case "@@WITHOUT-NULL": + case "@@WITHOUT_NULL": err = cmd.SetWithoutNull(p.(parser.Boolean).Bool()) default: err = errors.New(fmt.Sprintf("invalid flag name: %s", stmt.Name)) diff --git a/lib/query/query_test.go b/lib/query/query_test.go index 144a83c7..a218c4fc 100644 --- a/lib/query/query_test.go +++ b/lib/query/query_test.go @@ -2647,19 +2647,19 @@ var setFlagTests = []struct { { Name: "Set NoHeader", Query: parser.SetFlag{ - Name: "@@no-header", + Name: "@@no_header", Value: parser.NewBoolean(true), }, - ResultFlag: "no-header", + ResultFlag: "no_header", ResultBoolValue: true, }, { Name: "Set WithoutNull", Query: parser.SetFlag{ - Name: "@@without-null", + Name: "@@without_null", Value: parser.NewBoolean(true), }, - ResultFlag: "without-null", + ResultFlag: "without_null", ResultBoolValue: true, }, { @@ -2673,10 +2673,10 @@ var setFlagTests = []struct { { Name: "Set WithoutNull Value Error", Query: parser.SetFlag{ - Name: "@@without-null", + Name: "@@without_null", Value: parser.NewString("string"), }, - Error: "invalid flag value: @@without-null = 'string'", + Error: "invalid flag value: @@without_null = 'string'", }, { Name: "Invalid Flag Error", From 30bc1f5642d73827c347f5c31a27f3bf6183a3ed Mon Sep 17 00:00:00 2001 From: Mithrandie Date: Fri, 23 Jun 2017 02:16:20 +0900 Subject: [PATCH 2/2] Fix some bugs and update docs. --- docs/_posts/2006-01-02-datetime-functions.md | 1 + docs/_posts/2006-01-02-statement.md | 14 ++--- lib/cmd/utils.go | 55 ++++++++++++++++---- lib/cmd/utils_test.go | 4 +- lib/parser/ast.go | 6 +++ lib/parser/ast_test.go | 16 ++++++ lib/query/comparison.go | 18 +++++++ lib/query/comparison_test.go | 25 +++++++++ lib/query/encode.go | 2 +- 9 files changed, 120 insertions(+), 21 deletions(-) diff --git a/docs/_posts/2006-01-02-datetime-functions.md b/docs/_posts/2006-01-02-datetime-functions.md index 97d90a50..7b394302 100644 --- a/docs/_posts/2006-01-02-datetime-functions.md +++ b/docs/_posts/2006-01-02-datetime-functions.md @@ -97,6 +97,7 @@ Format the _datetime_ according to the string _format_. | %y | Year in two digits | | %Z | Time zone in time difference | | %z | Abbreviation of Time zone name | +| %% | '%' | > You can also use [the Time Layout of the Go Lang](https://golang.org/pkg/time/#Time.Format) as a format. diff --git a/docs/_posts/2006-01-02-statement.md b/docs/_posts/2006-01-02-statement.md index 2df57342..5da8622f 100644 --- a/docs/_posts/2006-01-02-statement.md +++ b/docs/_posts/2006-01-02-statement.md @@ -129,23 +129,23 @@ SELECT @id := @id + 1 AS id, -- Line Comment {: #reserved_words} ADD AFTER ALTER ALL AND ANY AS ASC -BEFORE BETWEEN BOOLEAN BREAK BY +BEFORE BETWEEN BREAK BY CASE COMMIT CREATE CLOSE CONTINUE CROSS CURSOR -DATETIME DECLARE DEFAULT DELETE DESC DISPOSE DISTINCT DO DROP DUAL +DECLARE DEFAULT DELETE DESC DISPOSE DISTINCT DO DROP DUAL ELSE ELSEIF END EXISTS EXIT -FETCH FIRST FLAG FLOAT FOR FROM FULL +FETCH FIRST FOR FROM FULL GROUP GROUP_CONCAT HAVING -IDENTIFIER IF IN INNER INSERT INTEGER INTO IS +IF IN INNER INSERT INTO IS JOIN LAST LEFT LIKE LIMIT NATURAL NOT NULL ON OPEN OR ORDER OUTER PRINT RENAME RIGHT ROLLBACK -SELECT SET SEPARATOR STDIN STRING -TABLE TERNARY THEN TO +SELECT SET SEPARATOR STDIN +TABLE THEN TO UNION UPDATE USING -VALUES VAR VARIABLE +VALUES VAR WHEN WHERE WHILE WITH diff --git a/lib/cmd/utils.go b/lib/cmd/utils.go index 32d5bf2e..6b128679 100644 --- a/lib/cmd/utils.go +++ b/lib/cmd/utils.go @@ -3,7 +3,6 @@ package cmd import ( "bufio" "io" - "strings" "golang.org/x/text/encoding/japanese" "golang.org/x/text/transform" @@ -17,14 +16,48 @@ func GetReader(r io.Reader, enc Encoding) io.Reader { } func UnescapeString(s string) string { - s = strings.Replace(s, "\\a", "\a", -1) - s = strings.Replace(s, "\\b", "\b", -1) - s = strings.Replace(s, "\\f", "\f", -1) - s = strings.Replace(s, "\\n", "\n", -1) - s = strings.Replace(s, "\\r", "\r", -1) - s = strings.Replace(s, "\\t", "\t", -1) - s = strings.Replace(s, "\\v", "\v", -1) - s = strings.Replace(s, "\\\"", "\"", -1) - s = strings.Replace(s, "\\\\", "\\", -1) - return s + runes := []rune(s) + unescaped := []rune{} + + escaped := false + for _, r := range runes { + if escaped { + switch r { + case 'a': + unescaped = append(unescaped, '\a') + case 'b': + unescaped = append(unescaped, '\b') + case 'f': + unescaped = append(unescaped, '\f') + case 'n': + unescaped = append(unescaped, '\n') + case 'r': + unescaped = append(unescaped, '\r') + case 't': + unescaped = append(unescaped, '\t') + case 'v': + unescaped = append(unescaped, '\v') + case '"': + unescaped = append(unescaped, '"') + case '\\': + unescaped = append(unescaped, '\\') + default: + unescaped = append(unescaped, '\\', r) + } + escaped = false + continue + } + + if r == '\\' { + escaped = true + continue + } + + unescaped = append(unescaped, r) + } + if escaped { + unescaped = append(unescaped, '\\') + } + + return string(unescaped) } diff --git a/lib/cmd/utils_test.go b/lib/cmd/utils_test.go index c724c655..ce4a6d61 100644 --- a/lib/cmd/utils_test.go +++ b/lib/cmd/utils_test.go @@ -21,8 +21,8 @@ func TestGetReader(t *testing.T) { } func TestUnescapeString(t *testing.T) { - str := "\\a\\b\\f\\n\\r\\t\\v\\\\\\\"" - expect := "\a\b\f\n\r\t\v\\\"" + str := "fo\\o\\a\\b\\f\\n\\r\\t\\v\\\\\\\"bar\\" + expect := "fo\\o\a\b\f\n\r\t\v\\\"bar\\" unescaped := UnescapeString(str) if unescaped != expect { t.Errorf("unescaped string = %q, want %q", unescaped, expect) diff --git a/lib/parser/ast.go b/lib/parser/ast.go index bf644d9c..296d61ed 100644 --- a/lib/parser/ast.go +++ b/lib/parser/ast.go @@ -799,6 +799,12 @@ func (f *Field) Name() string { if f.Alias != nil { return f.Alias.(Identifier).Literal } + if s, ok := f.Object.(String); ok { + return s.Value() + } + if dt, ok := f.Object.(Datetime); ok { + return dt.literal + } return f.Object.String() } diff --git a/lib/parser/ast_test.go b/lib/parser/ast_test.go index 9f43572d..972ad502 100644 --- a/lib/parser/ast_test.go +++ b/lib/parser/ast_test.go @@ -1117,6 +1117,22 @@ func TestField_Name(t *testing.T) { if e.Name() != expect { t.Errorf("name = %q, want %q for %#v", e.Name(), expect, e) } + + e = Field{ + Object: String{literal: "foo"}, + } + expect = "foo" + if e.Name() != expect { + t.Errorf("name = %q, want %q for %#v", e.Name(), expect, e) + } + + e = Field{ + Object: NewDatetimeFromString("2012-01-01"), + } + expect = "2012-01-01" + if e.Name() != expect { + t.Errorf("name = %q, want %q for %#v", e.Name(), expect, e) + } } func TestAllColumns_String(t *testing.T) { diff --git a/lib/query/comparison.go b/lib/query/comparison.go index dc7e285c..895506b6 100644 --- a/lib/query/comparison.go +++ b/lib/query/comparison.go @@ -231,9 +231,22 @@ func stringPattern(pattern []rune, position int) (int, int, string, int) { search := []rune{} returnPostion := position + escaped := false for i := position; i < len(pattern); i++ { r := pattern[i] + if escaped { + switch r { + case '%', '_': + search = append(search, r) + default: + search = append(search, '\\', r) + } + returnPostion++ + escaped = false + continue + } + if (r == '%' || r == '_') && 0 < len(search) { break } @@ -247,10 +260,15 @@ func stringPattern(pattern []rune, position int) (int, int, string, int) { if -1 < anyRunesMaxLen { anyRunesMaxLen++ } + case '\\': + escaped = true default: search = append(search, r) } } + if escaped { + search = append(search, '\\') + } return anyRunesMinLen, anyRunesMaxLen, string(search), returnPostion } diff --git a/lib/query/comparison_test.go b/lib/query/comparison_test.go index 94cfe620..c2024dae 100644 --- a/lib/query/comparison_test.go +++ b/lib/query/comparison_test.go @@ -377,6 +377,31 @@ var likeTests = []struct { Pattern: parser.NewString("%def%_abc%"), Result: ternary.TRUE, }, + { + LHS: parser.NewString("abcde"), + Pattern: parser.NewString("abc\\_e"), + Result: ternary.FALSE, + }, + { + LHS: parser.NewString("abc_e"), + Pattern: parser.NewString("abc\\_e"), + Result: ternary.TRUE, + }, + { + LHS: parser.NewString("abcde"), + Pattern: parser.NewString("abc\\%e"), + Result: ternary.FALSE, + }, + { + LHS: parser.NewString("a\\bc%e"), + Pattern: parser.NewString("a\\bc\\%e"), + Result: ternary.TRUE, + }, + { + LHS: parser.NewString("abcde\\"), + Pattern: parser.NewString("abcde\\"), + Result: ternary.TRUE, + }, } func TestLike(t *testing.T) { diff --git a/lib/query/encode.go b/lib/query/encode.go index f8516f16..83e206e4 100644 --- a/lib/query/encode.go +++ b/lib/query/encode.go @@ -181,7 +181,7 @@ func formatTextCell(c Cell) textField { s = primary.(parser.Datetime).Format() sign = -1 case parser.Null: - s = primary.String() + s = "NULL" } return textField{value: s, sign: sign}