From 8ed94320625a5750e8656ae1e281dfb09c0efc57 Mon Sep 17 00:00:00 2001 From: Yuanjia Zhang Date: Fri, 28 Jul 2023 10:52:36 +0800 Subject: [PATCH 1/2] This is an automated cherry-pick of #45617 Signed-off-by: ti-chi-bot --- planner/core/exhaust_physical_plans.go | 129 ++++++++++++++++++------- 1 file changed, 92 insertions(+), 37 deletions(-) diff --git a/planner/core/exhaust_physical_plans.go b/planner/core/exhaust_physical_plans.go index 10d8272de75ea..b81c8847f0bff 100644 --- a/planner/core/exhaust_physical_plans.go +++ b/planner/core/exhaust_physical_plans.go @@ -1976,43 +1976,40 @@ func (p *LogicalJoin) preferAny(joinFlags ...uint) bool { return false } -// satisfyIndexJoinHint returns whether this join plan can satisfy current index join hints. -func (p *LogicalJoin) satisfyIndexJoinHint(join PhysicalPlan) bool { - const left, right = 0, 1 - const indexJoin, indexHashJoin, indexMergeJoin = 0, 1, 2 - var innerSide, innerIdx, joinMethod int +const ( + joinLeft = 0 + joinRight = 1 + indexJoinMethod = 0 + indexHashJoinMethod = 1 + indexMergeJoinMethod = 2 +) + +func (*LogicalJoin) getIndexJoinSideAndMethod(join PhysicalPlan) (innerSide, joinMethod int, ok bool) { + var innerIdx int switch ij := join.(type) { case *PhysicalIndexJoin: innerIdx = ij.getInnerChildIdx() - joinMethod = indexJoin + joinMethod = indexJoinMethod case *PhysicalIndexHashJoin: innerIdx = ij.getInnerChildIdx() - joinMethod = indexHashJoin + joinMethod = indexHashJoinMethod case *PhysicalIndexMergeJoin: innerIdx = ij.getInnerChildIdx() - joinMethod = indexMergeJoin + joinMethod = indexMergeJoinMethod default: - return false + return 0, 0, false } - innerSide = left + ok = true + innerSide = joinLeft if innerIdx == 1 { - innerSide = right - } - - if (p.preferAny(preferLeftAsINLJInner) && innerSide == left && joinMethod == indexJoin) || - (p.preferAny(preferRightAsINLJInner) && innerSide == right && joinMethod == indexJoin) || - (p.preferAny(preferLeftAsINLHJInner) && innerSide == left && joinMethod == indexHashJoin) || - (p.preferAny(preferRightAsINLHJInner) && innerSide == right && joinMethod == indexHashJoin) || - (p.preferAny(preferLeftAsINLMJInner) && innerSide == left && joinMethod == indexMergeJoin) || - (p.preferAny(preferRightAsINLMJInner) && innerSide == right && joinMethod == indexMergeJoin) { - return true + innerSide = joinRight } - return false + return } -// tryToGetIndexJoin will get index join by hints. If we can generate a valid index join by hint, the second return value -// will be true, which means we force to choose this index join. Otherwise we will select a join algorithm with min-cost. +// tryToGetIndexJoin returns all available index join plans, and the second returned value indicates whether this plan is enforced by hints. func (p *LogicalJoin) tryToGetIndexJoin(prop *property.PhysicalProperty) (indexJoins []PhysicalPlan, canForced bool) { +<<<<<<< HEAD forceLeftOuter := p.preferAny(preferRightAsINLJInner, preferRightAsINLHJInner, preferRightAsINLMJInner) // left as outer == right as inner forceRightOuter := p.preferAny(preferLeftAsINLJInner, preferLeftAsINLHJInner, preferLeftAsINLMJInner) // right as outer == left as inner needForced := forceLeftOuter || forceRightOuter @@ -2047,6 +2044,8 @@ func (p *LogicalJoin) tryToGetIndexJoin(prop *property.PhysicalProperty) (indexJ } }() +======= +>>>>>>> 79f00016016 (planner: refactor `tryToGetIndexJoin` (#45617)) // supportLeftOuter and supportRightOuter indicates whether this type of join // supports the left side or right side to be the outer side. var supportLeftOuter, supportRightOuter bool @@ -2058,9 +2057,9 @@ func (p *LogicalJoin) tryToGetIndexJoin(prop *property.PhysicalProperty) (indexJ case InnerJoin: supportLeftOuter, supportRightOuter = true, true } - - var allLeftOuterJoins, allRightOuterJoins, forcedLeftOuterJoins, forcedRightOuterJoins []PhysicalPlan + candidates := make([]PhysicalPlan, 0, 2) if supportLeftOuter { +<<<<<<< HEAD allLeftOuterJoins = p.getIndexJoinByOuterIdx(prop, 0) forcedLeftOuterJoins = make([]PhysicalPlan, 0, len(allLeftOuterJoins)) for _, j := range allLeftOuterJoins { @@ -2074,31 +2073,87 @@ func (p *LogicalJoin) tryToGetIndexJoin(prop *property.PhysicalProperty) (indexJ case len(forcedLeftOuterJoins) != 0 && (!supportRightOuter || (forceLeftOuter && !forceRightOuter)): return forcedLeftOuterJoins, true } +======= + candidates = append(candidates, p.getIndexJoinByOuterIdx(prop, 0)...) +>>>>>>> 79f00016016 (planner: refactor `tryToGetIndexJoin` (#45617)) } - if supportRightOuter { - allRightOuterJoins = p.getIndexJoinByOuterIdx(prop, 1) - forcedRightOuterJoins = make([]PhysicalPlan, 0, len(allRightOuterJoins)) - for _, j := range allRightOuterJoins { - if p.satisfyIndexJoinHint(j) { - forcedRightOuterJoins = append(forcedRightOuterJoins, j) - } + candidates = append(candidates, p.getIndexJoinByOuterIdx(prop, 1)...) + } + + // handle hints and variables about index join. + // the priority is: force hints like TIDB_INLJ > filter hints like NO_INDEX_JOIN > variables. + candidates, canForced = p.handleForceIndexJoinHints(prop, candidates) + if canForced { + return candidates, canForced + } + return filterIndexJoinBySessionVars(p.SCtx(), candidates), false +} + +// handleForceIndexJoinHints handles the force index join hints and returns all plans that can satisfy the hints. +func (p *LogicalJoin) handleForceIndexJoinHints(prop *property.PhysicalProperty, candidates []PhysicalPlan) (indexJoins []PhysicalPlan, canForced bool) { + if !p.preferAny(preferRightAsINLJInner, preferRightAsINLHJInner, preferRightAsINLMJInner, + preferLeftAsINLJInner, preferLeftAsINLHJInner, preferLeftAsINLMJInner) { + return candidates, false // no force index join hints + } + forced := make([]PhysicalPlan, 0, len(candidates)) + for _, candidate := range candidates { + innerSide, joinMethod, ok := p.getIndexJoinSideAndMethod(candidate) + if !ok { + continue } +<<<<<<< HEAD switch { case len(forcedRightOuterJoins) == 0 && !supportLeftOuter: return filterIndexJoinBySessionVars(p.ctx, allRightOuterJoins), false case len(forcedRightOuterJoins) != 0 && (!supportLeftOuter || (forceRightOuter && !forceLeftOuter)): return forcedRightOuterJoins, true +======= + if (p.preferAny(preferLeftAsINLJInner) && innerSide == joinLeft && joinMethod == indexJoinMethod) || + (p.preferAny(preferRightAsINLJInner) && innerSide == joinRight && joinMethod == indexJoinMethod) || + (p.preferAny(preferLeftAsINLHJInner) && innerSide == joinLeft && joinMethod == indexHashJoinMethod) || + (p.preferAny(preferRightAsINLHJInner) && innerSide == joinRight && joinMethod == indexHashJoinMethod) || + (p.preferAny(preferLeftAsINLMJInner) && innerSide == joinLeft && joinMethod == indexMergeJoinMethod) || + (p.preferAny(preferRightAsINLMJInner) && innerSide == joinRight && joinMethod == indexMergeJoinMethod) { + forced = append(forced, candidate) +>>>>>>> 79f00016016 (planner: refactor `tryToGetIndexJoin` (#45617)) } } - canForceLeft := len(forcedLeftOuterJoins) != 0 && forceLeftOuter - canForceRight := len(forcedRightOuterJoins) != 0 && forceRightOuter - canForced = canForceLeft || canForceRight - if canForced { - return append(forcedLeftOuterJoins, forcedRightOuterJoins...), true + if len(forced) > 0 { + return forced, true } +<<<<<<< HEAD return filterIndexJoinBySessionVars(p.ctx, append(allLeftOuterJoins, allRightOuterJoins...)), false +======= + // Cannot find any valid index join plan with these force hints. + // Print warning message if any hints cannot work. + // If the required property is not empty, we will enforce it and try the hint again. + // So we only need to generate warning message when the property is empty. + if prop.IsSortItemEmpty() { + var indexJoinTables, indexHashJoinTables, indexMergeJoinTables []hintTableInfo + if p.hintInfo != nil { + t := p.hintInfo.indexNestedLoopJoinTables + indexJoinTables, indexHashJoinTables, indexMergeJoinTables = t.inljTables, t.inlhjTables, t.inlmjTables + } + var errMsg string + switch { + case p.preferAny(preferLeftAsINLJInner, preferRightAsINLJInner): // prefer index join + errMsg = fmt.Sprintf("Optimizer Hint %s or %s is inapplicable", restore2JoinHint(HintINLJ, indexJoinTables), restore2JoinHint(TiDBIndexNestedLoopJoin, indexJoinTables)) + case p.preferAny(preferLeftAsINLHJInner, preferRightAsINLHJInner): // prefer index hash join + errMsg = fmt.Sprintf("Optimizer Hint %s is inapplicable", restore2JoinHint(HintINLHJ, indexHashJoinTables)) + case p.preferAny(preferLeftAsINLMJInner, preferRightAsINLMJInner): // prefer index merge join + errMsg = fmt.Sprintf("Optimizer Hint %s is inapplicable", restore2JoinHint(HintINLMJ, indexMergeJoinTables)) + } + // Append inapplicable reason. + if len(p.EqualConditions) == 0 { + errMsg += " without column equal ON condition" + } + // Generate warning message to client. + p.SCtx().GetSessionVars().StmtCtx.AppendWarning(ErrInternal.GenWithStack(errMsg)) + } + return candidates, false +>>>>>>> 79f00016016 (planner: refactor `tryToGetIndexJoin` (#45617)) } func checkChildFitBC(p Plan) bool { From 7ea168d6754685a72afd529ca0dc7fc775ca2072 Mon Sep 17 00:00:00 2001 From: qw4990 Date: Mon, 7 Aug 2023 10:59:31 +0800 Subject: [PATCH 2/2] fixup --- planner/core/exhaust_physical_plans.go | 65 -------------------------- 1 file changed, 65 deletions(-) diff --git a/planner/core/exhaust_physical_plans.go b/planner/core/exhaust_physical_plans.go index b81c8847f0bff..6d96d7f78f411 100644 --- a/planner/core/exhaust_physical_plans.go +++ b/planner/core/exhaust_physical_plans.go @@ -2009,43 +2009,6 @@ func (*LogicalJoin) getIndexJoinSideAndMethod(join PhysicalPlan) (innerSide, joi // tryToGetIndexJoin returns all available index join plans, and the second returned value indicates whether this plan is enforced by hints. func (p *LogicalJoin) tryToGetIndexJoin(prop *property.PhysicalProperty) (indexJoins []PhysicalPlan, canForced bool) { -<<<<<<< HEAD - forceLeftOuter := p.preferAny(preferRightAsINLJInner, preferRightAsINLHJInner, preferRightAsINLMJInner) // left as outer == right as inner - forceRightOuter := p.preferAny(preferLeftAsINLJInner, preferLeftAsINLHJInner, preferLeftAsINLMJInner) // right as outer == left as inner - needForced := forceLeftOuter || forceRightOuter - - defer func() { - // Print warning message if any hints cannot work. - // If the required property is not empty, we will enforce it and try the hint again. - // So we only need to generate warning message when the property is empty. - if !canForced && needForced && prop.IsSortItemEmpty() { - // Construct warning message prefix. - var indexJoinTables, indexHashJoinTables, indexMergeJoinTables []hintTableInfo - if p.hintInfo != nil { - t := p.hintInfo.indexNestedLoopJoinTables - indexJoinTables, indexHashJoinTables, indexMergeJoinTables = t.inljTables, t.inlhjTables, t.inlmjTables - } - var errMsg string - switch { - case p.preferAny(preferLeftAsINLJInner, preferRightAsINLJInner): // prefer index join - errMsg = fmt.Sprintf("Optimizer Hint %s or %s is inapplicable", restore2JoinHint(HintINLJ, indexJoinTables), restore2JoinHint(TiDBIndexNestedLoopJoin, indexJoinTables)) - case p.preferAny(preferLeftAsINLHJInner, preferRightAsINLHJInner): // prefer index hash join - errMsg = fmt.Sprintf("Optimizer Hint %s is inapplicable", restore2JoinHint(HintINLHJ, indexHashJoinTables)) - case p.preferAny(preferLeftAsINLMJInner, preferRightAsINLMJInner): // prefer index merge join - errMsg = fmt.Sprintf("Optimizer Hint %s is inapplicable", restore2JoinHint(HintINLMJ, indexMergeJoinTables)) - } - // Append inapplicable reason. - if len(p.EqualConditions) == 0 { - errMsg += " without column equal ON condition" - } - // Generate warning message to client. - warning := ErrInternal.GenWithStack(errMsg) - p.ctx.GetSessionVars().StmtCtx.AppendWarning(warning) - } - }() - -======= ->>>>>>> 79f00016016 (planner: refactor `tryToGetIndexJoin` (#45617)) // supportLeftOuter and supportRightOuter indicates whether this type of join // supports the left side or right side to be the outer side. var supportLeftOuter, supportRightOuter bool @@ -2059,23 +2022,7 @@ func (p *LogicalJoin) tryToGetIndexJoin(prop *property.PhysicalProperty) (indexJ } candidates := make([]PhysicalPlan, 0, 2) if supportLeftOuter { -<<<<<<< HEAD - allLeftOuterJoins = p.getIndexJoinByOuterIdx(prop, 0) - forcedLeftOuterJoins = make([]PhysicalPlan, 0, len(allLeftOuterJoins)) - for _, j := range allLeftOuterJoins { - if p.satisfyIndexJoinHint(j) { - forcedLeftOuterJoins = append(forcedLeftOuterJoins, j) - } - } - switch { - case len(forcedLeftOuterJoins) == 0 && !supportRightOuter: - return filterIndexJoinBySessionVars(p.ctx, allLeftOuterJoins), false - case len(forcedLeftOuterJoins) != 0 && (!supportRightOuter || (forceLeftOuter && !forceRightOuter)): - return forcedLeftOuterJoins, true - } -======= candidates = append(candidates, p.getIndexJoinByOuterIdx(prop, 0)...) ->>>>>>> 79f00016016 (planner: refactor `tryToGetIndexJoin` (#45617)) } if supportRightOuter { candidates = append(candidates, p.getIndexJoinByOuterIdx(prop, 1)...) @@ -2102,13 +2049,6 @@ func (p *LogicalJoin) handleForceIndexJoinHints(prop *property.PhysicalProperty, if !ok { continue } -<<<<<<< HEAD - switch { - case len(forcedRightOuterJoins) == 0 && !supportLeftOuter: - return filterIndexJoinBySessionVars(p.ctx, allRightOuterJoins), false - case len(forcedRightOuterJoins) != 0 && (!supportLeftOuter || (forceRightOuter && !forceLeftOuter)): - return forcedRightOuterJoins, true -======= if (p.preferAny(preferLeftAsINLJInner) && innerSide == joinLeft && joinMethod == indexJoinMethod) || (p.preferAny(preferRightAsINLJInner) && innerSide == joinRight && joinMethod == indexJoinMethod) || (p.preferAny(preferLeftAsINLHJInner) && innerSide == joinLeft && joinMethod == indexHashJoinMethod) || @@ -2116,16 +2056,12 @@ func (p *LogicalJoin) handleForceIndexJoinHints(prop *property.PhysicalProperty, (p.preferAny(preferLeftAsINLMJInner) && innerSide == joinLeft && joinMethod == indexMergeJoinMethod) || (p.preferAny(preferRightAsINLMJInner) && innerSide == joinRight && joinMethod == indexMergeJoinMethod) { forced = append(forced, candidate) ->>>>>>> 79f00016016 (planner: refactor `tryToGetIndexJoin` (#45617)) } } if len(forced) > 0 { return forced, true } -<<<<<<< HEAD - return filterIndexJoinBySessionVars(p.ctx, append(allLeftOuterJoins, allRightOuterJoins...)), false -======= // Cannot find any valid index join plan with these force hints. // Print warning message if any hints cannot work. // If the required property is not empty, we will enforce it and try the hint again. @@ -2153,7 +2089,6 @@ func (p *LogicalJoin) handleForceIndexJoinHints(prop *property.PhysicalProperty, p.SCtx().GetSessionVars().StmtCtx.AppendWarning(ErrInternal.GenWithStack(errMsg)) } return candidates, false ->>>>>>> 79f00016016 (planner: refactor `tryToGetIndexJoin` (#45617)) } func checkChildFitBC(p Plan) bool {