From 6462e4c519ec06a3706c94b29fc5a7ba7b797b85 Mon Sep 17 00:00:00 2001 From: Qiannan Lu Date: Wed, 15 Mar 2017 23:03:08 +0800 Subject: [PATCH 1/7] builtin: add lpad built-in function --- expression/builtin_string.go | 35 ++++++++++++++++++++++++++++++- expression/builtin_string_test.go | 34 ++++++++++++++++++++++++++++++ plan/typeinferer.go | 2 +- plan/typeinferer_test.go | 1 + 4 files changed, 70 insertions(+), 2 deletions(-) diff --git a/expression/builtin_string.go b/expression/builtin_string.go index 3d64f5c093386..30a07a76827ab 100644 --- a/expression/builtin_string.go +++ b/expression/builtin_string.go @@ -1599,5 +1599,38 @@ type builtinLpadSig struct { // See https://dev.mysql.com/doc/refman/5.6/en/string-functions.html#function_lpad func (b *builtinLpadSig) eval(row []types.Datum) (d types.Datum, err error) { - return d, errFunctionNotExists.GenByArgs("lpad") + args, err := b.evalArgs(row) + if err != nil { + return types.Datum{}, errors.Trace(err) + } + // LPAD(str,len,padstr) + // args[0] string, args[1] int, args[2] string + str, err := args[0].ToString() + if err != nil { + return d, errors.Trace(err) + } + length, err := args[1].ToInt64(b.ctx.GetSessionVars().StmtCtx) + if err != nil { + return d, errors.Trace(err) + } + l := int(length) + + padStr, err := args[2].ToString() + if err != nil { + return d, errors.Trace(err) + } + + if l < 0 || (len(str) < l && padStr == "") { + d.SetNull() + return d, nil + } + + tailLen := l - len(str) + if tailLen > 0 { + repeatCount := tailLen/len(padStr) + 1 + str = strings.Repeat(padStr, repeatCount)[:tailLen] + str + } + d.SetString(str[:l]) + + return d, nil } diff --git a/expression/builtin_string_test.go b/expression/builtin_string_test.go index b9fa2bebd93c5..abdab609db3f4 100644 --- a/expression/builtin_string_test.go +++ b/expression/builtin_string_test.go @@ -949,3 +949,37 @@ func (s *testEvaluatorSuite) TestField(c *C) { c.Assert(r, testutil.DatumEquals, types.NewDatum(t.ret)) } } + +func (s *testEvaluatorSuite) TestLpad(c *C) { + tests := []struct { + str string + len int64 + padStr string + expect interface{} + }{ + {"hi", 5, "?", "???hi"}, + {"hi", 1, "?", "h"}, + {"hi", 0, "?", ""}, + {"hi", -1, "?", nil}, + {"hi", 1, "", "h"}, + {"hi", 5, "", nil}, + {"hi", 5, "ab", "abahi"}, + {"hi", 6, "ab", "ababhi"}, + } + fc := funcs[ast.Lpad] + for _, test := range tests { + str := types.NewStringDatum(test.str) + length := types.NewIntDatum(test.len) + padStr := types.NewStringDatum(test.padStr) + f, err := fc.getFunction(datumsToConstants([]types.Datum{str, length, padStr}), s.ctx) + c.Assert(err, IsNil) + result, err := f.eval(nil) + c.Assert(err, IsNil) + if test.expect == nil { + c.Assert(result.Kind(), Equals, types.KindNull) + } else { + expect, _ := test.expect.(string) + c.Assert(result.GetString(), Equals, expect) + } + } +} diff --git a/plan/typeinferer.go b/plan/typeinferer.go index c3b61a92ae101..6354e93cba650 100644 --- a/plan/typeinferer.go +++ b/plan/typeinferer.go @@ -372,7 +372,7 @@ func (v *typeInferrer) handleFuncCallExpr(x *ast.FuncCallExpr) { case "dayname", "version", "database", "user", "current_user", "schema", "concat", "concat_ws", "left", "lcase", "lower", "repeat", "replace", "ucase", "upper", "convert", "substring", - "substring_index", "trim", "ltrim", "rtrim", "reverse", "hex", "unhex", "date_format", "rpad", "char_func", "conv": + "substring_index", "trim", "ltrim", "rtrim", "reverse", "hex", "unhex", "date_format", "rpad", "lpad", "char_func", "conv": tp = types.NewFieldType(mysql.TypeVarString) chs = v.defaultCharset case "strcmp", "isnull", "bit_length", "char_length", "character_length", "crc32", "timestampdiff", "sign": diff --git a/plan/typeinferer_test.go b/plan/typeinferer_test.go index b45340a9db2ce..ae5e92719199b 100644 --- a/plan/typeinferer_test.go +++ b/plan/typeinferer_test.go @@ -214,6 +214,7 @@ func (ts *testTypeInferrerSuite) TestInferType(c *C) { {"unhex(12)", mysql.TypeVarString, "utf8"}, {"DATE_FORMAT('2009-10-04 22:23:00', '%W %M %Y')", mysql.TypeVarString, "utf8"}, {"rpad('TiDB', 12, 'go')", mysql.TypeVarString, charset.CharsetUTF8}, + {"lpad('TiDB', 12, 'go')", mysql.TypeVarString, charset.CharsetUTF8}, {"bit_length('TiDB')", mysql.TypeLonglong, charset.CharsetBin}, {"char(66)", mysql.TypeVarString, charset.CharsetUTF8}, {"char_length('TiDB')", mysql.TypeLonglong, charset.CharsetBin}, From 67406354c42b34b823947cf0f0f73d84ca93db69 Mon Sep 17 00:00:00 2001 From: Qiannan Lu Date: Thu, 16 Mar 2017 09:34:03 +0800 Subject: [PATCH 2/7] builtin: add lpad built-in function remove the unnecessary setNull --- expression/builtin_string.go | 1 - 1 file changed, 1 deletion(-) diff --git a/expression/builtin_string.go b/expression/builtin_string.go index 30a07a76827ab..faad75aa23fcc 100644 --- a/expression/builtin_string.go +++ b/expression/builtin_string.go @@ -1621,7 +1621,6 @@ func (b *builtinLpadSig) eval(row []types.Datum) (d types.Datum, err error) { } if l < 0 || (len(str) < l && padStr == "") { - d.SetNull() return d, nil } From 0166b1154ac926c4028a38a2a54dc870bd7f333f Mon Sep 17 00:00:00 2001 From: hsqlu Date: Sat, 18 Mar 2017 00:36:14 +0800 Subject: [PATCH 3/7] builtin: add elt built-in function --- expression/builtin_string.go | 23 ++++++++++++++++++++++- expression/builtin_string_test.go | 23 +++++++++++++++++++++++ plan/typeinferer.go | 2 +- plan/typeinferer_test.go | 1 + 4 files changed, 47 insertions(+), 2 deletions(-) diff --git a/expression/builtin_string.go b/expression/builtin_string.go index 92fca3a7e2f41..974372d1345b2 100644 --- a/expression/builtin_string.go +++ b/expression/builtin_string.go @@ -1557,7 +1557,28 @@ type builtinEltSig struct { // See https://dev.mysql.com/doc/refman/5.6/en/string-functions.html#function_elt func (b *builtinEltSig) eval(row []types.Datum) (d types.Datum, err error) { - return d, errFunctionNotExists.GenByArgs("elt") + args, err := b.evalArgs(row) + if err != nil { + return types.Datum{}, errors.Trace(err) + } + + index, err := args[0].ToInt64(b.ctx.GetSessionVars().StmtCtx) + if err != nil { + return d, errors.Trace(err) + } + + argsLength := int64(len(args)); + if index < 1 || index > (argsLength - 1) { + return d, nil + } + + result, err := args[index].ToString(); + if err != nil { + return d, errors.Trace(err) + } + d.SetString(result) + + return d, nil } type exportSetFunctionClass struct { diff --git a/expression/builtin_string_test.go b/expression/builtin_string_test.go index 00cd238523731..34f334ffbef92 100644 --- a/expression/builtin_string_test.go +++ b/expression/builtin_string_test.go @@ -1054,3 +1054,26 @@ func (s *testEvaluatorSuite) TestOct(c *C) { c.Assert(err, IsNil) c.Assert(r.IsNull(), IsTrue) } + +func (s *testEvaluatorSuite) TestElt(c *C) { + defer testleak.AfterTest(c)() + + tbl := []struct { + argLst []interface{} + ret interface{} + }{ + {[]interface{}{1, "Hej", "ej", "Heja", "hej", "foo"}, "Hej"}, + {[]interface{}{9, "Hej", "ej", "Heja", "hej", "foo"}, nil}, + {[]interface{}{-1, "Hej", "ej", "Heja", "ej", "hej", "foo"}, nil}, + {[]interface{}{0, 2, 3, 11, 1}, nil}, + {[]interface{}{3, 2, 3, 11, 1}, "11"}, + {[]interface{}{1.1, "2.1", "3.1", "11.1", "1.1"}, "2.1"}, + } + for _, t := range tbl { + fc := funcs[ast.Elt] + f, err := fc.getFunction(datumsToConstants(types.MakeDatums(t.argLst...)), s.ctx) + c.Assert(err, IsNil) + r, err := f.eval(nil) + c.Assert(r, testutil.DatumEquals, types.NewDatum(t.ret)) + } +} \ No newline at end of file diff --git a/plan/typeinferer.go b/plan/typeinferer.go index 5d0838141fd11..96791ccd2baf2 100644 --- a/plan/typeinferer.go +++ b/plan/typeinferer.go @@ -373,7 +373,7 @@ func (v *typeInferrer) handleFuncCallExpr(x *ast.FuncCallExpr) { tp = types.NewFieldType(mysql.TypeDatetime) case "dayname", "version", "database", "user", "current_user", "schema", "concat", "concat_ws", "left", "lcase", "lower", "repeat", - "replace", "ucase", "upper", "convert", "substring", + "replace", "ucase", "upper", "convert", "substring", "elt", "substring_index", "trim", "ltrim", "rtrim", "reverse", "hex", "unhex", "date_format", "rpad", "lpad", "char_func", "conv", "make_set", "oct": tp = types.NewFieldType(mysql.TypeVarString) diff --git a/plan/typeinferer_test.go b/plan/typeinferer_test.go index d6c4ba67116cd..eb387cb2d4ee5 100644 --- a/plan/typeinferer_test.go +++ b/plan/typeinferer_test.go @@ -221,6 +221,7 @@ func (ts *testTypeInferrerSuite) TestInferType(c *C) { {"DATE_FORMAT('2009-10-04 22:23:00', '%W %M %Y')", mysql.TypeVarString, "utf8"}, {"rpad('TiDB', 12, 'go')", mysql.TypeVarString, charset.CharsetUTF8}, {"lpad('TiDB', 12, 'go')", mysql.TypeVarString, charset.CharsetUTF8}, + {"elt(1, 'TiDB', 17, 'go')", mysql.TypeVarString, charset.CharsetUTF8}, {"bit_length('TiDB')", mysql.TypeLonglong, charset.CharsetBin}, {"char(66)", mysql.TypeVarString, charset.CharsetUTF8}, {"char_length('TiDB')", mysql.TypeLonglong, charset.CharsetBin}, From dd6752cf4d4d138a07ad485056420715e07b7e6c Mon Sep 17 00:00:00 2001 From: hsqlu Date: Sat, 18 Mar 2017 00:38:03 +0800 Subject: [PATCH 4/7] add newline at the end of file --- expression/builtin_string_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/expression/builtin_string_test.go b/expression/builtin_string_test.go index 34f334ffbef92..98d9b77f60eb7 100644 --- a/expression/builtin_string_test.go +++ b/expression/builtin_string_test.go @@ -1076,4 +1076,4 @@ func (s *testEvaluatorSuite) TestElt(c *C) { r, err := f.eval(nil) c.Assert(r, testutil.DatumEquals, types.NewDatum(t.ret)) } -} \ No newline at end of file +} From bd2244e026e1463794c87681680fddbbe20ca94f Mon Sep 17 00:00:00 2001 From: hsqlu Date: Sat, 18 Mar 2017 00:58:43 +0800 Subject: [PATCH 5/7] resolve the format error --- expression/builtin_string.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/expression/builtin_string.go b/expression/builtin_string.go index 974372d1345b2..8ca113c27ed32 100644 --- a/expression/builtin_string.go +++ b/expression/builtin_string.go @@ -1567,12 +1567,12 @@ func (b *builtinEltSig) eval(row []types.Datum) (d types.Datum, err error) { return d, errors.Trace(err) } - argsLength := int64(len(args)); + argsLength := int64(len(args)) if index < 1 || index > (argsLength - 1) { return d, nil } - result, err := args[index].ToString(); + result, err := args[index].ToString() if err != nil { return d, errors.Trace(err) } From f54d0bf2b6789913b6974bf8fe2803a4205a0ea2 Mon Sep 17 00:00:00 2001 From: hsqlu Date: Sat, 18 Mar 2017 01:27:48 +0800 Subject: [PATCH 6/7] resolve the make check error --- expression/builtin_string.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/expression/builtin_string.go b/expression/builtin_string.go index 8ca113c27ed32..067668f638e7a 100644 --- a/expression/builtin_string.go +++ b/expression/builtin_string.go @@ -1566,9 +1566,9 @@ func (b *builtinEltSig) eval(row []types.Datum) (d types.Datum, err error) { if err != nil { return d, errors.Trace(err) } - + argsLength := int64(len(args)) - if index < 1 || index > (argsLength - 1) { + if index < 1 || index > (argsLength-1) { return d, nil } From e08d88cc3cc61339267805bae02d5f30804decab Mon Sep 17 00:00:00 2001 From: Qiannan Date: Mon, 20 Mar 2017 12:13:20 +0800 Subject: [PATCH 7/7] builtin: add elt built-in function make the error return type unified --- expression/builtin_string.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/expression/builtin_string.go b/expression/builtin_string.go index 067668f638e7a..e1c2535564dab 100644 --- a/expression/builtin_string.go +++ b/expression/builtin_string.go @@ -1559,7 +1559,7 @@ type builtinEltSig struct { func (b *builtinEltSig) eval(row []types.Datum) (d types.Datum, err error) { args, err := b.evalArgs(row) if err != nil { - return types.Datum{}, errors.Trace(err) + return d, errors.Trace(err) } index, err := args[0].ToInt64(b.ctx.GetSessionVars().StmtCtx)