Skip to content

Commit a4158c2

Browse files
planner: adjust estimated row count when pushing Limit and keep order for partitioned table (#41103)
close #40986
1 parent 5e8cf8f commit a4158c2

5 files changed

+368
-0
lines changed

planner/core/integration_partition_test.go

+40
Original file line numberDiff line numberDiff line change
@@ -1655,3 +1655,43 @@ func TestPartitionProcessorWithUninitializedTable(t *testing.T) {
16551655
}
16561656
tk.MustQuery("explain format=brief select * from q1,q2").CheckAt([]int{0}, rows)
16571657
}
1658+
1659+
func TestEstimationForTopNPushToDynamicPartition(t *testing.T) {
1660+
store := testkit.CreateMockStore(t)
1661+
tk := testkit.NewTestKit(t, store)
1662+
tk.MustExec("use test")
1663+
tk.MustExec("set tidb_cost_model_version=2")
1664+
tk.MustExec("drop table if exists tlist")
1665+
tk.MustExec(`set tidb_enable_list_partition = 1`)
1666+
tk.MustExec(`create table trange (a int, b int, c int, index ia(a), primary key (b) clustered)
1667+
partition by range(b) (
1668+
partition p1 values less than(100),
1669+
partition p2 values less than(200),
1670+
partition p3 values less than maxvalue);`)
1671+
tk.MustExec(`create table tlist (a int, b int, c int, index ia(a), primary key (b) clustered)
1672+
partition by list (b) (
1673+
partition p0 values in (0, 1, 2),
1674+
partition p1 values in (3, 4, 5));`)
1675+
tk.MustExec(`create table thash (a int, b int, c int, index ia(a), primary key (b) clustered)
1676+
partition by hash(b) partitions 4;`)
1677+
tk.MustExec(`create table t (a int, b int, c int, index ia(a), primary key (b) clustered);`)
1678+
tk.MustExec(`analyze table trange;`)
1679+
tk.MustExec(`analyze table tlist;`)
1680+
tk.MustExec(`analyze table thash;`)
1681+
tk.MustExec(`analyze table t;`)
1682+
1683+
var input []string
1684+
var output []struct {
1685+
SQL string
1686+
Plan []string
1687+
}
1688+
integrationPartitionSuiteData := core.GetIntegrationPartitionSuiteData()
1689+
integrationPartitionSuiteData.LoadTestCases(t, &input, &output)
1690+
for i, tt := range input {
1691+
testdata.OnRecord(func() {
1692+
output[i].SQL = tt
1693+
output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows())
1694+
})
1695+
tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...))
1696+
}
1697+
}

planner/core/plan.go

