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: support no_merge_join hint on optimizer (#45562) #45705

Merged
Merged
Show file tree
Hide file tree
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
Empty file.
11 changes: 11 additions & 0 deletions planner/core/casetest/rule_join_reorder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,17 @@ func TestNoHashJoinHint(t *testing.T) {
runJoinReorderTestData(t, tk, "TestNoHashJoinHint")
}

func TestNoMergeJoinHint(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("create table t1(a int, key(a));")
tk.MustExec("create table t2(a int, key(a));")
tk.MustExec("create table t3(a int, key(a));")
tk.MustExec("create table t4(a int, key(a));")
runJoinReorderTestData(t, tk, "TestNoMergeJoinHint")
}

func TestLeadingJoinHint(t *testing.T) {
store := testkit.CreateMockStore(t)

Expand Down
13 changes: 13 additions & 0 deletions planner/core/casetest/testdata/join_reorder_suite_in.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,19 @@
"select /*+ leading(t1, t2, t3, t4), hash_join(t1, t2), no_hash_join(t3), hash_join(t4) */ * from t1, t2, t3, t4"
]
},
{
"name": "TestNoMergeJoinHint",
"cases": [
"select /*+ no_merge_join() */ * from t1, t2 where t1.a=t2.a",
"select /*+ no_merge_join(t1), merge_join(t1) */ * from t1, t2 where t1.a=t2.a",
"select /*+ no_merge_join(t1), merge_join(t2) */ * from t1, t2 where t1.a=t2.a",
"select /*+ no_merge_join(t1) */ * from t1, t2 where t1.a=t2.a",
"select /*+ no_merge_join(t1, t2) */ * from t1, t2 where t1.a=t2.a",
"select /*+ no_merge_join(t2) */ * from t1 right join t2 on t1.a=t2.a",
"select /*+ leading(t4, t3, t2, t1), no_merge_join(t2, t3) */ * from t1, t2, t3, t4 where t1.a=t2.a and t2.a=t3.a and t3.a=t4.a",
"select /*+ leading(t1, t2, t3, t4), merge_join(t1, t2), no_merge_join(t3), merge_join(t4) */ * from t1, t2, t3, t4 where t1.a=t2.a and t2.a=t3.a and t3.a=t4.a"
]
},
{
"name": "TestLeadingJoinHint",
"cases": [
Expand Down
113 changes: 113 additions & 0 deletions planner/core/casetest/testdata/join_reorder_suite_out.json
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,119 @@
}
]
},
{
"Name": "TestNoMergeJoinHint",
"Cases": [
{
"SQL": "select /*+ no_merge_join() */ * from t1, t2 where t1.a=t2.a",
"Plan": [
"MergeJoin 12487.50 root inner join, left key:test.t1.a, right key:test.t2.a",
"├─IndexReader(Build) 9990.00 root index:IndexFullScan",
"│ └─IndexFullScan 9990.00 cop[tikv] table:t2, index:a(a) keep order:true, stats:pseudo",
"└─IndexReader(Probe) 9990.00 root index:IndexFullScan",
" └─IndexFullScan 9990.00 cop[tikv] table:t1, index:a(a) keep order:true, stats:pseudo"
],
"Warning": [
"Warning 1815 Hint no_merge_join() is inapplicable. Please specify the table names in the arguments."
]
},
{
"SQL": "select /*+ no_merge_join(t1), merge_join(t1) */ * from t1, t2 where t1.a=t2.a",
"Plan": [
"MergeJoin 12487.50 root inner join, left key:test.t1.a, right key:test.t2.a",
"├─IndexReader(Build) 9990.00 root index:IndexFullScan",
"│ └─IndexFullScan 9990.00 cop[tikv] table:t2, index:a(a) keep order:true, stats:pseudo",
"└─IndexReader(Probe) 9990.00 root index:IndexFullScan",
" └─IndexFullScan 9990.00 cop[tikv] table:t1, index:a(a) keep order:true, stats:pseudo"
],
"Warning": [
"Warning 1815 Some MERGE_JOIN and NO_MERGE_JOIN hints conflict, NO_MERGE_JOIN is ignored"
]
},
{
"SQL": "select /*+ no_merge_join(t1), merge_join(t2) */ * from t1, t2 where t1.a=t2.a",
"Plan": [
"MergeJoin 12487.50 root inner join, left key:test.t1.a, right key:test.t2.a",
"├─IndexReader(Build) 9990.00 root index:IndexFullScan",
"│ └─IndexFullScan 9990.00 cop[tikv] table:t2, index:a(a) keep order:true, stats:pseudo",
"└─IndexReader(Probe) 9990.00 root index:IndexFullScan",
" └─IndexFullScan 9990.00 cop[tikv] table:t1, index:a(a) keep order:true, stats:pseudo"
],
"Warning": [
"Warning 1815 Some MERGE_JOIN and NO_MERGE_JOIN hints conflict, NO_MERGE_JOIN is ignored"
]
},
{
"SQL": "select /*+ no_merge_join(t1) */ * from t1, t2 where t1.a=t2.a",
"Plan": [
"HashJoin 12487.50 root inner join, equal:[eq(test.t1.a, test.t2.a)]",
"├─IndexReader(Build) 9990.00 root index:IndexFullScan",
"│ └─IndexFullScan 9990.00 cop[tikv] table:t2, index:a(a) keep order:false, stats:pseudo",
"└─IndexReader(Probe) 9990.00 root index:IndexFullScan",
" └─IndexFullScan 9990.00 cop[tikv] table:t1, index:a(a) keep order:false, stats:pseudo"
],
"Warning": null
},
{
"SQL": "select /*+ no_merge_join(t1, t2) */ * from t1, t2 where t1.a=t2.a",
"Plan": [
"HashJoin 12487.50 root inner join, equal:[eq(test.t1.a, test.t2.a)]",
"├─IndexReader(Build) 9990.00 root index:IndexFullScan",
"│ └─IndexFullScan 9990.00 cop[tikv] table:t2, index:a(a) keep order:false, stats:pseudo",
"└─IndexReader(Probe) 9990.00 root index:IndexFullScan",
" └─IndexFullScan 9990.00 cop[tikv] table:t1, index:a(a) keep order:false, stats:pseudo"
],
"Warning": null
},
{
"SQL": "select /*+ no_merge_join(t2) */ * from t1 right join t2 on t1.a=t2.a",
"Plan": [
"HashJoin 12487.50 root right outer join, equal:[eq(test.t1.a, test.t2.a)]",
"├─IndexReader(Build) 9990.00 root index:IndexFullScan",
"│ └─IndexFullScan 9990.00 cop[tikv] table:t1, index:a(a) keep order:false, stats:pseudo",
"└─IndexReader(Probe) 10000.00 root index:IndexFullScan",
" └─IndexFullScan 10000.00 cop[tikv] table:t2, index:a(a) keep order:false, stats:pseudo"
],
"Warning": null
},
{
"SQL": "select /*+ leading(t4, t3, t2, t1), no_merge_join(t2, t3) */ * from t1, t2, t3, t4 where t1.a=t2.a and t2.a=t3.a and t3.a=t4.a",
"Plan": [
"Projection 19511.72 root test.t1.a, test.t2.a, test.t3.a, test.t4.a",
"└─HashJoin 19511.72 root inner join, equal:[eq(test.t2.a, test.t1.a)]",
" ├─IndexReader(Build) 9990.00 root index:IndexFullScan",
" │ └─IndexFullScan 9990.00 cop[tikv] table:t1, index:a(a) keep order:false, stats:pseudo",
" └─HashJoin(Probe) 15609.38 root inner join, equal:[eq(test.t3.a, test.t2.a)]",
" ├─IndexReader(Build) 9990.00 root index:IndexFullScan",
" │ └─IndexFullScan 9990.00 cop[tikv] table:t2, index:a(a) keep order:false, stats:pseudo",
" └─HashJoin(Probe) 12487.50 root inner join, equal:[eq(test.t4.a, test.t3.a)]",
" ├─IndexReader(Build) 9990.00 root index:IndexFullScan",
" │ └─IndexFullScan 9990.00 cop[tikv] table:t3, index:a(a) keep order:false, stats:pseudo",
" └─IndexReader(Probe) 9990.00 root index:IndexFullScan",
" └─IndexFullScan 9990.00 cop[tikv] table:t4, index:a(a) keep order:false, stats:pseudo"
],
"Warning": null
},
{
"SQL": "select /*+ leading(t1, t2, t3, t4), merge_join(t1, t2), no_merge_join(t3), merge_join(t4) */ * from t1, t2, t3, t4 where t1.a=t2.a and t2.a=t3.a and t3.a=t4.a",
"Plan": [
"MergeJoin 19511.72 root inner join, left key:test.t3.a, right key:test.t4.a",
"├─IndexReader(Build) 9990.00 root index:IndexFullScan",
"│ └─IndexFullScan 9990.00 cop[tikv] table:t4, index:a(a) keep order:true, stats:pseudo",
"└─Sort(Probe) 15609.38 root test.t3.a",
" └─HashJoin 15609.38 root inner join, equal:[eq(test.t2.a, test.t3.a)]",
" ├─IndexReader(Build) 9990.00 root index:IndexFullScan",
" │ └─IndexFullScan 9990.00 cop[tikv] table:t3, index:a(a) keep order:false, stats:pseudo",
" └─MergeJoin(Probe) 12487.50 root inner join, left key:test.t1.a, right key:test.t2.a",
" ├─IndexReader(Build) 9990.00 root index:IndexFullScan",
" │ └─IndexFullScan 9990.00 cop[tikv] table:t2, index:a(a) keep order:true, stats:pseudo",
" └─IndexReader(Probe) 9990.00 root index:IndexFullScan",
" └─IndexFullScan 9990.00 cop[tikv] table:t1, index:a(a) keep order:true, stats:pseudo"
],
"Warning": null
}
]
},
{
"Name": "TestLeadingJoinHint",
"Cases": [
Expand Down
9 changes: 9 additions & 0 deletions planner/core/exhaust_physical_plans.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,15 @@ func (p *LogicalJoin) GetMergeJoin(prop *property.PhysicalProperty, schema *expr
joins = append(joins, mergeJoin)
}
}

if p.preferJoinType&preferNoMergeJoin > 0 {
if p.preferJoinType&preferMergeJoin == 0 {
return nil
}
p.SCtx().GetSessionVars().StmtCtx.AppendWarning(ErrInternal.GenWithStack(
"Some MERGE_JOIN and NO_MERGE_JOIN hints conflict, NO_MERGE_JOIN is ignored"))
}

// If TiDB_SMJ hint is existed, it should consider enforce merge join,
// because we can't trust lhsChildProperty completely.
if (p.preferJoinType&preferMergeJoin) > 0 ||
Expand Down
18 changes: 16 additions & 2 deletions planner/core/logical_plan_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ const (
TiDBMergeJoin = "tidb_smj"
// HintSMJ is hint enforce merge join.
HintSMJ = "merge_join"
// HintNoMergeJoin is the hint to enforce the query not to use merge join.
HintNoMergeJoin = "no_merge_join"

// TiDBBroadCastJoin indicates applying broadcast join by force.
TiDBBroadCastJoin = "tidb_bcj"
Expand Down Expand Up @@ -603,6 +605,14 @@ func (p *LogicalJoin) setPreferredJoinTypeAndOrder(hintInfo *tableHintInfo) {
p.preferJoinType |= preferMergeJoin
p.rightPreferJoinType |= preferMergeJoin
}
if hintInfo.ifPreferNoMergeJoin(lhsAlias) {
p.preferJoinType |= preferNoMergeJoin
p.leftPreferJoinType |= preferNoMergeJoin
}
if hintInfo.ifPreferNoMergeJoin(rhsAlias) {
p.preferJoinType |= preferNoMergeJoin
p.rightPreferJoinType |= preferNoMergeJoin
}
if hintInfo.ifPreferBroadcastJoin(lhsAlias) {
p.preferJoinType |= preferBCJoin
p.leftPreferJoinType |= preferBCJoin
Expand Down Expand Up @@ -3720,7 +3730,7 @@ func (b *PlanBuilder) pushTableHints(hints []*ast.TableOptimizerHint, currentLev
hints = b.hintProcessor.GetCurrentStmtHints(hints, currentLevel)
var (
sortMergeTables, inljTables, inlhjTables, inlmjTables, hashJoinTables, bcTables []hintTableInfo
noHashJoinTables []hintTableInfo
noHashJoinTables, noMergeJoinTables []hintTableInfo
shuffleJoinTables []hintTableInfo
indexHintList, indexMergeHintList []indexHintInfo
tiflashTables, tikvTables []hintTableInfo
Expand All @@ -3735,7 +3745,7 @@ func (b *PlanBuilder) pushTableHints(hints []*ast.TableOptimizerHint, currentLev
for _, hint := range hints {
// Set warning for the hint that requires the table name.
switch hint.HintName.L {
case TiDBMergeJoin, HintSMJ, TiDBIndexNestedLoopJoin, HintINLJ, HintINLHJ, HintINLMJ, HintNoHashJoin,
case TiDBMergeJoin, HintSMJ, TiDBIndexNestedLoopJoin, HintINLJ, HintINLHJ, HintINLMJ, HintNoHashJoin, HintNoMergeJoin,
TiDBHashJoin, HintHJ, HintUseIndex, HintIgnoreIndex, HintForceIndex, HintOrderIndex, HintNoOrderIndex, HintIndexMerge, HintLeading:
if len(hint.Tables) == 0 {
b.pushHintWithoutTableWarning(hint)
Expand All @@ -3760,6 +3770,8 @@ func (b *PlanBuilder) pushTableHints(hints []*ast.TableOptimizerHint, currentLev
hashJoinTables = append(hashJoinTables, tableNames2HintTableInfo(b.ctx, hint.HintName.L, hint.Tables, b.hintProcessor, currentLevel)...)
case HintNoHashJoin:
noHashJoinTables = append(noHashJoinTables, tableNames2HintTableInfo(b.ctx, hint.HintName.L, hint.Tables, b.hintProcessor, currentLevel)...)
case HintNoMergeJoin:
noMergeJoinTables = append(noMergeJoinTables, tableNames2HintTableInfo(b.ctx, hint.HintName.L, hint.Tables, b.hintProcessor, currentLevel)...)
case HintMPP1PhaseAgg:
aggHints.preferAggType |= preferMPP1PhaseAgg
case HintMPP2PhaseAgg:
Expand Down Expand Up @@ -3871,6 +3883,7 @@ func (b *PlanBuilder) pushTableHints(hints []*ast.TableOptimizerHint, currentLev
indexNestedLoopJoinTables: indexNestedLoopJoinTables{inljTables, inlhjTables, inlmjTables},
hashJoinTables: hashJoinTables,
noHashJoinTables: noHashJoinTables,
noMergeJoinTables: noMergeJoinTables,
indexHintList: indexHintList,
tiflashTables: tiflashTables,
tikvTables: tikvTables,
Expand Down Expand Up @@ -7077,6 +7090,7 @@ func getInnerFromParenthesesAndUnaryPlus(expr ast.ExprNode) ast.ExprNode {
// join types.
func containDifferentJoinTypes(preferJoinType uint) bool {
preferJoinType &= ^preferNoHashJoin
preferJoinType &= ^preferNoMergeJoin

inlMask := preferRightAsINLJInner ^ preferLeftAsINLJInner
inlhjMask := preferRightAsINLHJInner ^ preferLeftAsINLHJInner
Expand Down
1 change: 1 addition & 0 deletions planner/core/logical_plans.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ const (
preferHashJoin
preferNoHashJoin
preferMergeJoin
preferNoMergeJoin
preferBCJoin
preferShuffleJoin
preferRewriteSemiJoin
Expand Down
5 changes: 5 additions & 0 deletions planner/core/planbuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ type tableHintInfo struct {
shuffleJoinTables []hintTableInfo
hashJoinTables []hintTableInfo
noHashJoinTables []hintTableInfo
noMergeJoinTables []hintTableInfo
indexHintList []indexHintInfo
tiflashTables []hintTableInfo
tikvTables []hintTableInfo
Expand Down Expand Up @@ -242,6 +243,10 @@ func (info *tableHintInfo) ifPreferNoHashJoin(tableNames ...*hintTableInfo) bool
return info.matchTableName(tableNames, info.noHashJoinTables)
}

func (info *tableHintInfo) ifPreferNoMergeJoin(tableNames ...*hintTableInfo) bool {
return info.matchTableName(tableNames, info.noMergeJoinTables)
}

func (info *tableHintInfo) ifPreferHJBuild(tableNames ...*hintTableInfo) bool {
return info.matchTableName(tableNames, info.hjBuildTables)
}
Expand Down