diff --git a/expression/expressions/cast.go b/expression/expressions/cast.go index 8c29b6df39..02d9a1de1e 100644 --- a/expression/expressions/cast.go +++ b/expression/expressions/cast.go @@ -29,6 +29,8 @@ type FunctionCast struct { Expr expression.Expression // Tp is the conversion type. Tp *types.FieldType + // Cast and Convert share this struct. + IsConvert bool } // Clone implements the Expression Clone interface. @@ -38,8 +40,9 @@ func (f *FunctionCast) Clone() (expression.Expression, error) { return nil, err } nf := &FunctionCast{ - Expr: expr, - Tp: f.Tp, + Expr: expr, + Tp: f.Tp, + IsConvert: f.IsConvert, } return nf, nil } @@ -61,6 +64,9 @@ func (f *FunctionCast) String() string { } else { tpStr = f.Tp.String() } + if f.IsConvert { + return fmt.Sprintf("CONVERT(%s, %s)", f.Expr.String(), tpStr) + } return fmt.Sprintf("CAST(%s AS %s)", f.Expr.String(), tpStr) } diff --git a/expression/expressions/cast_test.go b/expression/expressions/cast_test.go index 0484c77e17..b89de7c836 100644 --- a/expression/expressions/cast_test.go +++ b/expression/expressions/cast_test.go @@ -74,4 +74,16 @@ func (s *testCastSuite) TestCast(c *C) { _, err = expr.Eval(nil, nil) c.Assert(err, NotNil) + + // For String() + f = types.NewFieldType(mysql.TypeLonglong) + expr = &FunctionCast{ + Expr: Value{1}, + Tp: f, + } + str := expr.String() + c.Assert(str, Equals, "CAST(1 AS SIGNED)") + expr.IsConvert = true + str = expr.String() + c.Assert(str, Equals, "CONVERT(1, SIGNED)") } diff --git a/parser/parser.y b/parser/parser.y index 17297bf92e..3d437bb8aa 100644 --- a/parser/parser.y +++ b/parser/parser.y @@ -1808,6 +1808,15 @@ Function: Charset: $5.(string), } } +| "CONVERT" '(' Expression ',' CastType ')' + { + // See: https://dev.mysql.com/doc/refman/5.7/en/cast-functions.html#function_convert + $$ = &expressions.FunctionCast{ + Expr: $3.(expression.Expression), + Tp: $5.(*types.FieldType), + IsConvert: true, + } + } | "SUBSTRING" '(' Expression ',' Expression ')' { $$ = &expressions.FunctionSubstring{ diff --git a/parser/parser_test.go b/parser/parser_test.go index d6011651de..36acf4c41a 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -18,6 +18,8 @@ import ( "testing" . "github.com/pingcap/check" + "github.com/pingcap/tidb/expression/expressions" + "github.com/pingcap/tidb/stmt/stmts" ) func TestT(t *testing.T) { @@ -257,6 +259,8 @@ func (s *testParserSuite) TestParser0(c *C) { {"SELECT SUBSTRING('Quadratically' FROM 5);", true}, {"SELECT SUBSTRING('Quadratically' FROM 5 FOR 3);", true}, + {"SELECT CONVERT('111', SIGNED);", true}, + // For delete statement {"DELETE t1, t2 FROM t1 INNER JOIN t2 INNER JOIN t3 WHERE t1.id=t2.id AND t2.id=t3.id;", true}, {"DELETE FROM t1, t2 USING t1 INNER JOIN t2 INNER JOIN t3 WHERE t1.id=t2.id AND t2.id=t3.id;", true}, @@ -284,8 +288,7 @@ func (s *testParserSuite) TestParser0(c *C) { src := "SELECT id+?, id+? from t;" l := NewLexer(src) l.SetPrepare() - ok := yyParse(l) == 0 - c.Assert(ok, Equals, true) + c.Assert(yyParse(l), Equals, 0) c.Assert(len(l.ParamList), Equals, 2) c.Assert(len(l.Stmts()), Equals, 1) @@ -293,7 +296,17 @@ func (s *testParserSuite) TestParser0(c *C) { src = "CREATE TABLE foo (a SMALLINT UNSIGNED, b INT UNSIGNED,); -- foo\nSelect --1 from foo;" l = NewLexer(src) l.SetPrepare() - ok = yyParse(l) == 0 - c.Assert(ok, Equals, true) + c.Assert(yyParse(l), Equals, 0) c.Assert(len(l.Stmts()), Equals, 2) + + // Testcase for CONVERT(expr,type) + src = "SELECT CONVERT('111', SIGNED);" + l = NewLexer(src) + c.Assert(yyParse(l), Equals, 0) + st := l.Stmts()[0] + ss, ok := st.(*stmts.SelectStmt) + c.Assert(ok, IsTrue) + cv, ok := ss.Fields[0].Expr.(*expressions.FunctionCast) + c.Assert(ok, IsTrue) + c.Assert(cv.IsConvert, IsTrue) }