Merge pull request #1000 from pingcap/zxylvlp/change-datum-in-ast
evaluator: change the remaining string functions.
This commit is contained in:
@ -53,15 +53,6 @@ var OldFuncs = map[string]OldFunc{
|
||||
"ifnull": {builtinIfNull, 2, 2, true, false},
|
||||
"nullif": {builtinNullIf, 2, 2, true, false},
|
||||
|
||||
// string functions
|
||||
"replace": {builtinReplace, 3, 3, true, false},
|
||||
"strcmp": {builtinStrcmp, 2, 2, true, false},
|
||||
"convert": {builtinConvert, 2, 2, true, false},
|
||||
"substring": {builtinSubstring, 2, 3, true, false},
|
||||
"substring_index": {builtinSubstringIndex, 3, 3, true, false},
|
||||
"locate": {builtinLocate, 2, 3, true, false},
|
||||
"trim": {builtinTrim, 1, 3, true, false},
|
||||
|
||||
// information functions
|
||||
"current_user": {builtinCurrentUser, 0, 0, false, false},
|
||||
"database": {builtinDatabase, 0, 0, false, false},
|
||||
@ -110,13 +101,20 @@ var Funcs = map[string]Func{
|
||||
"date_arith": {builtinDateArith, 3, 3},
|
||||
|
||||
// string functions
|
||||
"concat": {builtinConcat, 1, -1},
|
||||
"concat_ws": {builtinConcatWS, 2, -1},
|
||||
"left": {builtinLeft, 2, 2},
|
||||
"length": {builtinLength, 1, 1},
|
||||
"lower": {builtinLower, 1, 1},
|
||||
"repeat": {builtinRepeat, 2, 2},
|
||||
"upper": {builtinUpper, 1, 1},
|
||||
"concat": {builtinConcat, 1, -1},
|
||||
"concat_ws": {builtinConcatWS, 2, -1},
|
||||
"left": {builtinLeft, 2, 2},
|
||||
"length": {builtinLength, 1, 1},
|
||||
"lower": {builtinLower, 1, 1},
|
||||
"repeat": {builtinRepeat, 2, 2},
|
||||
"upper": {builtinUpper, 1, 1},
|
||||
"replace": {builtinReplace, 3, 3},
|
||||
"strcmp": {builtinStrcmp, 2, 2},
|
||||
"convert": {builtinConvert, 2, 2},
|
||||
"substring": {builtinSubstring, 2, 3},
|
||||
"substring_index": {builtinSubstringIndex, 3, 3},
|
||||
"locate": {builtinLocate, 2, 3},
|
||||
"trim": {builtinTrim, 1, 3},
|
||||
}
|
||||
|
||||
// See: http://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html#function_coalesce
|
||||
|
||||
@ -35,12 +35,10 @@ import (
|
||||
func builtinLength(args []types.Datum, _ context.Context) (d types.Datum, err error) {
|
||||
switch args[0].Kind() {
|
||||
case types.KindNull:
|
||||
d.SetNull()
|
||||
return d, nil
|
||||
default:
|
||||
s, err := args[0].ToString()
|
||||
if err != nil {
|
||||
d.SetNull()
|
||||
return d, errors.Trace(err)
|
||||
}
|
||||
d.SetInt64(int64(len(s)))
|
||||
@ -53,13 +51,11 @@ func builtinConcat(args []types.Datum, _ context.Context) (d types.Datum, err er
|
||||
var s []byte
|
||||
for _, a := range args {
|
||||
if a.Kind() == types.KindNull {
|
||||
d.SetNull()
|
||||
return d, nil
|
||||
}
|
||||
var ss string
|
||||
ss, err = a.ToString()
|
||||
if err != nil {
|
||||
d.SetNull()
|
||||
return d, errors.Trace(err)
|
||||
}
|
||||
s = append(s, []byte(ss)...)
|
||||
@ -75,14 +71,12 @@ func builtinConcatWS(args []types.Datum, _ context.Context) (d types.Datum, err
|
||||
for i, a := range args {
|
||||
if a.Kind() == types.KindNull {
|
||||
if i == 0 {
|
||||
d.SetNull()
|
||||
return d, nil
|
||||
}
|
||||
continue
|
||||
}
|
||||
ss, err := a.ToString()
|
||||
if err != nil {
|
||||
d.SetNull()
|
||||
return d, errors.Trace(err)
|
||||
}
|
||||
|
||||
@ -101,12 +95,10 @@ func builtinConcatWS(args []types.Datum, _ context.Context) (d types.Datum, err
|
||||
func builtinLeft(args []types.Datum, _ context.Context) (d types.Datum, err error) {
|
||||
str, err := args[0].ToString()
|
||||
if err != nil {
|
||||
d.SetNull()
|
||||
return d, errors.Trace(err)
|
||||
}
|
||||
length, err := args[1].ToInt64()
|
||||
if err != nil {
|
||||
d.SetNull()
|
||||
return d, errors.Trace(err)
|
||||
}
|
||||
l := int(length)
|
||||
@ -123,7 +115,6 @@ func builtinLeft(args []types.Datum, _ context.Context) (d types.Datum, err erro
|
||||
func builtinRepeat(args []types.Datum, _ context.Context) (d types.Datum, err error) {
|
||||
str, err := args[0].ToString()
|
||||
if err != nil {
|
||||
d.SetNull()
|
||||
return d, err
|
||||
}
|
||||
ch := fmt.Sprintf("%v", str)
|
||||
@ -148,12 +139,10 @@ func builtinLower(args []types.Datum, _ context.Context) (d types.Datum, err err
|
||||
x := args[0]
|
||||
switch x.Kind() {
|
||||
case types.KindNull:
|
||||
d.SetNull()
|
||||
return d, nil
|
||||
default:
|
||||
s, err := x.ToString()
|
||||
if err != nil {
|
||||
d.SetNull()
|
||||
return d, errors.Trace(err)
|
||||
}
|
||||
d.SetString(strings.ToLower(s))
|
||||
@ -166,12 +155,10 @@ func builtinUpper(args []types.Datum, _ context.Context) (d types.Datum, err err
|
||||
x := args[0]
|
||||
switch x.Kind() {
|
||||
case types.KindNull:
|
||||
d.SetNull()
|
||||
return d, nil
|
||||
default:
|
||||
s, err := x.ToString()
|
||||
if err != nil {
|
||||
d.SetNull()
|
||||
return d, errors.Trace(err)
|
||||
}
|
||||
d.SetString(strings.ToUpper(s))
|
||||
@ -180,103 +167,101 @@ func builtinUpper(args []types.Datum, _ context.Context) (d types.Datum, err err
|
||||
}
|
||||
|
||||
// See: https://dev.mysql.com/doc/refman/5.7/en/string-comparison-functions.html
|
||||
func builtinStrcmp(args []interface{}, _ context.Context) (interface{}, error) {
|
||||
if args[0] == nil || args[1] == nil {
|
||||
return nil, nil
|
||||
func builtinStrcmp(args []types.Datum, _ context.Context) (d types.Datum, err error) {
|
||||
if args[0].Kind() == types.KindNull || args[1].Kind() == types.KindNull {
|
||||
return d, nil
|
||||
}
|
||||
left, err := types.ToString(args[0])
|
||||
left, err := args[0].ToString()
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
return d, errors.Trace(err)
|
||||
}
|
||||
right, err := types.ToString(args[1])
|
||||
right, err := args[1].ToString()
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
return d, errors.Trace(err)
|
||||
}
|
||||
res := types.CompareString(left, right)
|
||||
return res, nil
|
||||
d.SetInt64(int64(res))
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// See: https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_replace
|
||||
func builtinReplace(args []interface{}, _ context.Context) (interface{}, error) {
|
||||
func builtinReplace(args []types.Datum, _ context.Context) (d types.Datum, err error) {
|
||||
for _, arg := range args {
|
||||
if arg == nil {
|
||||
return nil, nil
|
||||
if arg.Kind() == types.KindNull {
|
||||
return d, nil
|
||||
}
|
||||
}
|
||||
|
||||
str, err := types.ToString(args[0])
|
||||
str, err := args[0].ToString()
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
return d, errors.Trace(err)
|
||||
}
|
||||
oldStr, err := types.ToString(args[1])
|
||||
oldStr, err := args[1].ToString()
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
return d, errors.Trace(err)
|
||||
}
|
||||
newStr, err := types.ToString(args[2])
|
||||
newStr, err := args[2].ToString()
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
return d, errors.Trace(err)
|
||||
}
|
||||
d.SetString(strings.Replace(str, oldStr, newStr, -1))
|
||||
|
||||
return strings.Replace(str, oldStr, newStr, -1), nil
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// See: https://dev.mysql.com/doc/refman/5.7/en/cast-functions.html#function_convert
|
||||
func builtinConvert(args []interface{}, _ context.Context) (interface{}, error) {
|
||||
value := args[0]
|
||||
Charset := args[1].(string)
|
||||
|
||||
func builtinConvert(args []types.Datum, _ context.Context) (d types.Datum, err error) {
|
||||
// Casting nil to any type returns nil
|
||||
if value == nil {
|
||||
return nil, nil
|
||||
}
|
||||
str, ok := value.(string)
|
||||
if !ok {
|
||||
return nil, nil
|
||||
if args[0].Kind() != types.KindString {
|
||||
return d, nil
|
||||
}
|
||||
|
||||
str := args[0].GetString()
|
||||
Charset := args[1].GetString()
|
||||
|
||||
if strings.ToLower(Charset) == "ascii" {
|
||||
return value, nil
|
||||
d.SetString(str)
|
||||
return d, nil
|
||||
} else if strings.ToLower(Charset) == "utf8mb4" {
|
||||
return value, nil
|
||||
d.SetString(str)
|
||||
return d, nil
|
||||
}
|
||||
|
||||
encoding, _ := charset.Lookup(Charset)
|
||||
if encoding == nil {
|
||||
return nil, errors.Errorf("unknown encoding: %s", Charset)
|
||||
return d, errors.Errorf("unknown encoding: %s", Charset)
|
||||
}
|
||||
|
||||
target, _, err := transform.String(encoding.NewDecoder(), str)
|
||||
if err != nil {
|
||||
log.Errorf("Convert %s to %s with error: %v", str, Charset, err)
|
||||
return nil, errors.Trace(err)
|
||||
return d, errors.Trace(err)
|
||||
}
|
||||
return target, nil
|
||||
d.SetString(target)
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func builtinSubstring(args []interface{}, _ context.Context) (interface{}, error) {
|
||||
func builtinSubstring(args []types.Datum, _ context.Context) (d types.Datum, err error) {
|
||||
// The meaning of the elements of args.
|
||||
// arg[0] -> StrExpr
|
||||
// arg[1] -> Pos
|
||||
// arg[2] -> Len (Optional)
|
||||
str, err := types.ToString(args[0])
|
||||
str, err := args[0].ToString()
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("Substring invalid args, need string but get %T", args[0])
|
||||
return d, errors.Errorf("Substring invalid args, need string but get %T", args[0].GetValue())
|
||||
}
|
||||
|
||||
t := args[1]
|
||||
p, ok := t.(int64)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("Substring invalid pos args, need int but get %T", t)
|
||||
if args[1].Kind() != types.KindInt64 {
|
||||
return d, errors.Errorf("Substring invalid pos args, need int but get %T", args[1].GetValue())
|
||||
}
|
||||
pos := int(p)
|
||||
pos := args[1].GetInt64()
|
||||
|
||||
length := -1
|
||||
length := int64(-1)
|
||||
if len(args) == 3 {
|
||||
t = args[2]
|
||||
p, ok = t.(int64)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("Substring invalid pos args, need int but get %T", t)
|
||||
if args[2].Kind() != types.KindInt64 {
|
||||
return d, errors.Errorf("Substring invalid pos args, need int but get %T", args[2].GetValue())
|
||||
}
|
||||
length = int(p)
|
||||
length = args[2].GetInt64()
|
||||
}
|
||||
// The forms without a len argument return a substring from string str starting at position pos.
|
||||
// The forms with a len argument return a substring len characters long from string str, starting at position pos.
|
||||
@ -284,48 +269,47 @@ func builtinSubstring(args []interface{}, _ context.Context) (interface{}, error
|
||||
// In this case, the beginning of the substring is pos characters from the end of the string, rather than the beginning.
|
||||
// A negative value may be used for pos in any of the forms of this function.
|
||||
if pos < 0 {
|
||||
pos = len(str) + pos
|
||||
pos = int64(len(str)) + pos
|
||||
} else {
|
||||
pos--
|
||||
}
|
||||
if pos > len(str) || pos <= 0 {
|
||||
pos = len(str)
|
||||
if pos > int64(len(str)) || pos <= int64(0) {
|
||||
pos = int64(len(str))
|
||||
}
|
||||
end := len(str)
|
||||
if length != -1 {
|
||||
end := int64(len(str))
|
||||
if length != int64(-1) {
|
||||
end = pos + length
|
||||
}
|
||||
if end > len(str) {
|
||||
end = len(str)
|
||||
if end > int64(len(str)) {
|
||||
end = int64(len(str))
|
||||
}
|
||||
return str[pos:end], nil
|
||||
d.SetString(str[pos:end])
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// See: https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_substring-index
|
||||
func builtinSubstringIndex(args []interface{}, _ context.Context) (interface{}, error) {
|
||||
func builtinSubstringIndex(args []types.Datum, _ context.Context) (d types.Datum, err error) {
|
||||
// The meaning of the elements of args.
|
||||
// args[0] -> StrExpr
|
||||
// args[1] -> Delim
|
||||
// args[2] -> Count
|
||||
fs := args[0]
|
||||
str, err := types.ToString(fs)
|
||||
str, err := args[0].ToString()
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("Substring_Index invalid args, need string but get %T", fs)
|
||||
return d, errors.Errorf("Substring_Index invalid args, need string but get %T", args[0].GetValue())
|
||||
}
|
||||
|
||||
t := args[1]
|
||||
delim, err := types.ToString(t)
|
||||
delim, err := args[1].ToString()
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("Substring_Index invalid delim, need string but get %T", t)
|
||||
return d, errors.Errorf("Substring_Index invalid delim, need string but get %T", args[1].GetValue())
|
||||
}
|
||||
if len(delim) == 0 {
|
||||
return "", nil
|
||||
d.SetString("")
|
||||
return d, nil
|
||||
}
|
||||
|
||||
t = args[2]
|
||||
c, err := types.ToInt64(t)
|
||||
c, err := args[2].ToInt64()
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
return d, errors.Trace(err)
|
||||
}
|
||||
count := int(c)
|
||||
strs := strings.Split(str, delim)
|
||||
@ -346,83 +330,84 @@ func builtinSubstringIndex(args []interface{}, _ context.Context) (interface{},
|
||||
}
|
||||
}
|
||||
substrs := strs[start:end]
|
||||
return strings.Join(substrs, delim), nil
|
||||
d.SetString(strings.Join(substrs, delim))
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// See: https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_locate
|
||||
func builtinLocate(args []interface{}, _ context.Context) (interface{}, error) {
|
||||
func builtinLocate(args []types.Datum, _ context.Context) (d types.Datum, err error) {
|
||||
// The meaning of the elements of args.
|
||||
// args[0] -> SubStr
|
||||
// args[1] -> Str
|
||||
// args[2] -> Pos
|
||||
// eval str
|
||||
fs := args[1]
|
||||
if fs == nil {
|
||||
return nil, nil
|
||||
if args[1].Kind() == types.KindNull {
|
||||
return d, nil
|
||||
}
|
||||
str, err := types.ToString(fs)
|
||||
str, err := args[1].ToString()
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
return d, errors.Trace(err)
|
||||
}
|
||||
// eval substr
|
||||
fs = args[0]
|
||||
if fs == nil {
|
||||
return nil, nil
|
||||
if args[0].Kind() == types.KindNull {
|
||||
return d, nil
|
||||
}
|
||||
subStr, err := types.ToString(fs)
|
||||
subStr, err := args[0].ToString()
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
return d, errors.Trace(err)
|
||||
}
|
||||
// eval pos
|
||||
pos := int64(0)
|
||||
if len(args) == 3 {
|
||||
t := args[2]
|
||||
p, err := types.ToInt64(t)
|
||||
p, err := args[2].ToInt64()
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
return d, errors.Trace(err)
|
||||
}
|
||||
pos = p - 1
|
||||
if pos < 0 || pos > int64(len(str)) {
|
||||
return 0, nil
|
||||
d.SetInt64(0)
|
||||
return d, nil
|
||||
}
|
||||
if pos > int64(len(str)-len(subStr)) {
|
||||
return 0, nil
|
||||
d.SetInt64(0)
|
||||
return d, nil
|
||||
}
|
||||
}
|
||||
if len(subStr) == 0 {
|
||||
return pos + 1, nil
|
||||
d.SetInt64(pos + 1)
|
||||
return d, nil
|
||||
}
|
||||
i := strings.Index(str[pos:], subStr)
|
||||
if i == -1 {
|
||||
return 0, nil
|
||||
d.SetInt64(0)
|
||||
return d, nil
|
||||
}
|
||||
return int64(i) + pos + 1, nil
|
||||
d.SetInt64(int64(i) + pos + 1)
|
||||
return d, nil
|
||||
}
|
||||
|
||||
const spaceChars = "\n\t\r "
|
||||
|
||||
// See: https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_trim
|
||||
func builtinTrim(args []interface{}, _ context.Context) (interface{}, error) {
|
||||
func builtinTrim(args []types.Datum, _ context.Context) (d types.Datum, err error) {
|
||||
// args[0] -> Str
|
||||
// args[1] -> RemStr
|
||||
// args[2] -> Direction
|
||||
// eval str
|
||||
fs := args[0]
|
||||
if fs == nil {
|
||||
return nil, nil
|
||||
if args[0].Kind() == types.KindNull {
|
||||
return d, nil
|
||||
}
|
||||
str, err := types.ToString(fs)
|
||||
str, err := args[0].ToString()
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
return d, errors.Trace(err)
|
||||
}
|
||||
remstr := ""
|
||||
// eval remstr
|
||||
if len(args) > 1 {
|
||||
fs = args[1]
|
||||
if fs != nil {
|
||||
remstr, err = types.ToString(fs)
|
||||
if args[1].Kind() != types.KindNull {
|
||||
remstr, err = args[1].ToString()
|
||||
if err != nil {
|
||||
return nil, errors.Trace(err)
|
||||
return d, errors.Trace(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -430,7 +415,7 @@ func builtinTrim(args []interface{}, _ context.Context) (interface{}, error) {
|
||||
var result string
|
||||
var direction ast.TrimDirectionType
|
||||
if len(args) > 2 {
|
||||
direction = args[2].(ast.TrimDirectionType)
|
||||
direction = args[2].GetValue().(ast.TrimDirectionType)
|
||||
} else {
|
||||
direction = ast.TrimBothDefault
|
||||
}
|
||||
@ -452,7 +437,8 @@ func builtinTrim(args []interface{}, _ context.Context) (interface{}, error) {
|
||||
} else {
|
||||
result = strings.Trim(str, spaceChars)
|
||||
}
|
||||
return result, nil
|
||||
d.SetString(result)
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func trimLeft(str, remstr string) string {
|
||||
|
||||
@ -19,7 +19,10 @@ import (
|
||||
"time"
|
||||
|
||||
. "github.com/pingcap/check"
|
||||
"github.com/pingcap/tidb/ast"
|
||||
"github.com/pingcap/tidb/model"
|
||||
"github.com/pingcap/tidb/mysql"
|
||||
"github.com/pingcap/tidb/util/mock"
|
||||
"github.com/pingcap/tidb/util/types"
|
||||
)
|
||||
|
||||
@ -192,10 +195,11 @@ func (s *testEvaluatorSuite) TestStrcmp(c *C) {
|
||||
{[]interface{}{nil, ""}, nil},
|
||||
}
|
||||
|
||||
for _, t := range tbl {
|
||||
v, err := builtinStrcmp(t.Input, nil)
|
||||
dtbl := tblToDtbl(tbl)
|
||||
for _, t := range dtbl {
|
||||
d, err := builtinStrcmp(t["Input"], nil)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(v, Equals, t.Expect)
|
||||
c.Assert(d, DatumEquals, t["Expect"][0])
|
||||
}
|
||||
}
|
||||
|
||||
@ -212,9 +216,301 @@ func (s *testEvaluatorSuite) TestReplace(c *C) {
|
||||
{[]interface{}{12345, 2, "aa"}, "1aa345"},
|
||||
}
|
||||
|
||||
for _, t := range tbl {
|
||||
v, err := builtinReplace(t.Input, nil)
|
||||
dtbl := tblToDtbl(tbl)
|
||||
|
||||
for _, t := range dtbl {
|
||||
d, err := builtinReplace(t["Input"], nil)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(v, Equals, t.Expect)
|
||||
c.Assert(d, DatumEquals, t["Expect"][0])
|
||||
}
|
||||
}
|
||||
|
||||
func (s *testEvaluatorSuite) TestSubstring(c *C) {
|
||||
tbl := []struct {
|
||||
str string
|
||||
pos int64
|
||||
slen int64
|
||||
result string
|
||||
}{
|
||||
{"Quadratically", 5, -1, "ratically"},
|
||||
{"foobarbar", 4, -1, "barbar"},
|
||||
{"Quadratically", 5, 6, "ratica"},
|
||||
{"Sakila", -3, -1, "ila"},
|
||||
{"Sakila", -5, 3, "aki"},
|
||||
{"Sakila", -4, 2, "ki"},
|
||||
{"Sakila", 1000, 2, ""},
|
||||
{"", 2, 3, ""},
|
||||
}
|
||||
ctx := mock.NewContext()
|
||||
for _, v := range tbl {
|
||||
f := &ast.FuncCallExpr{
|
||||
FnName: model.NewCIStr("SUBSTRING"),
|
||||
Args: []ast.ExprNode{ast.NewValueExpr(v.str), ast.NewValueExpr(v.pos)},
|
||||
}
|
||||
if v.slen != -1 {
|
||||
f.Args = append(f.Args, ast.NewValueExpr(v.slen))
|
||||
}
|
||||
r, err := Eval(ctx, f)
|
||||
c.Assert(err, IsNil)
|
||||
s, ok := r.(string)
|
||||
c.Assert(ok, IsTrue)
|
||||
c.Assert(s, Equals, v.result)
|
||||
|
||||
r1, err := Eval(ctx, f)
|
||||
c.Assert(err, IsNil)
|
||||
s1, ok := r1.(string)
|
||||
c.Assert(ok, IsTrue)
|
||||
c.Assert(s, Equals, s1)
|
||||
}
|
||||
errTbl := []struct {
|
||||
str interface{}
|
||||
pos interface{}
|
||||
len interface{}
|
||||
result string
|
||||
}{
|
||||
{"foobarbar", "4", -1, "barbar"},
|
||||
{"Quadratically", 5, "6", "ratica"},
|
||||
}
|
||||
for _, v := range errTbl {
|
||||
f := &ast.FuncCallExpr{
|
||||
FnName: model.NewCIStr("SUBSTRING"),
|
||||
Args: []ast.ExprNode{ast.NewValueExpr(v.str), ast.NewValueExpr(v.pos)},
|
||||
}
|
||||
if v.len != -1 {
|
||||
f.Args = append(f.Args, ast.NewValueExpr(v.len))
|
||||
}
|
||||
_, err := Eval(ctx, f)
|
||||
c.Assert(err, NotNil)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *testEvaluatorSuite) TestConvert(c *C) {
|
||||
ctx := mock.NewContext()
|
||||
tbl := []struct {
|
||||
str string
|
||||
cs string
|
||||
result string
|
||||
}{
|
||||
{"haha", "utf8", "haha"},
|
||||
{"haha", "ascii", "haha"},
|
||||
}
|
||||
for _, v := range tbl {
|
||||
f := &ast.FuncCallExpr{
|
||||
FnName: model.NewCIStr("CONVERT"),
|
||||
Args: []ast.ExprNode{
|
||||
ast.NewValueExpr(v.str),
|
||||
ast.NewValueExpr(v.cs),
|
||||
},
|
||||
}
|
||||
|
||||
r, err := Eval(ctx, f)
|
||||
c.Assert(err, IsNil)
|
||||
s, ok := r.(string)
|
||||
c.Assert(ok, IsTrue)
|
||||
c.Assert(s, Equals, v.result)
|
||||
}
|
||||
|
||||
// Test case for error
|
||||
errTbl := []struct {
|
||||
str interface{}
|
||||
cs string
|
||||
result string
|
||||
}{
|
||||
{"haha", "wrongcharset", "haha"},
|
||||
}
|
||||
for _, v := range errTbl {
|
||||
f := &ast.FuncCallExpr{
|
||||
FnName: model.NewCIStr("CONVERT"),
|
||||
Args: []ast.ExprNode{
|
||||
ast.NewValueExpr(v.str),
|
||||
ast.NewValueExpr(v.cs),
|
||||
},
|
||||
}
|
||||
|
||||
_, err := Eval(ctx, f)
|
||||
c.Assert(err, NotNil)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *testEvaluatorSuite) TestSubstringIndex(c *C) {
|
||||
tbl := []struct {
|
||||
str string
|
||||
delim string
|
||||
count int64
|
||||
result string
|
||||
}{
|
||||
{"www.mysql.com", ".", 2, "www.mysql"},
|
||||
{"www.mysql.com", ".", -2, "mysql.com"},
|
||||
{"www.mysql.com", ".", 0, ""},
|
||||
{"www.mysql.com", ".", 3, "www.mysql.com"},
|
||||
{"www.mysql.com", ".", 4, "www.mysql.com"},
|
||||
{"www.mysql.com", ".", -3, "www.mysql.com"},
|
||||
{"www.mysql.com", ".", -4, "www.mysql.com"},
|
||||
|
||||
{"www.mysql.com", "d", 1, "www.mysql.com"},
|
||||
{"www.mysql.com", "d", 0, ""},
|
||||
{"www.mysql.com", "d", -1, "www.mysql.com"},
|
||||
|
||||
{"", ".", 2, ""},
|
||||
{"", ".", -2, ""},
|
||||
{"", ".", 0, ""},
|
||||
|
||||
{"www.mysql.com", "", 1, ""},
|
||||
{"www.mysql.com", "", -1, ""},
|
||||
{"www.mysql.com", "", 0, ""},
|
||||
}
|
||||
ctx := mock.NewContext()
|
||||
for _, v := range tbl {
|
||||
f := &ast.FuncCallExpr{
|
||||
FnName: model.NewCIStr("SUBSTRING_INDEX"),
|
||||
Args: []ast.ExprNode{ast.NewValueExpr(v.str), ast.NewValueExpr(v.delim), ast.NewValueExpr(v.count)},
|
||||
}
|
||||
r, err := Eval(ctx, f)
|
||||
c.Assert(err, IsNil)
|
||||
s, ok := r.(string)
|
||||
c.Assert(ok, IsTrue)
|
||||
c.Assert(s, Equals, v.result)
|
||||
}
|
||||
errTbl := []struct {
|
||||
str interface{}
|
||||
delim interface{}
|
||||
count interface{}
|
||||
}{
|
||||
{nil, ".", 2},
|
||||
{nil, ".", -2},
|
||||
{nil, ".", 0},
|
||||
{"asdf", nil, 2},
|
||||
{"asdf", nil, -2},
|
||||
{"asdf", nil, 0},
|
||||
{"www.mysql.com", ".", nil},
|
||||
}
|
||||
for _, v := range errTbl {
|
||||
f := &ast.FuncCallExpr{
|
||||
FnName: model.NewCIStr("SUBSTRING_INDEX"),
|
||||
Args: []ast.ExprNode{ast.NewValueExpr(v.str), ast.NewValueExpr(v.delim), ast.NewValueExpr(v.count)},
|
||||
}
|
||||
r, err := Eval(ctx, f)
|
||||
c.Assert(err, NotNil)
|
||||
c.Assert(r, IsNil)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *testEvaluatorSuite) TestLocate(c *C) {
|
||||
tbl := []struct {
|
||||
subStr string
|
||||
Str string
|
||||
result int64
|
||||
}{
|
||||
{"bar", "foobarbar", 4},
|
||||
{"xbar", "foobar", 0},
|
||||
{"", "foobar", 1},
|
||||
{"foobar", "", 0},
|
||||
{"", "", 1},
|
||||
}
|
||||
ctx := mock.NewContext()
|
||||
for _, v := range tbl {
|
||||
f := &ast.FuncCallExpr{
|
||||
FnName: model.NewCIStr("LOCATE"),
|
||||
Args: []ast.ExprNode{ast.NewValueExpr(v.subStr), ast.NewValueExpr(v.Str)},
|
||||
}
|
||||
r, err := Eval(ctx, f)
|
||||
c.Assert(err, IsNil)
|
||||
s, ok := r.(int64)
|
||||
c.Assert(ok, IsTrue)
|
||||
c.Assert(s, Equals, v.result)
|
||||
}
|
||||
|
||||
tbl2 := []struct {
|
||||
subStr string
|
||||
Str string
|
||||
pos int64
|
||||
result int64
|
||||
}{
|
||||
{"bar", "foobarbar", 5, 7},
|
||||
{"xbar", "foobar", 1, 0},
|
||||
{"", "foobar", 2, 2},
|
||||
{"foobar", "", 1, 0},
|
||||
{"", "", 2, 0},
|
||||
}
|
||||
for _, v := range tbl2 {
|
||||
f := &ast.FuncCallExpr{
|
||||
FnName: model.NewCIStr("LOCATE"),
|
||||
Args: []ast.ExprNode{ast.NewValueExpr(v.subStr), ast.NewValueExpr(v.Str), ast.NewValueExpr(v.pos)},
|
||||
}
|
||||
r, err := Eval(ctx, f)
|
||||
c.Assert(err, IsNil)
|
||||
s, ok := r.(int64)
|
||||
c.Assert(ok, IsTrue)
|
||||
c.Assert(s, Equals, v.result)
|
||||
}
|
||||
|
||||
errTbl := []struct {
|
||||
subStr interface{}
|
||||
Str interface{}
|
||||
}{
|
||||
{nil, nil},
|
||||
{"", nil},
|
||||
{nil, ""},
|
||||
{"foo", nil},
|
||||
{nil, "bar"},
|
||||
}
|
||||
for _, v := range errTbl {
|
||||
f := &ast.FuncCallExpr{
|
||||
FnName: model.NewCIStr("LOCATE"),
|
||||
Args: []ast.ExprNode{ast.NewValueExpr(v.subStr), ast.NewValueExpr(v.Str)},
|
||||
}
|
||||
r, _ := Eval(ctx, f)
|
||||
c.Assert(r, IsNil)
|
||||
}
|
||||
|
||||
errTbl2 := []struct {
|
||||
subStr interface{}
|
||||
Str interface{}
|
||||
pos interface{}
|
||||
}{
|
||||
{nil, nil, 1},
|
||||
{"", nil, 1},
|
||||
{nil, "", 1},
|
||||
{"foo", nil, -1},
|
||||
{nil, "bar", 0},
|
||||
}
|
||||
for _, v := range errTbl2 {
|
||||
f := &ast.FuncCallExpr{
|
||||
FnName: model.NewCIStr("LOCATE"),
|
||||
Args: []ast.ExprNode{ast.NewValueExpr(v.subStr), ast.NewValueExpr(v.Str), ast.NewValueExpr(v.pos)},
|
||||
}
|
||||
r, _ := Eval(ctx, f)
|
||||
c.Assert(r, IsNil)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *testEvaluatorSuite) TestTrim(c *C) {
|
||||
tbl := []struct {
|
||||
str interface{}
|
||||
remstr interface{}
|
||||
dir ast.TrimDirectionType
|
||||
result interface{}
|
||||
}{
|
||||
{" bar ", nil, ast.TrimBothDefault, "bar"},
|
||||
{"xxxbarxxx", "x", ast.TrimLeading, "barxxx"},
|
||||
{"xxxbarxxx", "x", ast.TrimBoth, "bar"},
|
||||
{"barxxyz", "xyz", ast.TrimTrailing, "barx"},
|
||||
{nil, "xyz", ast.TrimBoth, nil},
|
||||
{1, 2, ast.TrimBoth, "1"},
|
||||
{" \t\rbar\n ", nil, ast.TrimBothDefault, "bar"},
|
||||
}
|
||||
ctx := mock.NewContext()
|
||||
for _, v := range tbl {
|
||||
f := &ast.FuncCallExpr{
|
||||
FnName: model.NewCIStr("TRIM"),
|
||||
Args: []ast.ExprNode{
|
||||
ast.NewValueExpr(v.str),
|
||||
ast.NewValueExpr(v.remstr),
|
||||
ast.NewValueExpr(v.dir),
|
||||
},
|
||||
}
|
||||
r, err := Eval(ctx, f)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(r, Equals, v.result)
|
||||
}
|
||||
}
|
||||
|
||||
@ -352,54 +352,6 @@ func (s *testEvaluatorSuite) TestCaseWhen(c *C) {
|
||||
c.Assert(v, IsNil)
|
||||
}
|
||||
|
||||
func (s *testEvaluatorSuite) TestConvert(c *C) {
|
||||
ctx := mock.NewContext()
|
||||
tbl := []struct {
|
||||
str string
|
||||
cs string
|
||||
result string
|
||||
}{
|
||||
{"haha", "utf8", "haha"},
|
||||
{"haha", "ascii", "haha"},
|
||||
}
|
||||
for _, v := range tbl {
|
||||
f := &ast.FuncCallExpr{
|
||||
FnName: model.NewCIStr("CONVERT"),
|
||||
Args: []ast.ExprNode{
|
||||
ast.NewValueExpr(v.str),
|
||||
ast.NewValueExpr(v.cs),
|
||||
},
|
||||
}
|
||||
|
||||
r, err := Eval(ctx, f)
|
||||
c.Assert(err, IsNil)
|
||||
s, ok := r.(string)
|
||||
c.Assert(ok, IsTrue)
|
||||
c.Assert(s, Equals, v.result)
|
||||
}
|
||||
|
||||
// Test case for error
|
||||
errTbl := []struct {
|
||||
str interface{}
|
||||
cs string
|
||||
result string
|
||||
}{
|
||||
{"haha", "wrongcharset", "haha"},
|
||||
}
|
||||
for _, v := range errTbl {
|
||||
f := &ast.FuncCallExpr{
|
||||
FnName: model.NewCIStr("CONVERT"),
|
||||
Args: []ast.ExprNode{
|
||||
ast.NewValueExpr(v.str),
|
||||
ast.NewValueExpr(v.cs),
|
||||
},
|
||||
}
|
||||
|
||||
_, err := Eval(ctx, f)
|
||||
c.Assert(err, NotNil)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *testEvaluatorSuite) TestCall(c *C) {
|
||||
ctx := mock.NewContext()
|
||||
|
||||
@ -766,248 +718,6 @@ func (s *testEvaluatorSuite) TestRegexp(c *C) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *testEvaluatorSuite) TestSubstring(c *C) {
|
||||
tbl := []struct {
|
||||
str string
|
||||
pos int64
|
||||
slen int64
|
||||
result string
|
||||
}{
|
||||
{"Quadratically", 5, -1, "ratically"},
|
||||
{"foobarbar", 4, -1, "barbar"},
|
||||
{"Quadratically", 5, 6, "ratica"},
|
||||
{"Sakila", -3, -1, "ila"},
|
||||
{"Sakila", -5, 3, "aki"},
|
||||
{"Sakila", -4, 2, "ki"},
|
||||
{"Sakila", 1000, 2, ""},
|
||||
{"", 2, 3, ""},
|
||||
}
|
||||
ctx := mock.NewContext()
|
||||
for _, v := range tbl {
|
||||
f := &ast.FuncCallExpr{
|
||||
FnName: model.NewCIStr("SUBSTRING"),
|
||||
Args: []ast.ExprNode{ast.NewValueExpr(v.str), ast.NewValueExpr(v.pos)},
|
||||
}
|
||||
if v.slen != -1 {
|
||||
f.Args = append(f.Args, ast.NewValueExpr(v.slen))
|
||||
}
|
||||
r, err := Eval(ctx, f)
|
||||
c.Assert(err, IsNil)
|
||||
s, ok := r.(string)
|
||||
c.Assert(ok, IsTrue)
|
||||
c.Assert(s, Equals, v.result)
|
||||
|
||||
r1, err := Eval(ctx, f)
|
||||
c.Assert(err, IsNil)
|
||||
s1, ok := r1.(string)
|
||||
c.Assert(ok, IsTrue)
|
||||
c.Assert(s, Equals, s1)
|
||||
}
|
||||
errTbl := []struct {
|
||||
str interface{}
|
||||
pos interface{}
|
||||
len interface{}
|
||||
result string
|
||||
}{
|
||||
{"foobarbar", "4", -1, "barbar"},
|
||||
{"Quadratically", 5, "6", "ratica"},
|
||||
}
|
||||
for _, v := range errTbl {
|
||||
f := &ast.FuncCallExpr{
|
||||
FnName: model.NewCIStr("SUBSTRING"),
|
||||
Args: []ast.ExprNode{ast.NewValueExpr(v.str), ast.NewValueExpr(v.pos)},
|
||||
}
|
||||
if v.len != -1 {
|
||||
f.Args = append(f.Args, ast.NewValueExpr(v.len))
|
||||
}
|
||||
_, err := Eval(ctx, f)
|
||||
c.Assert(err, NotNil)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *testEvaluatorSuite) TestSubstringIndex(c *C) {
|
||||
tbl := []struct {
|
||||
str string
|
||||
delim string
|
||||
count int64
|
||||
result string
|
||||
}{
|
||||
{"www.mysql.com", ".", 2, "www.mysql"},
|
||||
{"www.mysql.com", ".", -2, "mysql.com"},
|
||||
{"www.mysql.com", ".", 0, ""},
|
||||
{"www.mysql.com", ".", 3, "www.mysql.com"},
|
||||
{"www.mysql.com", ".", 4, "www.mysql.com"},
|
||||
{"www.mysql.com", ".", -3, "www.mysql.com"},
|
||||
{"www.mysql.com", ".", -4, "www.mysql.com"},
|
||||
|
||||
{"www.mysql.com", "d", 1, "www.mysql.com"},
|
||||
{"www.mysql.com", "d", 0, ""},
|
||||
{"www.mysql.com", "d", -1, "www.mysql.com"},
|
||||
|
||||
{"", ".", 2, ""},
|
||||
{"", ".", -2, ""},
|
||||
{"", ".", 0, ""},
|
||||
|
||||
{"www.mysql.com", "", 1, ""},
|
||||
{"www.mysql.com", "", -1, ""},
|
||||
{"www.mysql.com", "", 0, ""},
|
||||
}
|
||||
ctx := mock.NewContext()
|
||||
for _, v := range tbl {
|
||||
f := &ast.FuncCallExpr{
|
||||
FnName: model.NewCIStr("SUBSTRING_INDEX"),
|
||||
Args: []ast.ExprNode{ast.NewValueExpr(v.str), ast.NewValueExpr(v.delim), ast.NewValueExpr(v.count)},
|
||||
}
|
||||
r, err := Eval(ctx, f)
|
||||
c.Assert(err, IsNil)
|
||||
s, ok := r.(string)
|
||||
c.Assert(ok, IsTrue)
|
||||
c.Assert(s, Equals, v.result)
|
||||
}
|
||||
errTbl := []struct {
|
||||
str interface{}
|
||||
delim interface{}
|
||||
count interface{}
|
||||
}{
|
||||
{nil, ".", 2},
|
||||
{nil, ".", -2},
|
||||
{nil, ".", 0},
|
||||
{"asdf", nil, 2},
|
||||
{"asdf", nil, -2},
|
||||
{"asdf", nil, 0},
|
||||
{"www.mysql.com", ".", nil},
|
||||
}
|
||||
for _, v := range errTbl {
|
||||
f := &ast.FuncCallExpr{
|
||||
FnName: model.NewCIStr("SUBSTRING_INDEX"),
|
||||
Args: []ast.ExprNode{ast.NewValueExpr(v.str), ast.NewValueExpr(v.delim), ast.NewValueExpr(v.count)},
|
||||
}
|
||||
r, err := Eval(ctx, f)
|
||||
c.Assert(err, NotNil)
|
||||
c.Assert(r, IsNil)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *testEvaluatorSuite) TestLocate(c *C) {
|
||||
tbl := []struct {
|
||||
subStr string
|
||||
Str string
|
||||
result int64
|
||||
}{
|
||||
{"bar", "foobarbar", 4},
|
||||
{"xbar", "foobar", 0},
|
||||
{"", "foobar", 1},
|
||||
{"foobar", "", 0},
|
||||
{"", "", 1},
|
||||
}
|
||||
ctx := mock.NewContext()
|
||||
for _, v := range tbl {
|
||||
f := &ast.FuncCallExpr{
|
||||
FnName: model.NewCIStr("LOCATE"),
|
||||
Args: []ast.ExprNode{ast.NewValueExpr(v.subStr), ast.NewValueExpr(v.Str)},
|
||||
}
|
||||
r, err := Eval(ctx, f)
|
||||
c.Assert(err, IsNil)
|
||||
s, ok := r.(int64)
|
||||
c.Assert(ok, IsTrue)
|
||||
c.Assert(s, Equals, v.result)
|
||||
}
|
||||
|
||||
tbl2 := []struct {
|
||||
subStr string
|
||||
Str string
|
||||
pos int64
|
||||
result int64
|
||||
}{
|
||||
{"bar", "foobarbar", 5, 7},
|
||||
{"xbar", "foobar", 1, 0},
|
||||
{"", "foobar", 2, 2},
|
||||
{"foobar", "", 1, 0},
|
||||
{"", "", 2, 0},
|
||||
}
|
||||
for _, v := range tbl2 {
|
||||
f := &ast.FuncCallExpr{
|
||||
FnName: model.NewCIStr("LOCATE"),
|
||||
Args: []ast.ExprNode{ast.NewValueExpr(v.subStr), ast.NewValueExpr(v.Str), ast.NewValueExpr(v.pos)},
|
||||
}
|
||||
r, err := Eval(ctx, f)
|
||||
c.Assert(err, IsNil)
|
||||
s, ok := r.(int64)
|
||||
c.Assert(ok, IsTrue)
|
||||
c.Assert(s, Equals, v.result)
|
||||
}
|
||||
|
||||
errTbl := []struct {
|
||||
subStr interface{}
|
||||
Str interface{}
|
||||
}{
|
||||
{nil, nil},
|
||||
{"", nil},
|
||||
{nil, ""},
|
||||
{"foo", nil},
|
||||
{nil, "bar"},
|
||||
}
|
||||
for _, v := range errTbl {
|
||||
f := &ast.FuncCallExpr{
|
||||
FnName: model.NewCIStr("LOCATE"),
|
||||
Args: []ast.ExprNode{ast.NewValueExpr(v.subStr), ast.NewValueExpr(v.Str)},
|
||||
}
|
||||
r, _ := Eval(ctx, f)
|
||||
c.Assert(r, IsNil)
|
||||
}
|
||||
|
||||
errTbl2 := []struct {
|
||||
subStr interface{}
|
||||
Str interface{}
|
||||
pos interface{}
|
||||
}{
|
||||
{nil, nil, 1},
|
||||
{"", nil, 1},
|
||||
{nil, "", 1},
|
||||
{"foo", nil, -1},
|
||||
{nil, "bar", 0},
|
||||
}
|
||||
for _, v := range errTbl2 {
|
||||
f := &ast.FuncCallExpr{
|
||||
FnName: model.NewCIStr("LOCATE"),
|
||||
Args: []ast.ExprNode{ast.NewValueExpr(v.subStr), ast.NewValueExpr(v.Str), ast.NewValueExpr(v.pos)},
|
||||
}
|
||||
r, _ := Eval(ctx, f)
|
||||
c.Assert(r, IsNil)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *testEvaluatorSuite) TestTrim(c *C) {
|
||||
tbl := []struct {
|
||||
str interface{}
|
||||
remstr interface{}
|
||||
dir ast.TrimDirectionType
|
||||
result interface{}
|
||||
}{
|
||||
{" bar ", nil, ast.TrimBothDefault, "bar"},
|
||||
{"xxxbarxxx", "x", ast.TrimLeading, "barxxx"},
|
||||
{"xxxbarxxx", "x", ast.TrimBoth, "bar"},
|
||||
{"barxxyz", "xyz", ast.TrimTrailing, "barx"},
|
||||
{nil, "xyz", ast.TrimBoth, nil},
|
||||
{1, 2, ast.TrimBoth, "1"},
|
||||
{" \t\rbar\n ", nil, ast.TrimBothDefault, "bar"},
|
||||
}
|
||||
ctx := mock.NewContext()
|
||||
for _, v := range tbl {
|
||||
f := &ast.FuncCallExpr{
|
||||
FnName: model.NewCIStr("TRIM"),
|
||||
Args: []ast.ExprNode{
|
||||
ast.NewValueExpr(v.str),
|
||||
ast.NewValueExpr(v.remstr),
|
||||
ast.NewValueExpr(v.dir),
|
||||
},
|
||||
}
|
||||
r, err := Eval(ctx, f)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(r, Equals, v.result)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *testEvaluatorSuite) TestUnaryOp(c *C) {
|
||||
tbl := []struct {
|
||||
arg interface{}
|
||||
|
||||
Reference in New Issue
Block a user