-
Notifications
You must be signed in to change notification settings - Fork 5.8k
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
ranger: merge multiple EQ or In expressions if possible #7577
Changes from 12 commits
efe0437
52f0b7b
d70c96b
9e2ac09
1efbd86
0c3460a
c027370
7b2cf0f
0a603d1
b1c1b24
2aa9a99
4e8f961
26d7e7c
7950156
615517f
c48c57e
7287827
5342c9b
2cb7426
81ce95a
e2f8a97
8074c7b
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 |
---|---|---|
|
@@ -146,19 +146,22 @@ func detachCNFCondAndBuildRangeForIndex(sctx sessionctx.Context, conditions []ex | |
err error | ||
) | ||
|
||
accessConds, filterConds := extractEqAndInCondition(conditions, cols, lengths) | ||
accessConds, filterConds, newConditions, emptyRange := extractEqAndInCondition(sctx, conditions, cols, lengths) | ||
if emptyRange { | ||
return ranges, nil, nil, 0, nil | ||
} | ||
|
||
for ; eqCount < len(accessConds); eqCount++ { | ||
if accessConds[eqCount].(*expression.ScalarFunction).FuncName.L != ast.EQ { | ||
break | ||
} | ||
} | ||
// We should remove all accessConds, so that they will not be added to filter conditions. | ||
conditions = removeAccessConditions(conditions, accessConds) | ||
newConditions = removeAccessConditions(newConditions, accessConds) | ||
eqOrInCount := len(accessConds) | ||
if eqOrInCount == len(cols) { | ||
// If curIndex equals to len of index columns, it means the rest conditions haven't been appended to filter conditions. | ||
filterConds = append(filterConds, conditions...) | ||
filterConds = append(filterConds, newConditions...) | ||
ranges, err = buildCNFIndexRange(sctx.GetSessionVars().StmtCtx, cols, tpSlice, lengths, eqOrInCount, accessConds) | ||
if err != nil { | ||
return nil, nil, nil, 0, errors.Trace(err) | ||
|
@@ -171,11 +174,11 @@ func detachCNFCondAndBuildRangeForIndex(sctx sessionctx.Context, conditions []ex | |
shouldReserve: lengths[eqOrInCount] != types.UnspecifiedLength, | ||
} | ||
if considerDNF { | ||
accesses, filters := detachColumnCNFConditions(sctx, conditions, checker) | ||
accesses, filters := detachColumnCNFConditions(sctx, newConditions, checker) | ||
accessConds = append(accessConds, accesses...) | ||
filterConds = append(filterConds, filters...) | ||
} else { | ||
for _, cond := range conditions { | ||
for _, cond := range newConditions { | ||
if !checker.check(cond) { | ||
filterConds = append(filterConds, cond) | ||
continue | ||
|
@@ -187,14 +190,45 @@ func detachCNFCondAndBuildRangeForIndex(sctx sessionctx.Context, conditions []ex | |
return ranges, accessConds, filterConds, eqCount, errors.Trace(err) | ||
} | ||
|
||
func extractEqAndInCondition(conditions []expression.Expression, cols []*expression.Column, | ||
lengths []int) (accesses, filters []expression.Expression) { | ||
accesses = make([]expression.Expression, len(cols)) | ||
func extractEqAndInCondition(sctx sessionctx.Context, conditions []expression.Expression, | ||
cols []*expression.Column, lengths []int) ([]expression.Expression, []expression.Expression, []expression.Expression, bool) { | ||
var filters []expression.Expression | ||
rb := builder{sc: sctx.GetSessionVars().StmtCtx} | ||
accesses := make([]expression.Expression, len(cols)) | ||
points := make([][]point, len(cols)) | ||
mergedAccesses := make([]expression.Expression, len(cols)) | ||
newConditions := make([]expression.Expression, 0, len(conditions)) | ||
for _, cond := range conditions { | ||
offset := getEqOrInColOffset(cond, cols) | ||
if offset != -1 { | ||
if offset == -1 { | ||
newConditions = append(newConditions, cond) | ||
continue | ||
} | ||
if accesses[offset] == nil { | ||
accesses[offset] = cond | ||
continue | ||
} | ||
//multiple Eq/In conditions for one column in CNF, apply intersection on them | ||
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. Space between |
||
//lazily compute the points for the previously visited Eq/In | ||
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 space. |
||
if mergedAccesses[offset] == nil { | ||
mergedAccesses[offset] = accesses[offset] | ||
points[offset] = rb.build(accesses[offset]) | ||
} | ||
points[offset] = rb.intersection(points[offset], rb.build(cond)) | ||
//early termination if false expression found | ||
if len(points[offset]) == 0 { | ||
return nil, nil, nil, true | ||
} | ||
} | ||
for i, ma := range mergedAccesses { | ||
if ma == nil { | ||
if accesses[i] != nil { | ||
newConditions = append(newConditions, accesses[i]) | ||
} | ||
continue | ||
} | ||
accesses[i] = points2EqOrInCond(sctx, points[i], mergedAccesses[i]) | ||
newConditions = append(newConditions, accesses[i]) | ||
} | ||
for i, cond := range accesses { | ||
if cond == nil { | ||
|
@@ -205,7 +239,7 @@ func extractEqAndInCondition(conditions []expression.Expression, cols []*express | |
filters = append(filters, cond) | ||
} | ||
} | ||
return accesses, filters | ||
return accesses, filters, newConditions, false | ||
} | ||
|
||
// detachDNFCondAndBuildRangeForIndex will detach the index filters from table filters when it's a DNF. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,6 +24,7 @@ import ( | |
"github.com/pingcap/tidb/expression" | ||
"github.com/pingcap/tidb/kv" | ||
"github.com/pingcap/tidb/mysql" | ||
"github.com/pingcap/tidb/sessionctx" | ||
"github.com/pingcap/tidb/sessionctx/stmtctx" | ||
"github.com/pingcap/tidb/types" | ||
"github.com/pingcap/tidb/util/charset" | ||
|
@@ -466,3 +467,33 @@ func newFieldType(tp *types.FieldType) *types.FieldType { | |
return tp | ||
} | ||
} | ||
|
||
func points2EqOrInCond(ctx sessionctx.Context, points []point, expr expression.Expression) expression.Expression { | ||
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. we need a comment for this function to explain:
how about: // points2EqOrInCond constructs a 'EQUAL' or 'IN' scalar function based on the
// 'points'. The target column is extracted from the 'expr'.
// NOTE:
// 1. 'expr' must be either 'EQUAL' or 'IN' function.
// 2. 'points' should not be empty. |
||
//len(points) cannot be 0 here, since we impose early termination in extractEqAndInCondition | ||
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 space. |
||
sf, _ := expr.(*expression.ScalarFunction) | ||
//Constant and Column args should have same RetType, simply get from first arg | ||
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 space. |
||
retType := sf.GetArgs()[0].GetType() | ||
values := make([]expression.Expression, 0, len(points)/2) | ||
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. how about |
||
if sf.FuncName.L == ast.EQ { | ||
if c, ok := sf.GetArgs()[0].(*expression.Column); ok { | ||
values = append(values, c) | ||
} else if c, ok := sf.GetArgs()[1].(*expression.Column); ok { | ||
values = append(values, c) | ||
} | ||
} else { | ||
values = append(values, sf.GetArgs()[0]) | ||
} | ||
for i := 0; i < len(points); i = i + 2 { | ||
value := &expression.Constant{ | ||
Value: points[i].value, | ||
RetType: retType, | ||
} | ||
values = append(values, value) | ||
} | ||
funcName := ast.EQ | ||
if len(values) > 2 { | ||
funcName = ast.In | ||
} | ||
f := expression.NewFunctionInternal(ctx, funcName, sf.GetType(), values...) | ||
return f | ||
} |
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.
Maybe it's better to remove other access paths if there is a path which has an empty range. This can be done in
DataSource.deriveStats()
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.
updated