Skip to content

Commit

Permalink
planner: support no_merge_join hint on optimizer (pingcap#45562)
Browse files Browse the repository at this point in the history
  • Loading branch information
qw4990 authored Jul 25, 2023
1 parent 4b50fd8 commit ef27b0e
Show file tree
Hide file tree
Showing 8 changed files with 169 additions and 3 deletions.
2 changes: 1 addition & 1 deletion planner/core/casetest/rule/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ go_test(
],
data = glob(["testdata/**"]),
flaky = True,
shard_count = 21,
shard_count = 22,
deps = [
"//domain",
"//expression",
Expand Down
11 changes: 11 additions & 0 deletions planner/core/casetest/rule/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/rule/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/rule/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 @@ -231,6 +231,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 @@ -75,6 +75,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 @@ -723,6 +725,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 @@ -3924,7 +3934,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 @@ -3939,7 +3949,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 @@ -3964,6 +3974,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 @@ -4075,6 +4087,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 @@ -7320,6 +7333,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 @@ -124,6 +124,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 @@ -97,6 +97,7 @@ type tableHintInfo struct {
shuffleJoinTables []hintTableInfo
hashJoinTables []hintTableInfo
noHashJoinTables []hintTableInfo
noMergeJoinTables []hintTableInfo
indexHintList []indexHintInfo
tiflashTables []hintTableInfo
tikvTables []hintTableInfo
Expand Down Expand Up @@ -245,6 +246,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

0 comments on commit ef27b0e

Please sign in to comment.