Skip to content

Commit

Permalink
Merge branch 'master' into master_5
Browse files Browse the repository at this point in the history
  • Loading branch information
xhebox committed May 19, 2021
2 parents 3354e2e + 0a1c3c0 commit ad68f2c
Show file tree
Hide file tree
Showing 19 changed files with 997 additions and 192 deletions.
330 changes: 330 additions & 0 deletions executor/partition_table_test.go

Large diffs are not rendered by default.

90 changes: 90 additions & 0 deletions expression/builtin_cast.go
Original file line number Diff line number Diff line change
Expand Up @@ -1804,6 +1804,7 @@ func BuildCastFunction4Union(ctx sessionctx.Context, expr Expression, tp *types.

// BuildCastFunction builds a CAST ScalarFunction from the Expression.
func BuildCastFunction(ctx sessionctx.Context, expr Expression, tp *types.FieldType) (res Expression) {
expr = TryPushCastIntoControlFunctionForHybridType(ctx, expr, tp)
var fc functionClass
switch tp.EvalType() {
case types.ETInt:
Expand Down Expand Up @@ -1983,3 +1984,92 @@ func WrapWithCastAsJSON(ctx sessionctx.Context, expr Expression) Expression {
}
return BuildCastFunction(ctx, expr, tp)
}

// TryPushCastIntoControlFunctionForHybridType try to push cast into control function for Hybrid Type.
// If necessary, it will rebuild control function using changed args.
// When a hybrid type is the output of a control function, the result may be as a numeric type to subsequent calculation
// We should perform the `Cast` operation early to avoid using the wrong type for calculation
// For example, the condition `if(1, e, 'a') = 1`, `if` function will output `e` and compare with `1`.
// If the evaltype is ETString, it will get wrong result. So we can rewrite the condition to
// `IfInt(1, cast(e as int), cast('a' as int)) = 1` to get the correct result.
func TryPushCastIntoControlFunctionForHybridType(ctx sessionctx.Context, expr Expression, tp *types.FieldType) (res Expression) {
sf, ok := expr.(*ScalarFunction)
if !ok {
return expr
}

var wrapCastFunc func(ctx sessionctx.Context, expr Expression) Expression
switch tp.EvalType() {
case types.ETInt:
wrapCastFunc = WrapWithCastAsInt
case types.ETReal:
wrapCastFunc = WrapWithCastAsReal
default:
return expr
}

isHybrid := func(ft *types.FieldType) bool {
// todo: compatible with mysql control function using bit type. issue 24725
return ft.Hybrid() && ft.Tp != mysql.TypeBit
}

args := sf.GetArgs()
switch sf.FuncName.L {
case ast.If:
if isHybrid(args[1].GetType()) || isHybrid(args[2].GetType()) {
args[1] = wrapCastFunc(ctx, args[1])
args[2] = wrapCastFunc(ctx, args[2])
f, err := funcs[ast.If].getFunction(ctx, args)
if err != nil {
return expr
}
sf.RetType, sf.Function = f.getRetTp(), f
return sf
}
case ast.Case:
hasHybrid := false
for i := 0; i < len(args)-1; i += 2 {
hasHybrid = hasHybrid || isHybrid(args[i+1].GetType())
}
if len(args)%2 == 1 {
hasHybrid = hasHybrid || isHybrid(args[len(args)-1].GetType())
}
if !hasHybrid {
return expr
}

for i := 0; i < len(args)-1; i += 2 {
args[i+1] = wrapCastFunc(ctx, args[i+1])
}
if len(args)%2 == 1 {
args[len(args)-1] = wrapCastFunc(ctx, args[len(args)-1])
}
f, err := funcs[ast.Case].getFunction(ctx, args)
if err != nil {
return expr
}
sf.RetType, sf.Function = f.getRetTp(), f
return sf
case ast.Elt:
hasHybrid := false
for i := 1; i < len(args); i++ {
hasHybrid = hasHybrid || isHybrid(args[i].GetType())
}
if !hasHybrid {
return expr
}

for i := 1; i < len(args); i++ {
args[i] = wrapCastFunc(ctx, args[i])
}
f, err := funcs[ast.Elt].getFunction(ctx, args)
if err != nil {
return expr
}
sf.RetType, sf.Function = f.getRetTp(), f
return sf
default:
return expr
}
return expr
}
Loading

0 comments on commit ad68f2c

Please sign in to comment.