Skip to content

Commit 175edc2

Browse files
authored
planner: fix the issue that the optimizer terminates the optimization process for DataSource too early (#48186) (#48265)
close #46177
1 parent a06279f commit 175edc2

File tree

3 files changed

+82
-7
lines changed

3 files changed

+82
-7
lines changed

planner/core/find_best_task.go

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"github.com/pingcap/tidb/planner/util"
3030
"github.com/pingcap/tidb/sessionctx"
3131
"github.com/pingcap/tidb/sessionctx/stmtctx"
32+
"github.com/pingcap/tidb/sessionctx/variable"
3233
"github.com/pingcap/tidb/statistics"
3334
"github.com/pingcap/tidb/types"
3435
tidbutil "github.com/pingcap/tidb/util"
@@ -841,6 +842,17 @@ func (ds *DataSource) isPointGetConvertableSchema() bool {
841842
return true
842843
}
843844

845+
// exploreEnforcedPlan determines whether to explore enforced plans for this DataSource if it has already found an unenforced plan.
846+
// See #46177 for more information.
847+
func (ds *DataSource) exploreEnforcedPlan() bool {
848+
// default value is false to keep it compatible with previous versions.
849+
fixValue, ok := ds.ctx.GetSessionVars().GetOptimizerFixControlValue(variable.TiDBOptFixControl46177)
850+
if !ok {
851+
return false
852+
}
853+
return variable.TiDBOptOn(fixValue)
854+
}
855+
844856
// findBestTask implements the PhysicalPlan interface.
845857
// It will enumerate all the available indices and choose a plan with least cost.
846858
func (ds *DataSource) findBestTask(prop *property.PhysicalProperty, planCounter *PlanCounterTp, opt *physicalOptimizeOp) (t task, cntPlan int64, err error) {
@@ -881,23 +893,25 @@ func (ds *DataSource) findBestTask(prop *property.PhysicalProperty, planCounter
881893
return
882894
}
883895
var cnt int64
896+
var unenforcedTask task
884897
// If prop.CanAddEnforcer is true, the prop.SortItems need to be set nil for ds.findBestTask.
885898
// Before function return, reset it for enforcing task prop and storing map<prop,task>.
886899
oldProp := prop.CloneEssentialFields()
887900
if prop.CanAddEnforcer {
888901
// First, get the bestTask without enforced prop
889902
prop.CanAddEnforcer = false
890-
t, cnt, err = ds.findBestTask(prop, planCounter, opt)
903+
unenforcedTask, cnt, err = ds.findBestTask(prop, planCounter, opt)
891904
if err != nil {
892905
return nil, 0, err
893906
}
894-
prop.CanAddEnforcer = true
895-
if t != invalidTask {
896-
ds.storeTask(prop, t)
897-
cntPlan = cnt
898-
return
907+
if !unenforcedTask.invalid() && !ds.exploreEnforcedPlan() {
908+
ds.storeTask(prop, unenforcedTask)
909+
return unenforcedTask, cnt, nil
899910
}
900-
// Next, get the bestTask with enforced prop
911+
912+
// Then, explore the bestTask with enforced prop
913+
prop.CanAddEnforcer = true
914+
cntPlan += cnt
901915
prop.SortItems = []property.SortItem{}
902916
prop.MPPPartitionTp = property.AnyType
903917
} else if prop.MPPPartitionTp != property.AnyType {
@@ -912,6 +926,18 @@ func (ds *DataSource) findBestTask(prop *property.PhysicalProperty, planCounter
912926
t = enforceProperty(prop, t, ds.basePlan.ctx)
913927
prop.CanAddEnforcer = true
914928
}
929+
930+
if unenforcedTask != nil && !unenforcedTask.invalid() {
931+
curIsBest, cerr := compareTaskCost(ds.SCtx(), unenforcedTask, t, opt)
932+
if cerr != nil {
933+
err = cerr
934+
return
935+
}
936+
if curIsBest {
937+
t = unenforcedTask
938+
}
939+
}
940+
915941
ds.storeTask(prop, t)
916942
err = validateTableSamplePlan(ds, t, err)
917943
}()

planner/core/integration_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8353,6 +8353,53 @@ func TestVirtualExprPushDown(t *testing.T) {
83538353
tk.MustQuery("explain select * from t where c2 > 1;").CheckAt([]int{0, 2, 4}, rows)
83548354
}
83558355

8356+
func TestIssue46177(t *testing.T) {
8357+
store := testkit.CreateMockStore(t)
8358+
tk := testkit.NewTestKit(t, store)
8359+
tk.MustExec(`use test`)
8360+
tk.MustExec(` CREATE TABLE sbtest (
8361+
id int(10) unsigned NOT NULL AUTO_INCREMENT,
8362+
k int(10) unsigned NOT NULL DEFAULT '0',
8363+
c char(120) NOT NULL DEFAULT '',
8364+
pad char(60) NOT NULL DEFAULT '',
8365+
PRIMARY KEY (id) /*T![clustered_index] CLUSTERED */,
8366+
KEY k (k)
8367+
)`)
8368+
8369+
// cannot choose the best plan with RangeScan.
8370+
tk.MustExec(`set @@tidb_opt_fix_control = '46177:OFF'`)
8371+
tk.MustQuery(`explain format='brief' select row_number() over(order by a.k) from (select * from sbtest where id<10) a`).Check(testkit.Rows(
8372+
`Projection 10.00 root Column#6`,
8373+
`└─Window 10.00 root row_number()->Column#6 over(order by test.sbtest.k rows between current row and current row)`,
8374+
` └─IndexReader 10.00 root index:Selection`,
8375+
` └─Selection 10.00 cop[tikv] lt(test.sbtest.id, 10)`,
8376+
` └─IndexFullScan 10000.00 cop[tikv] table:sbtest, index:k(k) keep order:true, stats:pseudo`))
8377+
8378+
tk.MustExec(`set @@tidb_opt_fix_control = '46177:ON'`)
8379+
tk.MustQuery(`explain format='brief' select row_number() over(order by a.k) from (select * from sbtest where id<10) a`).Check(testkit.Rows(
8380+
`Projection 10.00 root Column#6`,
8381+
`└─Window 10.00 root row_number()->Column#6 over(order by test.sbtest.k rows between current row and current row)`,
8382+
` └─Sort 10.00 root test.sbtest.k`,
8383+
` └─TableReader 10.00 root data:TableRangeScan`,
8384+
` └─TableRangeScan 10.00 cop[tikv] table:sbtest range:[0,10), keep order:false, stats:pseudo`))
8385+
8386+
// cannot choose the range scan plan.
8387+
tk.MustExec(`set @@tidb_opt_fix_control = '46177:OFF'`)
8388+
tk.MustQuery(`explain format='brief' select /*+ stream_agg() */ count(1) from sbtest where id<1 group by k`).Check(testkit.Rows(
8389+
`StreamAgg 1.00 root group by:test.sbtest.k, funcs:count(Column#6)->Column#5`,
8390+
`└─IndexReader 1.00 root index:StreamAgg`,
8391+
` └─StreamAgg 1.00 cop[tikv] group by:test.sbtest.k, funcs:count(1)->Column#6`,
8392+
` └─Selection 1.00 cop[tikv] lt(test.sbtest.id, 1)`,
8393+
` └─IndexFullScan 10000.00 cop[tikv] table:sbtest, index:k(k) keep order:true, stats:pseudo`))
8394+
8395+
tk.MustExec(`set @@tidb_opt_fix_control = '46177:ON'`)
8396+
tk.MustQuery(`explain format='brief' select /*+ stream_agg() */ count(1) from sbtest where id<1 group by k`).Check(testkit.Rows(
8397+
`StreamAgg 1.00 root group by:test.sbtest.k, funcs:count(1)->Column#5`,
8398+
`└─Sort 1.00 root test.sbtest.k`,
8399+
` └─TableReader 1.00 root data:TableRangeScan`,
8400+
` └─TableRangeScan 1.00 cop[tikv] table:sbtest range:[0,1), keep order:false, stats:pseudo`))
8401+
}
8402+
83568403
func TestIssue45044(t *testing.T) {
83578404
store := testkit.CreateMockStore(t)
83588405
tk := testkit.NewTestKit(t, store)

sessionctx/variable/session.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1346,6 +1346,8 @@ var (
13461346
// TiDBOptFixControl44855 controls whether to use a more accurate upper bound when estimating row count of index
13471347
// range scan under inner side of index join.
13481348
TiDBOptFixControl44855 uint64 = 44855
1349+
// TiDBOptFixControl46177 controls whether to explore enforced plans for DataSource if it has already found an unenforced plan.
1350+
TiDBOptFixControl46177 uint64 = 46177
13491351
)
13501352

13511353
// GetOptimizerFixControlValue returns the specified value of the optimizer fix control.

0 commit comments

Comments
 (0)