From f48e0cd449f4372f4f4693cd4ab273346c369cf5 Mon Sep 17 00:00:00 2001 From: qw4990 Date: Wed, 26 Jul 2023 14:18:16 +0800 Subject: [PATCH 1/5] fixup --- planner/core/exhaust_physical_plans.go | 192 ++++++++++--------------- planner/core/planbuilder.go | 3 + 2 files changed, 75 insertions(+), 120 deletions(-) diff --git a/planner/core/exhaust_physical_plans.go b/planner/core/exhaust_physical_plans.go index 748e2d9563d76..5014478d8c379 100644 --- a/planner/core/exhaust_physical_plans.go +++ b/planner/core/exhaust_physical_plans.go @@ -1957,76 +1957,9 @@ func (ijHelper *indexJoinBuildHelper) buildTemplateRange(matchedKeyCnt int, eqAn return } -func filterIndexJoinBySessionVars(sc sessionctx.Context, indexJoins []PhysicalPlan) []PhysicalPlan { - if sc.GetSessionVars().EnableIndexMergeJoin { - return indexJoins - } - for i := len(indexJoins) - 1; i >= 0; i-- { - if _, ok := indexJoins[i].(*PhysicalIndexMergeJoin); ok { - indexJoins = append(indexJoins[:i], indexJoins[i+1:]...) - } - } - 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 - - inlhjRightOuter := (p.preferJoinType & preferLeftAsINLHJInner) > 0 - inlhjLeftOuter := (p.preferJoinType & preferRightAsINLHJInner) > 0 - hasINLHJHint := inlhjLeftOuter || inlhjRightOuter - - inlmjRightOuter := (p.preferJoinType & preferLeftAsINLMJInner) > 0 - inlmjLeftOuter := (p.preferJoinType & preferRightAsINLMJInner) > 0 - hasINLMJHint := inlmjLeftOuter || inlmjRightOuter - - forceLeftOuter := inljLeftOuter || inlhjLeftOuter || inlmjLeftOuter - forceRightOuter := inljRightOuter || inlhjRightOuter || inlmjRightOuter - needForced := forceLeftOuter || forceRightOuter - - defer func() { - // refine error message - // 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 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)) - } - } - - // 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) - } - }() - // 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 @@ -2038,68 +1971,87 @@ func (p *LogicalJoin) tryToGetIndexJoin(prop *property.PhysicalProperty) (indexJ case InnerJoin: supportLeftOuter, supportRightOuter = true, true } - - var allLeftOuterJoins, allRightOuterJoins, forcedLeftOuterJoins, forcedRightOuterJoins []PhysicalPlan + var leftAsInner, rightAsInner []PhysicalPlan if supportLeftOuter { - 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) - } - } + rightAsInner = p.getIndexJoinByOuterIdx(prop, 0) + } + if supportRightOuter { + leftAsInner = p.getIndexJoinByOuterIdx(prop, 1) + } + return p.filterIndexJoinCandidates(prop, leftAsInner, rightAsInner) +} + +// filterIndexJoinCandidates will filter the candidates according to hints and SQL variables. +func (p *LogicalJoin) filterIndexJoinCandidates(prop *property.PhysicalProperty, leftAsInner, rightAsInner []PhysicalPlan) ([]PhysicalPlan, bool) { + candidates := make([]PhysicalPlan, 0, len(leftAsInner)+len(rightAsInner)) + candidates = append(candidates, leftAsInner...) + candidates = append(candidates, rightAsInner...) + + const Left, Right = 0, 1 + const IndexJoin, IndexMergeJoin, IndexHashJoin = 0, 1, 2 + selected := make([]PhysicalPlan, len(candidates)) + for i, candidate := range candidates { + innerSide := Left + if i >= len(leftAsInner) { + innerSide = Right } - switch { - case len(forcedLeftOuterJoins) == 0 && !supportRightOuter: - return filterIndexJoinBySessionVars(p.SCtx(), allLeftOuterJoins), false - case len(forcedLeftOuterJoins) != 0 && (!supportRightOuter || (forceLeftOuter && !forceRightOuter)): - return forcedLeftOuterJoins, true + joinMethod := IndexJoin + switch candidate.(type) { + case *PhysicalIndexMergeJoin: + joinMethod = IndexMergeJoin + case *PhysicalIndexHashJoin: + joinMethod = IndexHashJoin } + + if !p.SCtx().GetSessionVars().EnableIndexMergeJoin && joinMethod == IndexMergeJoin { + continue + } + if (p.preferJoin(preferLeftAsINLJInner) && !(innerSide == Left && joinMethod == IndexJoin)) || + (p.preferJoin(preferRightAsINLJInner) && !(innerSide == Right && joinMethod == IndexJoin)) || + (p.preferJoin(preferLeftAsINLHJInner) && !(innerSide == Left && joinMethod == IndexHashJoin)) || + (p.preferJoin(preferRightAsINLHJInner) && !(innerSide == Right && joinMethod == IndexHashJoin)) || + (p.preferJoin(preferLeftAsINLMJInner) && !(innerSide == Left && joinMethod == IndexMergeJoin)) || + (p.preferJoin(preferRightAsINLMJInner) && !(innerSide == Right && joinMethod == IndexMergeJoin)) { + continue + } + selected = append(selected, candidate) } - 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.preferJoin(preferLeftAsINLJInner, preferRightAsINLJInner, preferLeftAsINLHJInner, + preferRightAsINLHJInner, preferLeftAsINLMJInner, preferRightAsINLMJInner) { + if len(selected) > 0 { + return selected, true } - switch { - case len(forcedRightOuterJoins) == 0 && !supportLeftOuter: - return filterIndexJoinBySessionVars(p.SCtx(), allRightOuterJoins), false - case len(forcedRightOuterJoins) != 0 && (!supportLeftOuter || (forceRightOuter && !forceLeftOuter)): - return forcedRightOuterJoins, true + // Cannot find any index join under the current hints, generate some warnings. + // If the required property is not empty, we will enforce it and try the hint again. + // So we only need to generate warning messages when the property is empty. + if prop.IsSortItemEmpty() { + t := p.hintInfo.indexNestedLoopJoinTables + var errMsg string + if p.preferJoin(preferLeftAsINLJInner, preferRightAsINLJInner) { + errMsg = fmt.Sprintf("Optimizer Hint %s or %s is inapplicable", restore2JoinHint(HintINLJ, t.inljTables), restore2JoinHint(TiDBIndexNestedLoopJoin, t.inljTables)) + } else if p.preferJoin(preferLeftAsINLHJInner, preferRightAsINLHJInner) { + errMsg = fmt.Sprintf("Optimizer Hint %s is inapplicable", restore2JoinHint(HintINLHJ, t.inlhjTables)) + } else if p.preferJoin(preferLeftAsINLMJInner, preferRightAsINLMJInner) { + errMsg = fmt.Sprintf("Optimizer Hint %s is inapplicable", restore2JoinHint(HintINLMJ, t.inlmjTables)) + } + if len(p.EqualConditions) == 0 { + errMsg += " without column equal ON condition" + } + warning := ErrInternal.GenWithStack(errMsg) + p.SCtx().GetSessionVars().StmtCtx.AppendWarning(warning) } } + return candidates, false +} - canForceLeft := len(forcedLeftOuterJoins) != 0 && forceLeftOuter - canForceRight := len(forcedRightOuterJoins) != 0 && forceRightOuter - canForced = canForceLeft || canForceRight - if canForced { - return append(forcedLeftOuterJoins, forcedRightOuterJoins...), true +func (p *LogicalJoin) preferJoin(flags ...uint) bool { + for _, flag := range flags { + if (p.preferJoinType & flag) > 0 { + return true + } } - return filterIndexJoinBySessionVars(p.SCtx(), append(allLeftOuterJoins, allRightOuterJoins...)), false + return false } func checkChildFitBC(p Plan) bool { diff --git a/planner/core/planbuilder.go b/planner/core/planbuilder.go index f5a4ab7d11e4b..05b7fd7a50526 100644 --- a/planner/core/planbuilder.go +++ b/planner/core/planbuilder.go @@ -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("(") From e6273b9131714092730522897061ae42dec57da0 Mon Sep 17 00:00:00 2001 From: qw4990 Date: Wed, 26 Jul 2023 14:26:55 +0800 Subject: [PATCH 2/5] fixup --- planner/core/exhaust_physical_plans.go | 48 +++++++++++++------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/planner/core/exhaust_physical_plans.go b/planner/core/exhaust_physical_plans.go index 5014478d8c379..6fad5128e5c0d 100644 --- a/planner/core/exhaust_physical_plans.go +++ b/planner/core/exhaust_physical_plans.go @@ -1957,30 +1957,6 @@ func (ijHelper *indexJoinBuildHelper) buildTemplateRange(matchedKeyCnt int, eqAn 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. -func (p *LogicalJoin) tryToGetIndexJoin(prop *property.PhysicalProperty) (indexJoins []PhysicalPlan, canForced bool) { - // 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 - switch p.JoinType { - case SemiJoin, AntiSemiJoin, LeftOuterSemiJoin, AntiLeftOuterSemiJoin, LeftOuterJoin: - supportLeftOuter = true - case RightOuterJoin: - supportRightOuter = true - case InnerJoin: - supportLeftOuter, supportRightOuter = true, true - } - var leftAsInner, rightAsInner []PhysicalPlan - if supportLeftOuter { - rightAsInner = p.getIndexJoinByOuterIdx(prop, 0) - } - if supportRightOuter { - leftAsInner = p.getIndexJoinByOuterIdx(prop, 1) - } - return p.filterIndexJoinCandidates(prop, leftAsInner, rightAsInner) -} - // filterIndexJoinCandidates will filter the candidates according to hints and SQL variables. func (p *LogicalJoin) filterIndexJoinCandidates(prop *property.PhysicalProperty, leftAsInner, rightAsInner []PhysicalPlan) ([]PhysicalPlan, bool) { candidates := make([]PhysicalPlan, 0, len(leftAsInner)+len(rightAsInner)) @@ -2054,6 +2030,30 @@ func (p *LogicalJoin) preferJoin(flags ...uint) bool { return false } +// 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) { + // 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 + switch p.JoinType { + case SemiJoin, AntiSemiJoin, LeftOuterSemiJoin, AntiLeftOuterSemiJoin, LeftOuterJoin: + supportLeftOuter = true + case RightOuterJoin: + supportRightOuter = true + case InnerJoin: + supportLeftOuter, supportRightOuter = true, true + } + var leftAsInner, rightAsInner []PhysicalPlan + if supportLeftOuter { + rightAsInner = p.getIndexJoinByOuterIdx(prop, 0) + } + if supportRightOuter { + leftAsInner = p.getIndexJoinByOuterIdx(prop, 1) + } + return p.filterIndexJoinCandidates(prop, leftAsInner, rightAsInner) +} + func checkChildFitBC(p Plan) bool { if p.StatsInfo().HistColl == nil { return p.SCtx().GetSessionVars().BroadcastJoinThresholdCount == -1 || p.StatsInfo().Count() < p.SCtx().GetSessionVars().BroadcastJoinThresholdCount From 677a010208337eaba62ae9c67390d57e80065be1 Mon Sep 17 00:00:00 2001 From: qw4990 Date: Wed, 26 Jul 2023 14:29:16 +0800 Subject: [PATCH 3/5] fixup --- planner/core/exhaust_physical_plans.go | 194 ++++++++++++------------- 1 file changed, 97 insertions(+), 97 deletions(-) diff --git a/planner/core/exhaust_physical_plans.go b/planner/core/exhaust_physical_plans.go index 6fad5128e5c0d..d526c15514184 100644 --- a/planner/core/exhaust_physical_plans.go +++ b/planner/core/exhaust_physical_plans.go @@ -1957,103 +1957,6 @@ func (ijHelper *indexJoinBuildHelper) buildTemplateRange(matchedKeyCnt int, eqAn return } -// filterIndexJoinCandidates will filter the candidates according to hints and SQL variables. -func (p *LogicalJoin) filterIndexJoinCandidates(prop *property.PhysicalProperty, leftAsInner, rightAsInner []PhysicalPlan) ([]PhysicalPlan, bool) { - candidates := make([]PhysicalPlan, 0, len(leftAsInner)+len(rightAsInner)) - candidates = append(candidates, leftAsInner...) - candidates = append(candidates, rightAsInner...) - - const Left, Right = 0, 1 - const IndexJoin, IndexMergeJoin, IndexHashJoin = 0, 1, 2 - selected := make([]PhysicalPlan, len(candidates)) - for i, candidate := range candidates { - innerSide := Left - if i >= len(leftAsInner) { - innerSide = Right - } - joinMethod := IndexJoin - switch candidate.(type) { - case *PhysicalIndexMergeJoin: - joinMethod = IndexMergeJoin - case *PhysicalIndexHashJoin: - joinMethod = IndexHashJoin - } - - if !p.SCtx().GetSessionVars().EnableIndexMergeJoin && joinMethod == IndexMergeJoin { - continue - } - if (p.preferJoin(preferLeftAsINLJInner) && !(innerSide == Left && joinMethod == IndexJoin)) || - (p.preferJoin(preferRightAsINLJInner) && !(innerSide == Right && joinMethod == IndexJoin)) || - (p.preferJoin(preferLeftAsINLHJInner) && !(innerSide == Left && joinMethod == IndexHashJoin)) || - (p.preferJoin(preferRightAsINLHJInner) && !(innerSide == Right && joinMethod == IndexHashJoin)) || - (p.preferJoin(preferLeftAsINLMJInner) && !(innerSide == Left && joinMethod == IndexMergeJoin)) || - (p.preferJoin(preferRightAsINLMJInner) && !(innerSide == Right && joinMethod == IndexMergeJoin)) { - continue - } - selected = append(selected, candidate) - } - - if p.preferJoin(preferLeftAsINLJInner, preferRightAsINLJInner, preferLeftAsINLHJInner, - preferRightAsINLHJInner, preferLeftAsINLMJInner, preferRightAsINLMJInner) { - if len(selected) > 0 { - return selected, true - } - // Cannot find any index join under the current hints, generate some warnings. - // If the required property is not empty, we will enforce it and try the hint again. - // So we only need to generate warning messages when the property is empty. - if prop.IsSortItemEmpty() { - t := p.hintInfo.indexNestedLoopJoinTables - var errMsg string - if p.preferJoin(preferLeftAsINLJInner, preferRightAsINLJInner) { - errMsg = fmt.Sprintf("Optimizer Hint %s or %s is inapplicable", restore2JoinHint(HintINLJ, t.inljTables), restore2JoinHint(TiDBIndexNestedLoopJoin, t.inljTables)) - } else if p.preferJoin(preferLeftAsINLHJInner, preferRightAsINLHJInner) { - errMsg = fmt.Sprintf("Optimizer Hint %s is inapplicable", restore2JoinHint(HintINLHJ, t.inlhjTables)) - } else if p.preferJoin(preferLeftAsINLMJInner, preferRightAsINLMJInner) { - errMsg = fmt.Sprintf("Optimizer Hint %s is inapplicable", restore2JoinHint(HintINLMJ, t.inlmjTables)) - } - if len(p.EqualConditions) == 0 { - errMsg += " without column equal ON condition" - } - warning := ErrInternal.GenWithStack(errMsg) - p.SCtx().GetSessionVars().StmtCtx.AppendWarning(warning) - } - } - return candidates, false -} - -func (p *LogicalJoin) preferJoin(flags ...uint) bool { - for _, flag := range flags { - if (p.preferJoinType & flag) > 0 { - return true - } - } - return false -} - -// 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) { - // 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 - switch p.JoinType { - case SemiJoin, AntiSemiJoin, LeftOuterSemiJoin, AntiLeftOuterSemiJoin, LeftOuterJoin: - supportLeftOuter = true - case RightOuterJoin: - supportRightOuter = true - case InnerJoin: - supportLeftOuter, supportRightOuter = true, true - } - var leftAsInner, rightAsInner []PhysicalPlan - if supportLeftOuter { - rightAsInner = p.getIndexJoinByOuterIdx(prop, 0) - } - if supportRightOuter { - leftAsInner = p.getIndexJoinByOuterIdx(prop, 1) - } - return p.filterIndexJoinCandidates(prop, leftAsInner, rightAsInner) -} - func checkChildFitBC(p Plan) bool { if p.StatsInfo().HistColl == nil { return p.SCtx().GetSessionVars().BroadcastJoinThresholdCount == -1 || p.StatsInfo().Count() < p.SCtx().GetSessionVars().BroadcastJoinThresholdCount @@ -2178,6 +2081,103 @@ func (p *LogicalJoin) preferMppBCJ() bool { return checkChildFitBC(p.children[0]) || checkChildFitBC(p.children[1]) } +// filterIndexJoinCandidates will filter the candidates according to hints and SQL variables. +func (p *LogicalJoin) filterIndexJoinCandidates(prop *property.PhysicalProperty, leftAsInner, rightAsInner []PhysicalPlan) ([]PhysicalPlan, bool) { + candidates := make([]PhysicalPlan, 0, len(leftAsInner)+len(rightAsInner)) + candidates = append(candidates, leftAsInner...) + candidates = append(candidates, rightAsInner...) + + const Left, Right = 0, 1 + const IndexJoin, IndexMergeJoin, IndexHashJoin = 0, 1, 2 + selected := make([]PhysicalPlan, len(candidates)) + for i, candidate := range candidates { + innerSide := Left + if i >= len(leftAsInner) { + innerSide = Right + } + joinMethod := IndexJoin + switch candidate.(type) { + case *PhysicalIndexMergeJoin: + joinMethod = IndexMergeJoin + case *PhysicalIndexHashJoin: + joinMethod = IndexHashJoin + } + + if !p.SCtx().GetSessionVars().EnableIndexMergeJoin && joinMethod == IndexMergeJoin { + continue + } + if (p.preferJoin(preferLeftAsINLJInner) && !(innerSide == Left && joinMethod == IndexJoin)) || + (p.preferJoin(preferRightAsINLJInner) && !(innerSide == Right && joinMethod == IndexJoin)) || + (p.preferJoin(preferLeftAsINLHJInner) && !(innerSide == Left && joinMethod == IndexHashJoin)) || + (p.preferJoin(preferRightAsINLHJInner) && !(innerSide == Right && joinMethod == IndexHashJoin)) || + (p.preferJoin(preferLeftAsINLMJInner) && !(innerSide == Left && joinMethod == IndexMergeJoin)) || + (p.preferJoin(preferRightAsINLMJInner) && !(innerSide == Right && joinMethod == IndexMergeJoin)) { + continue + } + selected = append(selected, candidate) + } + + if p.preferJoin(preferLeftAsINLJInner, preferRightAsINLJInner, preferLeftAsINLHJInner, + preferRightAsINLHJInner, preferLeftAsINLMJInner, preferRightAsINLMJInner) { + if len(selected) > 0 { + return selected, true + } + // Cannot find any index join under the current hints, generate some warnings. + // If the required property is not empty, we will enforce it and try the hint again. + // So we only need to generate warning messages when the property is empty. + if prop.IsSortItemEmpty() { + t := p.hintInfo.indexNestedLoopJoinTables + var errMsg string + if p.preferJoin(preferLeftAsINLJInner, preferRightAsINLJInner) { + errMsg = fmt.Sprintf("Optimizer Hint %s or %s is inapplicable", restore2JoinHint(HintINLJ, t.inljTables), restore2JoinHint(TiDBIndexNestedLoopJoin, t.inljTables)) + } else if p.preferJoin(preferLeftAsINLHJInner, preferRightAsINLHJInner) { + errMsg = fmt.Sprintf("Optimizer Hint %s is inapplicable", restore2JoinHint(HintINLHJ, t.inlhjTables)) + } else if p.preferJoin(preferLeftAsINLMJInner, preferRightAsINLMJInner) { + errMsg = fmt.Sprintf("Optimizer Hint %s is inapplicable", restore2JoinHint(HintINLMJ, t.inlmjTables)) + } + if len(p.EqualConditions) == 0 { + errMsg += " without column equal ON condition" + } + warning := ErrInternal.GenWithStack(errMsg) + p.SCtx().GetSessionVars().StmtCtx.AppendWarning(warning) + } + } + return candidates, false +} + +func (p *LogicalJoin) preferJoin(flags ...uint) bool { + for _, flag := range flags { + if (p.preferJoinType & flag) > 0 { + return true + } + } + return false +} + +// 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) { + // 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 + switch p.JoinType { + case SemiJoin, AntiSemiJoin, LeftOuterSemiJoin, AntiLeftOuterSemiJoin, LeftOuterJoin: + supportLeftOuter = true + case RightOuterJoin: + supportRightOuter = true + case InnerJoin: + supportLeftOuter, supportRightOuter = true, true + } + var leftAsInner, rightAsInner []PhysicalPlan + if supportLeftOuter { + rightAsInner = p.getIndexJoinByOuterIdx(prop, 0) + } + if supportRightOuter { + leftAsInner = p.getIndexJoinByOuterIdx(prop, 1) + } + return p.filterIndexJoinCandidates(prop, leftAsInner, rightAsInner) +} + // LogicalJoin can generates hash join, index join and sort merge join. // Firstly we check the hint, if hint is figured by user, we force to choose the corresponding physical plan. // If the hint is not matched, it will get other candidates. From 4f02ffbd5303b16b22f0d30bf2b5437e13896d91 Mon Sep 17 00:00:00 2001 From: qw4990 Date: Wed, 26 Jul 2023 14:39:52 +0800 Subject: [PATCH 4/5] fixup --- planner/core/exhaust_physical_plans.go | 30 +++++++++++++------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/planner/core/exhaust_physical_plans.go b/planner/core/exhaust_physical_plans.go index d526c15514184..9cc42bba09074 100644 --- a/planner/core/exhaust_physical_plans.go +++ b/planner/core/exhaust_physical_plans.go @@ -2087,31 +2087,31 @@ func (p *LogicalJoin) filterIndexJoinCandidates(prop *property.PhysicalProperty, candidates = append(candidates, leftAsInner...) candidates = append(candidates, rightAsInner...) - const Left, Right = 0, 1 - const IndexJoin, IndexMergeJoin, IndexHashJoin = 0, 1, 2 - selected := make([]PhysicalPlan, len(candidates)) + const left, right = 0, 1 + const indexJoin, indexMergeJoin, indexHashJoin = 0, 1, 2 + selected := make([]PhysicalPlan, 0, len(candidates)) for i, candidate := range candidates { - innerSide := Left + innerSide := left if i >= len(leftAsInner) { - innerSide = Right + innerSide = right } - joinMethod := IndexJoin + joinMethod := indexJoin switch candidate.(type) { case *PhysicalIndexMergeJoin: - joinMethod = IndexMergeJoin + joinMethod = indexMergeJoin case *PhysicalIndexHashJoin: - joinMethod = IndexHashJoin + joinMethod = indexHashJoin } - if !p.SCtx().GetSessionVars().EnableIndexMergeJoin && joinMethod == IndexMergeJoin { + if !p.SCtx().GetSessionVars().EnableIndexMergeJoin && joinMethod == indexMergeJoin { continue } - if (p.preferJoin(preferLeftAsINLJInner) && !(innerSide == Left && joinMethod == IndexJoin)) || - (p.preferJoin(preferRightAsINLJInner) && !(innerSide == Right && joinMethod == IndexJoin)) || - (p.preferJoin(preferLeftAsINLHJInner) && !(innerSide == Left && joinMethod == IndexHashJoin)) || - (p.preferJoin(preferRightAsINLHJInner) && !(innerSide == Right && joinMethod == IndexHashJoin)) || - (p.preferJoin(preferLeftAsINLMJInner) && !(innerSide == Left && joinMethod == IndexMergeJoin)) || - (p.preferJoin(preferRightAsINLMJInner) && !(innerSide == Right && joinMethod == IndexMergeJoin)) { + if (p.preferJoin(preferLeftAsINLJInner) && !(innerSide == left && joinMethod == indexJoin)) || + (p.preferJoin(preferRightAsINLJInner) && !(innerSide == right && joinMethod == indexJoin)) || + (p.preferJoin(preferLeftAsINLHJInner) && !(innerSide == left && joinMethod == indexHashJoin)) || + (p.preferJoin(preferRightAsINLHJInner) && !(innerSide == right && joinMethod == indexHashJoin)) || + (p.preferJoin(preferLeftAsINLMJInner) && !(innerSide == left && joinMethod == indexMergeJoin)) || + (p.preferJoin(preferRightAsINLMJInner) && !(innerSide == right && joinMethod == indexMergeJoin)) { continue } selected = append(selected, candidate) From d9d4a208f01caca88e450e7e2d956bf31044bd0e Mon Sep 17 00:00:00 2001 From: qw4990 Date: Wed, 26 Jul 2023 15:37:50 +0800 Subject: [PATCH 5/5] fixup --- planner/core/exhaust_physical_plans.go | 35 ++++++++++++++++++-------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/planner/core/exhaust_physical_plans.go b/planner/core/exhaust_physical_plans.go index 9cc42bba09074..62c65f11fc4a1 100644 --- a/planner/core/exhaust_physical_plans.go +++ b/planner/core/exhaust_physical_plans.go @@ -2081,15 +2081,29 @@ func (p *LogicalJoin) preferMppBCJ() bool { return checkChildFitBC(p.children[0]) || checkChildFitBC(p.children[1]) } -// filterIndexJoinCandidates will filter the candidates according to hints and SQL variables. -func (p *LogicalJoin) filterIndexJoinCandidates(prop *property.PhysicalProperty, leftAsInner, rightAsInner []PhysicalPlan) ([]PhysicalPlan, bool) { +func (p *LogicalJoin) filterIndexJoinByVars(candidates []PhysicalPlan) []PhysicalPlan { + if !p.SCtx().GetSessionVars().EnableIndexMergeJoin { + result := make([]PhysicalPlan, 0, len(candidates)) + for _, candidate := range candidates { + switch candidate.(type) { + case *PhysicalIndexMergeJoin: + default: + result = append(result, candidate) + } + } + candidates = result + } + return candidates +} + +func (p *LogicalJoin) filterIndexJoinByHints(prop *property.PhysicalProperty, leftAsInner, rightAsInner []PhysicalPlan) ([]PhysicalPlan, bool) { candidates := make([]PhysicalPlan, 0, len(leftAsInner)+len(rightAsInner)) candidates = append(candidates, leftAsInner...) candidates = append(candidates, rightAsInner...) const left, right = 0, 1 const indexJoin, indexMergeJoin, indexHashJoin = 0, 1, 2 - selected := make([]PhysicalPlan, 0, len(candidates)) + preferJoins := make([]PhysicalPlan, 0, len(candidates)) for i, candidate := range candidates { innerSide := left if i >= len(leftAsInner) { @@ -2103,9 +2117,6 @@ func (p *LogicalJoin) filterIndexJoinCandidates(prop *property.PhysicalProperty, joinMethod = indexHashJoin } - if !p.SCtx().GetSessionVars().EnableIndexMergeJoin && joinMethod == indexMergeJoin { - continue - } if (p.preferJoin(preferLeftAsINLJInner) && !(innerSide == left && joinMethod == indexJoin)) || (p.preferJoin(preferRightAsINLJInner) && !(innerSide == right && joinMethod == indexJoin)) || (p.preferJoin(preferLeftAsINLHJInner) && !(innerSide == left && joinMethod == indexHashJoin)) || @@ -2114,13 +2125,14 @@ func (p *LogicalJoin) filterIndexJoinCandidates(prop *property.PhysicalProperty, (p.preferJoin(preferRightAsINLMJInner) && !(innerSide == right && joinMethod == indexMergeJoin)) { continue } - selected = append(selected, candidate) + + preferJoins = append(preferJoins, candidate) } if p.preferJoin(preferLeftAsINLJInner, preferRightAsINLJInner, preferLeftAsINLHJInner, preferRightAsINLHJInner, preferLeftAsINLMJInner, preferRightAsINLMJInner) { - if len(selected) > 0 { - return selected, true + if len(preferJoins) > 0 { + return preferJoins, true } // Cannot find any index join under the current hints, generate some warnings. // If the required property is not empty, we will enforce it and try the hint again. @@ -2175,7 +2187,10 @@ func (p *LogicalJoin) tryToGetIndexJoin(prop *property.PhysicalProperty) (indexJ if supportRightOuter { leftAsInner = p.getIndexJoinByOuterIdx(prop, 1) } - return p.filterIndexJoinCandidates(prop, leftAsInner, rightAsInner) + + leftAsInner = p.filterIndexJoinByVars(leftAsInner) + rightAsInner = p.filterIndexJoinByVars(rightAsInner) + return p.filterIndexJoinByHints(prop, leftAsInner, rightAsInner) } // LogicalJoin can generates hash join, index join and sort merge join.