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: refactor tryToGetIndexJoin (#45617) #45631

Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 115 additions & 7 deletions planner/core/exhaust_physical_plans.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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
Expand All @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down