diff --git a/ast/functions.go b/ast/functions.go index e50e36da95..42bc1c9075 100644 --- a/ast/functions.go +++ b/ast/functions.go @@ -79,6 +79,8 @@ const ( Abs = "abs" Ceil = "ceil" Ceiling = "ceiling" + Conv = "conv" + CRC32 = "crc32" Ln = "ln" Log = "log" Log2 = "log2" diff --git a/expression/builtin.go b/expression/builtin.go index 5563c1b4f8..9d5f30987f 100644 --- a/expression/builtin.go +++ b/expression/builtin.go @@ -138,6 +138,8 @@ var Funcs = map[string]Func{ ast.Power: {builtinPow, 2, 2}, ast.Rand: {builtinRand, 0, 1}, ast.Round: {builtinRound, 1, 2}, + ast.Conv: {builtinConv, 3, 3}, + ast.CRC32: {builtinCRC32, 1, 1}, // time functions ast.Curdate: {builtinCurrentDate, 0, 0}, diff --git a/expression/builtin_math.go b/expression/builtin_math.go index 7ee4251f8d..3cf2dff2d3 100644 --- a/expression/builtin_math.go +++ b/expression/builtin_math.go @@ -18,6 +18,7 @@ package expression import ( + "hash/crc32" "math" "math/rand" @@ -185,3 +186,23 @@ func builtinRound(args []types.Datum, ctx context.Context) (d types.Datum, err e d.SetFloat64(types.Round(x, dec)) return d, nil } + +// See http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html#function_conv +func builtinConv(args []types.Datum, ctx context.Context) (d types.Datum, err error) { + //TODO implement + return d, errors.New("Function unimplement") +} + +// See http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html#function_crc32 +func builtinCRC32(args []types.Datum, ctx context.Context) (d types.Datum, err error) { + if args[0].IsNull() { + return d, nil + } + x, err := args[0].ToString() + if err != nil { + return d, errors.Trace(err) + } + r := crc32.ChecksumIEEE([]byte(x)) + d.SetUint64(uint64(r)) + return d, nil +} diff --git a/expression/builtin_math_test.go b/expression/builtin_math_test.go index ee6ecc958f..ddd6b0a6fe 100644 --- a/expression/builtin_math_test.go +++ b/expression/builtin_math_test.go @@ -171,3 +171,23 @@ func (s *testEvaluatorSuite) TestRound(c *C) { c.Assert(v, testutil.DatumEquals, t["Ret"][0]) } } + +func (s *testEvaluatorSuite) TestCRC32(c *C) { + defer testleak.AfterTest(c)() + tbl := []struct { + Arg []interface{} + Ret uint64 + }{ + {[]interface{}{"mysql"}, 2501908538}, + {[]interface{}{"MySQL"}, 3259397556}, + {[]interface{}{"hello"}, 907060870}, + } + + Dtbl := tblToDtbl(tbl) + + for _, t := range Dtbl { + v, err := builtinCRC32(t["Arg"], s.ctx) + c.Assert(err, IsNil) + c.Assert(v, testutil.DatumEquals, t["Ret"][0]) + } +} diff --git a/parser/misc.go b/parser/misc.go index 6d019e2304..8e8a04c20a 100644 --- a/parser/misc.go +++ b/parser/misc.go @@ -484,6 +484,9 @@ var tokenMap = map[string]int{ "CHAR_FUNC": charFunc, "CHAR_LENGTH": charLength, "CHARACTER_LENGTH": charLength, + "CONV": conv, + "BIT_XOR": bitXor, + "CRC32": crc32, } func isTokenIdentifier(s string, buf *bytes.Buffer) int { diff --git a/parser/parser.y b/parser/parser.y index 4787761770..b51df46100 100644 --- a/parser/parser.y +++ b/parser/parser.y @@ -291,6 +291,9 @@ import ( charFunc "CHAR_FUNC" charLength "CHAR_LENGTH" characterLength "CHARACTER_LENGTH" + conv "CONV" + bitXor "BIT_XOR" + crc32 "CRC32" /* the following tokens belong to UnReservedKeyword*/ action "ACTION" @@ -3021,6 +3024,21 @@ FunctionCallNonKeyword: Args: []ast.ExprNode{$3.(ast.ExprNode)}, } } +| "CONV" '(' Expression ',' Expression ',' Expression ')' + { + $$ = &ast.FuncCallExpr{ + FnName: model.NewCIStr($1), + Args: []ast.ExprNode{$3.(ast.ExprNode), $5.(ast.ExprNode), $7.(ast.ExprNode)}, + } + } +| "CRC32" '(' Expression ')' + { + $$ = &ast.FuncCallExpr{ + FnName: model.NewCIStr($1), + Args: []ast.ExprNode{$3.(ast.ExprNode)}, + } + } + DateArithOpt: "DATE_ADD" @@ -3099,6 +3117,10 @@ FunctionCallAgg: { $$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$4.(ast.ExprNode)}, Distinct: $3.(bool)} } +| "BIT_XOR" '(' DistinctOpt Expression ')' + { + $$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$4.(ast.ExprNode)}, Distinct: $3.(bool)} + } FuncDatetimePrec: { diff --git a/parser/parser_test.go b/parser/parser_test.go index 7e4476de2d..e43243307f 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -511,6 +511,8 @@ func (s *testParserSuite) TestBuiltin(c *C) { {"SELECT LOG(2, 65536);", true}, {"SELECT LOG2(2);", true}, {"SELECT LOG10(10);", true}, + {"SELECT CONV(10+'10'+'10'+X'0a',10,10);", true}, + {"SELECT CRC32('MySQL');", true}, {"SELECT SUBSTR('Quadratically',5);", true}, {"SELECT SUBSTR('Quadratically',5, 3);", true}, diff --git a/plan/typeinferer.go b/plan/typeinferer.go index 92f1420587..98f55cd3d1 100644 --- a/plan/typeinferer.go +++ b/plan/typeinferer.go @@ -314,7 +314,7 @@ func (v *typeInferrer) handleFuncCallExpr(x *ast.FuncCallExpr) { "substring_index", "trim", "ltrim", "rtrim", "reverse", "hex", "unhex", "date_format", "rpad", "char_func": tp = types.NewFieldType(mysql.TypeVarString) chs = v.defaultCharset - case "strcmp", "isnull", "bit_length", "char_length", "character_length": + case "strcmp", "isnull", "bit_length", "char_length", "character_length", "crc32": tp = types.NewFieldType(mysql.TypeLonglong) case "connection_id": tp = types.NewFieldType(mysql.TypeLonglong) diff --git a/plan/typeinferer_test.go b/plan/typeinferer_test.go index dfc84bc88a..c6198a634c 100644 --- a/plan/typeinferer_test.go +++ b/plan/typeinferer_test.go @@ -158,6 +158,7 @@ func (ts *testTypeInferrerSuite) TestInferType(c *C) { {"char(66)", mysql.TypeVarString, charset.CharsetUTF8}, {"char_length('TiDB')", mysql.TypeLonglong, charset.CharsetBin}, {"character_length('TiDB')", mysql.TypeLonglong, charset.CharsetBin}, + {"crc32('TiDB')", mysql.TypeLonglong, charset.CharsetBin}, } for _, ca := range cases { ctx := testKit.Se.(context.Context)