-
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
planner: support push part of order property down to the partition table #36108
Conversation
[REVIEW NOTIFICATION] This pull request has been approved by:
To complete the pull request process, please ask the reviewers in the list to review by filling The full list of commands accepted by this bot can be found here. Reviewer can indicate their review by submitting an approval review. |
planner/core/task.go
Outdated
} | ||
|
||
// If we reach here, the index matches the order property of the top-n, we could push a limit down. | ||
copTsk.keepOrder = true |
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.
It seems like we don't need to set copTask.keepOrder
to true
.
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.
for partitions maybe
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.
func buildIndexLookUpTask(ctx sessionctx.Context, t *copTask) *rootTask {
newTask := &rootTask{cst: t.cst}
p := PhysicalIndexLookUpReader{
tablePlan: t.tablePlan,
indexPlan: t.indexPlan,
ExtraHandleCol: t.extraHandleCol,
CommonHandleCols: t.commonHandleCols,
expectedCnt: t.expectCnt,
keepOrder: t.keepOrder,
}.Init(ctx, t.tablePlan.SelectBlockOffset())
This func uses it.
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.
Oh actually we don't need the keep order, remove it.
planner/core/task.go
Outdated
return nil, false | ||
} | ||
} | ||
copTsk.keepOrder = true |
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.
ditto.
} | ||
tblScan = finalTblScanPlan.(*PhysicalTableScan) | ||
} | ||
if !copTsk.indexPlanFinished { |
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 didn't consider that idxScan
might be nil
in the logic below.
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.
When indexPlanFinished
is not true, there should be a index scan to be finished later.
planner/core/task.go
Outdated
// pushTopNDownToDynamicPartition is a temp solution for partition table. It actually does the same thing as DataSource's isMatchProp. | ||
func (p *PhysicalTopN) pushTopNDownToDynamicPartition(copTsk *copTask) (task, 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.
I think we need more detailed comments since it's a tricky way to modify the plan.
I think we should clarify what the resulting plan would be like and why we have to use this method as a temporary solution.
if tblScan.HandleCols.IsInt() { | ||
pk := tblScan.HandleCols.GetCol(0) | ||
if len(colsProp.SortItems) != 1 || !colsProp.SortItems[0].Col.Equal(p.SCtx(), pk) { |
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.
This is different from (*DataSource).isMatchProp
. According to isMatchProp
, tiflash doesn't support desc
scan.
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.
This change only affects tikv part.
planner/core/task.go
Outdated
if copTsk.tablePlan != nil && !idxScan.Table.IsCommonHandle && copTsk.extraHandleCol == nil { | ||
// The clustered index would always add handle, so we don't need to consider that case. | ||
copTsk.extraHandleCol = &expression.Column{ | ||
RetType: types.NewFieldType(mysql.TypeLonglong), | ||
ID: model.ExtraHandleID, | ||
UniqueID: idxScan.ctx.GetSessionVars().AllocPlanColumnID(), | ||
} | ||
tblScan.Schema().Append(copTsk.extraHandleCol) | ||
tblScan.Columns = append(tblScan.Columns, model.NewExtraHandleColInfo()) | ||
copTsk.needExtraProj = true | ||
copTsk.originSchema = idxScan.dataSourceSchema | ||
} |
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 missed the case that the int primary key is not _tidb_rowid
but a column specified by the user.
I think we can modify the (*PhysicalTableScan).appendExtraHandleCol
a bit and reuse that.
Code Coverage Details: https://codecov.io/github/pingcap/tidb/commit/27324cbb5e663b42207d456b6b59dca30ab37351 |
planner/core/task.go
Outdated
func (p *PhysicalTopN) pushTopNDownToDynamicPartition(copTsk *copTask) (task, bool) { | ||
var err error | ||
copTsk = copTsk.copy().(*copTask) | ||
if err != 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.
the err isn't assigned anything before
planner/core/task.go
Outdated
} | ||
|
||
// If we reach here, the index matches the order property of the top-n, we could push a limit down. | ||
copTsk.keepOrder = true |
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.
for partitions maybe
if len(constColsByCond) == 0 || idxPos > len(constColsByCond) || !constColsByCond[idxPos] { | ||
found = false | ||
break | ||
} |
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.
missed set found = true at the end? (indicating that we are sure that this col is constant, then just go to outer's continued column)
In:
order by (a,b,c), once index(a,c) exists and b=1, we can still access this path right?
reader -> limit -> selection -> indexScan
order by (a,c), once index(a,b,c) exists and b=1 is what actually it already does.
3dfbb1f
to
7778f01
Compare
7778f01
to
e285406
Compare
return true | ||
} | ||
if len(j) == 0 { | ||
return 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.
why the len(j) = 0 has different logic with the above?
@@ -310,17 +310,20 @@ func TestListPartitionPruner(t *testing.T) { | |||
partitionPrunerData.LoadTestCases(t, &input, &output) | |||
valid := false | |||
for i, tt := range input { | |||
if tt == "select * from t1 where (id = 1 and a = 1) or a is null" { | |||
fmt.Println("1") |
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.
remove the debug info
planner/core/task.go
Outdated
return nil, false | ||
} | ||
finalTblScanPlan := copTsk.tablePlan | ||
if len(finalTblScanPlan.Children()) > 0 { |
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/ if / for?
} | ||
tblScan.Desc = isDesc | ||
childProfile := copTsk.plan().statsInfo() | ||
newCount := p.Offset + p.Count |
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.
should we consider there's a selection on the table side? the count here may won't pass the selection
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.
In such case, the plan should be Limit->Selection->TableScan
. The selection should not matter
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.
get!make sense
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.
Rest LGTM
/merge |
This pull request has been accepted and is ready to merge. Commit hash: 953273f
|
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.
+1
TiDB MergeCI notify🔴 Bad News! New failing [1] after this pr merged.
|
What problem does this PR solve?
Issue Number: ref #26166 (a quick solution)
Problem Summary:
The partition table shields the partition information in the execution phase. So we cannot convert the
order by index limit x
to LIMIT with the order property pushed to the index kv.What is changed and how it works?
This time we don't change the codes of the execution. So we cannot push the full order property down to the index kv.
We just change the plan from
TopN(TiDB)->Reader(TiDB)->TopN(TiKV)->ScanKV(TiKV, no order)
toTopN(TiDB)->Reader(TiDB)->Limit(TiKV)->ScanKV(TiKV, in order)
.Check List
Tests
Side effects
Documentation
Release note
Please refer to Release Notes Language Style Guide to write a quality release note.