Skip to content

Commit af47b70

Browse files
authored
planner: fix the issue that the optimizer terminates the optimization process for DataSource too early (#48186) (#48267)
close #46177
1 parent aa93091 commit af47b70

File tree

3 files changed

+77
-7
lines changed

3 files changed

+77
-7
lines changed

pkg/planner/core/find_best_task.go

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -991,6 +991,13 @@ func (ds *DataSource) isPointGetConvertableSchema() bool {
991991
return true
992992
}
993993

994+
// exploreEnforcedPlan determines whether to explore enforced plans for this DataSource if it has already found an unenforced plan.
995+
// See #46177 for more information.
996+
func (ds *DataSource) exploreEnforcedPlan() bool {
997+
// default value is false to keep it compatible with previous versions.
998+
return fixcontrol.GetBoolWithDefault(ds.SCtx().GetSessionVars().GetOptimizerFixControlMap(), fixcontrol.Fix46177, false)
999+
}
1000+
9941001
// findBestTask implements the PhysicalPlan interface.
9951002
// It will enumerate all the available indices and choose a plan with least cost.
9961003
func (ds *DataSource) findBestTask(prop *property.PhysicalProperty, planCounter *PlanCounterTp, opt *physicalOptimizeOp) (t task, cntPlan int64, err error) {
@@ -1031,23 +1038,25 @@ func (ds *DataSource) findBestTask(prop *property.PhysicalProperty, planCounter
10311038
return
10321039
}
10331040
var cnt int64
1041+
var unenforcedTask task
10341042
// If prop.CanAddEnforcer is true, the prop.SortItems need to be set nil for ds.findBestTask.
10351043
// Before function return, reset it for enforcing task prop and storing map<prop,task>.
10361044
oldProp := prop.CloneEssentialFields()
10371045
if prop.CanAddEnforcer {
10381046
// First, get the bestTask without enforced prop
10391047
prop.CanAddEnforcer = false
1040-
t, cnt, err = ds.findBestTask(prop, planCounter, opt)
1048+
unenforcedTask, cnt, err = ds.findBestTask(prop, planCounter, opt)
10411049
if err != nil {
10421050
return nil, 0, err
10431051
}
1044-
prop.CanAddEnforcer = true
1045-
if t != invalidTask {
1046-
ds.storeTask(prop, t)
1047-
cntPlan = cnt
1048-
return
1052+
if !unenforcedTask.invalid() && !ds.exploreEnforcedPlan() {
1053+
ds.storeTask(prop, unenforcedTask)
1054+
return unenforcedTask, cnt, nil
10491055
}
1050-
// Next, get the bestTask with enforced prop
1056+
1057+
// Then, explore the bestTask with enforced prop
1058+
prop.CanAddEnforcer = true
1059+
cntPlan += cnt
10511060
prop.SortItems = []property.SortItem{}
10521061
prop.MPPPartitionTp = property.AnyType
10531062
} else if prop.MPPPartitionTp != property.AnyType {
@@ -1062,6 +1071,18 @@ func (ds *DataSource) findBestTask(prop *property.PhysicalProperty, planCounter
10621071
t = enforceProperty(prop, t, ds.Plan.SCtx())
10631072
prop.CanAddEnforcer = true
10641073
}
1074+
1075+
if unenforcedTask != nil && !unenforcedTask.invalid() {
1076+
curIsBest, cerr := compareTaskCost(ds.SCtx(), unenforcedTask, t, opt)
1077+
if cerr != nil {
1078+
err = cerr
1079+
return
1080+
}
1081+
if curIsBest {
1082+
t = unenforcedTask
1083+
}
1084+
}
1085+
10651086
ds.storeTask(prop, t)
10661087
if ds.SampleInfo != nil && !t.invalid() {
10671088
if _, ok := t.plan().(*PhysicalTableSample); !ok {

pkg/planner/core/integration_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2529,6 +2529,53 @@ func TestIssue46298(t *testing.T) {
25292529
tk.MustQuery("select *, first_value(v) over (partition by p order by o range between 3.1 preceding and 2.9 following) as a from test.first_range;")
25302530
}
25312531

2532+
func TestIssue46177(t *testing.T) {
2533+
store := testkit.CreateMockStore(t)
2534+
tk := testkit.NewTestKit(t, store)
2535+
tk.MustExec(`use test`)
2536+
tk.MustExec(` CREATE TABLE sbtest (
2537+
id int(10) unsigned NOT NULL AUTO_INCREMENT,
2538+
k int(10) unsigned NOT NULL DEFAULT '0',
2539+
c char(120) NOT NULL DEFAULT '',
2540+
pad char(60) NOT NULL DEFAULT '',
2541+
PRIMARY KEY (id) /*T![clustered_index] CLUSTERED */,
2542+
KEY k (k)
2543+
)`)
2544+
2545+
// cannot choose the best plan with RangeScan.
2546+
tk.MustExec(`set @@tidb_opt_fix_control = '46177:off'`)
2547+
tk.MustQuery(`explain format='brief' select row_number() over(order by a.k) from (select * from sbtest where id<10) a`).Check(testkit.Rows(
2548+
`Projection 10.00 root Column#6->Column#7`,
2549+
`└─Window 10.00 root row_number()->Column#6 over(order by test.sbtest.k rows between current row and current row)`,
2550+
` └─IndexReader 10.00 root index:Selection`,
2551+
` └─Selection 10.00 cop[tikv] lt(test.sbtest.id, 10)`,
2552+
` └─IndexFullScan 10000.00 cop[tikv] table:sbtest, index:k(k) keep order:true, stats:pseudo`))
2553+
2554+
tk.MustExec(`set @@tidb_opt_fix_control = '46177:on'`)
2555+
tk.MustQuery(`explain format='brief' select row_number() over(order by a.k) from (select * from sbtest where id<10) a`).Check(testkit.Rows(
2556+
`Projection 10.00 root Column#6->Column#7`,
2557+
`└─Window 10.00 root row_number()->Column#6 over(order by test.sbtest.k rows between current row and current row)`,
2558+
` └─Sort 10.00 root test.sbtest.k`,
2559+
` └─TableReader 10.00 root data:TableRangeScan`,
2560+
` └─TableRangeScan 10.00 cop[tikv] table:sbtest range:[0,10), keep order:false, stats:pseudo`))
2561+
2562+
// cannot choose the range scan plan.
2563+
tk.MustExec(`set @@tidb_opt_fix_control = '46177:off'`)
2564+
tk.MustQuery(`explain format='brief' select /*+ stream_agg() */ count(1) from sbtest where id<1 group by k`).Check(testkit.Rows(
2565+
`StreamAgg 1.00 root group by:test.sbtest.k, funcs:count(Column#6)->Column#5`,
2566+
`└─IndexReader 1.00 root index:StreamAgg`,
2567+
` └─StreamAgg 1.00 cop[tikv] group by:test.sbtest.k, funcs:count(1)->Column#6`,
2568+
` └─Selection 1.00 cop[tikv] lt(test.sbtest.id, 1)`,
2569+
` └─IndexFullScan 10000.00 cop[tikv] table:sbtest, index:k(k) keep order:true, stats:pseudo`))
2570+
2571+
tk.MustExec(`set @@tidb_opt_fix_control = '46177:on'`)
2572+
tk.MustQuery(`explain format='brief' select /*+ stream_agg() */ count(1) from sbtest where id<1 group by k`).Check(testkit.Rows(
2573+
`StreamAgg 1.00 root group by:test.sbtest.k, funcs:count(1)->Column#5`,
2574+
`└─Sort 1.00 root test.sbtest.k`,
2575+
` └─TableReader 1.00 root data:TableRangeScan`,
2576+
` └─TableRangeScan 1.00 cop[tikv] table:sbtest range:[0,1), keep order:false, stats:pseudo`))
2577+
}
2578+
25322579
// https://github.com/pingcap/tidb/issues/41458
25332580
func TestIssue41458(t *testing.T) {
25342581
store := testkit.CreateMockStore(t)

pkg/planner/util/fixcontrol/get.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ const (
3434
Fix44855 uint64 = 44855
3535
// Fix45132 controls whether to use access range row count to determine access path on the Skyline pruning.
3636
Fix45132 uint64 = 45132
37+
// Fix46177 controls whether to explore enforced plans for DataSource if it has already found an unenforced plan.
38+
Fix46177 uint64 = 46177
3739
)
3840

3941
// GetStr fetches the given key from the fix control map as a string type.

0 commit comments

Comments
 (0)