Skip to content

Commit

Permalink
planner: fix index merge shouldn't push partial limit down when index…
Browse files Browse the repository at this point in the history
… plans are keep ordered (#52979) (#53417)

close #52947
  • Loading branch information
ti-chi-bot authored May 28, 2024
1 parent 62e0c8d commit ec938d7
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 3 deletions.
33 changes: 33 additions & 0 deletions pkg/planner/core/casetest/physicalplantest/physical_plan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2271,6 +2271,39 @@ func TestIndexMergeSinkLimit(t *testing.T) {
}
}

func TestIndexMergeIssue52947(t *testing.T) {
var (
input []string
output []struct {
SQL string
Plan []string
Warning []string
}
)
planSuiteData := GetPlanSuiteData()
planSuiteData.LoadTestCases(t, &input, &output)
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("CREATE TABLE `tbl_43` (\n `col_304` binary(207) NOT NULL DEFAULT 'eIenHx\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0'," +
" PRIMARY KEY (`col_304`) /*T![clustered_index] CLUSTERED */," +
" UNIQUE KEY `idx_259` (`col_304`(5))," +
" UNIQUE KEY `idx_260` (`col_304`(2))," +
" KEY `idx_261` (`col_304`)," +
" UNIQUE KEY `idx_262` (`col_304`)" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;")
tk.MustExec("insert into tbl_43 values(\"BCmuENPHzSOIMJLPB\"),(\"LDOdXZYpOR\"),(\"R\"),(\"TloTqcHhdgpwvMsSoJ\"),(\"UajN\"),(\"mAwLZbiyq\"),(\"swLIoWa\");")
for i, ts := range input {
testdata.OnRecord(func() {
output[i].SQL = ts
output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(ts).Rows())
output[i].Warning = testdata.ConvertRowsToStrings(tk.MustQuery("show warnings").Rows())
})
tk.MustQuery(ts).Check(testkit.Rows(output[i].Plan...))
tk.MustQuery("show warnings").Check(testkit.Rows(output[i].Warning...))
}
}

