From 00e656355959d33ce42067a9a18ed5ed24849a05 Mon Sep 17 00:00:00 2001 From: Yuanjia Zhang Date: Fri, 28 Jul 2023 10:52:36 +0800 Subject: [PATCH] This is an automated cherry-pick of #45617 Signed-off-by: ti-chi-bot --- planner/core/exhaust_physical_plans.go | 122 +++++++++++++++++++++++-- 1 file changed, 115 insertions(+), 7 deletions(-) diff --git a/planner/core/exhaust_physical_plans.go b/planner/core/exhaust_physical_plans.go index ae23a2949ca3e..d91aa8110db0c 100644 --- a/planner/core/exhaust_physical_plans.go +++ b/planner/core/exhaust_physical_plans.go @@ -1948,6 +1948,7 @@ func filterIndexJoinBySessionVars(sc sessionctx.Context, indexJoins []PhysicalPl return indexJoins } +<<<<<<< HEAD // 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. func (p *LogicalJoin) tryToGetIndexJoin(prop *property.PhysicalProperty) (indexJoins []PhysicalPlan, canForced bool) { @@ -2006,6 +2007,50 @@ func (p *LogicalJoin) tryToGetIndexJoin(prop *property.PhysicalProperty) (indexJ } }() +======= +func (p *LogicalJoin) preferAny(joinFlags ...uint) bool { + for _, flag := range joinFlags { + if p.preferJoinType&flag > 0 { + return true + } + } + return false +} + +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 = indexJoinMethod + case *PhysicalIndexHashJoin: + innerIdx = ij.getInnerChildIdx() + joinMethod = indexHashJoinMethod + case *PhysicalIndexMergeJoin: + innerIdx = ij.getInnerChildIdx() + joinMethod = indexMergeJoinMethod + default: + return 0, 0, false + } + ok = true + innerSide = joinLeft + if innerIdx == 1 { + innerSide = joinRight + } + return +} + +// 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) { +>>>>>>> 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 @@ -2017,9 +2062,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 { @@ -2069,16 +2114,79 @@ func (p *LogicalJoin) tryToGetIndexJoin(prop *property.PhysicalProperty) (indexJ return filterIndexJoinBySessionVars(p.ctx, allRightOuterJoins), false case len(forcedRightOuterJoins) != 0 && (!supportLeftOuter || (forceRightOuter && !forceLeftOuter)): return forcedRightOuterJoins, true - } +======= + candidates = append(candidates, p.getIndexJoinByOuterIdx(prop, 0)...) + } + if supportRightOuter { + candidates = append(candidates, p.getIndexJoinByOuterIdx(prop, 1)...) } - canForceLeft := len(forcedLeftOuterJoins) != 0 && forceLeftOuter - canForceRight := len(forcedRightOuterJoins) != 0 && forceRightOuter - canForced = canForceLeft || canForceRight + // 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 append(forcedLeftOuterJoins, forcedRightOuterJoins...), true + 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 + } + 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)) + } + } + + 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 {