Skip to content

Commit

Permalink
evaluator: move the evaluate of ast functions to buildin
Browse files Browse the repository at this point in the history
Move the evaluate of ast functions extract, convert and substring to buildin.
  • Loading branch information
zxylvlp committed Mar 8, 2016
1 parent eff9f1d commit afd369e
Show file tree
Hide file tree
Showing 12 changed files with 204 additions and 309 deletions.
14 changes: 0 additions & 14 deletions ast/flag.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,10 @@ func (f *flagSetter) Leave(in Node) (Node, bool) {
f.funcCall(x)
case *FuncCastExpr:
x.SetFlag(FlagHasFunc | x.Expr.GetFlag())
case *FuncConvertExpr:
x.SetFlag(FlagHasFunc | x.Expr.GetFlag())
case *FuncDateArithExpr:
f.funcDateArith(x)
case *FuncExtractExpr:
x.SetFlag(FlagHasFunc | x.Date.GetFlag())
case *FuncLocateExpr:
f.funcLocate(x)
case *FuncSubstringExpr:
f.funcSubstring(x)
case *FuncSubstringIndexExpr:
f.funcSubstringIndex(x)
case *FuncTrimExpr:
Expand Down Expand Up @@ -170,14 +164,6 @@ func (f *flagSetter) funcCall(x *FuncCallExpr) {
x.SetFlag(flag)
}

func (f *flagSetter) funcSubstring(x *FuncSubstringExpr) {
flag := FlagHasFunc | x.StrExpr.GetFlag() | x.Pos.GetFlag()
if x.Len != nil {
flag |= x.Len.GetFlag()
}
x.SetFlag(flag)
}

func (f *flagSetter) funcSubstringIndex(x *FuncSubstringIndexExpr) {
flag := FlagHasFunc | x.StrExpr.GetFlag()
if x.Delim != nil {
Expand Down
89 changes: 0 additions & 89 deletions ast/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,8 @@ var (
_ FuncNode = &AggregateFuncExpr{}
_ FuncNode = &FuncCallExpr{}
_ FuncNode = &FuncCastExpr{}
_ FuncNode = &FuncConvertExpr{}
_ FuncNode = &FuncDateArithExpr{}
_ FuncNode = &FuncExtractExpr{}
_ FuncNode = &FuncLocateExpr{}
_ FuncNode = &FuncSubstringExpr{}
_ FuncNode = &FuncSubstringIndexExpr{}
_ FuncNode = &FuncTrimExpr{}
)
Expand Down Expand Up @@ -66,55 +63,6 @@ func (n *FuncCallExpr) Accept(v Visitor) (Node, bool) {
return v.Leave(n)
}

// FuncExtractExpr is for time extract function.
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_extract
type FuncExtractExpr struct {
funcNode

Unit string
Date ExprNode
}

// Accept implements Node Accept interface.
func (n *FuncExtractExpr) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
if skipChildren {
return v.Leave(newNode)
}
n = newNode.(*FuncExtractExpr)
node, ok := n.Date.Accept(v)
if !ok {
return n, false
}
n.Date = node.(ExprNode)
return v.Leave(n)
}

// FuncConvertExpr provides a way to convert data between different character sets.
// See: https://dev.mysql.com/doc/refman/5.7/en/cast-functions.html#function_convert
type FuncConvertExpr struct {
funcNode
// Expr is the expression to be converted.
Expr ExprNode
// Charset is the target character set to convert.
Charset string
}

// Accept implements Node Accept interface.
func (n *FuncConvertExpr) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
if skipChildren {
return v.Leave(newNode)
}
n = newNode.(*FuncConvertExpr)
node, ok := n.Expr.Accept(v)
if !ok {
return n, false
}
n.Expr = node.(ExprNode)
return v.Leave(n)
}

// CastFunctionType is the type for cast function.
type CastFunctionType int

Expand Down Expand Up @@ -152,43 +100,6 @@ func (n *FuncCastExpr) Accept(v Visitor) (Node, bool) {
return v.Leave(n)
}

// FuncSubstringExpr returns the substring as specified.
// See: https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_substring
type FuncSubstringExpr struct {
funcNode

StrExpr ExprNode
Pos ExprNode
Len ExprNode
}

// Accept implements Node Accept interface.
func (n *FuncSubstringExpr) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
if skipChildren {
return v.Leave(newNode)
}
n = newNode.(*FuncSubstringExpr)
node, ok := n.StrExpr.Accept(v)
if !ok {
return n, false
}
n.StrExpr = node.(ExprNode)
node, ok = n.Pos.Accept(v)
if !ok {
return n, false
}
n.Pos = node.(ExprNode)
if n.Len != nil {
node, ok = n.Len.Accept(v)
if !ok {
return n, false
}
n.Len = node.(ExprNode)
}
return v.Leave(n)
}

