Skip to content

Commit

Permalink
planner: refine tryToGetIndexJoin (#45587)
Browse files Browse the repository at this point in the history
ref #45520
  • Loading branch information
qw4990 authored Jul 26, 2023
1 parent 93a880a commit 64be9ec
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 63 deletions.
126 changes: 63 additions & 63 deletions planner/core/exhaust_physical_plans.go
Original file line number Diff line number Diff line change
Expand Up @@ -1969,61 +1969,82 @@ func filterIndexJoinBySessionVars(sc sessionctx.Context, indexJoins []PhysicalPl
return indexJoins
}

// 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) {
inljRightOuter := (p.preferJoinType & preferLeftAsINLJInner) > 0
inljLeftOuter := (p.preferJoinType & preferRightAsINLJInner) > 0
hasINLJHint := inljLeftOuter || inljRightOuter
func (p *LogicalJoin) preferAny(joinFlags ...uint) bool {
for _, flag := range joinFlags {
if p.preferJoinType&flag > 0 {
return true
}
}
return false
}

inlhjRightOuter := (p.preferJoinType & preferLeftAsINLHJInner) > 0
inlhjLeftOuter := (p.preferJoinType & preferRightAsINLHJInner) > 0
hasINLHJHint := inlhjLeftOuter || inlhjRightOuter
// 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
switch ij := join.(type) {
case *PhysicalIndexJoin:
innerIdx = ij.getInnerChildIdx()
joinMethod = indexJoin
case *PhysicalIndexHashJoin:
innerIdx = ij.getInnerChildIdx()
joinMethod = indexHashJoin
case *PhysicalIndexMergeJoin:
innerIdx = ij.getInnerChildIdx()
joinMethod = indexMergeJoin
default:
return false
}
innerSide = left
if innerIdx == 1 {
innerSide = right
}

inlmjRightOuter := (p.preferJoinType & preferLeftAsINLMJInner) > 0
inlmjLeftOuter := (p.preferJoinType & preferRightAsINLMJInner) > 0
hasINLMJHint := inlmjLeftOuter || inlmjRightOuter
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
}
return false
}

forceLeftOuter := inljLeftOuter || inlhjLeftOuter || inlmjLeftOuter
forceRightOuter := inljRightOuter || inlhjRightOuter || inlmjRightOuter
// 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) {
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() {
// refine error message
// 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 hasINLJHint:
errMsg = "Optimizer Hint INL_JOIN or TIDB_INLJ is inapplicable"
case hasINLHJHint:
errMsg = "Optimizer Hint INL_HASH_JOIN is inapplicable"
case hasINLMJHint:
errMsg = "Optimizer Hint INL_MERGE_JOIN is inapplicable"
}
if p.hintInfo != nil && p.preferJoinType > 0 {
t := p.hintInfo.indexNestedLoopJoinTables
switch {
case len(t.inljTables) != 0 && ((p.preferJoinType&preferLeftAsINLJInner > 0) || (p.preferJoinType&preferRightAsINLJInner > 0)):
errMsg = fmt.Sprintf("Optimizer Hint %s or %s is inapplicable",
restore2JoinHint(HintINLJ, t.inljTables), restore2JoinHint(TiDBIndexNestedLoopJoin, t.inljTables))
case len(t.inlhjTables) != 0 && ((p.preferJoinType&preferLeftAsINLHJInner > 0) || (p.preferJoinType&preferRightAsINLHJInner > 0)):
errMsg = fmt.Sprintf("Optimizer Hint %s is inapplicable", restore2JoinHint(HintINLHJ, t.inlhjTables))
case len(t.inlmjTables) != 0 && ((p.preferJoinType&preferLeftAsINLMJInner > 0) || (p.preferJoinType&preferRightAsINLMJInner > 0)):
errMsg = fmt.Sprintf("Optimizer Hint %s is inapplicable", restore2JoinHint(HintINLMJ, t.inlmjTables))
}
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.SCtx().GetSessionVars().StmtCtx.AppendWarning(warning)
p.SCtx().GetSessionVars().StmtCtx.AppendWarning(ErrInternal.GenWithStack(errMsg))
}
}()

Expand All @@ -2044,19 +2065,8 @@ func (p *LogicalJoin) tryToGetIndexJoin(prop *property.PhysicalProperty) (indexJ
allLeftOuterJoins = p.getIndexJoinByOuterIdx(prop, 0)
forcedLeftOuterJoins = make([]PhysicalPlan, 0, len(allLeftOuterJoins))
for _, j := range allLeftOuterJoins {
switch j.(type) {
case *PhysicalIndexJoin:
if inljLeftOuter {
forcedLeftOuterJoins = append(forcedLeftOuterJoins, j)
}
case *PhysicalIndexHashJoin:
if inlhjLeftOuter {
forcedLeftOuterJoins = append(forcedLeftOuterJoins, j)
}
case *PhysicalIndexMergeJoin:
if inlmjLeftOuter {
forcedLeftOuterJoins = append(forcedLeftOuterJoins, j)
}
if p.satisfyIndexJoinHint(j) {
forcedLeftOuterJoins = append(forcedLeftOuterJoins, j)
}
}
switch {
Expand All @@ -2066,23 +2076,13 @@ func (p *LogicalJoin) tryToGetIndexJoin(prop *property.PhysicalProperty) (indexJ
return forcedLeftOuterJoins, true
}
}

if supportRightOuter {
allRightOuterJoins = p.getIndexJoinByOuterIdx(prop, 1)
forcedRightOuterJoins = make([]PhysicalPlan, 0, len(allRightOuterJoins))
for _, j := range allRightOuterJoins {
switch j.(type) {
case *PhysicalIndexJoin:
if inljRightOuter {
forcedRightOuterJoins = append(forcedRightOuterJoins, j)
}
case *PhysicalIndexHashJoin:
if inlhjRightOuter {
forcedRightOuterJoins = append(forcedRightOuterJoins, j)
}
case *PhysicalIndexMergeJoin:
if inlmjRightOuter {
forcedRightOuterJoins = append(forcedRightOuterJoins, j)
}
if p.satisfyIndexJoinHint(j) {
forcedRightOuterJoins = append(forcedRightOuterJoins, j)
}
}
switch {
Expand Down
3 changes: 3 additions & 0 deletions planner/core/planbuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,9 @@ func restore2TableHint(hintTables ...hintTableInfo) string {
}

func restore2JoinHint(hintType string, hintTables []hintTableInfo) string {
if len(hintTables) == 0 {
return strings.ToUpper(hintType)
}
buffer := bytes.NewBufferString("/*+ ")
buffer.WriteString(strings.ToUpper(hintType))
buffer.WriteString("(")
Expand Down

0 comments on commit 64be9ec

Please sign in to comment.