From 05816bdb30a90ac212c755179e44561b2b7e3f28 Mon Sep 17 00:00:00 2001 From: Shen Li Date: Sun, 6 Sep 2015 22:38:01 +0800 Subject: [PATCH 1/3] parser: Add convert type function Support mysql builtin function CONVERT(expr,type) See: https://dev.mysql.com/doc/refman/5.7/en/cast-functions.html#function_convert --- parser/parser.y | 8 ++++++++ parser/parser_test.go | 2 ++ 2 files changed, 10 insertions(+) diff --git a/parser/parser.y b/parser/parser.y index 17297bf92ee58..cca574a3eda1c 100644 --- a/parser/parser.y +++ b/parser/parser.y @@ -1808,6 +1808,14 @@ 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), + } + } | "SUBSTRING" '(' Expression ',' Expression ')' { $$ = &expressions.FunctionSubstring{ diff --git a/parser/parser_test.go b/parser/parser_test.go index d6011651de3bd..3ee1eabeae16f 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -257,6 +257,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}, From 84b2a6b8afce91abedd4dd5f39da9ef027f9b270 Mon Sep 17 00:00:00 2001 From: Shen Li Date: Sun, 6 Sep 2015 23:23:39 +0800 Subject: [PATCH 2/3] parser: Support Convert better and add unit test 1. Change FunctionCast.String() to support Convert String() 2. Add unit test in parser for convert --- expression/expressions/cast.go | 10 ++++++++-- expression/expressions/cast_test.go | 12 ++++++++++++ parser/parser.y | 1 + parser/parser_test.go | 13 +++++++++++++ 4 files changed, 34 insertions(+), 2 deletions(-) diff --git a/expression/expressions/cast.go b/expression/expressions/cast.go index 8c29b6df39ed1..02d9a1de1eb65 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 0484c77e175f4..b89de7c836414 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 cca574a3eda1c..3d437bb8aae5e 100644 --- a/parser/parser.y +++ b/parser/parser.y @@ -1814,6 +1814,7 @@ Function: $$ = &expressions.FunctionCast{ Expr: $3.(expression.Expression), Tp: $5.(*types.FieldType), + IsConvert: true, } } | "SUBSTRING" '(' Expression ',' Expression ')' diff --git a/parser/parser_test.go b/parser/parser_test.go index 3ee1eabeae16f..66e327a95209b 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) { @@ -298,4 +300,15 @@ func (s *testParserSuite) TestParser0(c *C) { ok = yyParse(l) == 0 c.Assert(ok, Equals, true) c.Assert(len(l.Stmts()), Equals, 2) + + // Testcase for CONVERT(expr,type) + src = "SELECT CONVERT('111', SIGNED);" + l = NewLexer(src) + ok = yyParse(l) == 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) } From 8663db2c1332ac921d0b1a84218e8ca487121bac Mon Sep 17 00:00:00 2001 From: shenli Date: Mon, 7 Sep 2015 10:21:29 +0800 Subject: [PATCH 3/3] parser: Remove useless code Address comment --- parser/parser_test.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/parser/parser_test.go b/parser/parser_test.go index 66e327a95209b..36acf4c41a42c 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -288,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) @@ -297,14 +296,13 @@ 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) - ok = yyParse(l) == 0 + c.Assert(yyParse(l), Equals, 0) st := l.Stmts()[0] ss, ok := st.(*stmts.SelectStmt) c.Assert(ok, IsTrue)