// FuncSubstringIndexExpr returns the substring as specified.
// See: https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_substring-index
type FuncSubstringIndexExpr struct {
Expand Down
3 changes: 3 additions & 0 deletions evaluator/builtin/builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ var Funcs = map[string]Func{
"weekofyear": {builtinWeekOfYear, 1, 1, true, false},
"year": {builtinYear, 1, 1, true, false},
"yearweek": {builtinYearWeek, 1, 2, true, false},
"extract": {builtinExtract, 2, 2, true, false},

// control functions
"if": {builtinIf, 3, 3, true, false},
Expand All @@ -87,6 +88,8 @@ var Funcs = map[string]Func{
"replace": {builtinReplace, 3, 3, true, false},
"upper": {builtinUpper, 1, 1, true, false},
"strcmp": {builtinStrcmp, 2, 2, true, false},
"convert": {builtinConvert, 2, 2, true, false},
"substring": {builtinSubstring, 2, 3, true, false},

// information functions
"current_user": {builtinCurrentUser, 0, 0, false, false},
Expand Down
84 changes: 84 additions & 0 deletions evaluator/builtin/string.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@ import (
"strings"

"github.com/juju/errors"
"github.com/ngaut/log"
"github.com/pingcap/tidb/context"
"github.com/pingcap/tidb/util/charset"
"github.com/pingcap/tidb/util/types"
"golang.org/x/text/transform"
)

// https://dev.mysql.com/doc/refman/5.7/en/string-functions.html
Expand Down Expand Up @@ -188,3 +191,84 @@ func builtinReplace(args []interface{}, _ context.Context) (interface{}, error)

return strings.Replace(str, oldStr, newStr, -1), 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)

// Casting nil to any type returns nil
if value == nil {
return nil, nil
}
str, ok := value.(string)
if !ok {
return nil, nil
}
if strings.ToLower(Charset) == "ascii" {
return value, nil
} else if strings.ToLower(Charset) == "utf8mb4" {
return value, nil
}

encoding, _ := charset.Lookup(Charset)
if encoding == nil {
return nil, 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 target, nil
}

func builtinSubstring(args []interface{}, _ context.Context) (interface{}, error) {
// The meaning of the elements of args.
// arg[0] -> StrExpr
// arg[1] -> Pos
// arg[2] -> Len (Optional)
str, err := types.ToString(args[0])
if err != nil {
return nil, errors.Errorf("Substring invalid args, need string but get %T", args[0])
}

t := args[1]
p, ok := t.(int64)
if !ok {
return nil, errors.Errorf("Substring invalid pos args, need int but get %T", t)
}
pos := int(p)

length := -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)
}
length = int(p)
}
// 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.
// The forms that use FROM are standard SQL syntax. It is also possible to use a negative value for pos.
// 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
} else {
pos--
}
if pos > len(str) || pos <= 0 {
pos = len(str)
}
end := len(str)
if length != -1 {
end = pos + length
}
if end > len(str) {
end = len(str)
}
return str[pos:end], nil
}
31 changes: 31 additions & 0 deletions evaluator/builtin/time.go
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,37 @@ func builtinCurrentTime(args []interface{}, _ context.Context) (interface{}, err
return convertToDuration(time.Now().Format("15:04:05.000000"), fsp)
}

// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_extract
func builtinExtract(args []interface{}, _ context.Context) (interface{}, error) {
unit := args[0].(string)
val := args[1]

if val == nil {
return nil, nil
}

f := types.NewFieldType(mysql.TypeDatetime)
f.Decimal = mysql.MaxFsp
var err error
val, err = types.Convert(val, f)
if err != nil {
return nil, errors.Trace(err)
}
if val == nil {
return nil, nil
}

t, ok := val.(mysql.Time)
if !ok {
return nil, errors.Errorf("need time type, but got %T", val)
}
n, err1 := mysql.ExtractTimeNum(unit, t)
if err1 != nil {
return nil, errors.Trace(err1)
}
return n, nil
}

func checkFsp(arg interface{}) (int, error) {
fsp, err := types.ToInt64(arg)
if err != nil {
Expand Down
Loading

0 comments on commit afd369e

Please sign in to comment.