Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
28 changes: 26 additions & 2 deletions pkg/planner/core/joinorder/join_order.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ func extractJoinGroup(p base.LogicalPlan) (resJoinGroup *joinGroup) {

var leftHasHint, rightHasHint bool
var vertexHints map[int]*JoinMethodHint
preserveHintedJoinEdge := false
if p.SCtx().GetSessionVars().EnableAdvancedJoinHint && join.PreferJoinType > uint(0) {
vertexHints = make(map[int]*JoinMethodHint)
if join.LeftPreferJoinType > uint(0) {
Expand All @@ -193,6 +194,11 @@ func extractJoinGroup(p base.LogicalPlan) (resJoinGroup *joinGroup) {
}
rightHasHint = true
}
// Side-specific index join hints belong to the current join edge. If only the hinted side is
// preserved and the opposite subtree is still expanded into the reorder group, the hint may be
// rebound onto a different join edge after reorder.
preserveHintedJoinEdge = join.LeftPreferJoinType&(hint.PreferINLJ|hint.PreferINLHJ|hint.PreferINLMJ) > 0 ||
join.RightPreferJoinType&(hint.PreferINLJ|hint.PreferINLHJ|hint.PreferINLMJ) > 0
}

resJoinGroup = &joinGroup{
Expand All @@ -204,15 +210,15 @@ func extractJoinGroup(p base.LogicalPlan) (resJoinGroup *joinGroup) {

leftShouldPreserve := curLeadingHint != nil && IsDerivedTableInLeadingHint(join.Children()[0], curLeadingHint)
var leftJoinGroup, rightJoinGroup *joinGroup
if !leftHasHint && !leftShouldPreserve {
if !leftHasHint && !leftShouldPreserve && !preserveHintedJoinEdge {
leftJoinGroup = extractJoinGroup(join.Children()[0])
} else {
leftJoinGroup = makeSingleGroup(join.Children()[0])
}
resJoinGroup.merge(leftJoinGroup)

rightShouldPreserve := curLeadingHint != nil && IsDerivedTableInLeadingHint(join.Children()[1], curLeadingHint)
if !rightHasHint && !rightShouldPreserve {
if !rightHasHint && !rightShouldPreserve && !preserveHintedJoinEdge {
rightJoinGroup = extractJoinGroup(join.Children()[1])
} else {
rightJoinGroup = makeSingleGroup(join.Children()[1])
Expand Down Expand Up @@ -278,6 +284,9 @@ func optimizeRecursive(p base.LogicalPlan) (base.LogicalPlan, error) {
}
if len(vertexMap) > 0 {
joinGroup.root = replaceJoinGroupVertexes(joinGroup.root, vertexMap)
if len(joinGroup.vertexHints) > 0 {
joinGroup.vertexHints = rebindVertexHints(joinGroup.vertexHints, vertexMap)
}
}
if p, err = optimizeForJoinGroup(p.SCtx(), joinGroup); err != nil {
return nil, err
Expand Down Expand Up @@ -313,6 +322,21 @@ func replaceJoinGroupVertexes(root base.LogicalPlan, vertexMap map[int]base.Logi
return root
}

func rebindVertexHints(vertexHints map[int]*JoinMethodHint, vertexMap map[int]base.LogicalPlan) map[int]*JoinMethodHint {
if len(vertexHints) == 0 || len(vertexMap) == 0 {
return vertexHints
}
rebuilt := make(map[int]*JoinMethodHint, len(vertexHints))
for oldID, hintInfo := range vertexHints {
if optimizedVertex, ok := vertexMap[oldID]; ok {
rebuilt[optimizedVertex.ID()] = hintInfo
continue
}
rebuilt[oldID] = hintInfo
}
return rebuilt
}

func optimizeForJoinGroup(ctx base.PlanContext, group *joinGroup) (p base.LogicalPlan, err error) {
originalSchema := group.root.Schema()

Expand Down
58 changes: 58 additions & 0 deletions pkg/planner/core/physical_plan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,64 @@ func TestJoinHintCompatibilityWithVariable(t *testing.T) {
tk.MustExec("select /*+ leading(t2), hash_join(t2) */ * from t t1 join t t2 join t t3 where t1.a = t2.a and t2.b = t3.b;")
res := tk.MustQuery("show warnings").Rows()
require.Equal(t, len(res) > 0, true)
tk.MustExec("drop table if exists table_insurant, table_house, table_role_connection")
tk.MustExec("create table table_insurant(actualid varchar(32), serialNo varchar(32), key idx_actualid(actualid))")
tk.MustExec("create table table_house(actualid varchar(32), planNo varchar(32), topId varchar(32), insurantNo varchar(32), key idx_actualid(actualid), key idx_insurantNo(insurantNo))")
tk.MustExec("create table table_role_connection(actualid varchar(32), parentId varchar(32), specid varchar(32), key idx_parent_spec_actual(parentId, specid, actualid), key idx_actualid(actualid))")

tk.MustExec(`insert into table_insurant values
('a1', '2'),
('a2', '3')`)
tk.MustExec(`insert into table_house values
('h1', 'p1', 't1', '2'),
('h2', 'p2', 't2', '9')`)
tk.MustExec(`insert into table_role_connection values
('a1', '3561703379345', '14535'),
('h1', '3561703379345', '14550')`)

sql := `explain select
count(*)
from
(
select /*+ inl_join(t1) */
t2.actualId,
t2.planNo,
t2.topId,
t2.insurantNo
from
(
select *
from table_insurant
where actualid in (
select actualid
from table_role_connection
where parentId = '3561703379345'
and specid = '14535'
)
) t1,
(
select *
from table_house
where actualid in (
select actualid
from table_role_connection
where parentId = '3561703379345'
and specid = '14550'
)
) t2
where t1.serialNo = t2.insurantNo
and t2.insurantNo = '2'
) s`

expectedWarn := "Warning 1815 Optimizer Hint /*+ INL_JOIN(t1) */ or /*+ TIDB_INLJ(t1) */ is inapplicable"

tk.MustExec("set @@session.tidb_opt_advanced_join_hint=0")
tk.MustQuery(sql).Rows()
tk.MustQuery("show warnings").Check(testkit.Rows(expectedWarn))

tk.MustExec("set @@session.tidb_opt_advanced_join_hint=1")
tk.MustQuery(sql).Rows()
tk.MustQuery("show warnings").Check(testkit.Rows(expectedWarn))
Comment thread
hawkingrei marked this conversation as resolved.
})
}

Expand Down
31 changes: 29 additions & 2 deletions pkg/planner/core/rule_join_reorder.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ func extractJoinGroup(p base.LogicalPlan) *joinGroupResult {
}
// `leftHasHint` and `rightHasHint` are used to record whether the left child and right child are set by the join method hint.
leftHasHint, rightHasHint := false, false
preserveHintedJoinEdge := false
if isJoin && p.SCtx().GetSessionVars().EnableAdvancedJoinHint && join.PreferJoinType > uint(0) {
// If the current join node has the join method hint, we should store the hint information and restore it when we have finished the join reorder process.
if join.LeftPreferJoinType > uint(0) {
Expand All @@ -129,6 +130,11 @@ func extractJoinGroup(p base.LogicalPlan) *joinGroupResult {
joinMethodHintInfo[join.Children()[1].ID()] = &joinorder.JoinMethodHint{PreferJoinMethod: join.RightPreferJoinType, HintInfo: join.HintInfo}
rightHasHint = true
}
// Side-specific index join hints are attached to the current join edge. If only one side is
// preserved while the opposite subtree is still split into the reorder group, the hint can be
// rebound to a different join edge after reorder and suppress the original inapplicable warning.
preserveHintedJoinEdge = join.LeftPreferJoinType&(h.PreferINLJ|h.PreferINLHJ|h.PreferINLMJ) > 0 ||
join.RightPreferJoinType&(h.PreferINLJ|h.PreferINLHJ|h.PreferINLMJ) > 0
}
hasOuterJoin = hasOuterJoin || (join.JoinType != base.InnerJoin)
// If the left child has the hint, it means there are some join method hints want to specify the join method based on the left child.
Expand All @@ -138,7 +144,7 @@ func extractJoinGroup(p base.LogicalPlan) *joinGroupResult {
// Check if left child should be preserved due to LEADING hint reference
leftShouldPreserve := currentLeadingHint != nil && joinorder.IsDerivedTableInLeadingHint(join.Children()[0], currentLeadingHint)

if join.JoinType != base.RightOuterJoin && !leftHasHint && !leftShouldPreserve {
if join.JoinType != base.RightOuterJoin && !leftHasHint && !leftShouldPreserve && !preserveHintedJoinEdge {
lhsJoinGroupResult := extractJoinGroup(join.Children()[0])
lhsGroup, lhsEqualConds, lhsOtherConds, lhsJoinTypes, lhsJoinOrderHintInfo, lhsJoinMethodHintInfo, lhsHasOuterJoin := lhsJoinGroupResult.group, lhsJoinGroupResult.eqEdges, lhsJoinGroupResult.otherConds, lhsJoinGroupResult.joinTypes, lhsJoinGroupResult.joinOrderHintInfo, lhsJoinGroupResult.joinMethodHintInfo, lhsJoinGroupResult.hasOuterJoin
noExpand := false
Expand Down Expand Up @@ -186,7 +192,7 @@ func extractJoinGroup(p base.LogicalPlan) *joinGroupResult {
rightShouldPreserve := currentLeadingHint != nil && joinorder.IsDerivedTableInLeadingHint(join.Children()[1], currentLeadingHint)

// You can see the comments in the upside part which we try to split the left child part. It's the same here.
if join.JoinType != base.LeftOuterJoin && !rightHasHint && !rightShouldPreserve {
if join.JoinType != base.LeftOuterJoin && !rightHasHint && !rightShouldPreserve && !preserveHintedJoinEdge {
rhsJoinGroupResult := extractJoinGroup(join.Children()[1])
rhsGroup, rhsEqualConds, rhsOtherConds, rhsJoinTypes, rhsJoinOrderHintInfo, rhsJoinMethodHintInfo, rhsHasOuterJoin := rhsJoinGroupResult.group, rhsJoinGroupResult.eqEdges, rhsJoinGroupResult.otherConds, rhsJoinGroupResult.joinTypes, rhsJoinGroupResult.joinOrderHintInfo, rhsJoinGroupResult.joinMethodHintInfo, rhsJoinGroupResult.hasOuterJoin
noExpand := false
Expand Down Expand Up @@ -312,11 +318,17 @@ func (s *JoinReOrderSolver) optimizeRecursive(ctx base.PlanContext, p base.Logic
result := extractJoinGroup(p)
curJoinGroup, joinTypes, joinOrderHintInfo, hasOuterJoin := result.group, result.joinTypes, result.joinOrderHintInfo, result.hasOuterJoin
if len(curJoinGroup) > 1 {
optimizedVertexMap := make(map[int]base.LogicalPlan, len(curJoinGroup))
for i := range curJoinGroup {
oldID := curJoinGroup[i].ID()
curJoinGroup[i], err = s.optimizeRecursive(ctx, curJoinGroup[i])
if err != nil {
return nil, err
}
optimizedVertexMap[oldID] = curJoinGroup[i]
}
if len(result.joinMethodHintInfo) > 0 {
result.joinMethodHintInfo = rebindJoinMethodHints(result.joinMethodHintInfo, optimizedVertexMap)
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
originalSchema := p.Schema()

Expand Down Expand Up @@ -403,6 +415,21 @@ func (s *JoinReOrderSolver) optimizeRecursive(ctx base.PlanContext, p base.Logic
return p, nil
}

func rebindJoinMethodHints(hints map[int]*joinorder.JoinMethodHint, optimizedVertexMap map[int]base.LogicalPlan) map[int]*joinorder.JoinMethodHint {
if len(hints) == 0 || len(optimizedVertexMap) == 0 {
return hints
}
rebuilt := make(map[int]*joinorder.JoinMethodHint, len(hints))
for oldID, hintInfo := range hints {
if optimizedVertex, ok := optimizedVertexMap[oldID]; ok {
rebuilt[optimizedVertex.ID()] = hintInfo
continue
}
rebuilt[oldID] = hintInfo
}
return rebuilt
}

// basicJoinGroupInfo represents basic information for a join group in the join reorder process.
type basicJoinGroupInfo struct {
eqEdges []*expression.ScalarFunction
Expand Down
Loading