// Test issue #46962 plan
func TestAlwaysTruePredicateWithSubquery(t *testing.T) {
var (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1068,6 +1068,15 @@
"SHOW ERRORS WHERE TRUE = ALL ( SELECT TRUE GROUP BY 1 LIMIT 1 ) IS NULL IS NOT NULL;",
"explain select * from t WHERE TRUE = ALL ( SELECT TRUE GROUP BY 1 LIMIT 1 ) IS NULL IS NOT NULL;",
"explain select * from t WHERE TRUE = ALL ( SELECT TRUE from t GROUP BY 1 LIMIT 1 ) is null is not null;"
]
},
{
"name": "TestIndexMergeIssue52947",
"cases": [
"explain format = 'brief' select min(col_304) from (select /*+ use_index_merge( tbl_43 ) */ * from tbl_43 where not( tbl_43.col_304 between 'YEpfYfPVvhMlHGHSMKm' and 'PE' ) or tbl_43.col_304 in ( 'LUBGzGMA' ) and tbl_43.col_304 between 'HpsjfuSReCwBoh' and 'fta' or not( tbl_43.col_304 between 'MFWmuOsoyDv' and 'TSeMYpDXnFIyp' ) order by col_304) x;",
"select min(col_304) from (select /*+ use_index_merge( tbl_43 ) */ * from tbl_43 where not( tbl_43.col_304 between 'YEpfYfPVvhMlHGHSMKm' and 'PE' ) or tbl_43.col_304 in ( 'LUBGzGMA' ) and tbl_43.col_304 between 'HpsjfuSReCwBoh' and 'fta' or not( tbl_43.col_304 between 'MFWmuOsoyDv' and 'TSeMYpDXnFIyp' ) order by col_304) x;",
"explain format = 'brief' select max(col_304) from (select /*+ use_index_merge( tbl_43 ) */ * from tbl_43 where not( tbl_43.col_304 between 'YEpfYfPVvhMlHGHSMKm' and 'PE' ) or tbl_43.col_304 in ( 'LUBGzGMA' ) and tbl_43.col_304 between 'HpsjfuSReCwBoh' and 'fta' or not( tbl_43.col_304 between 'MFWmuOsoyDv' and 'TSeMYpDXnFIyp' ) order by col_304) x;",
"select max(col_304) from (select /*+ use_index_merge( tbl_43 ) */ * from tbl_43 where not( tbl_43.col_304 between 'YEpfYfPVvhMlHGHSMKm' and 'PE' ) or tbl_43.col_304 in ( 'LUBGzGMA' ) and tbl_43.col_304 between 'HpsjfuSReCwBoh' and 'fta' or not( tbl_43.col_304 between 'MFWmuOsoyDv' and 'TSeMYpDXnFIyp' ) order by col_304) x;"
]
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -8095,5 +8095,68 @@
"Warning": null
}
]
},
{
"Name": "TestIndexMergeIssue52947",
"Cases": [
{
"SQL": "explain format = 'brief' select min(col_304) from (select /*+ use_index_merge( tbl_43 ) */ * from tbl_43 where not( tbl_43.col_304 between 'YEpfYfPVvhMlHGHSMKm' and 'PE' ) or tbl_43.col_304 in ( 'LUBGzGMA' ) and tbl_43.col_304 between 'HpsjfuSReCwBoh' and 'fta' or not( tbl_43.col_304 between 'MFWmuOsoyDv' and 'TSeMYpDXnFIyp' ) order by col_304) x;",
"Plan": [
"StreamAgg 1.00 root funcs:min(test.tbl_43.col_304)->Column#2",
"└─TopN 1.00 root test.tbl_43.col_304, offset:0, count:1",
" └─IndexMerge 1.00 root type: union",
" ├─Selection(Build) 1104.45 cop[tikv] lt(test.tbl_43.col_304, \"YEpfYfPVvhMlHGHSMKm\")",
" │ └─IndexRangeScan 3323.33 cop[tikv] table:tbl_43, index:idx_259(col_304) range:[-inf,\"YEpfY\"], keep order:false, stats:pseudo",
" ├─Selection(Build) 1111.11 cop[tikv] gt(test.tbl_43.col_304, \"PE\")",
" │ └─IndexRangeScan 3333.33 cop[tikv] table:tbl_43, index:idx_259(col_304) range:(\"PE\",+inf], keep order:false, stats:pseudo",
" ├─Selection(Build) 1.00 cop[tikv] 1",
" │ └─TableRangeScan 1.00 cop[tikv] table:tbl_43 range:[\"LUBGzGMA\",\"LUBGzGMA\"], keep order:false, stats:pseudo",
" ├─Selection(Build) 1104.45 cop[tikv] lt(test.tbl_43.col_304, \"MFWmuOsoyDv\")",
" │ └─IndexRangeScan 3323.33 cop[tikv] table:tbl_43, index:idx_259(col_304) range:[-inf,\"MFWmu\"], keep order:false, stats:pseudo",
" ├─Selection(Build) 1111.11 cop[tikv] gt(test.tbl_43.col_304, \"TSeMYpDXnFIyp\")",
" │ └─IndexRangeScan 3333.33 cop[tikv] table:tbl_43, index:idx_259(col_304) range:[\"TSeMY\",+inf], keep order:false, stats:pseudo",
" └─TopN(Probe) 1.00 cop[tikv] test.tbl_43.col_304, offset:0, count:1",
" └─Selection 7984.01 cop[tikv] or(or(lt(test.tbl_43.col_304, \"YEpfYfPVvhMlHGHSMKm\"), gt(test.tbl_43.col_304, \"PE\")), or(and(eq(test.tbl_43.col_304, \"LUBGzGMA\"), 1), or(lt(test.tbl_43.col_304, \"MFWmuOsoyDv\"), gt(test.tbl_43.col_304, \"TSeMYpDXnFIyp\"))))",
" └─TableRowIDScan 9990.00 cop[tikv] table:tbl_43 keep order:false, stats:pseudo"
],
"Warning": null
},
{
"SQL": "select min(col_304) from (select /*+ use_index_merge( tbl_43 ) */ * from tbl_43 where not( tbl_43.col_304 between 'YEpfYfPVvhMlHGHSMKm' and 'PE' ) or tbl_43.col_304 in ( 'LUBGzGMA' ) and tbl_43.col_304 between 'HpsjfuSReCwBoh' and 'fta' or not( tbl_43.col_304 between 'MFWmuOsoyDv' and 'TSeMYpDXnFIyp' ) order by col_304) x;",
"Plan": [
"BCmuENPHzSOIMJLPB\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
],
"Warning": null
},
{
"SQL": "explain format = 'brief' select max(col_304) from (select /*+ use_index_merge( tbl_43 ) */ * from tbl_43 where not( tbl_43.col_304 between 'YEpfYfPVvhMlHGHSMKm' and 'PE' ) or tbl_43.col_304 in ( 'LUBGzGMA' ) and tbl_43.col_304 between 'HpsjfuSReCwBoh' and 'fta' or not( tbl_43.col_304 between 'MFWmuOsoyDv' and 'TSeMYpDXnFIyp' ) order by col_304) x;",
"Plan": [
"StreamAgg 1.00 root funcs:max(test.tbl_43.col_304)->Column#2",
"└─TopN 1.00 root test.tbl_43.col_304:desc, offset:0, count:1",
" └─IndexMerge 1.00 root type: union",
" ├─Selection(Build) 1104.45 cop[tikv] lt(test.tbl_43.col_304, \"YEpfYfPVvhMlHGHSMKm\")",
" │ └─IndexRangeScan 3323.33 cop[tikv] table:tbl_43, index:idx_259(col_304) range:[-inf,\"YEpfY\"], keep order:false, stats:pseudo",
" ├─Selection(Build) 1111.11 cop[tikv] gt(test.tbl_43.col_304, \"PE\")",
" │ └─IndexRangeScan 3333.33 cop[tikv] table:tbl_43, index:idx_259(col_304) range:(\"PE\",+inf], keep order:false, stats:pseudo",
" ├─Selection(Build) 1.00 cop[tikv] 1",
" │ └─TableRangeScan 1.00 cop[tikv] table:tbl_43 range:[\"LUBGzGMA\",\"LUBGzGMA\"], keep order:false, stats:pseudo",
" ├─Selection(Build) 1104.45 cop[tikv] lt(test.tbl_43.col_304, \"MFWmuOsoyDv\")",
" │ └─IndexRangeScan 3323.33 cop[tikv] table:tbl_43, index:idx_259(col_304) range:[-inf,\"MFWmu\"], keep order:false, stats:pseudo",
" ├─Selection(Build) 1111.11 cop[tikv] gt(test.tbl_43.col_304, \"TSeMYpDXnFIyp\")",
" │ └─IndexRangeScan 3333.33 cop[tikv] table:tbl_43, index:idx_259(col_304) range:[\"TSeMY\",+inf], keep order:false, stats:pseudo",
" └─TopN(Probe) 1.00 cop[tikv] test.tbl_43.col_304:desc, offset:0, count:1",
" └─Selection 7984.01 cop[tikv] or(or(lt(test.tbl_43.col_304, \"YEpfYfPVvhMlHGHSMKm\"), gt(test.tbl_43.col_304, \"PE\")), or(and(eq(test.tbl_43.col_304, \"LUBGzGMA\"), 1), or(lt(test.tbl_43.col_304, \"MFWmuOsoyDv\"), gt(test.tbl_43.col_304, \"TSeMYpDXnFIyp\"))))",
" └─TableRowIDScan 9990.00 cop[tikv] table:tbl_43 keep order:false, stats:pseudo"
],
"Warning": null
},
{
"SQL": "select max(col_304) from (select /*+ use_index_merge( tbl_43 ) */ * from tbl_43 where not( tbl_43.col_304 between 'YEpfYfPVvhMlHGHSMKm' and 'PE' ) or tbl_43.col_304 in ( 'LUBGzGMA' ) and tbl_43.col_304 between 'HpsjfuSReCwBoh' and 'fta' or not( tbl_43.col_304 between 'MFWmuOsoyDv' and 'TSeMYpDXnFIyp' ) order by col_304) x;",
"Plan": [
"swLIoWa\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
],
"Warning": null
}
]
}
]
12 changes: 9 additions & 3 deletions pkg/planner/core/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -877,10 +877,12 @@ func (p *PhysicalLimit) attach2Task(tasks ...task) task {
} else if !cop.idxMergeIsIntersection {
// We only support push part of the order prop down to index merge build case.
if len(cop.rootTaskConds) == 0 {
if cop.indexPlanFinished {
// when the index plan is finished, sink the limit to the index merge table side.
// For double read which requires order being kept, the limit cannot be pushed down to the table side,
// because handles would be reordered before being sent to table scan.
if cop.indexPlanFinished && !cop.keepOrder {
// when the index plan is finished and index plan is not ordered, sink the limit to the index merge table side.
suspendLimitAboveTablePlan()
} else {
} else if !cop.indexPlanFinished {
// cop.indexPlanFinished = false indicates the table side is a pure table-scan, sink the limit to the index merge index side.
newCount := p.Offset + p.Count
limitChildren := make([]PhysicalPlan, 0, len(cop.idxMergePartPlans))
Expand All @@ -895,6 +897,10 @@ func (p *PhysicalLimit) attach2Task(tasks ...task) task {
cop.idxMergePartPlans = limitChildren
t = cop.convertToRootTask(p.SCtx())
sunk = p.sinkIntoIndexMerge(t)
} else {
// when there are some limitations, just sink the limit upon the index merge reader.
t = cop.convertToRootTask(p.SCtx())
sunk = p.sinkIntoIndexMerge(t)
}
} else {
// when there are some root conditions, just sink the limit upon the index merge reader.
Expand Down

0 comments on commit ec938d7

Please sign in to comment.