Skip to content

Commit

Permalink
*: Convert RegexpExpr to ScalarFuntion and check the arguments length…
Browse files Browse the repository at this point in the history
… of builtin functions (pingcap#1388)

* *: convert RegexpExpr to ScalarFuntion and add check arguments length
  • Loading branch information
zimulala authored Jul 5, 2016
1 parent 5408833 commit 452637d
Show file tree
Hide file tree
Showing 6 changed files with 214 additions and 68 deletions.
3 changes: 2 additions & 1 deletion evaluator/builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,8 @@ var Funcs = map[string]Func{
ast.In: {builtinIn, 1, -1},
ast.IsTruth: {isTrueOpFactory(opcode.IsTruth), 1, 1},
ast.IsFalsity: {isTrueOpFactory(opcode.IsFalsity), 1, 1},
ast.Like: {builtinLike, 1, 3},
ast.Like: {builtinLike, 3, 3},
ast.Regexp: {builtinRegexp, 2, 2},
ast.Case: {builtinCaseWhen, 1, -1},
ast.RowFunc: {builtinRow, 2, -1},
}
Expand Down
28 changes: 28 additions & 0 deletions evaluator/builtin_other.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
package evaluator

import (
"regexp"

"github.com/juju/errors"
"github.com/pingcap/tidb/context"
"github.com/pingcap/tidb/mysql"
Expand Down Expand Up @@ -83,6 +85,7 @@ func builtinOrOr(args []types.Datum, _ context.Context) (d types.Datum, err erro
return
}

// See https://dev.mysql.com/doc/refman/5.7/en/case.html
func builtinCaseWhen(args []types.Datum, _ context.Context) (d types.Datum, err error) {
l := len(args)
for i := 0; i < l-1; i += 2 {
Expand All @@ -107,6 +110,7 @@ func builtinCaseWhen(args []types.Datum, _ context.Context) (d types.Datum, err
return
}

// See http://dev.mysql.com/doc/refman/5.7/en/string-comparison-functions.html
func builtinLike(args []types.Datum, _ context.Context) (d types.Datum, err error) {
if args[0].IsNull() {
return
Expand All @@ -132,6 +136,30 @@ func builtinLike(args []types.Datum, _ context.Context) (d types.Datum, err erro
return
}

// See http://dev.mysql.com/doc/refman/5.7/en/regexp.html#operator_regexp
func builtinRegexp(args []types.Datum, _ context.Context) (d types.Datum, err error) {
// TODO: We don't need to compile pattern if it has been compiled or it is static.
if args[0].IsNull() || args[1].IsNull() {
return
}

targetStr, err := args[0].ToString()
if err != nil {
return d, errors.Errorf("non-string Expression in LIKE: %v (Value of type %T)", args[0], args[0])
}
patternStr, err := args[1].ToString()
if err != nil {
return d, errors.Errorf("non-string Expression in LIKE: %v (Value of type %T)", args[1], args[1])
}
re, err := regexp.Compile(patternStr)
if err != nil {
return d, errors.Trace(err)
}
d.SetInt64(boolToInt64(re.MatchString(targetStr)))
return
}

// See http://dev.mysql.com/doc/refman/5.7/en/any-in-some-subqueries.html
func builtinIn(args []types.Datum, _ context.Context) (d types.Datum, err error) {
if args[0].IsNull() {
return
Expand Down
57 changes: 47 additions & 10 deletions executor/executor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1374,16 +1374,7 @@ func (s *testSuite) TestBuiltin(c *C) {
result = tk.MustQuery("select cast('11:11:11' as time)")
result.Check(testkit.Rows("11:11:11"))

// for like
tk.MustExec("drop table if exists t")
tk.MustExec("create table t (a varchar(255), b int)")
tk.MustExec("insert t values ('abc123', 1)")
tk.MustExec("insert t values ('ab123', 2)")
result = tk.MustQuery("select * from t where a like 'ab_123'")
rowStr := fmt.Sprintf("%v %v", []byte("abc123"), "1")
result.Check(testkit.Rows(rowStr))

// case
// for case
tk.MustExec("drop table if exists t")
tk.MustExec("create table t (a varchar(255), b int)")
tk.MustExec("insert t values ('str1', 1)")
Expand Down Expand Up @@ -1415,6 +1406,52 @@ func (s *testSuite) TestBuiltin(c *C) {
result = tk.MustQuery("select * from t where a = case null when b then 'str3' when 10 then 'str1' else 'str2' end")
result.Check(testkit.Rows(rowStr2))

// for like and regexp
type testCase struct {
pattern string
val string
result int
}
patternMatching := func(c *C, tk *testkit.TestKit, queryOp string, data []testCase) {
tk.MustExec("drop table if exists t")
tk.MustExec("create table t (a varchar(255), b int)")
for i, d := range data {
tk.MustExec(fmt.Sprintf("insert into t values('%s', %d)", d.val, i))
result := tk.MustQuery(fmt.Sprintf("select * from t where a %s '%s'", queryOp, d.pattern))
if d.result == 1 {
rowStr := fmt.Sprintf("%v %d", []byte(d.val), i)
result.Check(testkit.Rows(rowStr))
} else {
result.Check(nil)
}
tk.MustExec(fmt.Sprintf("delete from t where b = %d", i))
}
}
// for like
testCases := []testCase{
{"a", "a", 1},
{"a", "b", 0},
{"aA", "Aa", 1},
{"aA%", "aAab", 1},
{"aA_", "Aaab", 0},
{"aA_", "Aab", 1},
}
patternMatching(c, tk, "like", testCases)
// for regexp
testCases = []testCase{
{"^$", "a", 0},
{"a", "a", 1},
{"a", "b", 0},
{"aA", "aA", 1},
{".", "a", 1},
{"^.$", "ab", 0},
{"..", "b", 0},
{".ab", "aab", 1},
{"ab.", "abcd", 1},
{".*", "abcd", 1},
}
patternMatching(c, tk, "regexp", testCases)

plan.UseNewPlanner = false
}

Expand Down
13 changes: 9 additions & 4 deletions expression/expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,19 +226,23 @@ func (sf *ScalarFunction) ToString() string {
}

// NewFunction creates a new scalar function.
func NewFunction(funcName string, retType *types.FieldType, args ...Expression) *ScalarFunction {
func NewFunction(funcName string, retType *types.FieldType, args ...Expression) (*ScalarFunction, error) {
f, ok := evaluator.Funcs[funcName]
if !ok {
log.Errorf("Function %s is not implemented.", funcName)
return nil
return nil, nil
}
if len(args) < f.MinArgs || (f.MaxArgs != -1 && len(args) > f.MaxArgs) {
return nil, evaluator.ErrInvalidOperation.Gen("number of function arguments must in [%d, %d].",
f.MinArgs, f.MaxArgs)
}
funcArgs := make([]Expression, len(args))
copy(funcArgs, args)
return &ScalarFunction{
Args: funcArgs,
FuncName: model.NewCIStr(funcName),
RetType: retType,
Function: f.F}
Function: f.F}, nil
}

//Schema2Exprs converts []*Column to []Expression.
Expand Down Expand Up @@ -323,8 +327,9 @@ func ComposeCNFCondition(conditions []Expression) Expression {
if length == 1 {
return conditions[0]
}
return NewFunction(ast.AndAnd,
expr, _ := NewFunction(ast.AndAnd,
types.NewFieldType(mysql.TypeTiny),
ComposeCNFCondition(conditions[length/2:]),
ComposeCNFCondition(conditions[:length/2]))
return expr
}
Loading

0 comments on commit 452637d

Please sign in to comment.