Skip to content

Commit 032726c

Browse files
authored
planner: refactor tryToGetIndexJoin (pingcap#45617) (pingcap#45841)
ref pingcap#45520
1 parent 16e0c28 commit 032726c

File tree

1 file changed

+79
-89
lines changed

1 file changed

+79
-89
lines changed

planner/core/exhaust_physical_plans.go

Lines changed: 79 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1976,77 +1976,39 @@ func (p *LogicalJoin) preferAny(joinFlags ...uint) bool {
19761976
return false
19771977
}
19781978

1979-
// satisfyIndexJoinHint returns whether this join plan can satisfy current index join hints.
1980-
func (p *LogicalJoin) satisfyIndexJoinHint(join PhysicalPlan) bool {
1981-
const left, right = 0, 1
1982-
const indexJoin, indexHashJoin, indexMergeJoin = 0, 1, 2
1983-
var innerSide, innerIdx, joinMethod int
1979+
const (
1980+
joinLeft = 0
1981+
joinRight = 1
1982+
indexJoinMethod = 0
1983+
indexHashJoinMethod = 1
1984+
indexMergeJoinMethod = 2
1985+
)
1986+
1987+
func (*LogicalJoin) getIndexJoinSideAndMethod(join PhysicalPlan) (innerSide, joinMethod int, ok bool) {
1988+
var innerIdx int
19841989
switch ij := join.(type) {
19851990
case *PhysicalIndexJoin:
19861991
innerIdx = ij.getInnerChildIdx()
1987-
joinMethod = indexJoin
1992+
joinMethod = indexJoinMethod
19881993
case *PhysicalIndexHashJoin:
19891994
innerIdx = ij.getInnerChildIdx()
1990-
joinMethod = indexHashJoin
1995+
joinMethod = indexHashJoinMethod
19911996
case *PhysicalIndexMergeJoin:
19921997
innerIdx = ij.getInnerChildIdx()
1993-
joinMethod = indexMergeJoin
1998+
joinMethod = indexMergeJoinMethod
19941999
default:
1995-
return false
2000+
return 0, 0, false
19962001
}
1997-
innerSide = left
2002+
ok = true
2003+
innerSide = joinLeft
19982004
if innerIdx == 1 {
1999-
innerSide = right
2005+
innerSide = joinRight
20002006
}
2001-
2002-
if (p.preferAny(preferLeftAsINLJInner) && innerSide == left && joinMethod == indexJoin) ||
2003-
(p.preferAny(preferRightAsINLJInner) && innerSide == right && joinMethod == indexJoin) ||
2004-
(p.preferAny(preferLeftAsINLHJInner) && innerSide == left && joinMethod == indexHashJoin) ||
2005-
(p.preferAny(preferRightAsINLHJInner) && innerSide == right && joinMethod == indexHashJoin) ||
2006-
(p.preferAny(preferLeftAsINLMJInner) && innerSide == left && joinMethod == indexMergeJoin) ||
2007-
(p.preferAny(preferRightAsINLMJInner) && innerSide == right && joinMethod == indexMergeJoin) {
2008-
return true
2009-
}
2010-
return false
2007+
return
20112008
}
20122009

2013-
// tryToGetIndexJoin will get index join by hints. If we can generate a valid index join by hint, the second return value
2014-
// will be true, which means we force to choose this index join. Otherwise we will select a join algorithm with min-cost.
2010+
// tryToGetIndexJoin returns all available index join plans, and the second returned value indicates whether this plan is enforced by hints.
20152011
func (p *LogicalJoin) tryToGetIndexJoin(prop *property.PhysicalProperty) (indexJoins []PhysicalPlan, canForced bool) {
2016-
forceLeftOuter := p.preferAny(preferRightAsINLJInner, preferRightAsINLHJInner, preferRightAsINLMJInner) // left as outer == right as inner
2017-
forceRightOuter := p.preferAny(preferLeftAsINLJInner, preferLeftAsINLHJInner, preferLeftAsINLMJInner) // right as outer == left as inner
2018-
needForced := forceLeftOuter || forceRightOuter
2019-
2020-
defer func() {
2021-
// Print warning message if any hints cannot work.
2022-
// If the required property is not empty, we will enforce it and try the hint again.
2023-
// So we only need to generate warning message when the property is empty.
2024-
if !canForced && needForced && prop.IsSortItemEmpty() {
2025-
// Construct warning message prefix.
2026-
var indexJoinTables, indexHashJoinTables, indexMergeJoinTables []hintTableInfo
2027-
if p.hintInfo != nil {
2028-
t := p.hintInfo.indexNestedLoopJoinTables
2029-
indexJoinTables, indexHashJoinTables, indexMergeJoinTables = t.inljTables, t.inlhjTables, t.inlmjTables
2030-
}
2031-
var errMsg string
2032-
switch {
2033-
case p.preferAny(preferLeftAsINLJInner, preferRightAsINLJInner): // prefer index join
2034-
errMsg = fmt.Sprintf("Optimizer Hint %s or %s is inapplicable", restore2JoinHint(HintINLJ, indexJoinTables), restore2JoinHint(TiDBIndexNestedLoopJoin, indexJoinTables))
2035-
case p.preferAny(preferLeftAsINLHJInner, preferRightAsINLHJInner): // prefer index hash join
2036-
errMsg = fmt.Sprintf("Optimizer Hint %s is inapplicable", restore2JoinHint(HintINLHJ, indexHashJoinTables))
2037-
case p.preferAny(preferLeftAsINLMJInner, preferRightAsINLMJInner): // prefer index merge join
2038-
errMsg = fmt.Sprintf("Optimizer Hint %s is inapplicable", restore2JoinHint(HintINLMJ, indexMergeJoinTables))
2039-
}
2040-
// Append inapplicable reason.
2041-
if len(p.EqualConditions) == 0 {
2042-
errMsg += " without column equal ON condition"
2043-
}
2044-
// Generate warning message to client.
2045-
warning := ErrInternal.GenWithStack(errMsg)
2046-
p.ctx.GetSessionVars().StmtCtx.AppendWarning(warning)
2047-
}
2048-
}()
2049-
20502012
// supportLeftOuter and supportRightOuter indicates whether this type of join
20512013
// supports the left side or right side to be the outer side.
20522014
var supportLeftOuter, supportRightOuter bool
@@ -2058,47 +2020,75 @@ func (p *LogicalJoin) tryToGetIndexJoin(prop *property.PhysicalProperty) (indexJ
20582020
case InnerJoin:
20592021
supportLeftOuter, supportRightOuter = true, true
20602022
}
2061-
2062-
var allLeftOuterJoins, allRightOuterJoins, forcedLeftOuterJoins, forcedRightOuterJoins []PhysicalPlan
2023+
candidates := make([]PhysicalPlan, 0, 2)
20632024
if supportLeftOuter {
2064-
allLeftOuterJoins = p.getIndexJoinByOuterIdx(prop, 0)
2065-
forcedLeftOuterJoins = make([]PhysicalPlan, 0, len(allLeftOuterJoins))
2066-
for _, j := range allLeftOuterJoins {
2067-
if p.satisfyIndexJoinHint(j) {
2068-
forcedLeftOuterJoins = append(forcedLeftOuterJoins, j)
2069-
}
2025+
candidates = append(candidates, p.getIndexJoinByOuterIdx(prop, 0)...)
2026+
}
2027+
if supportRightOuter {
2028+
candidates = append(candidates, p.getIndexJoinByOuterIdx(prop, 1)...)
2029+
}
2030+
2031+
// handle hints and variables about index join.
2032+
// the priority is: force hints like TIDB_INLJ > filter hints like NO_INDEX_JOIN > variables.
2033+
candidates, canForced = p.handleForceIndexJoinHints(prop, candidates)
2034+
if canForced {
2035+
return candidates, canForced
2036+
}
2037+
return filterIndexJoinBySessionVars(p.SCtx(), candidates), false
2038+
}
2039+
2040+
// handleForceIndexJoinHints handles the force index join hints and returns all plans that can satisfy the hints.
2041+
func (p *LogicalJoin) handleForceIndexJoinHints(prop *property.PhysicalProperty, candidates []PhysicalPlan) (indexJoins []PhysicalPlan, canForced bool) {
2042+
if !p.preferAny(preferRightAsINLJInner, preferRightAsINLHJInner, preferRightAsINLMJInner,
2043+
preferLeftAsINLJInner, preferLeftAsINLHJInner, preferLeftAsINLMJInner) {
2044+
return candidates, false // no force index join hints
2045+
}
2046+
forced := make([]PhysicalPlan, 0, len(candidates))
2047+
for _, candidate := range candidates {
2048+
innerSide, joinMethod, ok := p.getIndexJoinSideAndMethod(candidate)
2049+
if !ok {
2050+
continue
20702051
}
2071-
switch {
2072-
case len(forcedLeftOuterJoins) == 0 && !supportRightOuter:
2073-
return filterIndexJoinBySessionVars(p.ctx, allLeftOuterJoins), false
2074-
case len(forcedLeftOuterJoins) != 0 && (!supportRightOuter || (forceLeftOuter && !forceRightOuter)):
2075-
return forcedLeftOuterJoins, true
2052+
if (p.preferAny(preferLeftAsINLJInner) && innerSide == joinLeft && joinMethod == indexJoinMethod) ||
2053+
(p.preferAny(preferRightAsINLJInner) && innerSide == joinRight && joinMethod == indexJoinMethod) ||
2054+
(p.preferAny(preferLeftAsINLHJInner) && innerSide == joinLeft && joinMethod == indexHashJoinMethod) ||
2055+
(p.preferAny(preferRightAsINLHJInner) && innerSide == joinRight && joinMethod == indexHashJoinMethod) ||
2056+
(p.preferAny(preferLeftAsINLMJInner) && innerSide == joinLeft && joinMethod == indexMergeJoinMethod) ||
2057+
(p.preferAny(preferRightAsINLMJInner) && innerSide == joinRight && joinMethod == indexMergeJoinMethod) {
2058+
forced = append(forced, candidate)
20762059
}
20772060
}
20782061

2079-
if supportRightOuter {
2080-
allRightOuterJoins = p.getIndexJoinByOuterIdx(prop, 1)
2081-
forcedRightOuterJoins = make([]PhysicalPlan, 0, len(allRightOuterJoins))
2082-
for _, j := range allRightOuterJoins {
2083-
if p.satisfyIndexJoinHint(j) {
2084-
forcedRightOuterJoins = append(forcedRightOuterJoins, j)
2085-
}
2062+
if len(forced) > 0 {
2063+
return forced, true
2064+
}
2065+
// Cannot find any valid index join plan with these force hints.
2066+
// Print warning message if any hints cannot work.
2067+
// If the required property is not empty, we will enforce it and try the hint again.
2068+
// So we only need to generate warning message when the property is empty.
2069+
if prop.IsSortItemEmpty() {
2070+
var indexJoinTables, indexHashJoinTables, indexMergeJoinTables []hintTableInfo
2071+
if p.hintInfo != nil {
2072+
t := p.hintInfo.indexNestedLoopJoinTables
2073+
indexJoinTables, indexHashJoinTables, indexMergeJoinTables = t.inljTables, t.inlhjTables, t.inlmjTables
20862074
}
2075+
var errMsg string
20872076
switch {
2088-
case len(forcedRightOuterJoins) == 0 && !supportLeftOuter:
2089-
return filterIndexJoinBySessionVars(p.ctx, allRightOuterJoins), false
2090-
case len(forcedRightOuterJoins) != 0 && (!supportLeftOuter || (forceRightOuter && !forceLeftOuter)):
2091-
return forcedRightOuterJoins, true
2077+
case p.preferAny(preferLeftAsINLJInner, preferRightAsINLJInner): // prefer index join
2078+
errMsg = fmt.Sprintf("Optimizer Hint %s or %s is inapplicable", restore2JoinHint(HintINLJ, indexJoinTables), restore2JoinHint(TiDBIndexNestedLoopJoin, indexJoinTables))
2079+
case p.preferAny(preferLeftAsINLHJInner, preferRightAsINLHJInner): // prefer index hash join
2080+
errMsg = fmt.Sprintf("Optimizer Hint %s is inapplicable", restore2JoinHint(HintINLHJ, indexHashJoinTables))
2081+
case p.preferAny(preferLeftAsINLMJInner, preferRightAsINLMJInner): // prefer index merge join
2082+
errMsg = fmt.Sprintf("Optimizer Hint %s is inapplicable", restore2JoinHint(HintINLMJ, indexMergeJoinTables))
20922083
}
2084+
// Append inapplicable reason.
2085+
if len(p.EqualConditions) == 0 {
2086+
errMsg += " without column equal ON condition"
2087+
}
2088+
// Generate warning message to client.
2089+
p.SCtx().GetSessionVars().StmtCtx.AppendWarning(ErrInternal.GenWithStack(errMsg))
20932090
}
2094-
2095-
canForceLeft := len(forcedLeftOuterJoins) != 0 && forceLeftOuter
2096-
canForceRight := len(forcedRightOuterJoins) != 0 && forceRightOuter
2097-
canForced = canForceLeft || canForceRight
2098-
if canForced {
2099-
return append(forcedLeftOuterJoins, forcedRightOuterJoins...), true
2100-
}
2101-
return filterIndexJoinBySessionVars(p.ctx, append(allLeftOuterJoins, allRightOuterJoins...)), false
2091+
return candidates, false
21022092
}
21032093

21042094
func checkChildFitBC(p Plan) bool {

0 commit comments

Comments
 (0)