Skip to content

Commit 325a8b8

Browse files
authored
planner: refactor tryToGetIndexJoin (pingcap#45617) (pingcap#45840)
ref pingcap#45520
1 parent 0bbaa8a commit 325a8b8

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
@@ -1873,77 +1873,39 @@ func (p *LogicalJoin) preferAny(joinFlags ...uint) bool {
18731873
return false
18741874
}
18751875

1876-
// satisfyIndexJoinHint returns whether this join plan can satisfy current index join hints.
1877-
func (p *LogicalJoin) satisfyIndexJoinHint(join PhysicalPlan) bool {
1878-
const left, right = 0, 1
1879-
const indexJoin, indexHashJoin, indexMergeJoin = 0, 1, 2
1880-
var innerSide, innerIdx, joinMethod int
1876+
const (
1877+
joinLeft = 0
1878+
joinRight = 1
1879+
indexJoinMethod = 0
1880+
indexHashJoinMethod = 1
1881+
indexMergeJoinMethod = 2
1882+
)
1883+
1884+
func (*LogicalJoin) getIndexJoinSideAndMethod(join PhysicalPlan) (innerSide, joinMethod int, ok bool) {
1885+
var innerIdx int
18811886
switch ij := join.(type) {
18821887
case *PhysicalIndexJoin:
18831888
innerIdx = ij.getInnerChildIdx()
1884-
joinMethod = indexJoin
1889+
joinMethod = indexJoinMethod
18851890
case *PhysicalIndexHashJoin:
18861891
innerIdx = ij.getInnerChildIdx()
1887-
joinMethod = indexHashJoin
1892+
joinMethod = indexHashJoinMethod
18881893
case *PhysicalIndexMergeJoin:
18891894
innerIdx = ij.getInnerChildIdx()
1890-
joinMethod = indexMergeJoin
1895+
joinMethod = indexMergeJoinMethod
18911896
default:
1892-
return false
1897+
return 0, 0, false
18931898
}
1894-
innerSide = left
1899+
ok = true
1900+
innerSide = joinLeft
18951901
if innerIdx == 1 {
1896-
innerSide = right
1902+
innerSide = joinRight
18971903
}
1898-
1899-
if (p.preferAny(preferLeftAsINLJInner) && innerSide == left && joinMethod == indexJoin) ||
1900-
(p.preferAny(preferRightAsINLJInner) && innerSide == right && joinMethod == indexJoin) ||
1901-
(p.preferAny(preferLeftAsINLHJInner) && innerSide == left && joinMethod == indexHashJoin) ||
1902-
(p.preferAny(preferRightAsINLHJInner) && innerSide == right && joinMethod == indexHashJoin) ||
1903-
(p.preferAny(preferLeftAsINLMJInner) && innerSide == left && joinMethod == indexMergeJoin) ||
1904-
(p.preferAny(preferRightAsINLMJInner) && innerSide == right && joinMethod == indexMergeJoin) {
1905-
return true
1906-
}
1907-
return false
1904+
return
19081905
}
19091906

1910-
// tryToGetIndexJoin will get index join by hints. If we can generate a valid index join by hint, the second return value
1911-
// will be true, which means we force to choose this index join. Otherwise we will select a join algorithm with min-cost.
1907+
// tryToGetIndexJoin returns all available index join plans, and the second returned value indicates whether this plan is enforced by hints.
19121908
func (p *LogicalJoin) tryToGetIndexJoin(prop *property.PhysicalProperty) (indexJoins []PhysicalPlan, canForced bool) {
1913-
forceLeftOuter := p.preferAny(preferRightAsINLJInner, preferRightAsINLHJInner, preferRightAsINLMJInner) // left as outer == right as inner
1914-
forceRightOuter := p.preferAny(preferLeftAsINLJInner, preferLeftAsINLHJInner, preferLeftAsINLMJInner) // right as outer == left as inner
1915-
needForced := forceLeftOuter || forceRightOuter
1916-
1917-
defer func() {
1918-
// Print warning message if any hints cannot work.
1919-
// If the required property is not empty, we will enforce it and try the hint again.
1920-
// So we only need to generate warning message when the property is empty.
1921-
if !canForced && needForced && prop.IsSortItemEmpty() {
1922-
// Construct warning message prefix.
1923-
var indexJoinTables, indexHashJoinTables, indexMergeJoinTables []hintTableInfo
1924-
if p.hintInfo != nil {
1925-
t := p.hintInfo.indexNestedLoopJoinTables
1926-
indexJoinTables, indexHashJoinTables, indexMergeJoinTables = t.inljTables, t.inlhjTables, t.inlmjTables
1927-
}
1928-
var errMsg string
1929-
switch {
1930-
case p.preferAny(preferLeftAsINLJInner, preferRightAsINLJInner): // prefer index join
1931-
errMsg = fmt.Sprintf("Optimizer Hint %s or %s is inapplicable", restore2JoinHint(HintINLJ, indexJoinTables), restore2JoinHint(TiDBIndexNestedLoopJoin, indexJoinTables))
1932-
case p.preferAny(preferLeftAsINLHJInner, preferRightAsINLHJInner): // prefer index hash join
1933-
errMsg = fmt.Sprintf("Optimizer Hint %s is inapplicable", restore2JoinHint(HintINLHJ, indexHashJoinTables))
1934-
case p.preferAny(preferLeftAsINLMJInner, preferRightAsINLMJInner): // prefer index merge join
1935-
errMsg = fmt.Sprintf("Optimizer Hint %s is inapplicable", restore2JoinHint(HintINLMJ, indexMergeJoinTables))
1936-
}
1937-
// Append inapplicable reason.
1938-
if len(p.EqualConditions) == 0 {
1939-
errMsg += " without column equal ON condition"
1940-
}
1941-
// Generate warning message to client.
1942-
warning := ErrInternal.GenWithStack(errMsg)
1943-
p.ctx.GetSessionVars().StmtCtx.AppendWarning(warning)
1944-
}
1945-
}()
1946-
19471909
// supportLeftOuter and supportRightOuter indicates whether this type of join
19481910
// supports the left side or right side to be the outer side.
19491911
var supportLeftOuter, supportRightOuter bool
@@ -1955,47 +1917,75 @@ func (p *LogicalJoin) tryToGetIndexJoin(prop *property.PhysicalProperty) (indexJ
19551917
case InnerJoin:
19561918
supportLeftOuter, supportRightOuter = true, true
19571919
}
1958-
1959-
var allLeftOuterJoins, allRightOuterJoins, forcedLeftOuterJoins, forcedRightOuterJoins []PhysicalPlan
1920+
candidates := make([]PhysicalPlan, 0, 2)
19601921
if supportLeftOuter {
1961-
allLeftOuterJoins = p.getIndexJoinByOuterIdx(prop, 0)
1962-
forcedLeftOuterJoins = make([]PhysicalPlan, 0, len(allLeftOuterJoins))
1963-
for _, j := range allLeftOuterJoins {
1964-
if p.satisfyIndexJoinHint(j) {
1965-
forcedLeftOuterJoins = append(forcedLeftOuterJoins, j)
1966-
}
1922+
candidates = append(candidates, p.getIndexJoinByOuterIdx(prop, 0)...)
1923+
}
1924+
if supportRightOuter {
1925+
candidates = append(candidates, p.getIndexJoinByOuterIdx(prop, 1)...)
1926+
}
1927+
1928+
// handle hints and variables about index join.
1929+
// the priority is: force hints like TIDB_INLJ > filter hints like NO_INDEX_JOIN > variables.
1930+
candidates, canForced = p.handleForceIndexJoinHints(prop, candidates)
1931+
if canForced {
1932+
return candidates, canForced
1933+
}
1934+
return filterIndexJoinBySessionVars(p.SCtx(), candidates), false
1935+
}
1936+
1937+
// handleForceIndexJoinHints handles the force index join hints and returns all plans that can satisfy the hints.
1938+
func (p *LogicalJoin) handleForceIndexJoinHints(prop *property.PhysicalProperty, candidates []PhysicalPlan) (indexJoins []PhysicalPlan, canForced bool) {
1939+
if !p.preferAny(preferRightAsINLJInner, preferRightAsINLHJInner, preferRightAsINLMJInner,
1940+
preferLeftAsINLJInner, preferLeftAsINLHJInner, preferLeftAsINLMJInner) {
1941+
return candidates, false // no force index join hints
1942+
}
1943+
forced := make([]PhysicalPlan, 0, len(candidates))
1944+
for _, candidate := range candidates {
1945+
innerSide, joinMethod, ok := p.getIndexJoinSideAndMethod(candidate)
1946+
if !ok {
1947+
continue
19671948
}
1968-
switch {
1969-
case len(forcedLeftOuterJoins) == 0 && !supportRightOuter:
1970-
return filterIndexJoinBySessionVars(p.ctx, allLeftOuterJoins), false
1971-
case len(forcedLeftOuterJoins) != 0 && (!supportRightOuter || (forceLeftOuter && !forceRightOuter)):
1972-
return forcedLeftOuterJoins, true
1949+
if (p.preferAny(preferLeftAsINLJInner) && innerSide == joinLeft && joinMethod == indexJoinMethod) ||
1950+
(p.preferAny(preferRightAsINLJInner) && innerSide == joinRight && joinMethod == indexJoinMethod) ||
1951+
(p.preferAny(preferLeftAsINLHJInner) && innerSide == joinLeft && joinMethod == indexHashJoinMethod) ||
1952+
(p.preferAny(preferRightAsINLHJInner) && innerSide == joinRight && joinMethod == indexHashJoinMethod) ||
1953+
(p.preferAny(preferLeftAsINLMJInner) && innerSide == joinLeft && joinMethod == indexMergeJoinMethod) ||
1954+
(p.preferAny(preferRightAsINLMJInner) && innerSide == joinRight && joinMethod == indexMergeJoinMethod) {
1955+
forced = append(forced, candidate)
19731956
}
19741957
}
19751958

1976-
if supportRightOuter {
1977-
allRightOuterJoins = p.getIndexJoinByOuterIdx(prop, 1)
1978-
forcedRightOuterJoins = make([]PhysicalPlan, 0, len(allRightOuterJoins))
1979-
for _, j := range allRightOuterJoins {
1980-
if p.satisfyIndexJoinHint(j) {
1981-
forcedRightOuterJoins = append(forcedRightOuterJoins, j)
1982-
}
1959+
if len(forced) > 0 {
1960+
return forced, true
1961+
}
1962+
// Cannot find any valid index join plan with these force hints.
1963+
// Print warning message if any hints cannot work.
1964+
// If the required property is not empty, we will enforce it and try the hint again.
1965+
// So we only need to generate warning message when the property is empty.
1966+
if prop.IsSortItemEmpty() {
1967+
var indexJoinTables, indexHashJoinTables, indexMergeJoinTables []hintTableInfo
1968+
if p.hintInfo != nil {
1969+
t := p.hintInfo.indexNestedLoopJoinTables
1970+
indexJoinTables, indexHashJoinTables, indexMergeJoinTables = t.inljTables, t.inlhjTables, t.inlmjTables
19831971
}
1972+
var errMsg string
19841973
switch {
1985-
case len(forcedRightOuterJoins) == 0 && !supportLeftOuter:
1986-
return filterIndexJoinBySessionVars(p.ctx, allRightOuterJoins), false
1987-
case len(forcedRightOuterJoins) != 0 && (!supportLeftOuter || (forceRightOuter && !forceLeftOuter)):
1988-
return forcedRightOuterJoins, true
1974+
case p.preferAny(preferLeftAsINLJInner, preferRightAsINLJInner): // prefer index join
1975+
errMsg = fmt.Sprintf("Optimizer Hint %s or %s is inapplicable", restore2JoinHint(HintINLJ, indexJoinTables), restore2JoinHint(TiDBIndexNestedLoopJoin, indexJoinTables))
1976+
case p.preferAny(preferLeftAsINLHJInner, preferRightAsINLHJInner): // prefer index hash join
1977+
errMsg = fmt.Sprintf("Optimizer Hint %s is inapplicable", restore2JoinHint(HintINLHJ, indexHashJoinTables))
1978+
case p.preferAny(preferLeftAsINLMJInner, preferRightAsINLMJInner): // prefer index merge join
1979+
errMsg = fmt.Sprintf("Optimizer Hint %s is inapplicable", restore2JoinHint(HintINLMJ, indexMergeJoinTables))
19891980
}
1981+
// Append inapplicable reason.
1982+
if len(p.EqualConditions) == 0 {
1983+
errMsg += " without column equal ON condition"
1984+
}
1985+
// Generate warning message to client.
1986+
p.SCtx().GetSessionVars().StmtCtx.AppendWarning(ErrInternal.GenWithStack(errMsg))
19901987
}
1991-
1992-
canForceLeft := len(forcedLeftOuterJoins) != 0 && forceLeftOuter
1993-
canForceRight := len(forcedRightOuterJoins) != 0 && forceRightOuter
1994-
canForced = canForceLeft || canForceRight
1995-
if canForced {
1996-
return append(forcedLeftOuterJoins, forcedRightOuterJoins...), true
1997-
}
1998-
return filterIndexJoinBySessionVars(p.ctx, append(allLeftOuterJoins, allRightOuterJoins...)), false
1988+
return candidates, false
19991989
}
20001990

20011991
func checkChildFitBC(p Plan) bool {

0 commit comments

Comments
 (0)