@@ -1976,77 +1976,39 @@ func (p *LogicalJoin) preferAny(joinFlags ...uint) bool {
19761976 return false
19771977}
19781978
1979- // satisfyIndexJoinHint returns whether this join plan can satisfy current index join hints.
1980- func (p * LogicalJoin ) satisfyIndexJoinHint (join PhysicalPlan ) bool {
1981- const left , right = 0 , 1
1982- const indexJoin , indexHashJoin , indexMergeJoin = 0 , 1 , 2
1983- var innerSide , innerIdx , joinMethod int
1979+ const (
1980+ joinLeft = 0
1981+ joinRight = 1
1982+ indexJoinMethod = 0
1983+ indexHashJoinMethod = 1
1984+ indexMergeJoinMethod = 2
1985+ )
1986+
1987+ func (* LogicalJoin ) getIndexJoinSideAndMethod (join PhysicalPlan ) (innerSide , joinMethod int , ok bool ) {
1988+ var innerIdx int
19841989 switch ij := join .(type ) {
19851990 case * PhysicalIndexJoin :
19861991 innerIdx = ij .getInnerChildIdx ()
1987- joinMethod = indexJoin
1992+ joinMethod = indexJoinMethod
19881993 case * PhysicalIndexHashJoin :
19891994 innerIdx = ij .getInnerChildIdx ()
1990- joinMethod = indexHashJoin
1995+ joinMethod = indexHashJoinMethod
19911996 case * PhysicalIndexMergeJoin :
19921997 innerIdx = ij .getInnerChildIdx ()
1993- joinMethod = indexMergeJoin
1998+ joinMethod = indexMergeJoinMethod
19941999 default :
1995- return false
2000+ return 0 , 0 , false
19962001 }
1997- innerSide = left
2002+ ok = true
2003+ innerSide = joinLeft
19982004 if innerIdx == 1 {
1999- innerSide = right
2005+ innerSide = joinRight
20002006 }
2001-
2002- if (p .preferAny (preferLeftAsINLJInner ) && innerSide == left && joinMethod == indexJoin ) ||
2003- (p .preferAny (preferRightAsINLJInner ) && innerSide == right && joinMethod == indexJoin ) ||
2004- (p .preferAny (preferLeftAsINLHJInner ) && innerSide == left && joinMethod == indexHashJoin ) ||
2005- (p .preferAny (preferRightAsINLHJInner ) && innerSide == right && joinMethod == indexHashJoin ) ||
2006- (p .preferAny (preferLeftAsINLMJInner ) && innerSide == left && joinMethod == indexMergeJoin ) ||
2007- (p .preferAny (preferRightAsINLMJInner ) && innerSide == right && joinMethod == indexMergeJoin ) {
2008- return true
2009- }
2010- return false
2007+ return
20112008}
20122009
2013- // tryToGetIndexJoin will get index join by hints. If we can generate a valid index join by hint, the second return value
2014- // will be true, which means we force to choose this index join. Otherwise we will select a join algorithm with min-cost.
2010+ // tryToGetIndexJoin returns all available index join plans, and the second returned value indicates whether this plan is enforced by hints.
20152011func (p * LogicalJoin ) tryToGetIndexJoin (prop * property.PhysicalProperty ) (indexJoins []PhysicalPlan , canForced bool ) {
2016- forceLeftOuter := p .preferAny (preferRightAsINLJInner , preferRightAsINLHJInner , preferRightAsINLMJInner ) // left as outer == right as inner
2017- forceRightOuter := p .preferAny (preferLeftAsINLJInner , preferLeftAsINLHJInner , preferLeftAsINLMJInner ) // right as outer == left as inner
2018- needForced := forceLeftOuter || forceRightOuter
2019-
2020- defer func () {
2021- // Print warning message if any hints cannot work.
2022- // If the required property is not empty, we will enforce it and try the hint again.
2023- // So we only need to generate warning message when the property is empty.
2024- if ! canForced && needForced && prop .IsSortItemEmpty () {
2025- // Construct warning message prefix.
2026- var indexJoinTables , indexHashJoinTables , indexMergeJoinTables []hintTableInfo
2027- if p .hintInfo != nil {
2028- t := p .hintInfo .indexNestedLoopJoinTables
2029- indexJoinTables , indexHashJoinTables , indexMergeJoinTables = t .inljTables , t .inlhjTables , t .inlmjTables
2030- }
2031- var errMsg string
2032- switch {
2033- case p .preferAny (preferLeftAsINLJInner , preferRightAsINLJInner ): // prefer index join
2034- errMsg = fmt .Sprintf ("Optimizer Hint %s or %s is inapplicable" , restore2JoinHint (HintINLJ , indexJoinTables ), restore2JoinHint (TiDBIndexNestedLoopJoin , indexJoinTables ))
2035- case p .preferAny (preferLeftAsINLHJInner , preferRightAsINLHJInner ): // prefer index hash join
2036- errMsg = fmt .Sprintf ("Optimizer Hint %s is inapplicable" , restore2JoinHint (HintINLHJ , indexHashJoinTables ))
2037- case p .preferAny (preferLeftAsINLMJInner , preferRightAsINLMJInner ): // prefer index merge join
2038- errMsg = fmt .Sprintf ("Optimizer Hint %s is inapplicable" , restore2JoinHint (HintINLMJ , indexMergeJoinTables ))
2039- }
2040- // Append inapplicable reason.
2041- if len (p .EqualConditions ) == 0 {
2042- errMsg += " without column equal ON condition"
2043- }
2044- // Generate warning message to client.
2045- warning := ErrInternal .GenWithStack (errMsg )
2046- p .ctx .GetSessionVars ().StmtCtx .AppendWarning (warning )
2047- }
2048- }()
2049-
20502012 // supportLeftOuter and supportRightOuter indicates whether this type of join
20512013 // supports the left side or right side to be the outer side.
20522014 var supportLeftOuter , supportRightOuter bool
@@ -2058,47 +2020,75 @@ func (p *LogicalJoin) tryToGetIndexJoin(prop *property.PhysicalProperty) (indexJ
20582020 case InnerJoin :
20592021 supportLeftOuter , supportRightOuter = true , true
20602022 }
2061-
2062- var allLeftOuterJoins , allRightOuterJoins , forcedLeftOuterJoins , forcedRightOuterJoins []PhysicalPlan
2023+ candidates := make ([]PhysicalPlan , 0 , 2 )
20632024 if supportLeftOuter {
2064- allLeftOuterJoins = p .getIndexJoinByOuterIdx (prop , 0 )
2065- forcedLeftOuterJoins = make ([]PhysicalPlan , 0 , len (allLeftOuterJoins ))
2066- for _ , j := range allLeftOuterJoins {
2067- if p .satisfyIndexJoinHint (j ) {
2068- forcedLeftOuterJoins = append (forcedLeftOuterJoins , j )
2069- }
2025+ candidates = append (candidates , p .getIndexJoinByOuterIdx (prop , 0 )... )
2026+ }
2027+ if supportRightOuter {
2028+ candidates = append (candidates , p .getIndexJoinByOuterIdx (prop , 1 )... )
2029+ }
2030+
2031+ // handle hints and variables about index join.
2032+ // the priority is: force hints like TIDB_INLJ > filter hints like NO_INDEX_JOIN > variables.
2033+ candidates , canForced = p .handleForceIndexJoinHints (prop , candidates )
2034+ if canForced {
2035+ return candidates , canForced
2036+ }
2037+ return filterIndexJoinBySessionVars (p .SCtx (), candidates ), false
2038+ }
2039+
2040+ // handleForceIndexJoinHints handles the force index join hints and returns all plans that can satisfy the hints.
2041+ func (p * LogicalJoin ) handleForceIndexJoinHints (prop * property.PhysicalProperty , candidates []PhysicalPlan ) (indexJoins []PhysicalPlan , canForced bool ) {
2042+ if ! p .preferAny (preferRightAsINLJInner , preferRightAsINLHJInner , preferRightAsINLMJInner ,
2043+ preferLeftAsINLJInner , preferLeftAsINLHJInner , preferLeftAsINLMJInner ) {
2044+ return candidates , false // no force index join hints
2045+ }
2046+ forced := make ([]PhysicalPlan , 0 , len (candidates ))
2047+ for _ , candidate := range candidates {
2048+ innerSide , joinMethod , ok := p .getIndexJoinSideAndMethod (candidate )
2049+ if ! ok {
2050+ continue
20702051 }
2071- switch {
2072- case len (forcedLeftOuterJoins ) == 0 && ! supportRightOuter :
2073- return filterIndexJoinBySessionVars (p .ctx , allLeftOuterJoins ), false
2074- case len (forcedLeftOuterJoins ) != 0 && (! supportRightOuter || (forceLeftOuter && ! forceRightOuter )):
2075- return forcedLeftOuterJoins , true
2052+ if (p .preferAny (preferLeftAsINLJInner ) && innerSide == joinLeft && joinMethod == indexJoinMethod ) ||
2053+ (p .preferAny (preferRightAsINLJInner ) && innerSide == joinRight && joinMethod == indexJoinMethod ) ||
2054+ (p .preferAny (preferLeftAsINLHJInner ) && innerSide == joinLeft && joinMethod == indexHashJoinMethod ) ||
2055+ (p .preferAny (preferRightAsINLHJInner ) && innerSide == joinRight && joinMethod == indexHashJoinMethod ) ||
2056+ (p .preferAny (preferLeftAsINLMJInner ) && innerSide == joinLeft && joinMethod == indexMergeJoinMethod ) ||
2057+ (p .preferAny (preferRightAsINLMJInner ) && innerSide == joinRight && joinMethod == indexMergeJoinMethod ) {
2058+ forced = append (forced , candidate )
20762059 }
20772060 }
20782061
2079- if supportRightOuter {
2080- allRightOuterJoins = p .getIndexJoinByOuterIdx (prop , 1 )
2081- forcedRightOuterJoins = make ([]PhysicalPlan , 0 , len (allRightOuterJoins ))
2082- for _ , j := range allRightOuterJoins {
2083- if p .satisfyIndexJoinHint (j ) {
2084- forcedRightOuterJoins = append (forcedRightOuterJoins , j )
2085- }
2062+ if len (forced ) > 0 {
2063+ return forced , true
2064+ }
2065+ // Cannot find any valid index join plan with these force hints.
2066+ // Print warning message if any hints cannot work.
2067+ // If the required property is not empty, we will enforce it and try the hint again.
2068+ // So we only need to generate warning message when the property is empty.
2069+ if prop .IsSortItemEmpty () {
2070+ var indexJoinTables , indexHashJoinTables , indexMergeJoinTables []hintTableInfo
2071+ if p .hintInfo != nil {
2072+ t := p .hintInfo .indexNestedLoopJoinTables
2073+ indexJoinTables , indexHashJoinTables , indexMergeJoinTables = t .inljTables , t .inlhjTables , t .inlmjTables
20862074 }
2075+ var errMsg string
20872076 switch {
2088- case len (forcedRightOuterJoins ) == 0 && ! supportLeftOuter :
2089- return filterIndexJoinBySessionVars (p .ctx , allRightOuterJoins ), false
2090- case len (forcedRightOuterJoins ) != 0 && (! supportLeftOuter || (forceRightOuter && ! forceLeftOuter )):
2091- return forcedRightOuterJoins , true
2077+ case p .preferAny (preferLeftAsINLJInner , preferRightAsINLJInner ): // prefer index join
2078+ errMsg = fmt .Sprintf ("Optimizer Hint %s or %s is inapplicable" , restore2JoinHint (HintINLJ , indexJoinTables ), restore2JoinHint (TiDBIndexNestedLoopJoin , indexJoinTables ))
2079+ case p .preferAny (preferLeftAsINLHJInner , preferRightAsINLHJInner ): // prefer index hash join
2080+ errMsg = fmt .Sprintf ("Optimizer Hint %s is inapplicable" , restore2JoinHint (HintINLHJ , indexHashJoinTables ))
2081+ case p .preferAny (preferLeftAsINLMJInner , preferRightAsINLMJInner ): // prefer index merge join
2082+ errMsg = fmt .Sprintf ("Optimizer Hint %s is inapplicable" , restore2JoinHint (HintINLMJ , indexMergeJoinTables ))
20922083 }
2084+ // Append inapplicable reason.
2085+ if len (p .EqualConditions ) == 0 {
2086+ errMsg += " without column equal ON condition"
2087+ }
2088+ // Generate warning message to client.
2089+ p .SCtx ().GetSessionVars ().StmtCtx .AppendWarning (ErrInternal .GenWithStack (errMsg ))
20932090 }
2094-
2095- canForceLeft := len (forcedLeftOuterJoins ) != 0 && forceLeftOuter
2096- canForceRight := len (forcedRightOuterJoins ) != 0 && forceRightOuter
2097- canForced = canForceLeft || canForceRight
2098- if canForced {
2099- return append (forcedLeftOuterJoins , forcedRightOuterJoins ... ), true
2100- }
2101- return filterIndexJoinBySessionVars (p .ctx , append (allLeftOuterJoins , allRightOuterJoins ... )), false
2091+ return candidates , false
21022092}
21032093
21042094func checkChildFitBC (p Plan ) bool {
0 commit comments