Files
tidb/evaluator/builtin_string_test.go
2016-09-01 19:25:04 +08:00

706 lines
17 KiB
Go

// Copyright 2015 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
package evaluator
import (
"errors"
"strings"
"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/testleak"
"github.com/pingcap/tidb/util/testutil"
"github.com/pingcap/tidb/util/types"
)
func (s *testEvaluatorSuite) TestLength(c *C) {
defer testleak.AfterTest(c)()
d, err := builtinLength(types.MakeDatums([]interface{}{nil}...), nil)
c.Assert(err, IsNil)
c.Assert(d.Kind(), Equals, types.KindNull)
tbl := []struct {
Input interface{}
Expected int64
}{
{"abc", 3},
{1, 1},
{3.14, 4},
{mysql.Time{Time: time.Now(), Fsp: 6, Type: mysql.TypeDatetime}, 26},
{mysql.Bit{Value: 1, Width: 8}, 1},
{mysql.Hex{Value: 1}, 1},
{mysql.Set{Value: 1, Name: "abc"}, 3},
}
dtbl := tblToDtbl(tbl)
for _, t := range dtbl {
d, err = builtinLength(t["Input"], nil)
c.Assert(err, IsNil)
c.Assert(d, testutil.DatumEquals, t["Expected"][0])
}
}
func (s *testEvaluatorSuite) TestASCII(c *C) {
defer testleak.AfterTest(c)()
v, err := builtinASCII(types.MakeDatums([]interface{}{nil}...), nil)
c.Assert(err, IsNil)
c.Assert(v.Kind(), Equals, types.KindNull)
for _, t := range []struct {
Input interface{}
Expected int64
}{
{"", 0},
{"A", 65},
{"你好", 228},
{1, 49},
{1.2, 49},
{true, 49},
{false, 48},
} {
v, err = builtinASCII(types.MakeDatums(t.Input), nil)
c.Assert(err, IsNil)
c.Assert(v.GetInt64(), Equals, t.Expected)
}
v, err = builtinASCII(types.MakeDatums([]interface{}{errors.New("must error")}...), nil)
c.Assert(err, NotNil)
}
func (s *testEvaluatorSuite) TestConcat(c *C) {
defer testleak.AfterTest(c)()
args := []interface{}{nil}
v, err := builtinConcat(types.MakeDatums(args...), nil)
c.Assert(err, IsNil)
c.Assert(v.Kind(), Equals, types.KindNull)
args = []interface{}{"a", "b", "c"}
v, err = builtinConcat(types.MakeDatums(args...), nil)
c.Assert(err, IsNil)
c.Assert(v.GetString(), Equals, "abc")
args = []interface{}{"a", "b", nil, "c"}
v, err = builtinConcat(types.MakeDatums(args...), nil)
c.Assert(err, IsNil)
c.Assert(v.Kind(), Equals, types.KindNull)
args = []interface{}{errors.New("must error")}
_, err = builtinConcat(types.MakeDatums(args...), nil)
c.Assert(err, NotNil)
}
func (s *testEvaluatorSuite) TestConcatWS(c *C) {
defer testleak.AfterTest(c)()
args := types.MakeDatums([]interface{}{nil}...)
v, err := builtinConcatWS(args, nil)
c.Assert(err, IsNil)
c.Assert(v.Kind(), Equals, types.KindNull)
args = types.MakeDatums([]interface{}{"|", "a", nil, "b", "c"}...)
v, err = builtinConcatWS(args, nil)
c.Assert(err, IsNil)
c.Assert(v.GetString(), Equals, "a|b|c")
args = types.MakeDatums([]interface{}{errors.New("must error")}...)
_, err = builtinConcatWS(args, nil)
c.Assert(err, NotNil)
}
func (s *testEvaluatorSuite) TestLeft(c *C) {
defer testleak.AfterTest(c)()
args := types.MakeDatums([]interface{}{"abcdefg", int64(2)}...)
v, err := builtinLeft(args, nil)
c.Assert(err, IsNil)
c.Assert(v.GetString(), Equals, "ab")
args = types.MakeDatums([]interface{}{"abcdefg", int64(-1)}...)
v, err = builtinLeft(args, nil)
c.Assert(err, IsNil)
c.Assert(v.GetString(), Equals, "")
args = types.MakeDatums([]interface{}{"abcdefg", int64(100)}...)
v, err = builtinLeft(args, nil)
c.Assert(err, IsNil)
c.Assert(v.GetString(), Equals, "abcdefg")
args = types.MakeDatums([]interface{}{1, int64(1)}...)
_, err = builtinLeft(args, nil)
c.Assert(err, IsNil)
args = types.MakeDatums([]interface{}{"abcdefg", "xxx"}...)
_, err = builtinLeft(args, nil)
c.Assert(err, NotNil)
}
func (s *testEvaluatorSuite) TestRepeat(c *C) {
defer testleak.AfterTest(c)()
args := []interface{}{"a", int64(2)}
v, err := builtinRepeat(types.MakeDatums(args...), nil)
c.Assert(err, IsNil)
c.Assert(v.GetString(), Equals, "aa")
args = []interface{}{"a", uint64(2)}
v, err = builtinRepeat(types.MakeDatums(args...), nil)
c.Assert(err, IsNil)
c.Assert(v.GetString(), Equals, "aa")
args = []interface{}{"a", int64(-1)}
v, err = builtinRepeat(types.MakeDatums(args...), nil)
c.Assert(err, IsNil)
c.Assert(v.GetString(), Equals, "")
args = []interface{}{"a", int64(0)}
v, err = builtinRepeat(types.MakeDatums(args...), nil)
c.Assert(err, IsNil)
c.Assert(v.GetString(), Equals, "")
args = []interface{}{"a", uint64(0)}
v, err = builtinRepeat(types.MakeDatums(args...), nil)
c.Assert(err, IsNil)
c.Assert(v.GetString(), Equals, "")
}
func (s *testEvaluatorSuite) TestLowerAndUpper(c *C) {
defer testleak.AfterTest(c)()
d, err := builtinLower(types.MakeDatums([]interface{}{nil}...), nil)
c.Assert(err, IsNil)
c.Assert(d.Kind(), Equals, types.KindNull)
d, err = builtinUpper(types.MakeDatums([]interface{}{nil}...), nil)
c.Assert(err, IsNil)
c.Assert(d.Kind(), Equals, types.KindNull)
tbl := []struct {
Input interface{}
Expect string
}{
{"abc", "abc"},
{1, "1"},
}
dtbl := tblToDtbl(tbl)
for _, t := range dtbl {
d, err = builtinLower(t["Input"], nil)
c.Assert(err, IsNil)
c.Assert(d, testutil.DatumEquals, t["Expect"][0])
d, err = builtinUpper(t["Input"], nil)
c.Assert(err, IsNil)
c.Assert(d.GetString(), Equals, strings.ToUpper(t["Expect"][0].GetString()))
}
}
func (s *testEvaluatorSuite) TestReverse(c *C) {
defer testleak.AfterTest(c)()
d, err := builtinReverse(types.MakeDatums([]interface{}{nil}...), nil)
c.Assert(err, IsNil)
c.Assert(d.Kind(), Equals, types.KindNull)
tbl := []struct {
Input interface{}
Expect string
}{
{"abc", "cba"},
{"LIKE", "EKIL"},
{123, "321"},
{"", ""},
}
dtbl := tblToDtbl(tbl)
for _, t := range dtbl {
d, err = builtinReverse(t["Input"], nil)
c.Assert(err, IsNil)
c.Assert(d, testutil.DatumEquals, t["Expect"][0])
}
}
func (s *testEvaluatorSuite) TestStrcmp(c *C) {
defer testleak.AfterTest(c)()
tbl := []struct {
Input []interface{}
Expect interface{}
}{
{[]interface{}{"1", "2"}, -1},
{[]interface{}{"2", "1"}, 1},
{[]interface{}{"123", "2"}, -1},
{[]interface{}{"1", "213"}, -1},
{[]interface{}{"123", "123"}, 0},
{[]interface{}{"", "123"}, -1},
{[]interface{}{"123", ""}, 1},
{[]interface{}{"", ""}, 0},
{[]interface{}{nil, "123"}, nil},
{[]interface{}{"123", nil}, nil},
{[]interface{}{nil, nil}, nil},
{[]interface{}{"", nil}, nil},
{[]interface{}{nil, ""}, nil},
}
dtbl := tblToDtbl(tbl)
for _, t := range dtbl {
d, err := builtinStrcmp(t["Input"], nil)
c.Assert(err, IsNil)
c.Assert(d, testutil.DatumEquals, t["Expect"][0])
}
}
func (s *testEvaluatorSuite) TestReplace(c *C) {
defer testleak.AfterTest(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"},
}
dtbl := tblToDtbl(tbl)
for _, t := range dtbl {
d, err := builtinReplace(t["Input"], nil)
c.Assert(err, IsNil)
c.Assert(d, testutil.DatumEquals, t["Expect"][0])
}
}
func (s *testEvaluatorSuite) TestSubstring(c *C) {
defer testleak.AfterTest(c)()
d, err := builtinSubstring(types.MakeDatums([]interface{}{"hello", 2, -1}...), nil)
c.Assert(err, IsNil)
c.Assert(d.GetString(), Equals, "")
tbl := []struct {
str string
pos int64
slen int64
result string
}{
{"Quadratically", 5, -1, "ratically"},
{"foobarbar", 4, -1, "barbar"},
{"Sakila", 1, -1, "Sakila"},
{"Sakila", 2, -1, "akila"},
{"Sakila", -3, -1, "ila"},
{"Sakila", -5, 3, "aki"},
{"Sakila", -4, 2, "ki"},
{"Quadratically", 5, 6, "ratica"},
{"Sakila", 1, 4, "Saki"},
{"Sakila", -6, 4, "Saki"},
{"Sakila", 2, 1000, "akila"},
{"Sakila", -5, 1000, "akila"},
{"Sakila", 2, -2, ""},
{"Sakila", -5, -2, ""},
{"Sakila", 2, 0, ""},
{"Sakila", -5, -3, ""},
{"Sakila", -1000, 3, ""},
{"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)
c.Assert(r.Kind(), Equals, types.KindString)
c.Assert(r.GetString(), Equals, v.result)
r1, err := Eval(ctx, f)
c.Assert(err, IsNil)
c.Assert(r1.Kind(), Equals, types.KindString)
c.Assert(r.GetString(), Equals, r1.GetString())
}
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) {
defer testleak.AfterTest(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)
c.Assert(r.Kind(), Equals, types.KindString)
c.Assert(r.GetString(), 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) {
defer testleak.AfterTest(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)
c.Assert(r.Kind(), Equals, types.KindString)
c.Assert(r.GetString(), 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.Kind(), Equals, types.KindNull)
}
}
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 {
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)
c.Assert(r.Kind(), Equals, types.KindInt64)
c.Assert(r.GetInt64(), 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)
c.Assert(r.Kind(), Equals, types.KindInt64)
c.Assert(r.GetInt64(), 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.Kind(), Equals, types.KindNull)
}
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.Kind(), Equals, types.KindNull)
}
}
func (s *testEvaluatorSuite) TestTrim(c *C) {
defer testleak.AfterTest(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, testutil.DatumEquals, types.NewDatum(v.result))
}
for _, v := range []struct {
str, result interface{}
fn string
}{
{" ", "", "LTRIM"},
{" ", "", "RTRIM"},
{"foo0", "foo0", "LTRIM"},
{"bar0", "bar0", "RTRIM"},
{" foo1", "foo1", "LTRIM"},
{"bar1 ", "bar1", "RTRIM"},
{spaceChars + "foo2 ", "foo2 ", "LTRIM"},
{" bar2" + spaceChars, " bar2", "RTRIM"},
{nil, nil, "LTRIM"},
{nil, nil, "RTRIM"},
} {
f := &ast.FuncCallExpr{
FnName: model.NewCIStr(v.fn),
Args: []ast.ExprNode{ast.NewValueExpr(v.str)},
}
r, err := Eval(ctx, f)
c.Assert(err, IsNil)
c.Assert(r, testutil.DatumEquals, types.NewDatum(v.result))
}
}
func (s *testEvaluatorSuite) TestHexFunc(c *C) {
defer testleak.AfterTest(c)()
tbl := []struct {
Input interface{}
Expect string
}{
{12, "C"},
{12.3, "C"},
{12.5, "D"},
{-12.3, "FFFFFFFFFFFFFFF4"},
{-12.5, "FFFFFFFFFFFFFFF3"},
{"12", "3132"},
{0x12, "12"},
{"", ""},
}
dtbl := tblToDtbl(tbl)
for _, t := range dtbl {
d, err := builtinHex(t["Input"], nil)
c.Assert(err, IsNil)
c.Assert(d, testutil.DatumEquals, t["Expect"][0])
}
}
func (s *testEvaluatorSuite) TestUnhexFunc(c *C) {
defer testleak.AfterTest(c)()
tbl := []struct {
Input interface{}
Expect string
}{
{"4D7953514C", "MySQL"},
{"31323334", "1234"},
{"", ""},
}
dtbl := tblToDtbl(tbl)
for _, t := range dtbl {
d, err := builtinUnHex(t["Input"], nil)
c.Assert(err, IsNil)
c.Assert(d, testutil.DatumEquals, t["Expect"][0])
}
}