diff --git a/mysql/const.go b/mysql/const.go index 3ebc97c693b4..41d478c12f29 100644 --- a/mysql/const.go +++ b/mysql/const.go @@ -427,6 +427,11 @@ func (m SQLMode) HasStrictMode() bool { return m&ModeStrictTransTables == ModeStrictTransTables || m&ModeStrictAllTables == ModeStrictAllTables } +// HasPipesAsConcatMode detects if 'PIPES_AS_CONCAT' mode is set in SQLMode +func (m SQLMode) HasPipesAsConcatMode() bool { + return m&ModePipesAsConcat == ModePipesAsConcat +} + // HasNoUnsignedSubtractionMode detects if 'NO_UNSIGNED_SUBTRACTION' mode is set in SQLMode func (m SQLMode) HasNoUnsignedSubtractionMode() bool { return m&ModeNoUnsignedSubtraction == ModeNoUnsignedSubtraction diff --git a/mysql/const_test.go b/mysql/const_test.go index b46e5a2ee400..c0e35ab93bc2 100644 --- a/mysql/const_test.go +++ b/mysql/const_test.go @@ -149,6 +149,13 @@ func (s *testMySQLConstSuite) TestRealAsFloatMode(c *C) { c.Assert(row[1], Equals, "float") } +func (s *testMySQLConstSuite) TestPipesAsConcatMode(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("SET sql_mode='PIPES_AS_CONCAT';") + r := tk.MustQuery(`SELECT 'hello' || 'world';`) + r.Check(testkit.Rows("helloworld")) +} + func (s *testMySQLConstSuite) TestNoUnsignedSubtractionMode(c *C) { tk := testkit.NewTestKit(c, s.store) tk.MustExec("set sql_mode='NO_UNSIGNED_SUBTRACTION'") diff --git a/parser/lexer.go b/parser/lexer.go index 57fa07c97bb7..fa0f6682c718 100644 --- a/parser/lexer.go +++ b/parser/lexer.go @@ -146,6 +146,10 @@ func (s *Scanner) Lex(v *yySymType) int { tok = identifier } + if tok == pipes && !(s.sqlMode.HasPipesAsConcatMode()) { + return pipesAsOr + } + if tok == not && s.sqlMode.HasHighNotPrecedenceMode() { return not2 } diff --git a/parser/misc.go b/parser/misc.go index 10e6966f834a..814aa93d9d61 100644 --- a/parser/misc.go +++ b/parser/misc.go @@ -103,7 +103,7 @@ func init() { initTokenByte('?', paramMarker) initTokenByte('=', eq) - initTokenString("||", oror) + initTokenString("||", pipes) initTokenString("&&", andand) initTokenString("&^", andnot) initTokenString(":=", assignmentEq) diff --git a/parser/parser.y b/parser/parser.y index a51e802d30fb..a93fe7681f2c 100644 --- a/parser/parser.y +++ b/parser/parser.y @@ -57,7 +57,7 @@ import ( hintBegin "hintBegin is a virtual token for optimizer hint grammar" hintEnd "hintEnd is a virtual token for optimizer hint grammar" andand "&&" - oror "||" + pipes "||" /* The following tokens belong to ReservedKeyword. */ add "ADD" @@ -323,6 +323,7 @@ import ( only "ONLY" password "PASSWORD" partitions "PARTITIONS" + pipesAsOr plugins "PLUGINS" prepare "PREPARE" privileges "PRIVILEGES" @@ -783,7 +784,7 @@ import ( %precedence lowerThanOn %precedence on using %right assignmentEq -%left oror or +%left pipes or pipesAsOr %left xor %left andand and %left between @@ -1865,7 +1866,7 @@ Expression: Value: $3, } } -| Expression logOr Expression %prec oror +| Expression logOr Expression %prec pipes { $$ = &ast.BinaryOperationExpr{Op: opcode.LogicOr, L: $1, R: $3} } @@ -1898,7 +1899,8 @@ Expression: logOr: -"||" | "OR" + pipesAsOr +| "OR" logAnd: "&&" | "AND" @@ -2769,6 +2771,10 @@ SimpleExpr: { $$ = &ast.UnaryOperationExpr{Op: opcode.Plus, V: $2} } +| SimpleExpr pipes SimpleExpr + { + $$ = &ast.FuncCallExpr{FnName: model.NewCIStr(ast.Concat), Args: []ast.ExprNode{$1, $3}} + } | not2 SimpleExpr %prec neg { $$ = &ast.UnaryOperationExpr{Op: opcode.Not, V: $2}