Skip to content
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

Merged
merged 13 commits into from Jun 7, 2018

Conversation

lzmhhh123
Copy link
Member

@lzmhhh123 lzmhhh123 commented May 29, 2018

Now, the constant folding of UDF if & ifnull is as follow.

tidb(localhost:4000) > desc select if(1, 10, 0) from ta;
+---------------+--------------+---------------+------+-----------------------------------------------+----------+
| id            | parents      | children      | task | operator info                                 | count    |
+---------------+--------------+---------------+------+-----------------------------------------------+----------+
| TableScan_4   |              |               | cop  | table:ta, range:[-inf,+inf], keep order:false | 10000.00 |
| TableReader_5 | Projection_3 |               | root | data:TableScan_4                              | 10000.00 |
| Projection_3  |              | TableReader_5 | root | 10                                            | 10000.00 |
+---------------+--------------+---------------+------+-----------------------------------------------+----------+
3 rows in set (0.00 sec)

tidb(localhost:4000) > desc select if(1, ta.a, 0) from ta;
+---------------+--------------+---------------+------+-----------------------------------------------+----------+
| id            | parents      | children      | task | operator info                                 | count    |
+---------------+--------------+---------------+------+-----------------------------------------------+----------+
| TableScan_4   |              |               | cop  | table:ta, range:[-inf,+inf], keep order:false | 10000.00 |
| TableReader_5 | Projection_3 |               | root | data:TableScan_4                              | 10000.00 |
| Projection_3  |              | TableReader_5 | root | if(1, root.ta.a, 0)                           | 10000.00 |
+---------------+--------------+---------------+------+-----------------------------------------------+----------+
3 rows in set (0.01 sec)

tidb(localhost:4000) > desc select ifnull(null, ta.a) from ta;
+---------------+--------------+---------------+------+-----------------------------------------------+----------+
| id            | parents      | children      | task | operator info                                 | count    |
+---------------+--------------+---------------+------+-----------------------------------------------+----------+
| TableScan_4   |              |               | cop  | table:ta, range:[-inf,+inf], keep order:false | 10000.00 |
| TableReader_5 | Projection_3 |               | root | data:TableScan_4                              | 10000.00 |
| Projection_3  |              | TableReader_5 | root | ifnull(null, root.ta.a)                       | 10000.00 |
+---------------+--------------+---------------+------+-----------------------------------------------+----------+
3 rows in set (0.00 sec)

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:

tidb(localhost:4000) > desc select if(1, ta.a, 0) from ta;
+---------------+---------+----------+------+-----------------------------------------------+----------+
| id            | parents | children | task | operator info                                 | count    |
+---------------+---------+----------+------+-----------------------------------------------+----------+
| TableScan_4   |         |          | cop  | table:ta, range:[-inf,+inf], keep order:false | 10000.00 |
| TableReader_5 |         |          | root | data:TableScan_4                              | 10000.00 |
+---------------+---------+----------+------+-----------------------------------------------+----------+
2 rows in set (0.01 sec)

tidb(localhost:4000) > desc select ifnull(null, ta.a) from ta;
+---------------+---------+----------+------+-----------------------------------------------+----------+
| id            | parents | children | task | operator info                                 | count    |
+---------------+---------+----------+------+-----------------------------------------------+----------+
| TableScan_4   |         |          | cop  | table:ta, range:[-inf,+inf], keep order:false | 10000.00 |
| TableReader_5 |         |          | root | data:TableScan_4                              | 10000.00 |
+---------------+---------+----------+------+-----------------------------------------------+----------+
2 rows in set (0.00 sec)

tidb(localhost:4000) > desc select ta.a from ta;
+---------------+---------+----------+------+-----------------------------------------------+----------+
| id            | parents | children | task | operator info                                 | count    |
+---------------+---------+----------+------+-----------------------------------------------+----------+
| TableScan_4   |         |          | cop  | table:ta, range:[-inf,+inf], keep order:false | 10000.00 |
| TableReader_5 |         |          | root | data:TableScan_4                              | 10000.00 |
+---------------+---------+----------+------+-----------------------------------------------+----------+
2 rows in set (0.00 sec)

The queries of desc select if(1, ta.a, 0) from ta; and desc select ifnull(null, ta.a) from ta; are as same as desc select ta.a from ta;.

@lzmhhh123 lzmhhh123 changed the title plan: improve constant folding of UDF if & ifnull expression: improve constant folding of UDF if & ifnull May 29, 2018
@lzmhhh123
Copy link
Member Author

@zz-jason @winoros PTAL.

@zz-jason
Copy link
Member

@XuHuaiyu PTAL

case *builtinIfNullDecimalSig:
foldedArg0, _ := foldConstant(args[0])
if _, conOK := foldedArg0.(*Constant); conOK {
_, isNull0, err := args[0].EvalDecimal(ctx, nil)
Copy link
Member

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])
}

// 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 {
Copy link
Member

@winoros winoros May 31, 2018

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.


// 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) {
Copy link
Member

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 ... ?

@@ -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) {
Copy link
Member

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:

  1. return the folded function if this IF function can be folded to a simper one
  2. return the origin function if this IF function can not be folded.

@shenli
Copy link
Member

shenli commented Jun 4, 2018

@zz-jason @winoros @XuHuaiyu PTAL

@shenli
Copy link
Member

shenli commented Jun 4, 2018

/run-all-tests

case ast.Ifnull:
foldedArg0, _ := foldConstant(args[0])
if constArg, isConst := foldedArg0.(*Constant); isConst {
valueNotNull, err := constArg.Value.ToBool(nil)
Copy link
Member

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?


// 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 {
Copy link
Contributor

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)
}

@XuHuaiyu
Copy link
Contributor

XuHuaiyu commented Jun 5, 2018

Plz add some explain test cases.

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){
Copy link
Contributor

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.

Copy link
Member Author

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.

Copy link
Member Author

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.

Copy link
Contributor

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,
	}
}

}
return foldConstant(args[2])
}
isDeferredConst := false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

var isDeferred, isDeferredConst bool

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/ flod/ fold

@XuHuaiyu
Copy link
Contributor

XuHuaiyu commented Jun 6, 2018

LGTM
PTAL @zz-jason

Copy link
Member

@zz-jason zz-jason left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@zz-jason zz-jason added the status/LGT2 Indicates that a PR has LGTM 2. label Jun 6, 2018
@zz-jason
Copy link
Member

zz-jason commented Jun 6, 2018

/run-all-tests

@XuHuaiyu XuHuaiyu merged commit 66c4112 into pingcap:master Jun 7, 2018
@lzmhhh123 lzmhhh123 deleted the dev/ifconstantfold branch July 24, 2019 05:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status/LGT2 Indicates that a PR has LGTM 2. type/enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants