New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
expression: improve constant folding of UDF if & ifnull #6677
Conversation
@XuHuaiyu PTAL |
expression/constant_fold.go
Outdated
case *builtinIfNullDecimalSig: | ||
foldedArg0, _ := foldConstant(args[0]) | ||
if _, conOK := foldedArg0.(*Constant); conOK { | ||
_, isNull0, err := args[0].EvalDecimal(ctx, nil) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we:
constArg, isConst := foldedArg0.(*Constant);
if isConst && constArg.Value.ToBool(sctx) {
return foldConstant(args[1])
}
expression/constant_fold.go
Outdated
// This switch is for UDF if or ifnull constant folding. | ||
// If first arg is constant, the rest args will not be checked whether is constant. | ||
var ctx sessionctx.Context | ||
if bbf, ok := x.Function.(*baseBuiltinFunc); ok { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can use x.Function.getCtx()
, so avoid one if
statement.
expression/constant_fold.go
Outdated
|
||
// This switch is for UDF if or ifnull constant folding. | ||
// If first arg is constant, the rest args will not be checked whether is constant. | ||
switch x.Function.(type) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we use x.FuncName.L == ast.If
to replace case *builtinIfIntSig ...
?
expression/constant_fold.go
Outdated
@@ -23,13 +25,50 @@ func FoldConstant(expr Expression) Expression { | |||
return e | |||
} | |||
|
|||
// foldConstant for BuiltinIfSig like BuiltinIfIntSig, BuiltinIfRealSig, BuiltinIfTimeSig and so on. | |||
func foldConstantForBuiltinIfSig(args []Expression, ctx sessionctx.Context) (Expression, bool) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about changing this function name to foldScalarFuncForIF
and:
- return the folded function if this
IF
function can be folded to a simper one - return the origin function if this
IF
function can not be folded.
/run-all-tests |
expression/constant_fold.go
Outdated
case ast.Ifnull: | ||
foldedArg0, _ := foldConstant(args[0]) | ||
if constArg, isConst := foldedArg0.(*Constant); isConst { | ||
valueNotNull, err := constArg.Value.ToBool(nil) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we call constArg.EvalInt
just like what we do in line 41?
expression/constant_fold.go
Outdated
|
||
// This switch is for UDF if or ifnull constant folding. | ||
// If first arg is constant, the rest args will not be checked whether is constant. | ||
switch x.FuncName.L { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can define a map like:
var specialFoldHandler = map[string]func(*ScalarFunction)(Expression, bool){
ast.If: ifFoldHandler,
ast.Ifnull: ifNullFoldHandler,
}
func ifFoldHandler(f *ScalarFunction) (Expression, bool) {
}
func ifNullFoldHandler(f *ScalarFunction) (Expression, bool) {
}
then, we check:
f, ok := specialFoldHandler[x.FuncName.L]; ok {
return f(x)
}
Plz add some explain test cases. |
expression/constant_fold.go
Outdated
func foldConstant(expr Expression) (Expression, bool) { | ||
switch x := expr.(type) { | ||
case *ScalarFunction: | ||
if _, ok := unFoldableFunctions[x.FuncName.L]; ok { | ||
return expr, false | ||
} | ||
// specialFoldHandler stores functions for special UDF to constant flod | ||
var specialFoldHandler = map[string]func(*ScalarFunction) (Expression, bool){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
put this map outside this function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have tried to put it in expression/function_traits.go
. But it cause Initialization Loop error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not just in expression/function_traits.go
. In any place outside function will cause initialization loop.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
var specialFoldHandler = map[string]func(*ScalarFunction) (Expression, bool){}
func init() {
// specialFoldHandler stores functions for special UDF to constant fold
specialFoldHandler = map[string]func(*ScalarFunction) (Expression, bool){
ast.If: ifFoldHandler,
ast.Ifnull: ifNullFoldHandler,
}
}
expression/constant_fold.go
Outdated
} | ||
return foldConstant(args[2]) | ||
} | ||
isDeferredConst := false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
var isDeferred, isDeferredConst bool
expression/constant_fold.go
Outdated
func foldConstant(expr Expression) (Expression, bool) { | ||
switch x := expr.(type) { | ||
case *ScalarFunction: | ||
if _, ok := unFoldableFunctions[x.FuncName.L]; ok { | ||
return expr, false | ||
} | ||
// specialFoldHandler stores functions for special UDF to constant flod |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/ flod/ fold
LGTM |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
/run-all-tests |
Now, the constant folding of UDF if & ifnull is as follow.
The second and third queries are expected to as same as
desc select ta.a from ta;
.This pr is to improve this situation.
After this pr changes, it will look like following:
The queries of
desc select if(1, ta.a, 0) from ta;
anddesc select ifnull(null, ta.a) from ta;
are as same asdesc select ta.a from ta;
.