+8
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,9 @@ type PhysicalPlan interface {
368368
// Stats returns the StatsInfo of the plan.
369369
Stats() *property.StatsInfo
370370

371+
// SetStats sets basePlan.stats inside the basePhysicalPlan.
372+
SetStats(s *property.StatsInfo)
373+
371374
// ExplainNormalizedInfo returns operator normalized information for generating digest.
372375
ExplainNormalizedInfo() string
373376

@@ -815,6 +818,11 @@ func (p *basePlan) Stats() *property.StatsInfo {
815818
return p.stats
816819
}
817820

821+
// SetStats sets basePlan.stats
822+
func (p *basePlan) SetStats(s *property.StatsInfo) {
823+
p.stats = s
824+
}
825+
818826
// basePlanSize is the size of basePlan.
819827
const basePlanSize = int64(unsafe.Sizeof(basePlan{}))
820828

planner/core/task.go

+36
Original file line numberDiff line numberDiff line change
@@ -1032,6 +1032,10 @@ func (p *PhysicalTopN) pushTopNDownToDynamicPartition(copTsk *copTask) (task, bo
10321032
return true
10331033
}
10341034
var (
1035+
selOnIdxScan *PhysicalSelection
1036+
selOnTblScan *PhysicalSelection
1037+
selSelectivity float64
1038+
10351039
idxScan *PhysicalIndexScan
10361040
tblScan *PhysicalTableScan
10371041
tblInfo *model.TableInfo
@@ -1044,6 +1048,7 @@ func (p *PhysicalTopN) pushTopNDownToDynamicPartition(copTsk *copTask) (task, bo
10441048
}
10451049
finalIdxScanPlan := copTsk.indexPlan
10461050
for len(finalIdxScanPlan.Children()) > 0 && finalIdxScanPlan.Children()[0] != nil {
1051+
selOnIdxScan, _ = finalIdxScanPlan.(*PhysicalSelection)
10471052
finalIdxScanPlan = finalIdxScanPlan.Children()[0]
10481053
}
10491054
idxScan = finalIdxScanPlan.(*PhysicalIndexScan)
@@ -1056,12 +1061,21 @@ func (p *PhysicalTopN) pushTopNDownToDynamicPartition(copTsk *copTask) (task, bo
10561061
}
10571062
finalTblScanPlan := copTsk.tablePlan
10581063
for len(finalTblScanPlan.Children()) > 0 {
1064+
selOnTblScan, _ = finalTblScanPlan.(*PhysicalSelection)
10591065
finalTblScanPlan = finalTblScanPlan.Children()[0]
10601066
}
10611067
tblScan = finalTblScanPlan.(*PhysicalTableScan)
10621068
tblInfo = tblScan.Table
10631069
}
10641070

1071+
// Note that we only need to care about one Selection at most.
1072+
if selOnIdxScan != nil && idxScan.statsInfo().RowCount > 0 {
1073+
selSelectivity = selOnIdxScan.statsInfo().RowCount / idxScan.statsInfo().RowCount
1074+
}
1075+
if idxScan == nil && selOnTblScan != nil && tblScan.statsInfo().RowCount > 0 {
1076+
selSelectivity = selOnTblScan.statsInfo().RowCount / tblScan.statsInfo().RowCount
1077+
}
1078+
10651079
pi := tblInfo.GetPartitionInfo()
10661080
if pi == nil {
10671081
return nil, false
@@ -1083,6 +1097,17 @@ func (p *PhysicalTopN) pushTopNDownToDynamicPartition(copTsk *copTask) (task, bo
10831097
}.Init(p.SCtx(), stats, p.SelectBlockOffset())
10841098
pushedLimit.SetSchema(copTsk.indexPlan.Schema())
10851099
copTsk = attachPlan2Task(pushedLimit, copTsk).(*copTask)
1100+
1101+
// A similar but simplified logic compared the ExpectedCnt handling logic in getOriginalPhysicalIndexScan.
1102+
child := pushedLimit.Children()[0]
1103+
// The row count of the direct child of Limit should be adjusted to be no larger than the Limit.Count.
1104+
child.SetStats(child.statsInfo().ScaleByExpectCnt(float64(newCount)))
1105+
// The Limit->Selection->IndexScan case:
1106+
// adjust the row count of IndexScan according to the selectivity of the Selection.
1107+
if selSelectivity > 0 && selSelectivity < 1 {
1108+
scaledRowCount := child.Stats().RowCount / selSelectivity
1109+
idxScan.SetStats(idxScan.Stats().ScaleByExpectCnt(scaledRowCount))
1110+
}
10861111
} else if copTsk.indexPlan == nil {
10871112
if tblScan.HandleCols == nil {
10881113
return nil, false
@@ -1111,6 +1136,17 @@ func (p *PhysicalTopN) pushTopNDownToDynamicPartition(copTsk *copTask) (task, bo
11111136
}.Init(p.SCtx(), stats, p.SelectBlockOffset())
11121137
pushedLimit.SetSchema(copTsk.tablePlan.Schema())
11131138
copTsk = attachPlan2Task(pushedLimit, copTsk).(*copTask)
1139+
1140+
// A similar but simplified logic compared the ExpectedCnt handling logic in getOriginalPhysicalTableScan.
1141+
child := pushedLimit.Children()[0]
1142+
// The row count of the direct child of Limit should be adjusted to be no larger than the Limit.Count.
1143+
child.SetStats(child.statsInfo().ScaleByExpectCnt(float64(newCount)))
1144+
// The Limit->Selection->TableScan case:
1145+
// adjust the row count of IndexScan according to the selectivity of the Selection.
1146+
if selSelectivity > 0 && selSelectivity < 1 {
1147+
scaledRowCount := child.Stats().RowCount / selSelectivity
1148+
tblScan.SetStats(tblScan.Stats().ScaleByExpectCnt(scaledRowCount))
1149+
}
11141150
} else {
11151151
return nil, false
11161152
}

planner/core/testdata/integration_partition_suite_in.json

+29
Original file line numberDiff line numberDiff line change
@@ -149,5 +149,34 @@
149149
"explain format='brief' select a from tcollist limit 10",
150150
"explain format='brief' select a from tcollist order by a limit 10"
151151
]
152+
},
153+
{
154+
"name": "TestEstimationForTopNPushToDynamicPartition",
155+
"cases": [
156+
"explain format='brief' select a from t use index (ia) where a > 10 order by a limit 10",
157+
"explain format='brief' select a from trange use index (ia) where a > 10 order by a limit 10",
158+
"explain format='brief' select a from tlist use index (ia) where a > 10 order by a limit 10",
159+
"explain format='brief' select a from thash use index (ia) where a > 10 order by a limit 10",
160+
"explain format='brief' select * from t use index (ia) where a > 10 order by a limit 10",
161+
"explain format='brief' select * from trange use index (ia) where a > 10 order by a limit 10",
162+
"explain format='brief' select * from tlist use index (ia) where a > 10 order by a limit 10",
163+
"explain format='brief' select * from thash use index (ia) where a > 10 order by a limit 10",
164+
"explain format='brief' select * from t use index (ia) where a + 1 > 10 order by a limit 10",
165+
"explain format='brief' select * from trange use index (ia) where a + 1 > 10 order by a limit 10",
166+
"explain format='brief' select * from tlist use index (ia) where a + 1 > 10 order by a limit 10",
167+
"explain format='brief' select * from thash use index (ia) where a + 1 > 10 order by a limit 10",
168+
"explain format='brief' select a from t use index (ia) where a > 10 and c = 10 order by a limit 10",
169+
"explain format='brief' select a from trange use index (ia) where a > 10 and c = 10 order by a limit 10",
170+
"explain format='brief' select a from tlist use index (ia) where a > 10 and c = 10 order by a limit 10",
171+
"explain format='brief' select a from thash use index (ia) where a > 10 and c = 10 order by a limit 10",
172+
"explain format='brief' select a from t use index () where b > 10 order by b limit 10",
173+
"explain format='brief' select a from trange use index () where b > 10 order by b limit 10",
174+
"explain format='brief' select a from tlist use index () where b > 10 order by b limit 10",
175+
"explain format='brief' select a from thash use index () where b > 10 order by b limit 10",
176+
"explain format='brief' select a from t use index () where a > 10 order by b limit 10",
177+
"explain format='brief' select a from trange use index () where a > 10 order by b limit 10",
178+
"explain format='brief' select a from tlist use index () where a > 10 order by b limit 10",
179+
"explain format='brief' select a from thash use index () where a > 10 order by b limit 10"
180+
]
152181
}
153182
]

0 commit comments

Comments
 (0)