-
Notifications
You must be signed in to change notification settings - Fork 5.9k
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
*: Selection can control the conditions of the below scan plan. #2834
Changes from 17 commits
5db58a9
1fe024d
5ce9107
364e172
36283cf
7451287
053c2d4
e663291
a25a3eb
6aadeaa
ba29a95
02cbcf1
9a9ae32
ef178a3
8029c1e
9771129
7251827
e45f669
4e0012b
f0ec2e0
02db63c
c791ff7
8674c3f
107a042
3fd0435
8f29d17
088e9e6
6351036
97998fc
55f92e6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -461,21 +461,111 @@ func (e *TableDualExec) Close() error { | |
return nil | ||
} | ||
|
||
func substituteCorCol2Constant(expr expression.Expression) (expression.Expression, error) { | ||
switch x := expr.(type) { | ||
case *expression.ScalarFunction: | ||
allConstant := true | ||
newArgs := make([]expression.Expression, 0, len(x.GetArgs())) | ||
for _, arg := range x.GetArgs() { | ||
newArg, err := substituteCorCol2Constant(arg) | ||
_, ok := newArg.(*expression.Constant) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Put this after checking error. |
||
if err != nil { | ||
return nil, errors.Trace(err) | ||
} | ||
newArgs = append(newArgs, newArg) | ||
allConstant = allConstant && ok | ||
} | ||
if allConstant { | ||
val, err := x.Eval(nil) | ||
if err != nil { | ||
return nil, errors.Trace(err) | ||
} | ||
return &expression.Constant{Value: val}, nil | ||
} | ||
var newSf expression.Expression | ||
if x.FuncName.L == ast.Cast { | ||
newSf = expression.NewCastFunc(x.RetType, newArgs[0], x.GetCtx()) | ||
} else { | ||
newSf, _ = expression.NewFunction(x.GetCtx(), x.FuncName.L, x.GetType(), newArgs...) | ||
} | ||
return newSf, nil | ||
case *expression.CorrelatedColumn: | ||
return &expression.Constant{Value: *x.Data, RetType: x.GetType()}, nil | ||
case *expression.Constant: | ||
return x.Clone(), nil | ||
default: | ||
return x.Clone(), nil | ||
} | ||
} | ||
|
||
// SelectionExec represents a filter executor. | ||
type SelectionExec struct { | ||
Src Executor | ||
Condition expression.Expression | ||
ctx context.Context | ||
schema *expression.Schema | ||
|
||
// scanController will tell whether this selection need to | ||
// control the condition of below scan executor. | ||
scanController bool | ||
controllerInit bool | ||
Conditions []expression.Expression | ||
usableIndices []*model.IndexInfo | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you can remove this from selection executor. |
||
} | ||
|
||
// Schema implements the Executor Schema interface. | ||
func (e *SelectionExec) Schema() *expression.Schema { | ||
return e.schema | ||
} | ||
|
||
// initController will init the conditions of the below scan executor. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add more comments for this function. |
||
// It will first substitute the correlated column to constant, then build range and filter by new conditions. | ||
func (e *SelectionExec) initController() error { | ||
sc := e.ctx.GetSessionVars().StmtCtx | ||
client := e.ctx.GetClient() | ||
newConds := make([]expression.Expression, 0, len(e.Conditions)) | ||
for _, cond := range e.Conditions { | ||
newCond, err := substituteCorCol2Constant(cond.Clone()) | ||
if err != nil { | ||
return errors.Trace(err) | ||
} | ||
newConds = append(newConds, newCond) | ||
} | ||
|
||
switch x := e.Src.(type) { | ||
case *XSelectTableExec: | ||
accessCondition, restCondtion := plan.DetachTableScanConditions(newConds, x.tableInfo) | ||
x.where, _, _ = plan.ExpressionsToPB(sc, restCondtion, client) | ||
ranges, err := plan.BuildTableRange(accessCondition, sc) | ||
if err != nil { | ||
return errors.Trace(err) | ||
} | ||
x.ranges = ranges | ||
case *XSelectIndexExec: | ||
x.indexPlan.AccessCondition, newConds = plan.DetachIndexScanConditions(newConds, x.indexPlan) | ||
idxConds, tblConds := plan.DetachIndexFilterConditions(newConds, x.indexPlan.Index.Columns, x.indexPlan.Table) | ||
x.indexPlan.IndexConditionPBExpr, _, _ = plan.ExpressionsToPB(sc, idxConds, client) | ||
x.indexPlan.TableConditionPBExpr, _, _ = plan.ExpressionsToPB(sc, tblConds, client) | ||
err := plan.BuildIndexRange(sc, x.indexPlan) | ||
if err != nil { | ||
return errors.Trace(err) | ||
} | ||
x.where = x.indexPlan.TableConditionPBExpr | ||
default: | ||
return errors.New("Error type of PhysicalPlan") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. print %T to tout put the plan type |
||
} | ||
return nil | ||
} | ||
|
||
// Next implements the Executor Next interface. | ||
func (e *SelectionExec) Next() (*Row, error) { | ||
if e.scanController && !e.controllerInit { | ||
err := e.initController() | ||
if err != nil { | ||
return nil, errors.Trace(err) | ||
} | ||
e.controllerInit = true | ||
} | ||
for { | ||
srcRow, err := e.Src.Next() | ||
if err != nil { | ||
|
@@ -484,18 +574,28 @@ func (e *SelectionExec) Next() (*Row, error) { | |
if srcRow == nil { | ||
return nil, nil | ||
} | ||
match, err := expression.EvalBool(e.Condition, srcRow.Data, e.ctx) | ||
if err != nil { | ||
return nil, errors.Trace(err) | ||
allMatch := true | ||
for _, cond := range e.Conditions { | ||
match, err := expression.EvalBool(cond, srcRow.Data, e.ctx) | ||
if err != nil { | ||
return nil, errors.Trace(err) | ||
} | ||
if !match { | ||
allMatch = false | ||
break | ||
} | ||
} | ||
if match { | ||
if allMatch { | ||
return srcRow, nil | ||
} | ||
} | ||
} | ||
|
||
// Close implements the Executor Close interface. | ||
func (e *SelectionExec) Close() error { | ||
if e.scanController { | ||
e.controllerInit = false | ||
} | ||
return e.Src.Close() | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -146,6 +146,18 @@ type Selection struct { | |
|
||
// onTable means if this selection's child is a table scan or index scan. | ||
onTable bool | ||
|
||
// If ScanController is true, then the child of this selection is a scan, | ||
// which use pk or index. we will record the accessConditions, idxConditions, | ||
// and tblConditions to control the below plan. | ||
ScanController bool | ||
|
||
// Since one selection may call convert2PhysicalScan many times. We extract the PkName and indices | ||
// used for scanController only once and store them to judge whether this selection can convert to | ||
// scanController mode. | ||
usefulPkName model.CIStr | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we don't need to store the usefulPkName and usefulIndices. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we don't store the usefulPkName and usefulIndices. We will need to extract them one more time when build controller. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. okay, it seems not cost too much. |
||
usefulIndices []*model.IndexInfo | ||
extractedUsefulThing bool | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. set a flag that we set controller when the flag is true. The flag is true only the selection has correlated column and its child is DataSource. You can check this during decorrelation. |
||
} | ||
|
||
func (p *Selection) extractCorrelatedCols() []*expression.CorrelatedColumn { | ||
|
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.
If you store the conditions, you don't need to store condition any more. Use a for loop to check condition.