From b3bd0dce4d547ebed68584472a112ed1f1890239 Mon Sep 17 00:00:00 2001 From: tiancaiamao <306345843@qq.com> Date: Tue, 21 Jun 2016 15:45:26 +0800 Subject: [PATCH] *: add builtin string function SPACE (#1332) --- evaluator/builtin.go | 1 + evaluator/builtin_string.go | 32 ++++++++++++++++++++++++ evaluator/builtin_string_test.go | 43 ++++++++++++++++++++++++++++++++ parser/parser.y | 7 +++++- parser/parser_test.go | 2 +- parser/scanner.l | 3 +++ 6 files changed, 86 insertions(+), 2 deletions(-) diff --git a/evaluator/builtin.go b/evaluator/builtin.go index 06ffa89fa8ca7..46ad23021444c 100644 --- a/evaluator/builtin.go +++ b/evaluator/builtin.go @@ -98,6 +98,7 @@ var Funcs = map[string]Func{ "replace": {builtinReplace, 3, 3}, "reverse": {builtinReverse, 1, 1}, "rtrim": {trimFn(strings.TrimRight, spaceChars), 1, 1}, + "space": {builtinSpace, 1, 1}, "strcmp": {builtinStrcmp, 2, 2}, "substring": {builtinSubstring, 2, 3}, "substring_index": {builtinSubstringIndex, 3, 3}, diff --git a/evaluator/builtin_string.go b/evaluator/builtin_string.go index 434c2901644f0..20305f1b6eec4 100644 --- a/evaluator/builtin_string.go +++ b/evaluator/builtin_string.go @@ -19,6 +19,8 @@ package evaluator import ( "fmt" + "math" + "strconv" "strings" "github.com/juju/errors" @@ -186,6 +188,36 @@ func builtinReverse(args []types.Datum, _ context.Context) (d types.Datum, err e } } +// See: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_space +func builtinSpace(args []types.Datum, _ context.Context) (d types.Datum, err error) { + x := args[0] + if x.IsNull() { + return d, nil + } + + if x.Kind() == types.KindString || x.Kind() == types.KindBytes { + if _, e := strconv.ParseInt(x.GetString(), 10, 64); e != nil { + return d, errors.Trace(e) + } + } + + v, err := x.ToInt64() + if err != nil { + return d, errors.Trace(err) + } + + if v < 0 { + v = 0 + } + + if v > math.MaxInt32 { + d.SetNull() + } else { + d.SetString(strings.Repeat(" ", int(v))) + } + return d, nil +} + // See: https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_upper func builtinUpper(args []types.Datum, _ context.Context) (d types.Datum, err error) { x := args[0] diff --git a/evaluator/builtin_string_test.go b/evaluator/builtin_string_test.go index 5a63825a54769..b3cd4cfe1c5f7 100644 --- a/evaluator/builtin_string_test.go +++ b/evaluator/builtin_string_test.go @@ -472,6 +472,49 @@ func (s *testEvaluatorSuite) TestSubstringIndex(c *C) { } } +func (s *testEvaluatorSuite) TestSpace(c *C) { + defer testleak.AfterTest(c)() + d, err := builtinSpace(types.MakeDatums([]interface{}{nil}...), nil) + c.Assert(err, IsNil) + c.Assert(d.Kind(), Equals, types.KindNull) + + d, err = builtinSpace(types.MakeDatums([]interface{}{8888888888}...), nil) + c.Assert(err, IsNil) + c.Assert(d.Kind(), Equals, types.KindNull) + + tbl := []struct { + Input interface{} + Expect string + }{ + {5, " "}, + {0, ""}, + {-1, ""}, + {"5", " "}, + } + + dtbl := tblToDtbl(tbl) + + for _, t := range dtbl { + d, err = builtinSpace(t["Input"], nil) + c.Assert(err, IsNil) + c.Assert(d, testutil.DatumEquals, t["Expect"][0]) + } + + wrong := []struct { + Input string + }{ + {"abc"}, + {"3.3"}, + {""}, + } + + dwrong := tblToDtbl(wrong) + for _, t := range dwrong { + _, err = builtinSpace(t["Input"], nil) + c.Assert(err, NotNil) + } +} + func (s *testEvaluatorSuite) TestLocate(c *C) { defer testleak.AfterTest(c)() tbl := []struct { diff --git a/parser/parser.y b/parser/parser.y index d47e245c9cbe5..55c65e9d05574 100644 --- a/parser/parser.y +++ b/parser/parser.y @@ -269,6 +269,7 @@ import ( show "SHOW" signed "SIGNED" some "SOME" + space "SPACE" start "START" status "STATUS" stringType "string" @@ -1954,7 +1955,7 @@ UnReservedKeyword: | "COMMENT" | "AVG_ROW_LENGTH" | "CONNECTION" | "CHECKSUM" | "COMPRESSION" | "KEY_BLOCK_SIZE" | "MAX_ROWS" | "MIN_ROWS" | "NATIONAL" | "ROW" | "ROW_FORMAT" | "QUARTER" | "ESCAPE" | "GRANTS" | "FIELDS" | "TRIGGERS" | "DELAY_KEY_WRITE" | "ISOLATION" | "REPEATABLE" | "COMMITTED" | "UNCOMMITTED" | "ONLY" | "SERIALIZABLE" | "LEVEL" | "VARIABLES" -| "SQL_CACHE" | "SQL_NO_CACHE" | "ACTION" | "DISABLE" | "ENABLE" | "REVERSE" +| "SQL_CACHE" | "SQL_NO_CACHE" | "ACTION" | "DISABLE" | "ENABLE" | "REVERSE" | "SPACE" NotKeywordToken: "ABS" | "ADDDATE" | "ADMIN" | "COALESCE" | "CONCAT" | "CONCAT_WS" | "CONNECTION_ID" | "CUR_TIME"| "COUNT" | "DAY" @@ -2647,6 +2648,10 @@ FunctionCallNonKeyword: { $$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: []ast.ExprNode{$3.(ast.ExprNode)}} } +| "SPACE" '(' Expression ')' + { + $$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: []ast.ExprNode{$3.(ast.ExprNode)}} + } | "STRCMP" '(' Expression ',' Expression ')' { $$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: []ast.ExprNode{$3.(ast.ExprNode), $5.(ast.ExprNode)}} diff --git a/parser/parser_test.go b/parser/parser_test.go index 2540fdf099cc7..838b7f744487b 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -47,7 +47,7 @@ func (s *testParserSuite) TestSimple(c *C) { "delay_key_write", "isolation", "repeatable", "committed", "uncommitted", "only", "serializable", "level", "curtime", "variables", "dayname", "version", "btree", "hash", "row_format", "dynamic", "fixed", "compressed", "compact", "redundant", "sql_no_cache sql_no_cache", "sql_cache sql_cache", "action", "round", - "enable", "disable", "reverse", + "enable", "disable", "reverse", "space", } for _, kw := range unreservedKws { src := fmt.Sprintf("SELECT %s FROM tbl;", kw) diff --git a/parser/scanner.l b/parser/scanner.l index e67a59dc1b984..05b664f1b3cea 100644 --- a/parser/scanner.l +++ b/parser/scanner.l @@ -470,6 +470,7 @@ set {s}{e}{t} share {s}{h}{a}{r}{e} show {s}{h}{o}{w} some {s}{o}{m}{e} +space {s}{p}{a}{c}{e} start {s}{t}{a}{r}{t} status {s}{t}{a}{t}{u}{s} subdate {s}{u}{b}{d}{a}{t}{e} @@ -972,6 +973,8 @@ redundant lval.item = string(l.val) return session {some} lval.item = string(l.val) return some +{space} lval.item = string(l.val) + return space {start} lval.item = string(l.val) return start {status} lval.item = string(l.val)