diff --git a/expression/builtin/builtin.go b/expression/builtin/builtin.go index bcf3126090..fc7fcdf26b 100644 --- a/expression/builtin/builtin.go +++ b/expression/builtin/builtin.go @@ -100,6 +100,7 @@ var Funcs = map[string]Func{ "length": {builtinLength, 1, 1, true, false}, "lower": {builtinLower, 1, 1, true, false}, "repeat": {builtinRepeat, 2, 2, true, false}, + "replace": {builtinReplace, 3, 3, true, false}, "upper": {builtinUpper, 1, 1, true, false}, // information functions diff --git a/expression/builtin/string.go b/expression/builtin/string.go index bb8027fec9..21129d2819 100644 --- a/expression/builtin/string.go +++ b/expression/builtin/string.go @@ -146,3 +146,27 @@ func builtinUpper(args []interface{}, ctx map[interface{}]interface{}) (interfac return strings.ToUpper(s), nil } } + +// See: https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_replace +func builtinReplace(args []interface{}, ctx map[interface{}]interface{}) (interface{}, error) { + for _, arg := range args { + if types.IsNil(arg) { + return nil, nil + } + } + + str, err := types.ToString(args[0]) + if err != nil { + return nil, errors.Trace(err) + } + oldStr, err := types.ToString(args[1]) + if err != nil { + return nil, errors.Trace(err) + } + newStr, err := types.ToString(args[2]) + if err != nil { + return nil, errors.Trace(err) + } + + return strings.Replace(str, oldStr, newStr, -1), nil +} diff --git a/expression/builtin/string_test.go b/expression/builtin/string_test.go index 5bb1d531b0..1474266ca9 100644 --- a/expression/builtin/string_test.go +++ b/expression/builtin/string_test.go @@ -166,3 +166,23 @@ func (s *testBuiltinSuite) TestLowerAndUpper(c *C) { c.Assert(v, Equals, strings.ToUpper(t.Expect)) } } + +func (s *testBuiltinSuite) TestReplace(c *C) { + tbl := []struct { + Input []interface{} + Expect interface{} + }{ + {[]interface{}{nil, nil, nil}, nil}, + {[]interface{}{1, nil, 2}, nil}, + {[]interface{}{1, 1, nil}, nil}, + {[]interface{}{"12345", 2, 222}, "1222345"}, + {[]interface{}{"12325", 2, "a"}, "1a3a5"}, + {[]interface{}{12345, 2, "aa"}, "1aa345"}, + } + + for _, t := range tbl { + v, err := builtinReplace(t.Input, nil) + c.Assert(err, IsNil) + c.Assert(v, Equals, t.Expect) + } +} diff --git a/expression/call.go b/expression/call.go index 0125bd7358..4804b034e3 100644 --- a/expression/call.go +++ b/expression/call.go @@ -63,18 +63,18 @@ func NewCall(f string, args []Expression, distinct bool) (v Expression, err erro } c := Call{F: f, Distinct: distinct, distinctKey: new(int)} - for _, Val := range args { - if !Val.IsStatic() { - c.Args = append(c.Args, Val) + for _, val := range args { + if !val.IsStatic() { + c.Args = append(c.Args, val) continue } - eVal, err := Val.Eval(nil, nil) + v, err := val.Eval(nil, nil) if err != nil { return nil, err } - c.Args = append(c.Args, Value{eVal}) + c.Args = append(c.Args, Value{v}) } return &c, nil diff --git a/parser/parser.y b/parser/parser.y index 6b8125f6de..0027cadc05 100644 --- a/parser/parser.y +++ b/parser/parser.y @@ -2439,6 +2439,19 @@ FunctionCallNonKeyword: return 1 } } +| "REPLACE" '(' Expression ',' Expression ',' Expression ')' + { + args := []expression.Expression{$3.(expression.Expression), + $5.(expression.Expression), + $7.(expression.Expression)} + var err error + $$, err = expression.NewCall($1.(string), args, false) + if err != nil { + l := yylex.(*lexer) + l.err(err) + return 1 + } + } | "SECOND" '(' Expression ')' { args := []expression.Expression{$3.(expression.Expression)} diff --git a/parser/parser_test.go b/parser/parser_test.go index f276e07ed9..bb22afb434 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -349,6 +349,8 @@ func (s *testParserSuite) TestBuiltin(c *C) { {`SELECT LOWER("A"), UPPER("a")`, true}, + {`SELECT REPLACE('www.mysql.com', 'w', 'Ww')`, true}, + {`SELECT LOCATE('bar', 'foobarbar');`, true}, {`SELECT LOCATE('bar', 'foobarbar', 5);`, true}, diff --git a/parser/scanner.l b/parser/scanner.l index b07a5104af..6756eba6d6 100644 --- a/parser/scanner.l +++ b/parser/scanner.l @@ -816,7 +816,8 @@ year_month {y}{e}{a}{r}_{m}{o}{n}{t}{h} return repeat {regexp} return regexp {references} return references -{replace} return replace +{replace} lval.item = string(l.val) + return replace {rlike} return rlike {sys_var} lval.item = string(l.val)