From 57c493821f7101b4714cbdff1415cbbd09c361be Mon Sep 17 00:00:00 2001 From: Chengpeng Yan <41809508+Reminiscent@users.noreply.github.com> Date: Fri, 2 Sep 2022 14:44:24 +0800 Subject: [PATCH] planner: support hash_join_build and hash_join_probe hints (#36667) close pingcap/tidb#35439 --- cmd/explaintest/r/clustered_index.result | 2 +- planner/core/exhaust_physical_plans.go | 52 ++- planner/core/hints.go | 1 + planner/core/logical_plan_builder.go | 58 ++- planner/core/logical_plans.go | 4 + planner/core/physical_plan_test.go | 33 ++ planner/core/planbuilder.go | 10 + planner/core/testdata/plan_suite_in.json | 13 + planner/core/testdata/plan_suite_out.json | 505 ++++++++++++++-------- 9 files changed, 467 insertions(+), 211 deletions(-) diff --git a/cmd/explaintest/r/clustered_index.result b/cmd/explaintest/r/clustered_index.result index c9bfb61c93604..5412b0c50cfb7 100644 --- a/cmd/explaintest/r/clustered_index.result +++ b/cmd/explaintest/r/clustered_index.result @@ -98,7 +98,7 @@ MergeJoin_7 2533.51 root right outer join, left key:with_cluster_index.tbl_2.co └─TableFullScan_23 2244.00 cop[tikv] table:tbl_0 keep order:true explain select tbl_2.col_14 , tbl_0.col_1 from wout_cluster_index.tbl_2 right join wout_cluster_index.tbl_0 on col_3 = col_11 ; id estRows task access object operator info -HashJoin_23 2533.51 root right outer join, equal:[eq(wout_cluster_index.tbl_2.col_11, wout_cluster_index.tbl_0.col_3)] +HashJoin_22 2533.51 root right outer join, equal:[eq(wout_cluster_index.tbl_2.col_11, wout_cluster_index.tbl_0.col_3)] ├─TableReader_41(Build) 2244.00 root data:TableFullScan_40 │ └─TableFullScan_40 2244.00 cop[tikv] table:tbl_0 keep order:false └─TableReader_44(Probe) 4509.00 root data:Selection_43 diff --git a/planner/core/exhaust_physical_plans.go b/planner/core/exhaust_physical_plans.go index c930c7ec552cd..09c70e7d508fd 100644 --- a/planner/core/exhaust_physical_plans.go +++ b/planner/core/exhaust_physical_plans.go @@ -373,37 +373,65 @@ var ForceUseOuterBuild4Test = atomic.NewBool(false) // TODO: use hint and remove this variable var ForcedHashLeftJoin4Test = atomic.NewBool(false) -func (p *LogicalJoin) getHashJoins(prop *property.PhysicalProperty) []PhysicalPlan { +func (p *LogicalJoin) getHashJoins(prop *property.PhysicalProperty) (joins []PhysicalPlan, forced bool) { if !prop.IsSortItemEmpty() { // hash join doesn't promise any orders - return nil + return } - joins := make([]PhysicalPlan, 0, 2) + forceLeftToBuild := ((p.preferJoinType & preferLeftAsHJBuild) > 0) || ((p.preferJoinType & preferRightAsHJProbe) > 0) + forceRightToBuild := ((p.preferJoinType & preferRightAsHJBuild) > 0) || ((p.preferJoinType & preferLeftAsHJProbe) > 0) + if forceLeftToBuild && forceRightToBuild { + p.ctx.GetSessionVars().StmtCtx.AppendWarning(ErrInternal.GenWithStack("Some HASH_JOIN_BUILD and HASH_JOIN_PROBE hints are conflicts, please check the hints")) + forceLeftToBuild = false + forceRightToBuild = false + } + joins = make([]PhysicalPlan, 0, 2) switch p.JoinType { case SemiJoin, AntiSemiJoin, LeftOuterSemiJoin, AntiLeftOuterSemiJoin: joins = append(joins, p.getHashJoin(prop, 1, false)) + if forceLeftToBuild || forceRightToBuild { + // Do not support specifying the build side. + p.ctx.GetSessionVars().StmtCtx.AppendWarning(ErrInternal.GenWithStack(fmt.Sprintf("We can't use the HASH_JOIN_BUILD or HASH_JOIN_PROBE hint for %s, please check the hint", p.JoinType))) + forceLeftToBuild = false + forceRightToBuild = false + } case LeftOuterJoin: if ForceUseOuterBuild4Test.Load() { joins = append(joins, p.getHashJoin(prop, 1, true)) } else { - joins = append(joins, p.getHashJoin(prop, 1, false)) - joins = append(joins, p.getHashJoin(prop, 1, true)) + if !forceLeftToBuild { + joins = append(joins, p.getHashJoin(prop, 1, false)) + } + if !forceRightToBuild { + joins = append(joins, p.getHashJoin(prop, 1, true)) + } } case RightOuterJoin: if ForceUseOuterBuild4Test.Load() { joins = append(joins, p.getHashJoin(prop, 0, true)) } else { - joins = append(joins, p.getHashJoin(prop, 0, false)) - joins = append(joins, p.getHashJoin(prop, 0, true)) + if !forceLeftToBuild { + joins = append(joins, p.getHashJoin(prop, 0, true)) + } + if !forceRightToBuild { + joins = append(joins, p.getHashJoin(prop, 0, false)) + } } case InnerJoin: if ForcedHashLeftJoin4Test.Load() { joins = append(joins, p.getHashJoin(prop, 1, false)) } else { - joins = append(joins, p.getHashJoin(prop, 1, false)) - joins = append(joins, p.getHashJoin(prop, 0, false)) + if forceLeftToBuild { + joins = append(joins, p.getHashJoin(prop, 0, false)) + } else if forceRightToBuild { + joins = append(joins, p.getHashJoin(prop, 1, false)) + } else { + joins = append(joins, p.getHashJoin(prop, 1, false)) + joins = append(joins, p.getHashJoin(prop, 0, false)) + } } } - return joins + forced = (p.preferJoinType&preferHashJoin > 0) || forceLeftToBuild || forceRightToBuild + return } func (p *LogicalJoin) getHashJoin(prop *property.PhysicalProperty, innerIdx int, useOuterToBuild bool) *PhysicalHashJoin { @@ -1832,8 +1860,8 @@ func (p *LogicalJoin) exhaustPhysicalPlans(prop *property.PhysicalProperty) ([]P } joins = append(joins, indexJoins...) - hashJoins := p.getHashJoins(prop) - if (p.preferJoinType&preferHashJoin) > 0 && len(hashJoins) > 0 { + hashJoins, forced := p.getHashJoins(prop) + if forced && len(hashJoins) > 0 { return hashJoins, true, nil } joins = append(joins, hashJoins...) diff --git a/planner/core/hints.go b/planner/core/hints.go index 241b95f1c47dd..850ffb32bd645 100644 --- a/planner/core/hints.go +++ b/planner/core/hints.go @@ -238,6 +238,7 @@ func genHintsFromSingle(p PhysicalPlan, nodeType utilhint.NodeType, res []*ast.T case *PhysicalMergeJoin: res = append(res, getJoinHints(p.SCtx(), HintSMJ, p.SelectBlockOffset(), nodeType, pp.children...)...) case *PhysicalHashJoin: + // TODO: support the hash_join_build and hash_join_probe hint for auto capture res = append(res, getJoinHints(p.SCtx(), HintHJ, p.SelectBlockOffset(), nodeType, pp.children...)...) case *PhysicalIndexJoin: res = append(res, getJoinHints(p.SCtx(), HintINLJ, p.SelectBlockOffset(), nodeType, pp.children[pp.InnerChildIdx])...) diff --git a/planner/core/logical_plan_builder.go b/planner/core/logical_plan_builder.go index 13f55806d296d..bdc3922858b14 100644 --- a/planner/core/logical_plan_builder.go +++ b/planner/core/logical_plan_builder.go @@ -92,6 +92,10 @@ const ( TiDBHashJoin = "tidb_hj" // HintHJ is hint enforce hash join. HintHJ = "hash_join" + // HintHashJoinBuild is hint enforce hash join's build side + HintHashJoinBuild = "hash_join_build" + // HintHashJoinProbe is hint enforce hash join's probe side + HintHashJoinProbe = "hash_join_probe" // HintHashAgg is hint enforce hash aggregation. HintHashAgg = "hash_agg" // HintStreamAgg is hint enforce stream aggregation. @@ -599,6 +603,18 @@ func (p *LogicalJoin) setPreferredJoinTypeAndOrder(hintInfo *tableHintInfo) { if hintInfo.ifPreferINLMJ(rhsAlias) { p.preferJoinType |= preferRightAsINLMJInner } + if hintInfo.ifPreferHJBuild(lhsAlias) { + p.preferJoinType |= preferLeftAsHJBuild + } + if hintInfo.ifPreferHJBuild(rhsAlias) { + p.preferJoinType |= preferRightAsHJBuild + } + if hintInfo.ifPreferHJProbe(lhsAlias) { + p.preferJoinType |= preferLeftAsHJProbe + } + if hintInfo.ifPreferHJProbe(rhsAlias) { + p.preferJoinType |= preferRightAsHJProbe + } if containDifferentJoinTypes(p.preferJoinType) { errMsg := "Join hints are conflict, you can only specify one type of join" warning := ErrInternal.GenWithStack(errMsg) @@ -3545,6 +3561,7 @@ func (b *PlanBuilder) pushTableHints(hints []*ast.TableOptimizerHint, currentLev limitHints limitHintInfo MergeHints MergeHintInfo leadingJoinOrder []hintTableInfo + hjBuildTables, hjProbeTables []hintTableInfo leadingHintCnt int ) for _, hint := range hints { @@ -3571,6 +3588,10 @@ func (b *PlanBuilder) pushTableHints(hints []*ast.TableOptimizerHint, currentLev INLMJTables = append(INLMJTables, tableNames2HintTableInfo(b.ctx, hint.HintName.L, hint.Tables, b.hintProcessor, currentLevel)...) case TiDBHashJoin, HintHJ: hashJoinTables = append(hashJoinTables, tableNames2HintTableInfo(b.ctx, hint.HintName.L, hint.Tables, b.hintProcessor, currentLevel)...) + case HintHashJoinBuild: + hjBuildTables = append(hjBuildTables, tableNames2HintTableInfo(b.ctx, hint.HintName.L, hint.Tables, b.hintProcessor, currentLevel)...) + case HintHashJoinProbe: + hjProbeTables = append(hjProbeTables, tableNames2HintTableInfo(b.ctx, hint.HintName.L, hint.Tables, b.hintProcessor, currentLevel)...) case HintHashAgg: aggHints.preferAggType |= preferHashAgg case HintStreamAgg: @@ -3692,6 +3713,8 @@ func (b *PlanBuilder) pushTableHints(hints []*ast.TableOptimizerHint, currentLev limitHints: limitHints, MergeHints: MergeHints, leadingJoinOrder: leadingJoinOrder, + hjBuildTables: hjBuildTables, + hjProbeTables: hjProbeTables, }) } @@ -3712,6 +3735,8 @@ func (b *PlanBuilder) popTableHints() { b.appendUnmatchedJoinHintWarning(HintSMJ, TiDBMergeJoin, hintInfo.sortMergeJoinTables) b.appendUnmatchedJoinHintWarning(HintBCJ, TiDBBroadCastJoin, hintInfo.broadcastJoinTables) b.appendUnmatchedJoinHintWarning(HintHJ, TiDBHashJoin, hintInfo.hashJoinTables) + b.appendUnmatchedJoinHintWarning(HintHashJoinBuild, "", hintInfo.hjBuildTables) + b.appendUnmatchedJoinHintWarning(HintHashJoinProbe, "", hintInfo.hjProbeTables) b.appendUnmatchedJoinHintWarning(HintLeading, "", hintInfo.leadingJoinOrder) b.appendUnmatchedStorageHintWarning(hintInfo.tiflashTables, hintInfo.tikvTables) b.tableHintInfo = b.tableHintInfo[:len(b.tableHintInfo)-1] @@ -5058,23 +5083,36 @@ func (b *PlanBuilder) buildSemiJoin(outerPlan, innerPlan LogicalPlan, onConditio } // Apply forces to choose hash join currently, so don't worry the hints will take effect if the semi join is in one apply. if b.TableHints() != nil { + hintInfo := b.TableHints() outerAlias := extractTableAlias(outerPlan, joinPlan.blockOffset) innerAlias := extractTableAlias(innerPlan, joinPlan.blockOffset) - if b.TableHints().ifPreferMergeJoin(outerAlias, innerAlias) { + if hintInfo.ifPreferMergeJoin(outerAlias, innerAlias) { joinPlan.preferJoinType |= preferMergeJoin } - if b.TableHints().ifPreferHashJoin(outerAlias, innerAlias) { + if hintInfo.ifPreferHashJoin(outerAlias, innerAlias) { joinPlan.preferJoinType |= preferHashJoin } - if b.TableHints().ifPreferINLJ(innerAlias) { + if hintInfo.ifPreferINLJ(innerAlias) { joinPlan.preferJoinType = preferRightAsINLJInner } - if b.TableHints().ifPreferINLHJ(innerAlias) { + if hintInfo.ifPreferINLHJ(innerAlias) { joinPlan.preferJoinType = preferRightAsINLHJInner } - if b.TableHints().ifPreferINLMJ(innerAlias) { + if hintInfo.ifPreferINLMJ(innerAlias) { joinPlan.preferJoinType = preferRightAsINLMJInner } + if hintInfo.ifPreferHJBuild(outerAlias) { + joinPlan.preferJoinType |= preferLeftAsHJBuild + } + if hintInfo.ifPreferHJBuild(innerAlias) { + joinPlan.preferJoinType |= preferRightAsHJBuild + } + if hintInfo.ifPreferHJProbe(outerAlias) { + joinPlan.preferJoinType |= preferLeftAsHJProbe + } + if hintInfo.ifPreferHJProbe(innerAlias) { + joinPlan.preferJoinType |= preferRightAsHJProbe + } // If there're multiple join hints, they're conflict. if bits.OnesCount(joinPlan.preferJoinType) > 1 { return nil, errors.New("Join hints are conflict, you can only specify one type of join") @@ -6706,8 +6744,10 @@ func containDifferentJoinTypes(preferJoinType uint) bool { inlMask := preferRightAsINLJInner ^ preferLeftAsINLJInner inlhjMask := preferRightAsINLHJInner ^ preferLeftAsINLHJInner inlmjMask := preferRightAsINLMJInner ^ preferLeftAsINLMJInner + hjRightBuildMask := preferRightAsHJBuild ^ preferLeftAsHJProbe + hjLeftBuildMask := preferLeftAsHJBuild ^ preferRightAsHJProbe - mask := inlMask ^ inlhjMask ^ inlmjMask + mask := inlMask ^ inlhjMask ^ inlmjMask ^ hjRightBuildMask ^ hjLeftBuildMask onesCount := bits.OnesCount(preferJoinType & ^mask) if onesCount > 1 || onesCount == 1 && preferJoinType&mask > 0 { return true @@ -6723,6 +6763,12 @@ func containDifferentJoinTypes(preferJoinType uint) bool { if preferJoinType&inlmjMask > 0 { cnt++ } + if preferJoinType&hjLeftBuildMask > 0 { + cnt++ + } + if preferJoinType&hjRightBuildMask > 0 { + cnt++ + } return cnt > 1 } diff --git a/planner/core/logical_plans.go b/planner/core/logical_plans.go index 0b3751ef20a4f..ed7df91314e47 100644 --- a/planner/core/logical_plans.go +++ b/planner/core/logical_plans.go @@ -115,6 +115,10 @@ const ( preferLeftAsINLMJInner preferRightAsINLMJInner preferHashJoin + preferLeftAsHJBuild + preferRightAsHJBuild + preferLeftAsHJProbe + preferRightAsHJProbe preferMergeJoin preferBCJoin preferRewriteSemiJoin diff --git a/planner/core/physical_plan_test.go b/planner/core/physical_plan_test.go index f48d787005fae..0a32314e52f82 100644 --- a/planner/core/physical_plan_test.go +++ b/planner/core/physical_plan_test.go @@ -1994,6 +1994,39 @@ func TestSkewDistinctAgg(t *testing.T) { } } +func TestHJBuildAndProbeHint(t *testing.T) { + var ( + input []string + output []struct { + SQL string + Plan []string + Result []string + Warning []string + } + ) + planSuiteData := core.GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("create table t1(a int primary key, b int not null)") + tk.MustExec("create table t2(a int primary key, b int not null)") + tk.MustExec("insert into t1 values(1,1),(2,2)") + tk.MustExec("insert into t2 values(1,1),(2,1)") + + for i, ts := range input { + testdata.OnRecord(func() { + output[i].SQL = ts + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + ts).Rows()) + output[i].Result = testdata.ConvertRowsToStrings(tk.MustQuery(ts).Sort().Rows()) + output[i].Warning = testdata.ConvertRowsToStrings(tk.MustQuery("show warnings").Rows()) + }) + tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...)) + tk.MustQuery(ts).Sort().Check(testkit.Rows(output[i].Result...)) + } +} + func TestMPPSinglePartitionType(t *testing.T) { var ( input []string diff --git a/planner/core/planbuilder.go b/planner/core/planbuilder.go index d2ac5b0de2c6b..bf88591adeff5 100644 --- a/planner/core/planbuilder.go +++ b/planner/core/planbuilder.go @@ -99,6 +99,8 @@ type tableHintInfo struct { limitHints limitHintInfo MergeHints MergeHintInfo leadingJoinOrder []hintTableInfo + hjBuildTables []hintTableInfo + hjProbeTables []hintTableInfo } type limitHintInfo struct { @@ -217,6 +219,14 @@ func (info *tableHintInfo) ifPreferHashJoin(tableNames ...*hintTableInfo) bool { return info.matchTableName(tableNames, info.hashJoinTables) } +func (info *tableHintInfo) ifPreferHJBuild(tableNames ...*hintTableInfo) bool { + return info.matchTableName(tableNames, info.hjBuildTables) +} + +func (info *tableHintInfo) ifPreferHJProbe(tableNames ...*hintTableInfo) bool { + return info.matchTableName(tableNames, info.hjProbeTables) +} + func (info *tableHintInfo) ifPreferINLJ(tableNames ...*hintTableInfo) bool { return info.matchTableName(tableNames, info.indexNestedLoopJoinTables.inljTables) } diff --git a/planner/core/testdata/plan_suite_in.json b/planner/core/testdata/plan_suite_in.json index 1430184d43e44..c144fa2f95d77 100644 --- a/planner/core/testdata/plan_suite_in.json +++ b/planner/core/testdata/plan_suite_in.json @@ -828,5 +828,18 @@ "select exists(select /*+ SEMI_JOIN_REWRITE() */ * from t t1 where t1.a=t.a) from t", "select * from t where exists (select /*+ SEMI_JOIN_REWRITE() */ 1 from t t1 where t1.a > t.a)" ] + }, + { + "name": "TestHJBuildAndProbeHint", + "cases": [ + "select /*+ hash_join_build(t2) */ t1.a, t2.a from t1 left join t2 on t1.a=t2.a and t1.b=t2.b", + "select /*+ hash_join_probe(t2) */ t1.a, t2.a from t1 left join t2 on t1.a=t2.a and t1.b=t2.b", + "select /*+ hash_join_build(t1) */ t1.a, t2.a from t1 left join t2 on t1.a=t2.a and t1.b=t2.b", + "select /*+ hash_join_probe(t1) */ t1.a, t2.a from t1 left join t2 on t1.a=t2.a and t1.b=t2.b", + "select /*+ hash_join_build(t2) */ t1.a, t2.a from t1 join t2 on t1.a=t2.a and t1.b=t2.b", + "select /*+ hash_join_probe(t2) */ t1.a, t2.a from t1 join t2 on t1.a=t2.a and t1.b=t2.b", + "select /*+ hash_join_build(t1) */ t1.a, t2.a from t1 join t2 on t1.a=t2.a and t1.b=t2.b", + "select /*+ hash_join_probe(t1) */ t1.a, t2.a from t1 join t2 on t1.a=t2.a and t1.b=t2.b" + ] } ] diff --git a/planner/core/testdata/plan_suite_out.json b/planner/core/testdata/plan_suite_out.json index 7a81174bcfb1b..21a65b206effc 100644 --- a/planner/core/testdata/plan_suite_out.json +++ b/planner/core/testdata/plan_suite_out.json @@ -1597,170 +1597,170 @@ { "Name": "TestCTEMergeHint", "Cases": [ - { - "SQL": "with cte as (select /*+ MERGE()*/ * from tc where tc.a < 60) select * from cte where cte.a <18", - "Plan": [ - "TableReader 4.00 root data:Selection", - "└─Selection 4.00 cop[tikv] lt(test.tc.a, 18), lt(test.tc.a, 60)", - " └─TableFullScan 7.00 cop[tikv] table:tc keep order:false" - ], - "Warning": null - }, - { - "SQL": "with cte as (select * from tc where tc.a < 60) select * from cte where cte.a <18", - "Plan": [ - "Selection 3.20 root lt(test.tc.a, 18)", - "└─CTEFullScan 4.00 root CTE:cte data:CTE_0", - "CTE_0 4.00 root Non-Recursive CTE", - "└─TableReader(Seed Part) 4.00 root data:Selection", - " └─Selection 4.00 cop[tikv] lt(test.tc.a, 18), lt(test.tc.a, 60)", - " └─TableFullScan 7.00 cop[tikv] table:tc keep order:false" - ], - "Warning": null - }, - { - "SQL": "with cte as (select /*+ MERGE() */ * from v) select * from cte", - "Plan": [ - "TableReader 7.00 root data:TableFullScan", - "└─TableFullScan 7.00 cop[tikv] table:tc keep order:false" - ], - "Warning": null - }, - { - "SQL": "WITH cte1 AS (SELECT /*+ MERGE()*/ a FROM tc), cte2 AS (SELECT /*+ MERGE()*/ c FROM te) SELECT * FROM cte1 JOIN cte2 WHERE cte1.a = cte2.c;", - "Plan": [ - "HashJoin 7.00 root inner join, equal:[eq(test.tc.a, test.te.c)]", - "├─TableReader(Build) 7.00 root data:Selection", - "│ └─Selection 7.00 cop[tikv] not(isnull(test.te.c))", - "│ └─TableFullScan 7.00 cop[tikv] table:te keep order:false", - "└─TableReader(Probe) 7.00 root data:Selection", - " └─Selection 7.00 cop[tikv] not(isnull(test.tc.a))", - " └─TableFullScan 7.00 cop[tikv] table:tc keep order:false" - ], - "Warning": null - }, - { - "SQL": "WITH cte1 AS (SELECT a FROM tc), cte2 AS (SELECT /*+ MERGE()*/ c FROM te) SELECT * FROM cte1 JOIN cte2 WHERE cte1.a = cte2.c;", - "Plan": [ - "Projection 4.48 root test.tc.a, test.te.c", - "└─HashJoin 4.48 root inner join, equal:[eq(test.te.c, test.tc.a)]", - " ├─Selection(Build) 4.48 root not(isnull(test.tc.a))", - " │ └─CTEFullScan 5.60 root CTE:cte1 data:CTE_0", - " └─TableReader(Probe) 7.00 root data:Selection", - " └─Selection 7.00 cop[tikv] not(isnull(test.te.c))", - " └─TableFullScan 7.00 cop[tikv] table:te keep order:false", - "CTE_0 5.60 root Non-Recursive CTE", - "└─Selection(Seed Part) 5.60 root not(isnull(test.tc.a))", - " └─TableReader 7.00 root data:TableFullScan", - " └─TableFullScan 7.00 cop[tikv] table:tc keep order:false" - ], - "Warning": null - }, - { - "SQL": "with recursive cte1(c1) as (select 1 union select /*+ MERGE() */ c1 + 1 c1 from cte1 where c1 < 100) select * from cte1;", - "Plan": [ - "CTEFullScan 2.00 root CTE:cte1 data:CTE_0", - "CTE_0 2.00 root Recursive CTE", - "├─Projection(Seed Part) 1.00 root 1->Column#2", - "│ └─TableDual 1.00 root rows:1", - "└─Projection(Recursive Part) 0.80 root cast(plus(Column#3, 1), bigint(1) BINARY)->Column#5", - " └─Selection 0.80 root lt(Column#3, 100)", - " └─CTETable 1.00 root Scan on CTE_0" - ], - "Warning": [ - "[planner:1815]Hint merge() is inapplicable. Please check whether the CTE use recursive." - ] - }, - { - "SQL": "WITH cte1 AS (SELECT * FROM t1) SELECT /*+ MERGE() */ * FROM cte1 join t2 on cte1.a = t2.b;", - "Plan": [ - "Projection 4.48 root test.t1.a, test.t2.b", - "└─HashJoin 4.48 root inner join, equal:[eq(test.t2.b, test.t1.a)]", - " ├─Selection(Build) 4.48 root not(isnull(test.t1.a))", - " │ └─CTEFullScan 5.60 root CTE:cte1 data:CTE_0", - " └─TableReader(Probe) 7.00 root data:Selection", - " └─Selection 7.00 cop[tikv] not(isnull(test.t2.b))", - " └─TableFullScan 7.00 cop[tikv] table:t2 keep order:false", - "CTE_0 5.60 root Non-Recursive CTE", - "└─Selection(Seed Part) 5.60 root not(isnull(test.t1.a))", - " └─TableReader 7.00 root data:TableFullScan", - " └─TableFullScan 7.00 cop[tikv] table:t1 keep order:false" - ], - "Warning": [ - "[planner:1815]Hint merge() is inapplicable. Please check whether the hint is using in the right place, you should use this hint in CTE inner query." - ] - }, - { - "SQL": "with cte1 as (with cte3 as (select /*+ MERGE() */ * from t1),cte4 as (select /*+ MERGE() */ * from t2) select /*+ MERGE() */ * from cte3,cte4) ,cte2 as (select /*+ MERGE() */ * from t3) select * from cte1,cte2;", - "Plan": [ - "HashJoin 343.00 root CARTESIAN inner join", - "├─TableReader(Build) 7.00 root data:TableFullScan", - "│ └─TableFullScan 7.00 cop[tikv] table:t3 keep order:false", - "└─HashJoin(Probe) 49.00 root CARTESIAN inner join", - " ├─TableReader(Build) 7.00 root data:TableFullScan", - " │ └─TableFullScan 7.00 cop[tikv] table:t2 keep order:false", - " └─TableReader(Probe) 7.00 root data:TableFullScan", - " └─TableFullScan 7.00 cop[tikv] table:t1 keep order:false" - ], - "Warning": null - }, - { - "SQL": "with cte1 as (select * from tc), cte2 as (with cte3 as (select /*+ MERGE() */ * from te) ,cte4 as (select * from tc) select * from cte3,cte4) select * from cte2;", - "Plan": [ - "CTEFullScan 49.00 root CTE:cte2 data:CTE_1", - "CTE_1 49.00 root Non-Recursive CTE", - "└─HashJoin(Seed Part) 49.00 root CARTESIAN inner join", - " ├─CTEFullScan(Build) 7.00 root CTE:cte4 data:CTE_3", - " └─TableReader(Probe) 7.00 root data:TableFullScan", - " └─TableFullScan 7.00 cop[tikv] table:te keep order:false", - "CTE_3 7.00 root Non-Recursive CTE", - "└─TableReader(Seed Part) 7.00 root data:TableFullScan", - " └─TableFullScan 7.00 cop[tikv] table:tc keep order:false" - ], - "Warning": null - }, - { - "SQL": "with cte1 as (with cte2 as (select /*+ MERGE() */ * from te) ,cte3 as (select /*+ MERGE() */ * from tc) select /*+ MERGE() */ * from cte2,cte3) select * from cte1;", - "Plan": [ - "HashJoin 49.00 root CARTESIAN inner join", - "├─TableReader(Build) 7.00 root data:TableFullScan", - "│ └─TableFullScan 7.00 cop[tikv] table:tc keep order:false", - "└─TableReader(Probe) 7.00 root data:TableFullScan", - " └─TableFullScan 7.00 cop[tikv] table:te keep order:false" - ], - "Warning": null - }, - { - "SQL": "with cte1 as (select * from t1), cte2 as (with cte3 as (with cte5 as (select * from t2),cte6 as (select * from t3) select * from cte5,cte6) ,cte4 as (select * from t4) select * from cte3,cte4) select * from cte1,cte2;", - "Plan": [ - "HashJoin 1920.80 root CARTESIAN inner join", - "├─CTEFullScan(Build) 5.60 root CTE:cte1 data:CTE_0", - "└─CTEFullScan(Probe) 343.00 root CTE:cte2 data:CTE_1", - "CTE_0 5.60 root Non-Recursive CTE", - "└─Selection(Seed Part) 5.60 root 1", - " └─TableReader 7.00 root data:TableFullScan", - " └─TableFullScan 7.00 cop[tikv] table:t1 keep order:false", - "CTE_1 343.00 root Non-Recursive CTE", - "└─Projection(Seed Part) 343.00 root test.t2.b, test.t3.c, test.t4.d", - " └─HashJoin 343.00 root CARTESIAN inner join", - " ├─CTEFullScan(Build) 7.00 root CTE:cte4 data:CTE_5", - " └─CTEFullScan(Probe) 49.00 root CTE:cte3 data:CTE_2", - "CTE_5 7.00 root Non-Recursive CTE", - "└─TableReader(Seed Part) 7.00 root data:TableFullScan", - " └─TableFullScan 7.00 cop[tikv] table:t4 keep order:false", - "CTE_2 49.00 root Non-Recursive CTE", - "└─HashJoin(Seed Part) 49.00 root CARTESIAN inner join", - " ├─CTEFullScan(Build) 7.00 root CTE:cte6 data:CTE_4", - " └─CTEFullScan(Probe) 7.00 root CTE:cte5 data:CTE_3", - "CTE_4 7.00 root Non-Recursive CTE", - "└─TableReader(Seed Part) 7.00 root data:TableFullScan", - " └─TableFullScan 7.00 cop[tikv] table:t3 keep order:false", - "CTE_3 7.00 root Non-Recursive CTE", - "└─TableReader(Seed Part) 7.00 root data:TableFullScan", - " └─TableFullScan 7.00 cop[tikv] table:t2 keep order:false" - ], - "Warning": null - }, + { + "SQL": "with cte as (select /*+ MERGE()*/ * from tc where tc.a < 60) select * from cte where cte.a <18", + "Plan": [ + "TableReader 4.00 root data:Selection", + "└─Selection 4.00 cop[tikv] lt(test.tc.a, 18), lt(test.tc.a, 60)", + " └─TableFullScan 7.00 cop[tikv] table:tc keep order:false" + ], + "Warning": null + }, + { + "SQL": "with cte as (select * from tc where tc.a < 60) select * from cte where cte.a <18", + "Plan": [ + "Selection 3.20 root lt(test.tc.a, 18)", + "└─CTEFullScan 4.00 root CTE:cte data:CTE_0", + "CTE_0 4.00 root Non-Recursive CTE", + "└─TableReader(Seed Part) 4.00 root data:Selection", + " └─Selection 4.00 cop[tikv] lt(test.tc.a, 18), lt(test.tc.a, 60)", + " └─TableFullScan 7.00 cop[tikv] table:tc keep order:false" + ], + "Warning": null + }, + { + "SQL": "with cte as (select /*+ MERGE() */ * from v) select * from cte", + "Plan": [ + "TableReader 7.00 root data:TableFullScan", + "└─TableFullScan 7.00 cop[tikv] table:tc keep order:false" + ], + "Warning": null + }, + { + "SQL": "WITH cte1 AS (SELECT /*+ MERGE()*/ a FROM tc), cte2 AS (SELECT /*+ MERGE()*/ c FROM te) SELECT * FROM cte1 JOIN cte2 WHERE cte1.a = cte2.c;", + "Plan": [ + "HashJoin 7.00 root inner join, equal:[eq(test.tc.a, test.te.c)]", + "├─TableReader(Build) 7.00 root data:Selection", + "│ └─Selection 7.00 cop[tikv] not(isnull(test.te.c))", + "│ └─TableFullScan 7.00 cop[tikv] table:te keep order:false", + "└─TableReader(Probe) 7.00 root data:Selection", + " └─Selection 7.00 cop[tikv] not(isnull(test.tc.a))", + " └─TableFullScan 7.00 cop[tikv] table:tc keep order:false" + ], + "Warning": null + }, + { + "SQL": "WITH cte1 AS (SELECT a FROM tc), cte2 AS (SELECT /*+ MERGE()*/ c FROM te) SELECT * FROM cte1 JOIN cte2 WHERE cte1.a = cte2.c;", + "Plan": [ + "Projection 4.48 root test.tc.a, test.te.c", + "└─HashJoin 4.48 root inner join, equal:[eq(test.te.c, test.tc.a)]", + " ├─Selection(Build) 4.48 root not(isnull(test.tc.a))", + " │ └─CTEFullScan 5.60 root CTE:cte1 data:CTE_0", + " └─TableReader(Probe) 7.00 root data:Selection", + " └─Selection 7.00 cop[tikv] not(isnull(test.te.c))", + " └─TableFullScan 7.00 cop[tikv] table:te keep order:false", + "CTE_0 5.60 root Non-Recursive CTE", + "└─Selection(Seed Part) 5.60 root not(isnull(test.tc.a))", + " └─TableReader 7.00 root data:TableFullScan", + " └─TableFullScan 7.00 cop[tikv] table:tc keep order:false" + ], + "Warning": null + }, + { + "SQL": "with recursive cte1(c1) as (select 1 union select /*+ MERGE() */ c1 + 1 c1 from cte1 where c1 < 100) select * from cte1;", + "Plan": [ + "CTEFullScan 2.00 root CTE:cte1 data:CTE_0", + "CTE_0 2.00 root Recursive CTE", + "├─Projection(Seed Part) 1.00 root 1->Column#2", + "│ └─TableDual 1.00 root rows:1", + "└─Projection(Recursive Part) 0.80 root cast(plus(Column#3, 1), bigint(1) BINARY)->Column#5", + " └─Selection 0.80 root lt(Column#3, 100)", + " └─CTETable 1.00 root Scan on CTE_0" + ], + "Warning": [ + "[planner:1815]Hint merge() is inapplicable. Please check whether the CTE use recursive." + ] + }, + { + "SQL": "WITH cte1 AS (SELECT * FROM t1) SELECT /*+ MERGE() */ * FROM cte1 join t2 on cte1.a = t2.b;", + "Plan": [ + "Projection 4.48 root test.t1.a, test.t2.b", + "└─HashJoin 4.48 root inner join, equal:[eq(test.t2.b, test.t1.a)]", + " ├─Selection(Build) 4.48 root not(isnull(test.t1.a))", + " │ └─CTEFullScan 5.60 root CTE:cte1 data:CTE_0", + " └─TableReader(Probe) 7.00 root data:Selection", + " └─Selection 7.00 cop[tikv] not(isnull(test.t2.b))", + " └─TableFullScan 7.00 cop[tikv] table:t2 keep order:false", + "CTE_0 5.60 root Non-Recursive CTE", + "└─Selection(Seed Part) 5.60 root not(isnull(test.t1.a))", + " └─TableReader 7.00 root data:TableFullScan", + " └─TableFullScan 7.00 cop[tikv] table:t1 keep order:false" + ], + "Warning": [ + "[planner:1815]Hint merge() is inapplicable. Please check whether the hint is using in the right place, you should use this hint in CTE inner query." + ] + }, + { + "SQL": "with cte1 as (with cte3 as (select /*+ MERGE() */ * from t1),cte4 as (select /*+ MERGE() */ * from t2) select /*+ MERGE() */ * from cte3,cte4) ,cte2 as (select /*+ MERGE() */ * from t3) select * from cte1,cte2;", + "Plan": [ + "HashJoin 343.00 root CARTESIAN inner join", + "├─TableReader(Build) 7.00 root data:TableFullScan", + "│ └─TableFullScan 7.00 cop[tikv] table:t3 keep order:false", + "└─HashJoin(Probe) 49.00 root CARTESIAN inner join", + " ├─TableReader(Build) 7.00 root data:TableFullScan", + " │ └─TableFullScan 7.00 cop[tikv] table:t2 keep order:false", + " └─TableReader(Probe) 7.00 root data:TableFullScan", + " └─TableFullScan 7.00 cop[tikv] table:t1 keep order:false" + ], + "Warning": null + }, + { + "SQL": "with cte1 as (select * from tc), cte2 as (with cte3 as (select /*+ MERGE() */ * from te) ,cte4 as (select * from tc) select * from cte3,cte4) select * from cte2;", + "Plan": [ + "CTEFullScan 49.00 root CTE:cte2 data:CTE_1", + "CTE_1 49.00 root Non-Recursive CTE", + "└─HashJoin(Seed Part) 49.00 root CARTESIAN inner join", + " ├─CTEFullScan(Build) 7.00 root CTE:cte4 data:CTE_3", + " └─TableReader(Probe) 7.00 root data:TableFullScan", + " └─TableFullScan 7.00 cop[tikv] table:te keep order:false", + "CTE_3 7.00 root Non-Recursive CTE", + "└─TableReader(Seed Part) 7.00 root data:TableFullScan", + " └─TableFullScan 7.00 cop[tikv] table:tc keep order:false" + ], + "Warning": null + }, + { + "SQL": "with cte1 as (with cte2 as (select /*+ MERGE() */ * from te) ,cte3 as (select /*+ MERGE() */ * from tc) select /*+ MERGE() */ * from cte2,cte3) select * from cte1;", + "Plan": [ + "HashJoin 49.00 root CARTESIAN inner join", + "├─TableReader(Build) 7.00 root data:TableFullScan", + "│ └─TableFullScan 7.00 cop[tikv] table:tc keep order:false", + "└─TableReader(Probe) 7.00 root data:TableFullScan", + " └─TableFullScan 7.00 cop[tikv] table:te keep order:false" + ], + "Warning": null + }, + { + "SQL": "with cte1 as (select * from t1), cte2 as (with cte3 as (with cte5 as (select * from t2),cte6 as (select * from t3) select * from cte5,cte6) ,cte4 as (select * from t4) select * from cte3,cte4) select * from cte1,cte2;", + "Plan": [ + "HashJoin 1920.80 root CARTESIAN inner join", + "├─CTEFullScan(Build) 5.60 root CTE:cte1 data:CTE_0", + "└─CTEFullScan(Probe) 343.00 root CTE:cte2 data:CTE_1", + "CTE_0 5.60 root Non-Recursive CTE", + "└─Selection(Seed Part) 5.60 root 1", + " └─TableReader 7.00 root data:TableFullScan", + " └─TableFullScan 7.00 cop[tikv] table:t1 keep order:false", + "CTE_1 343.00 root Non-Recursive CTE", + "└─Projection(Seed Part) 343.00 root test.t2.b, test.t3.c, test.t4.d", + " └─HashJoin 343.00 root CARTESIAN inner join", + " ├─CTEFullScan(Build) 7.00 root CTE:cte4 data:CTE_5", + " └─CTEFullScan(Probe) 49.00 root CTE:cte3 data:CTE_2", + "CTE_5 7.00 root Non-Recursive CTE", + "└─TableReader(Seed Part) 7.00 root data:TableFullScan", + " └─TableFullScan 7.00 cop[tikv] table:t4 keep order:false", + "CTE_2 49.00 root Non-Recursive CTE", + "└─HashJoin(Seed Part) 49.00 root CARTESIAN inner join", + " ├─CTEFullScan(Build) 7.00 root CTE:cte6 data:CTE_4", + " └─CTEFullScan(Probe) 7.00 root CTE:cte5 data:CTE_3", + "CTE_4 7.00 root Non-Recursive CTE", + "└─TableReader(Seed Part) 7.00 root data:TableFullScan", + " └─TableFullScan 7.00 cop[tikv] table:t3 keep order:false", + "CTE_3 7.00 root Non-Recursive CTE", + "└─TableReader(Seed Part) 7.00 root data:TableFullScan", + " └─TableFullScan 7.00 cop[tikv] table:t2 keep order:false" + ], + "Warning": null + }, { "SQL": "with cte1 as (select /*+ MERGE() */ * from t1), cte2 as (with cte3 as (with cte5 as (select * from t2),cte6 as (select * from t3) select * from cte5,cte6) ,cte4 as (select * from t4) select * from cte3,cte4) select * from cte1,cte2;", "Plan": [ @@ -1847,34 +1847,34 @@ ], "Warning": null }, - { - "SQL": "with cte2 as (with cte4 as (select * from tc) select * from te, cte4) select * from cte2;", - "Plan": [ - "CTEFullScan 49.00 root CTE:cte2 data:CTE_0", - "CTE_0 49.00 root Non-Recursive CTE", - "└─HashJoin(Seed Part) 49.00 root CARTESIAN inner join", - " ├─CTEFullScan(Build) 7.00 root CTE:cte4 data:CTE_1", - " └─TableReader(Probe) 7.00 root data:TableFullScan", - " └─TableFullScan 7.00 cop[tikv] table:te keep order:false", - "CTE_1 7.00 root Non-Recursive CTE", - "└─TableReader(Seed Part) 7.00 root data:TableFullScan", - " └─TableFullScan 7.00 cop[tikv] table:tc keep order:false" - ], - "Warning": null - }, - { - "SQL": "with cte2 as (with cte4 as (select /*+ merge() */ * from tc) select * from te, cte4) select * from cte2;", - "Plan": [ - "CTEFullScan 49.00 root CTE:cte2 data:CTE_0", - "CTE_0 49.00 root Non-Recursive CTE", - "└─HashJoin(Seed Part) 49.00 root CARTESIAN inner join", - " ├─TableReader(Build) 7.00 root data:TableFullScan", - " │ └─TableFullScan 7.00 cop[tikv] table:tc keep order:false", - " └─TableReader(Probe) 7.00 root data:TableFullScan", - " └─TableFullScan 7.00 cop[tikv] table:te keep order:false" - ], - "Warning": null - }, + { + "SQL": "with cte2 as (with cte4 as (select * from tc) select * from te, cte4) select * from cte2;", + "Plan": [ + "CTEFullScan 49.00 root CTE:cte2 data:CTE_0", + "CTE_0 49.00 root Non-Recursive CTE", + "└─HashJoin(Seed Part) 49.00 root CARTESIAN inner join", + " ├─CTEFullScan(Build) 7.00 root CTE:cte4 data:CTE_1", + " └─TableReader(Probe) 7.00 root data:TableFullScan", + " └─TableFullScan 7.00 cop[tikv] table:te keep order:false", + "CTE_1 7.00 root Non-Recursive CTE", + "└─TableReader(Seed Part) 7.00 root data:TableFullScan", + " └─TableFullScan 7.00 cop[tikv] table:tc keep order:false" + ], + "Warning": null + }, + { + "SQL": "with cte2 as (with cte4 as (select /*+ merge() */ * from tc) select * from te, cte4) select * from cte2;", + "Plan": [ + "CTEFullScan 49.00 root CTE:cte2 data:CTE_0", + "CTE_0 49.00 root Non-Recursive CTE", + "└─HashJoin(Seed Part) 49.00 root CARTESIAN inner join", + " ├─TableReader(Build) 7.00 root data:TableFullScan", + " │ └─TableFullScan 7.00 cop[tikv] table:tc keep order:false", + " └─TableReader(Probe) 7.00 root data:TableFullScan", + " └─TableFullScan 7.00 cop[tikv] table:te keep order:false" + ], + "Warning": null + }, { "SQL": "with cte1 as (with cte2 as (with cte3 as (select /*+ MERGE() */ * from t2) select /*+ MERGE() */ * from cte3) select * from cte2,(select /*+ MERGE() */ * from t1) ttt) select * from cte1;", "Plan": [ @@ -3552,5 +3552,126 @@ "Warning": "[planner:1815]SEMI_JOIN_REWRITE() is inapplicable for SemiJoin with left conditions or other conditions." } ] + }, + { + "Name": "TestHJBuildAndProbeHint", + "Cases": [ + { + "SQL": "select /*+ hash_join_build(t2) */ t1.a, t2.a from t1 left join t2 on t1.a=t2.a and t1.b=t2.b", + "Plan": [ + "HashJoin 12500.00 root left outer join, equal:[eq(test.t1.a, test.t2.a) eq(test.t1.b, test.t2.b)]", + "├─TableReader(Build) 10000.00 root data:TableFullScan", + "│ └─TableFullScan 10000.00 cop[tikv] table:t2 keep order:false, stats:pseudo", + "└─TableReader(Probe) 10000.00 root data:TableFullScan", + " └─TableFullScan 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo" + ], + "Result": [ + "1 1", + "2 " + ], + "Warning": null + }, + { + "SQL": "select /*+ hash_join_probe(t2) */ t1.a, t2.a from t1 left join t2 on t1.a=t2.a and t1.b=t2.b", + "Plan": [ + "HashJoin 12500.00 root left outer join, equal:[eq(test.t1.a, test.t2.a) eq(test.t1.b, test.t2.b)]", + "├─TableReader(Build) 10000.00 root data:TableFullScan", + "│ └─TableFullScan 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo", + "└─TableReader(Probe) 10000.00 root data:TableFullScan", + " └─TableFullScan 10000.00 cop[tikv] table:t2 keep order:false, stats:pseudo" + ], + "Result": [ + "1 1", + "2 " + ], + "Warning": null + }, + { + "SQL": "select /*+ hash_join_build(t1) */ t1.a, t2.a from t1 left join t2 on t1.a=t2.a and t1.b=t2.b", + "Plan": [ + "HashJoin 12500.00 root left outer join, equal:[eq(test.t1.a, test.t2.a) eq(test.t1.b, test.t2.b)]", + "├─TableReader(Build) 10000.00 root data:TableFullScan", + "│ └─TableFullScan 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo", + "└─TableReader(Probe) 10000.00 root data:TableFullScan", + " └─TableFullScan 10000.00 cop[tikv] table:t2 keep order:false, stats:pseudo" + ], + "Result": [ + "1 1", + "2 " + ], + "Warning": null + }, + { + "SQL": "select /*+ hash_join_probe(t1) */ t1.a, t2.a from t1 left join t2 on t1.a=t2.a and t1.b=t2.b", + "Plan": [ + "HashJoin 12500.00 root left outer join, equal:[eq(test.t1.a, test.t2.a) eq(test.t1.b, test.t2.b)]", + "├─TableReader(Build) 10000.00 root data:TableFullScan", + "│ └─TableFullScan 10000.00 cop[tikv] table:t2 keep order:false, stats:pseudo", + "└─TableReader(Probe) 10000.00 root data:TableFullScan", + " └─TableFullScan 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo" + ], + "Result": [ + "1 1", + "2 " + ], + "Warning": null + }, + { + "SQL": "select /*+ hash_join_build(t2) */ t1.a, t2.a from t1 join t2 on t1.a=t2.a and t1.b=t2.b", + "Plan": [ + "HashJoin 12500.00 root inner join, equal:[eq(test.t1.a, test.t2.a) eq(test.t1.b, test.t2.b)]", + "├─TableReader(Build) 10000.00 root data:TableFullScan", + "│ └─TableFullScan 10000.00 cop[tikv] table:t2 keep order:false, stats:pseudo", + "└─TableReader(Probe) 10000.00 root data:TableFullScan", + " └─TableFullScan 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo" + ], + "Result": [ + "1 1" + ], + "Warning": null + }, + { + "SQL": "select /*+ hash_join_probe(t2) */ t1.a, t2.a from t1 join t2 on t1.a=t2.a and t1.b=t2.b", + "Plan": [ + "HashJoin 12500.00 root inner join, equal:[eq(test.t1.a, test.t2.a) eq(test.t1.b, test.t2.b)]", + "├─TableReader(Build) 10000.00 root data:TableFullScan", + "│ └─TableFullScan 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo", + "└─TableReader(Probe) 10000.00 root data:TableFullScan", + " └─TableFullScan 10000.00 cop[tikv] table:t2 keep order:false, stats:pseudo" + ], + "Result": [ + "1 1" + ], + "Warning": null + }, + { + "SQL": "select /*+ hash_join_build(t1) */ t1.a, t2.a from t1 join t2 on t1.a=t2.a and t1.b=t2.b", + "Plan": [ + "HashJoin 12500.00 root inner join, equal:[eq(test.t1.a, test.t2.a) eq(test.t1.b, test.t2.b)]", + "├─TableReader(Build) 10000.00 root data:TableFullScan", + "│ └─TableFullScan 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo", + "└─TableReader(Probe) 10000.00 root data:TableFullScan", + " └─TableFullScan 10000.00 cop[tikv] table:t2 keep order:false, stats:pseudo" + ], + "Result": [ + "1 1" + ], + "Warning": null + }, + { + "SQL": "select /*+ hash_join_probe(t1) */ t1.a, t2.a from t1 join t2 on t1.a=t2.a and t1.b=t2.b", + "Plan": [ + "HashJoin 12500.00 root inner join, equal:[eq(test.t1.a, test.t2.a) eq(test.t1.b, test.t2.b)]", + "├─TableReader(Build) 10000.00 root data:TableFullScan", + "│ └─TableFullScan 10000.00 cop[tikv] table:t2 keep order:false, stats:pseudo", + "└─TableReader(Probe) 10000.00 root data:TableFullScan", + " └─TableFullScan 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo" + ], + "Result": [ + "1 1" + ], + "Warning": null + } + ] } ]