From 8a4bbe8ef3e897ffee2af2cb058001ed118c5d5e Mon Sep 17 00:00:00 2001 From: spongedc Date: Wed, 15 Nov 2017 00:29:00 +0800 Subject: [PATCH 1/7] parser: support sql_mode 'IGNORE_SPACE' --- mysql/const.go | 5 +++ mysql/const_test.go | 20 +++++++++ parser/lexer.go | 46 ++++++++++++++++++++- parser/parser.y | 94 +++++++++++++++++++++++++++++++------------ parser/parser_test.go | 2 +- 5 files changed, 139 insertions(+), 28 deletions(-) diff --git a/mysql/const.go b/mysql/const.go index 6fafa817d898d..65c7222a84f49 100644 --- a/mysql/const.go +++ b/mysql/const.go @@ -457,6 +457,11 @@ func (m SQLMode) HasNoBackslashEscapesMode() bool { return m&ModeNoBackslashEscapes == ModeNoBackslashEscapes } +// HasIgnoreSpaceMode detects if 'IGNORE_SPACE' mode is set in SQLMode +func (m SQLMode) HasIgnoreSpaceMode() bool { + return m&ModeIgnoreSpace == ModeIgnoreSpace +} + // consts for sql modes. const ( ModeNone SQLMode = 0 diff --git a/mysql/const_test.go b/mysql/const_test.go index 87ffb30dc7a67..e6ee949eff4ff 100644 --- a/mysql/const_test.go +++ b/mysql/const_test.go @@ -201,3 +201,23 @@ func (s *testMySQLConstSuite) TestNoBackslashEscapesMode(c *C) { r = tk.MustQuery("SELECT '\\\\'") r.Check(testkit.Rows("\\\\")) } + +func (s *testMySQLConstSuite) TestIgnoreSpaceMode(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + tk.MustExec("set sql_mode=''") + tk.MustExec("CREATE TABLE COUNT (a bigint);") + tk.MustExec("DROP TABLE COUNT;") + tk.MustExec("CREATE TABLE `COUNT` (a bigint);") + tk.MustExec("DROP TABLE COUNT;") + _, err := tk.Exec("CREATE TABLE COUNT(a bigint);") + c.Assert(err, NotNil) + + tk.MustExec("set sql_mode='IGNORE_SPACE'") + _, err = tk.Exec("CREATE TABLE COUNT (a bigint);") + c.Assert(err, NotNil) + tk.MustExec("CREATE TABLE `COUNT` (a bigint);") + tk.MustExec("DROP TABLE COUNT;") + _, err = tk.Exec("CREATE TABLE COUNT(a bigint);") + c.Assert(err, NotNil) +} diff --git a/parser/lexer.go b/parser/lexer.go index ba74a9536dc7c..b68a8c015f90d 100644 --- a/parser/lexer.go +++ b/parser/lexer.go @@ -25,6 +25,41 @@ import ( var _ = yyLexer(&Scanner{}) +var ignoreSpaceFuncMap = map[string]int{ + "adddate": builtinAddDate, + "bit_and": builtinBitAnd, + "bit_or": builtinBitOr, + "bit_xor": builtinBitXor, + "cast": builtinCast, + "count": builtinCount, + "curdate": builtinCurDate, + "curtime": builtinCurTime, + "date_add": builtinDateAdd, + "date_sub": builtinDateSub, + "extract": builtinExtract, + "group_concat": builtinGroupConcat, + "max": builtinMax, + "mid": builtinSubstring, + "min": builtinMin, + "now": builtinNow, + "position": builtinPosition, + "session_user": builtinUser, + "std": builtinStd, + "stddev": builtinStddev, + "stddev_pop": builtinStddevPop, + "stddev_samp": builtinStddevSamp, + "subdate": builtinSubDate, + "substr": builtinSubstring, + "substring": builtinSubstring, + "sum": builtinSum, + "sysdate": builtinSysDate, + "system_user": builtinUser, + "trim": builtinTrim, + "variance": builtinVariance, + "var_Pop": builtinVarPop, + "var_samp": builtinVarSamp, +} + // Pos represents the position of a token. type Pos struct { Line int @@ -435,7 +470,16 @@ func scanIdentifier(s *Scanner) (int, Pos, string) { pos := s.r.pos() s.r.inc() s.r.incAsLongAs(isIdentChar) - return identifier, pos, s.r.data(&pos) + lit := s.r.data(&pos) + if t, ok := ignoreSpaceFuncMap[strings.ToLower(lit)]; ok { + if s.sqlMode.HasIgnoreSpaceMode() { + s.skipWhitespace() + } + if s.r.peek() == '(' { + return t, pos, lit + } + } + return identifier, pos, lit } var ( diff --git a/parser/parser.y b/parser/parser.y index 7266153475e8f..f8d1d63c07fb1 100644 --- a/parser/parser.y +++ b/parser/parser.y @@ -413,6 +413,36 @@ import ( tidbSMJ "TIDB_SMJ" tidbINLJ "TIDB_INLJ" + builtinAddDate + builtinBitAnd + builtinBitOr + builtinBitXor + builtinCast + builtinCount + builtinCurDate + builtinCurTime + builtinDateAdd + builtinDateSub + builtinExtract + builtinGroupConcat + builtinMax + builtinMin + builtinNow + builtinPosition + builtinStd + builtinStddev + builtinStddevPop + builtinStddevSamp + builtinSubDate + builtinSubstring + builtinSum + builtinSysDate + builtinTrim + builtinUser + builtinVariance + builtinVarPop + builtinVarSamp + %token /*yy:token "1.%d" */ floatLit "floating-point literal" @@ -1441,7 +1471,7 @@ NowSymOptionFraction: * TODO: Process other three keywords */ NowSymFunc: - "CURRENT_TIMESTAMP" | "LOCALTIME" | "LOCALTIMESTAMP" | "NOW" + "CURRENT_TIMESTAMP" | "LOCALTIME" | "LOCALTIMESTAMP" | builtinNow NowSym: "CURRENT_TIMESTAMP" | "LOCALTIME" | "LOCALTIMESTAMP" @@ -2814,7 +2844,7 @@ SimpleExpr: FunctionType: ast.CastBinaryOperator, } } -| "CAST" '(' Expression "AS" CastType ')' +| builtinCast '(' Expression "AS" CastType ')' { /* See https://dev.mysql.com/doc/refman/5.7/en/cast-functions.html#function_cast */ tp := $5.(*types.FieldType) @@ -2938,7 +2968,7 @@ FunctionNameConflict: | "MICROSECOND" | "MINUTE" | "MONTH" -| "NOW" +| builtinNow | "QUARTER" | "REPEAT" | "REPLACE" @@ -2974,10 +3004,18 @@ FunctionCallKeyword: { $$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1), Args: $3.([]ast.ExprNode)} } +| builtinUser '(' ExpressionListOpt ')' + { + $$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1), Args: $3.([]ast.ExprNode)} + } | FunctionNameOptionalBraces OptionalBraces { $$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1)} } +| builtinCurDate '(' ')' + { + $$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1)} + } | FunctionNameDatetimePrecision FuncDatetimePrec { args := []ast.ExprNode{} @@ -3033,7 +3071,11 @@ FunctionCallKeyword: } FunctionCallNonKeyword: - "CURTIME" '(' FuncDatetimePrecListOpt ')' + builtinCurTime '(' FuncDatetimePrecListOpt ')' + { + $$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1), Args: $3.([]ast.ExprNode)} + } +| builtinSysDate '(' FuncDatetimePrecListOpt ')' { $$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1), Args: $3.([]ast.ExprNode)} } @@ -3070,7 +3112,7 @@ FunctionCallNonKeyword: }, } } -| "EXTRACT" '(' TimeUnit "FROM" Expression ')' +| builtinExtract '(' TimeUnit "FROM" Expression ')' { timeUnit := ast.NewValueExpr($3) $$ = &ast.FuncCallExpr{ @@ -3085,32 +3127,32 @@ FunctionCallNonKeyword: Args: []ast.ExprNode{ast.NewValueExpr($3), $5}, } } -| "POSITION" '(' BitExpr "IN" Expression ')' +| builtinPosition '(' BitExpr "IN" Expression ')' { $$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1), Args: []ast.ExprNode{$3, $5}} } -| "SUBSTRING" '(' Expression ',' Expression ')' +| builtinSubstring '(' Expression ',' Expression ')' { $$ = &ast.FuncCallExpr{ FnName: model.NewCIStr($1), Args: []ast.ExprNode{$3, $5}, } } -| "SUBSTRING" '(' Expression "FROM" Expression ')' +| builtinSubstring '(' Expression "FROM" Expression ')' { $$ = &ast.FuncCallExpr{ FnName: model.NewCIStr($1), Args: []ast.ExprNode{$3, $5}, } } -| "SUBSTRING" '(' Expression ',' Expression ',' Expression ')' +| builtinSubstring '(' Expression ',' Expression ',' Expression ')' { $$ = &ast.FuncCallExpr{ FnName: model.NewCIStr($1), Args: []ast.ExprNode{$3, $5, $7}, } } -| "SUBSTRING" '(' Expression "FROM" Expression "FOR" Expression ')' +| builtinSubstring '(' Expression "FROM" Expression "FOR" Expression ')' { $$ = &ast.FuncCallExpr{ FnName: model.NewCIStr($1), @@ -3131,21 +3173,21 @@ FunctionCallNonKeyword: Args: []ast.ExprNode{ast.NewValueExpr($3), $5, $7}, } } -| "TRIM" '(' Expression ')' +| builtinTrim '(' Expression ')' { $$ = &ast.FuncCallExpr{ FnName: model.NewCIStr($1), Args: []ast.ExprNode{$3}, } } -| "TRIM" '(' Expression "FROM" Expression ')' +| builtinTrim '(' Expression "FROM" Expression ')' { $$ = &ast.FuncCallExpr{ FnName: model.NewCIStr($1), Args: []ast.ExprNode{$5, $3}, } } -| "TRIM" '(' TrimDirection "FROM" Expression ')' +| builtinTrim '(' TrimDirection "FROM" Expression ')' { nilVal := ast.NewValueExpr(nil) direction := ast.NewValueExpr(int($3.(ast.TrimDirectionType))) @@ -3154,7 +3196,7 @@ FunctionCallNonKeyword: Args: []ast.ExprNode{$5, nilVal, direction}, } } -| "TRIM" '(' TrimDirection Expression "FROM" Expression ')' +| builtinTrim '(' TrimDirection Expression "FROM" Expression ')' { direction := ast.NewValueExpr(int($3.(ast.TrimDirectionType))) $$ = &ast.FuncCallExpr{ @@ -3183,13 +3225,13 @@ GetFormatSelector: FunctionNameDateArith: - "DATE_ADD" -| "DATE_SUB" + builtinDateAdd +| builtinDateSub FunctionNameDateArithMultiForms: - "ADDDATE" -| "SUBDATE" + builtinAddDate +| builtinSubDate TrimDirection: @@ -3215,36 +3257,36 @@ SumExpr: { $$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$3}} } -| "COUNT" '(' DistinctKwd ExpressionList ')' +| builtinCount '(' DistinctKwd ExpressionList ')' { $$ = &ast.AggregateFuncExpr{F: $1, Args: $4.([]ast.ExprNode), Distinct: true} } -| "COUNT" '(' "ALL" Expression ')' +| builtinCount '(' "ALL" Expression ')' { $$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$4}} } -| "COUNT" '(' Expression ')' +| builtinCount '(' Expression ')' { $$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$3}} } -| "COUNT" '(' '*' ')' +| builtinCount '(' '*' ')' { args := []ast.ExprNode{ast.NewValueExpr(1)} $$ = &ast.AggregateFuncExpr{F: $1, Args: args} } -| "GROUP_CONCAT" '(' BuggyDefaultFalseDistinctOpt ExpressionList ')' +| builtinGroupConcat '(' BuggyDefaultFalseDistinctOpt ExpressionList ')' { $$ = &ast.AggregateFuncExpr{F: $1, Args: $4.([]ast.ExprNode), Distinct: $3.(bool)} } -| "MAX" '(' BuggyDefaultFalseDistinctOpt Expression ')' +| builtinMax '(' BuggyDefaultFalseDistinctOpt Expression ')' { $$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$4}, Distinct: $3.(bool)} } -| "MIN" '(' BuggyDefaultFalseDistinctOpt Expression ')' +| builtinMin '(' BuggyDefaultFalseDistinctOpt Expression ')' { $$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$4}, Distinct: $3.(bool)} } -| "SUM" '(' BuggyDefaultFalseDistinctOpt Expression ')' +| builtinSum '(' BuggyDefaultFalseDistinctOpt Expression ')' { $$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$4}, Distinct: $3.(bool)} } diff --git a/parser/parser_test.go b/parser/parser_test.go index 3f20be41b9688..fc4106be68e0d 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -828,7 +828,7 @@ func (s *testParserSuite) TestBuiltin(c *C) { // for date, day, weekday {"SELECT CURRENT_DATE, CURRENT_DATE(), CURDATE()", true}, - {"SELECT CURRENT_DATE, CURRENT_DATE(), CURDATE(1)", true}, + {"SELECT CURRENT_DATE, CURRENT_DATE(), CURDATE(1)", false}, {"SELECT DATEDIFF('2003-12-31', '2003-12-30');", true}, {"SELECT DATE('2003-12-31 01:02:03');", true}, {"SELECT DATE();", true}, From 8439169ea5aea7da4907ffce0c507763b09b4226 Mon Sep 17 00:00:00 2001 From: "duchuan.dc" Date: Wed, 15 Nov 2017 09:10:06 +0800 Subject: [PATCH 2/7] Fix regression --- parser/lexer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parser/lexer.go b/parser/lexer.go index 27a1a386705dc..b68a8c015f90d 100644 --- a/parser/lexer.go +++ b/parser/lexer.go @@ -566,7 +566,7 @@ func (s *Scanner) scanString() (tok int, pos Pos, lit string) { } str := mb.r.data(&pos) mb.setUseBuf(str[1 : len(str)-1]) - } else if ch0 == '\\' { + } else if ch0 == '\\' && !s.sqlMode.HasNoBackslashEscapesMode() { mb.setUseBuf(mb.r.data(&pos)[1:]) ch0 = handleEscape(s) } From 2730b3b7b580831e9b0e9a51a0145dfca2f12518 Mon Sep 17 00:00:00 2001 From: "duchuan.dc" Date: Wed, 15 Nov 2017 09:33:00 +0800 Subject: [PATCH 3/7] revert #5073 related codes according to #5104 --- mysql/const_test.go | 10 ---------- parser/lexer.go | 2 +- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/mysql/const_test.go b/mysql/const_test.go index e6ee949eff4ff..e49b382aa8aa0 100644 --- a/mysql/const_test.go +++ b/mysql/const_test.go @@ -192,16 +192,6 @@ func (s *testMySQLConstSuite) TestHighNotPrecedenceMode(c *C) { r.Check(testkit.Rows("1")) } -func (s *testMySQLConstSuite) TestNoBackslashEscapesMode(c *C) { - tk := testkit.NewTestKit(c, s.store) - tk.MustExec("set sql_mode=''") - r := tk.MustQuery("SELECT '\\\\'") - r.Check(testkit.Rows("\\")) - tk.MustExec("set sql_mode='NO_BACKSLASH_ESCAPES'") - r = tk.MustQuery("SELECT '\\\\'") - r.Check(testkit.Rows("\\\\")) -} - func (s *testMySQLConstSuite) TestIgnoreSpaceMode(c *C) { tk := testkit.NewTestKit(c, s.store) tk.MustExec("use test") diff --git a/parser/lexer.go b/parser/lexer.go index b68a8c015f90d..27a1a386705dc 100644 --- a/parser/lexer.go +++ b/parser/lexer.go @@ -566,7 +566,7 @@ func (s *Scanner) scanString() (tok int, pos Pos, lit string) { } str := mb.r.data(&pos) mb.setUseBuf(str[1 : len(str)-1]) - } else if ch0 == '\\' && !s.sqlMode.HasNoBackslashEscapesMode() { + } else if ch0 == '\\' { mb.setUseBuf(mb.r.data(&pos)[1:]) ch0 = handleEscape(s) } From 6276a976b8e1ba89cb9f4da08ddb97413c9f0f51 Mon Sep 17 00:00:00 2001 From: "duchuan.dc" Date: Mon, 20 Nov 2017 15:46:22 +0800 Subject: [PATCH 4/7] Code format refine --- parser/parser.y | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parser/parser.y b/parser/parser.y index c704e0dbd26a7..3fe951a223c75 100644 --- a/parser/parser.y +++ b/parser/parser.y @@ -3019,7 +3019,7 @@ FunctionCallKeyword: { $$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1), Args: $3.([]ast.ExprNode)} } -| builtinUser '(' ExpressionListOpt ')' +| builtinUser '(' ExpressionListOpt ')' { $$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1), Args: $3.([]ast.ExprNode)} } From e61e76ad884cbdb5f336518175f85be0795f7f5d Mon Sep 17 00:00:00 2001 From: "duchuan.dc" Date: Fri, 24 Nov 2017 09:05:12 +0800 Subject: [PATCH 5/7] 1, Bug Fix: 'bit_xxx' and 'now' are not correctly recognized in 'IGNORE_SPACE' mode 2. Move check for 'IGNORE_SPACE' to 'isTokenIdentifier' 3. add more tests --- mysql/const_test.go | 41 +++++++++++++++++++++++++++++++++++ parser/lexer.go | 46 +-------------------------------------- parser/misc.go | 53 ++++++++++++++++++++++++++++++++++++++++++++- parser/parser.y | 10 +++------ 4 files changed, 97 insertions(+), 53 deletions(-) diff --git a/mysql/const_test.go b/mysql/const_test.go index 869ef5b56117c..e7252f0cfa9ae 100644 --- a/mysql/const_test.go +++ b/mysql/const_test.go @@ -203,6 +203,26 @@ func (s *testMySQLConstSuite) TestIgnoreSpaceMode(c *C) { tk.MustExec("DROP TABLE COUNT;") _, err := tk.Exec("CREATE TABLE COUNT(a bigint);") c.Assert(err, NotNil) + tk.MustExec("CREATE TABLE test.COUNT(a bigint);") + tk.MustExec("DROP TABLE COUNT;") + + tk.MustExec("CREATE TABLE BIT_AND (a bigint);") + tk.MustExec("DROP TABLE BIT_AND;") + tk.MustExec("CREATE TABLE `BIT_AND` (a bigint);") + tk.MustExec("DROP TABLE BIT_AND;") + _, err = tk.Exec("CREATE TABLE BIT_AND(a bigint);") + c.Assert(err, NotNil) + tk.MustExec("CREATE TABLE test.BIT_AND(a bigint);") + tk.MustExec("DROP TABLE BIT_AND;") + + tk.MustExec("CREATE TABLE NOW (a bigint);") + tk.MustExec("DROP TABLE NOW;") + tk.MustExec("CREATE TABLE `NOW` (a bigint);") + tk.MustExec("DROP TABLE NOW;") + _, err = tk.Exec("CREATE TABLE NOW(a bigint);") + c.Assert(err, NotNil) + tk.MustExec("CREATE TABLE test.NOW(a bigint);") + tk.MustExec("DROP TABLE NOW;") tk.MustExec("set sql_mode='IGNORE_SPACE'") _, err = tk.Exec("CREATE TABLE COUNT (a bigint);") @@ -211,6 +231,27 @@ func (s *testMySQLConstSuite) TestIgnoreSpaceMode(c *C) { tk.MustExec("DROP TABLE COUNT;") _, err = tk.Exec("CREATE TABLE COUNT(a bigint);") c.Assert(err, NotNil) + tk.MustExec("CREATE TABLE test.COUNT(a bigint);") + tk.MustExec("DROP TABLE COUNT;") + + _, err = tk.Exec("CREATE TABLE BIT_AND (a bigint);") + c.Assert(err, NotNil) + tk.MustExec("CREATE TABLE `BIT_AND` (a bigint);") + tk.MustExec("DROP TABLE BIT_AND;") + _, err = tk.Exec("CREATE TABLE BIT_AND(a bigint);") + c.Assert(err, NotNil) + tk.MustExec("CREATE TABLE test.BIT_AND(a bigint);") + tk.MustExec("DROP TABLE BIT_AND;") + + _, err = tk.Exec("CREATE TABLE NOW (a bigint);") + c.Assert(err, NotNil) + tk.MustExec("CREATE TABLE `NOW` (a bigint);") + tk.MustExec("DROP TABLE NOW;") + _, err = tk.Exec("CREATE TABLE NOW(a bigint);") + c.Assert(err, NotNil) + tk.MustExec("CREATE TABLE test.NOW(a bigint);") + tk.MustExec("DROP TABLE NOW;") + } func (s *testMySQLConstSuite) TestPadCharToFullLengthMode(c *C) { diff --git a/parser/lexer.go b/parser/lexer.go index b68a8c015f90d..ba74a9536dc7c 100644 --- a/parser/lexer.go +++ b/parser/lexer.go @@ -25,41 +25,6 @@ import ( var _ = yyLexer(&Scanner{}) -var ignoreSpaceFuncMap = map[string]int{ - "adddate": builtinAddDate, - "bit_and": builtinBitAnd, - "bit_or": builtinBitOr, - "bit_xor": builtinBitXor, - "cast": builtinCast, - "count": builtinCount, - "curdate": builtinCurDate, - "curtime": builtinCurTime, - "date_add": builtinDateAdd, - "date_sub": builtinDateSub, - "extract": builtinExtract, - "group_concat": builtinGroupConcat, - "max": builtinMax, - "mid": builtinSubstring, - "min": builtinMin, - "now": builtinNow, - "position": builtinPosition, - "session_user": builtinUser, - "std": builtinStd, - "stddev": builtinStddev, - "stddev_pop": builtinStddevPop, - "stddev_samp": builtinStddevSamp, - "subdate": builtinSubDate, - "substr": builtinSubstring, - "substring": builtinSubstring, - "sum": builtinSum, - "sysdate": builtinSysDate, - "system_user": builtinUser, - "trim": builtinTrim, - "variance": builtinVariance, - "var_Pop": builtinVarPop, - "var_samp": builtinVarSamp, -} - // Pos represents the position of a token. type Pos struct { Line int @@ -470,16 +435,7 @@ func scanIdentifier(s *Scanner) (int, Pos, string) { pos := s.r.pos() s.r.inc() s.r.incAsLongAs(isIdentChar) - lit := s.r.data(&pos) - if t, ok := ignoreSpaceFuncMap[strings.ToLower(lit)]; ok { - if s.sqlMode.HasIgnoreSpaceMode() { - s.skipWhitespace() - } - if s.r.peek() == '(' { - return t, pos, lit - } - } - return identifier, pos, lit + return identifier, pos, s.r.data(&pos) } var ( diff --git a/parser/misc.go b/parser/misc.go index 93760bd3b08d1..505f9b91a50ce 100644 --- a/parser/misc.go +++ b/parser/misc.go @@ -493,6 +493,42 @@ var tokenMap = map[string]int{ "ZEROFILL": zerofill, } +// See https://dev.mysql.com/doc/refman/5.7/en/function-resolution.html for details +var btFuncTokenMap = map[string]int{ + "ADDDATE": builtinAddDate, + "BIT_AND": builtinBitAnd, + "BIT_OR": builtinBitOr, + "BIT_XOR": builtinBitXor, + "CAST": builtinCast, + "COUNT": builtinCount, + "CURDATE": builtinCurDate, + "CURTIME": builtinCurTime, + "DATE_ADD": builtinDateAdd, + "DATE_SUB": builtinDateSub, + "EXTRACT": builtinExtract, + "GROUP_CONCAT": builtinGroupConcat, + "MAX": builtinMax, + "MID": builtinSubstring, + "MIN": builtinMin, + "NOW": builtinNow, + "POSITION": builtinPosition, + "SESSION_USER": builtinUser, + "STD": builtinStddevPop, + "STDDEV": builtinStddevPop, + "STDDEV_POP": builtinStddevPop, + "STDDEV_SAMP": builtinVarSamp, + "SUBDATE": builtinSubDate, + "SUBSTR": builtinSubstring, + "SUBSTRING": builtinSubstring, + "SUM": builtinSum, + "SYSDATE": builtinSysDate, + "SYSTEM_USER": builtinUser, + "TRIM": builtinTrim, + "VARIANCE": builtinVarPop, + "VAR_POP": builtinVarPop, + "VAR_SAMP": builtinVarSamp, +} + // aliases are strings directly map to another string and use the same token. var aliases = map[string]string{ "SCHEMA": "DATABASE", @@ -521,7 +557,22 @@ func (s *Scanner) isTokenIdentifier(lit string, offset int) int { data[i] = lit[i] } } - tok := tokenMap[hack.String(data)] + + checkBtFuncToken, tokenStr := false, hack.String(data) + if s.r.peek() == '(' { + checkBtFuncToken = true + } else if s.sqlMode.HasIgnoreSpaceMode() { + s.skipWhitespace() + if s.r.peek() == '(' { + checkBtFuncToken = true + } + } + if checkBtFuncToken { + if tok := btFuncTokenMap[tokenStr]; tok != 0 { + return tok + } + } + tok := tokenMap[tokenStr] return tok } diff --git a/parser/parser.y b/parser/parser.y index 018b0cdfeec38..7fed862a4b535 100644 --- a/parser/parser.y +++ b/parser/parser.y @@ -440,17 +440,13 @@ import ( builtinMin builtinNow builtinPosition - builtinStd - builtinStddev builtinStddevPop - builtinStddevSamp builtinSubDate builtinSubstring builtinSum builtinSysDate builtinTrim builtinUser - builtinVariance builtinVarPop builtinVarSamp @@ -3402,15 +3398,15 @@ SumExpr: { $$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$4}, Distinct: $3.(bool)} } -| "BIT_AND" '(' Expression ')' +| builtinBitAnd '(' Expression ')' { $$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$3}} } -| "BIT_OR" '(' Expression ')' +| builtinBitOr '(' Expression ')' { $$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$3}} } -| "BIT_XOR" '(' Expression ')' +| builtinBitXor '(' Expression ')' { $$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$3}} } From 83803846c24766893b0c7cbce14ddd379edf4925 Mon Sep 17 00:00:00 2001 From: "duchuan.dc" Date: Mon, 27 Nov 2017 14:31:05 +0800 Subject: [PATCH 6/7] Format refine --- parser/parser.y | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/parser/parser.y b/parser/parser.y index 7fed862a4b535..d886c007c8850 100644 --- a/parser/parser.y +++ b/parser/parser.y @@ -3157,7 +3157,7 @@ FunctionCallKeyword: { $$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1)} } -| builtinCurDate '(' ')' +| builtinCurDate '(' ')' { $$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1)} } @@ -3220,7 +3220,7 @@ FunctionCallNonKeyword: { $$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1), Args: $3.([]ast.ExprNode)} } -| builtinSysDate '(' FuncDatetimePrecListOpt ')' +| builtinSysDate '(' FuncDatetimePrecListOpt ')' { $$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1), Args: $3.([]ast.ExprNode)} } From 76b4f2649eecab4bc89400f6d263654ed51c7478 Mon Sep 17 00:00:00 2001 From: "duchuan.dc" Date: Thu, 7 Dec 2017 01:03:39 +0800 Subject: [PATCH 7/7] Fix case fail --- ast/format_test.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/ast/format_test.go b/ast/format_test.go index e84f411d23d42..3c14ba7ff87c1 100644 --- a/ast/format_test.go +++ b/ast/format_test.go @@ -55,12 +55,14 @@ func (ts *testAstFormatSuite) TestAstFormat(c *C) { {` json_extract ( a,'$.b',"$.\"c d\"" ) `, "json_extract(`a`, \"$.b\", \"$.\\\"c d\\\"\")"}, {` length ( a )`, "length(`a`)"}, // Cast, Convert and Binary. - {` cast ( a as signed ) `, "CAST(`a` AS SIGNED)"}, - {` cast ( a as unsigned integer) `, "CAST(`a` AS UNSIGNED)"}, - {` cast ( a as char(3) binary) `, "CAST(`a` AS CHAR(3) BINARY)"}, - {` cast ( a as decimal ) `, "CAST(`a` AS DECIMAL(11))"}, - {` cast ( a as decimal (3) ) `, "CAST(`a` AS DECIMAL(3))"}, - {` cast ( a as decimal (3,3) ) `, "CAST(`a` AS DECIMAL(3, 3))"}, + // There should not be spaces between 'cast' and '(' unless 'IGNORE_SPACE' mode is set. + // see: https://dev.mysql.com/doc/refman/5.7/en/function-resolution.html + {` cast( a as signed ) `, "CAST(`a` AS SIGNED)"}, + {` cast( a as unsigned integer) `, "CAST(`a` AS UNSIGNED)"}, + {` cast( a as char(3) binary) `, "CAST(`a` AS CHAR(3) BINARY)"}, + {` cast( a as decimal ) `, "CAST(`a` AS DECIMAL(11))"}, + {` cast( a as decimal (3) ) `, "CAST(`a` AS DECIMAL(3))"}, + {` cast( a as decimal (3,3) ) `, "CAST(`a` AS DECIMAL(3, 3))"}, {` convert (a, signed) `, "CONVERT(`a`, SIGNED)"}, {` binary "hello"`, `BINARY "hello"`}, }