Skip to content
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: fix index merge shouldn't push partial limit down when index plans are keep ordered #52979

Merged
merged 3 commits into from
May 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 9 additions & 3 deletions pkg/planner/core/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -674,10 +674,12 @@ func (p *PhysicalLimit) Attach2Task(tasks ...base.Task) base.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([]base.PhysicalPlan, 0, len(cop.idxMergePartPlans))
Expand All @@ -692,6 +694,10 @@ func (p *PhysicalLimit) Attach2Task(tasks ...base.Task) base.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
Original file line number Diff line number Diff line change
Expand Up @@ -3728,3 +3728,44 @@ IndexMerge_20 1.00 root type: union, limit embedded(offset:0, count:1)
└─TableRowIDScan_13(Probe) 1.00 cop[tikv] table:t3 keep order:false, stats:pseudo
show warnings;
Level Code Message
CREATE TABLE `tbl_43` (
`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;
insert into tbl_43 values("BCmuENPHzSOIMJLPB"),("LDOdXZYpOR"),("R"),("TloTqcHhdgpwvMsSoJ"),("UajN"),("mAwLZbiyq"),("swLIoWa");
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;
id estRows task access object operator info
StreamAgg 1.00 root funcs:min(planner__core__casetest__physicalplantest__physical_plan.tbl_43.col_304)->Column#2
└─Limit 1.00 root offset:0, count:1
└─IndexMerge 1.00 root type: union
├─Selection(Build) 0.00 cop[tikv] 1
│ └─TableRangeScan 0.00 cop[tikv] table:tbl_43 range:["LUBGzGMA","LUBGzGMA"], keep order:true, stats:pseudo
├─IndexRangeScan(Build) 0.42 cop[tikv] table:tbl_43, index:idx_261(col_304) range:[-inf,"YEpfYfPVvhMlHGHSMKm"), keep order:true, stats:pseudo
├─IndexRangeScan(Build) 0.42 cop[tikv] table:tbl_43, index:idx_262(col_304) range:("PE",+inf], keep order:true, stats:pseudo
├─TableRangeScan(Build) 0.42 cop[tikv] table:tbl_43 range:[-inf,"MFWmuOsoyDv"), keep order:true, stats:pseudo
├─TableRangeScan(Build) 0.42 cop[tikv] table:tbl_43 range:("TSeMYpDXnFIyp",+inf], keep order:true, stats:pseudo
└─Selection(Probe) 1.00 cop[tikv] or(or(lt(planner__core__casetest__physicalplantest__physical_plan.tbl_43.col_304, "YEpfYfPVvhMlHGHSMKm"), gt(planner__core__casetest__physicalplantest__physical_plan.tbl_43.col_304, "PE")), or(and(eq(planner__core__casetest__physicalplantest__physical_plan.tbl_43.col_304, "LUBGzGMA"), 1), or(lt(planner__core__casetest__physicalplantest__physical_plan.tbl_43.col_304, "MFWmuOsoyDv"), gt(planner__core__casetest__physicalplantest__physical_plan.tbl_43.col_304, "TSeMYpDXnFIyp"))))
└─TableRowIDScan 1.25 cop[tikv] table:tbl_43 keep order:false, stats:pseudo
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;
min(col_304)
BCmuENPHzSOIMJLPB
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;
id estRows task access object operator info
StreamAgg 1.00 root funcs:max(planner__core__casetest__physicalplantest__physical_plan.tbl_43.col_304)->Column#2
└─Limit 1.00 root offset:0, count:1
└─IndexMerge 1.00 root type: union
├─Selection(Build) 0.00 cop[tikv] 1
│ └─TableRangeScan 0.00 cop[tikv] table:tbl_43 range:["LUBGzGMA","LUBGzGMA"], keep order:true, desc, stats:pseudo
├─IndexRangeScan(Build) 0.42 cop[tikv] table:tbl_43, index:idx_261(col_304) range:[-inf,"YEpfYfPVvhMlHGHSMKm"), keep order:true, desc, stats:pseudo
├─IndexRangeScan(Build) 0.42 cop[tikv] table:tbl_43, index:idx_262(col_304) range:("PE",+inf], keep order:true, desc, stats:pseudo
├─TableRangeScan(Build) 0.42 cop[tikv] table:tbl_43 range:[-inf,"MFWmuOsoyDv"), keep order:true, desc, stats:pseudo
├─TableRangeScan(Build) 0.42 cop[tikv] table:tbl_43 range:("TSeMYpDXnFIyp",+inf], keep order:true, desc, stats:pseudo
└─Selection(Probe) 1.00 cop[tikv] or(or(lt(planner__core__casetest__physicalplantest__physical_plan.tbl_43.col_304, "YEpfYfPVvhMlHGHSMKm"), gt(planner__core__casetest__physicalplantest__physical_plan.tbl_43.col_304, "PE")), or(and(eq(planner__core__casetest__physicalplantest__physical_plan.tbl_43.col_304, "LUBGzGMA"), 1), or(lt(planner__core__casetest__physicalplantest__physical_plan.tbl_43.col_304, "MFWmuOsoyDv"), gt(planner__core__casetest__physicalplantest__physical_plan.tbl_43.col_304, "TSeMYpDXnFIyp"))))
└─TableRowIDScan 1.25 cop[tikv] table:tbl_43 keep order:false, stats:pseudo
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;
max(col_304)
swLIoWa
Original file line number Diff line number Diff line change
Expand Up @@ -992,3 +992,17 @@ set tidb_cost_model_version=DEFAULT;
explain select /*+ USE_INDEX_MERGE(t3, aid_c1, aid_c2) */ * from t3 where (aid = 1 and c1='aaa') or (aid = 1 and c2='bbb') limit 1;
show warnings;

# TestIndexMergeIssue52947
CREATE TABLE `tbl_43` (
`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;
insert into tbl_43 values("BCmuENPHzSOIMJLPB"),("LDOdXZYpOR"),("R"),("TloTqcHhdgpwvMsSoJ"),("UajN"),("mAwLZbiyq"),("swLIoWa");
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;