From 5a4f3c92a282c6820d335c729d023be3c898de12 Mon Sep 17 00:00:00 2001 From: Yuanjia Zhang Date: Tue, 25 Jul 2023 23:16:34 +0800 Subject: [PATCH] This is an automated cherry-pick of #45562 Signed-off-by: ti-chi-bot --- planner/core/casetest/rule/BUILD.bazel | 35 ++ .../core/casetest/rule_join_reorder_test.go | 25 ++ .../testdata/join_reorder_suite_in.json | 35 ++ .../testdata/join_reorder_suite_out.json | 315 ++++++++++++++++++ planner/core/exhaust_physical_plans.go | 9 + planner/core/logical_plan_builder.go | 36 ++ planner/core/logical_plans.go | 1 + planner/core/planbuilder.go | 16 + 8 files changed, 472 insertions(+) create mode 100644 planner/core/casetest/rule/BUILD.bazel diff --git a/planner/core/casetest/rule/BUILD.bazel b/planner/core/casetest/rule/BUILD.bazel new file mode 100644 index 0000000000000..50d121108f1d1 --- /dev/null +++ b/planner/core/casetest/rule/BUILD.bazel @@ -0,0 +1,35 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "rule_test", + timeout = "short", + srcs = [ + "main_test.go", + "rule_derive_topn_from_window_test.go", + "rule_inject_extra_projection_test.go", + "rule_join_reorder_test.go", + "rule_result_reorder_test.go", + ], + data = glob(["testdata/**"]), + flaky = True, + shard_count = 22, + deps = [ + "//domain", + "//expression", + "//expression/aggregation", + "//parser/ast", + "//parser/model", + "//parser/mysql", + "//planner/core/internal", + "//sessionctx/variable", + "//testkit", + "//testkit/testdata", + "//testkit/testmain", + "//testkit/testsetup", + "//types", + "//util/mock", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/planner/core/casetest/rule_join_reorder_test.go b/planner/core/casetest/rule_join_reorder_test.go index 2b0529da5c367..4880dfe62fa8c 100644 --- a/planner/core/casetest/rule_join_reorder_test.go +++ b/planner/core/casetest/rule_join_reorder_test.go @@ -61,6 +61,31 @@ func TestStraightJoinHint(t *testing.T) { runJoinReorderTestData(t, tk, "TestStraightJoinHint") } +<<<<<<< HEAD:planner/core/casetest/rule_join_reorder_test.go +======= +func TestNoHashJoinHint(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t1(a int, b int, key(a));") + tk.MustExec("create table t2(a int, b int, key(a));") + tk.MustExec("create table t3(a int, b int, key(a));") + tk.MustExec("create table t4(a int, b int, key(a));") + 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") +} + +>>>>>>> ef27b0ef7a6 (planner: support `no_merge_join` hint on optimizer (#45562)):planner/core/casetest/rule/rule_join_reorder_test.go func TestLeadingJoinHint(t *testing.T) { store := testkit.CreateMockStore(t) diff --git a/planner/core/casetest/testdata/join_reorder_suite_in.json b/planner/core/casetest/testdata/join_reorder_suite_in.json index 86090cc408d9f..d6d8e603f4afb 100644 --- a/planner/core/casetest/testdata/join_reorder_suite_in.json +++ b/planner/core/casetest/testdata/join_reorder_suite_in.json @@ -31,6 +31,41 @@ ] }, { +<<<<<<< HEAD:planner/core/casetest/testdata/join_reorder_suite_in.json +======= + "name": "TestNoHashJoinHint", + "cases": [ + "select /*+ no_hash_join() */ * from t1, t2", + "select /*+ no_hash_join(t1), hash_join(t1) */ * from t1, t2", + "select /*+ no_hash_join(t1), hash_join(t2) */ * from t1, t2", + "select /*+ no_hash_join(t1) */ * from t1, t2", + "select /*+ no_hash_join(t1, t2) */ * from t1, t2", + "select /*+ no_hash_join(t1) */ * from t1, t2 where t1.a=t2.a", + "select /*+ no_hash_join(t1, t2) */ * from t1, t2 where t1.b=t2.b", + "select /*+ no_hash_join(t1) */ * from t1, t2 where t1.a=t2.a and t1.b=t2.b", + "select /*+ no_hash_join(t2) */ * from t1 left join t2 on t1.b=t2.b", + "select /*+ no_hash_join(t2) */ * from t1 left join t2 on t1.a=t2.a", + "select /*+ no_hash_join(t2) */ * from t1 right join t2 on t1.b=t2.b", + "select /*+ no_hash_join(t2) */ * from t1 right join t2 on t1.a=t2.a", + "select /*+ leading(t4, t3, t2, t1), no_hash_join(t2, t3) */ * from t1, t2, t3, t4", + "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" + ] + }, + { +>>>>>>> ef27b0ef7a6 (planner: support `no_merge_join` hint on optimizer (#45562)):planner/core/casetest/rule/testdata/join_reorder_suite_in.json "name": "TestLeadingJoinHint", "cases": [ "select /*+ leading(t, t1) */ * from t, t1, t2, t3 where t.a = t1.a and t1.b=t2.b;", diff --git a/planner/core/casetest/testdata/join_reorder_suite_out.json b/planner/core/casetest/testdata/join_reorder_suite_out.json index 1c8059e0def40..b31f48803c1c6 100644 --- a/planner/core/casetest/testdata/join_reorder_suite_out.json +++ b/planner/core/casetest/testdata/join_reorder_suite_out.json @@ -584,6 +584,321 @@ ] }, { +<<<<<<< HEAD:planner/core/casetest/testdata/join_reorder_suite_out.json +======= + "Name": "TestNoHashJoinHint", + "Cases": [ + { + "SQL": "select /*+ no_hash_join() */ * from t1, t2", + "Plan": [ + "HashJoin 100000000.00 root CARTESIAN inner join", + "├─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" + ], + "Warning": [ + "Warning 1815 Hint no_hash_join() is inapplicable. Please specify the table names in the arguments." + ] + }, + { + "SQL": "select /*+ no_hash_join(t1), hash_join(t1) */ * from t1, t2", + "Plan": [ + "HashJoin 100000000.00 root CARTESIAN inner join", + "├─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" + ], + "Warning": [ + "Warning 1815 Some HASH_JOIN and NO_HASH_JOIN hints conflict, NO_HASH_JOIN is ignored" + ] + }, + { + "SQL": "select /*+ no_hash_join(t1), hash_join(t2) */ * from t1, t2", + "Plan": [ + "HashJoin 100000000.00 root CARTESIAN inner join", + "├─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" + ], + "Warning": [ + "Warning 1815 Some HASH_JOIN and NO_HASH_JOIN hints conflict, NO_HASH_JOIN is ignored" + ] + }, + { + "SQL": "select /*+ no_hash_join(t1) */ * from t1, t2", + "Plan": [ + "MergeJoin 100000000.00 root inner join", + "├─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" + ], + "Warning": null + }, + { + "SQL": "select /*+ no_hash_join(t1, t2) */ * from t1, t2", + "Plan": [ + "MergeJoin 100000000.00 root inner join", + "├─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" + ], + "Warning": null + }, + { + "SQL": "select /*+ no_hash_join(t1) */ * from t1, t2 where t1.a=t2.a", + "Plan": [ + "IndexHashJoin 12487.50 root inner join, inner:IndexLookUp, outer key:test.t1.a, inner key:test.t2.a, equal cond:eq(test.t1.a, test.t2.a)", + "├─TableReader(Build) 9990.00 root data:Selection", + "│ └─Selection 9990.00 cop[tikv] not(isnull(test.t1.a))", + "│ └─TableFullScan 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo", + "└─IndexLookUp(Probe) 12487.50 root ", + " ├─Selection(Build) 12487.50 cop[tikv] not(isnull(test.t2.a))", + " │ └─IndexRangeScan 12500.00 cop[tikv] table:t2, index:a(a) range: decided by [eq(test.t2.a, test.t1.a)], keep order:false, stats:pseudo", + " └─TableRowIDScan(Probe) 12487.50 cop[tikv] table:t2 keep order:false, stats:pseudo" + ], + "Warning": null + }, + { + "SQL": "select /*+ no_hash_join(t1, t2) */ * from t1, t2 where t1.b=t2.b", + "Plan": [ + "MergeJoin 12487.50 root inner join, left key:test.t1.b, right key:test.t2.b", + "├─Sort(Build) 9990.00 root test.t2.b", + "│ └─TableReader 9990.00 root data:Selection", + "│ └─Selection 9990.00 cop[tikv] not(isnull(test.t2.b))", + "│ └─TableFullScan 10000.00 cop[tikv] table:t2 keep order:false, stats:pseudo", + "└─Sort(Probe) 9990.00 root test.t1.b", + " └─TableReader 9990.00 root data:Selection", + " └─Selection 9990.00 cop[tikv] not(isnull(test.t1.b))", + " └─TableFullScan 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo" + ], + "Warning": null + }, + { + "SQL": "select /*+ no_hash_join(t1) */ * from t1, t2 where t1.a=t2.a and t1.b=t2.b", + "Plan": [ + "IndexHashJoin 12475.01 root inner join, inner:IndexLookUp, outer key:test.t1.a, inner key:test.t2.a, equal cond:eq(test.t1.a, test.t2.a), eq(test.t1.b, test.t2.b)", + "├─TableReader(Build) 9980.01 root data:Selection", + "│ └─Selection 9980.01 cop[tikv] not(isnull(test.t1.a)), not(isnull(test.t1.b))", + "│ └─TableFullScan 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo", + "└─IndexLookUp(Probe) 12475.01 root ", + " ├─Selection(Build) 12487.50 cop[tikv] not(isnull(test.t2.a))", + " │ └─IndexRangeScan 12500.00 cop[tikv] table:t2, index:a(a) range: decided by [eq(test.t2.a, test.t1.a)], keep order:false, stats:pseudo", + " └─Selection(Probe) 12475.01 cop[tikv] not(isnull(test.t2.b))", + " └─TableRowIDScan 12487.50 cop[tikv] table:t2 keep order:false, stats:pseudo" + ], + "Warning": null + }, + { + "SQL": "select /*+ no_hash_join(t2) */ * from t1 left join t2 on t1.b=t2.b", + "Plan": [ + "MergeJoin 12487.50 root left outer join, left key:test.t1.b, right key:test.t2.b", + "├─Sort(Build) 9990.00 root test.t2.b", + "│ └─TableReader 9990.00 root data:Selection", + "│ └─Selection 9990.00 cop[tikv] not(isnull(test.t2.b))", + "│ └─TableFullScan 10000.00 cop[tikv] table:t2 keep order:false, stats:pseudo", + "└─Sort(Probe) 10000.00 root test.t1.b", + " └─TableReader 10000.00 root data:TableFullScan", + " └─TableFullScan 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo" + ], + "Warning": null + }, + { + "SQL": "select /*+ no_hash_join(t2) */ * from t1 left join t2 on t1.a=t2.a", + "Plan": [ + "IndexHashJoin 12487.50 root left outer join, inner:IndexLookUp, outer key:test.t1.a, inner key:test.t2.a, equal cond:eq(test.t1.a, test.t2.a)", + "├─TableReader(Build) 10000.00 root data:TableFullScan", + "│ └─TableFullScan 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo", + "└─IndexLookUp(Probe) 12487.50 root ", + " ├─Selection(Build) 12487.50 cop[tikv] not(isnull(test.t2.a))", + " │ └─IndexRangeScan 12500.00 cop[tikv] table:t2, index:a(a) range: decided by [eq(test.t2.a, test.t1.a)], keep order:false, stats:pseudo", + " └─TableRowIDScan(Probe) 12487.50 cop[tikv] table:t2 keep order:false, stats:pseudo" + ], + "Warning": null + }, + { + "SQL": "select /*+ no_hash_join(t2) */ * from t1 right join t2 on t1.b=t2.b", + "Plan": [ + "MergeJoin 12487.50 root right outer join, left key:test.t1.b, right key:test.t2.b", + "├─Sort(Build) 9990.00 root test.t1.b", + "│ └─TableReader 9990.00 root data:Selection", + "│ └─Selection 9990.00 cop[tikv] not(isnull(test.t1.b))", + "│ └─TableFullScan 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo", + "└─Sort(Probe) 10000.00 root test.t2.b", + " └─TableReader 10000.00 root data:TableFullScan", + " └─TableFullScan 10000.00 cop[tikv] table:t2 keep order:false, stats:pseudo" + ], + "Warning": null + }, + { + "SQL": "select /*+ no_hash_join(t2) */ * from t1 right join t2 on t1.a=t2.a", + "Plan": [ + "IndexHashJoin 12487.50 root right outer join, inner:IndexLookUp, outer key:test.t2.a, inner key:test.t1.a, equal cond:eq(test.t2.a, test.t1.a)", + "├─TableReader(Build) 10000.00 root data:TableFullScan", + "│ └─TableFullScan 10000.00 cop[tikv] table:t2 keep order:false, stats:pseudo", + "└─IndexLookUp(Probe) 12487.50 root ", + " ├─Selection(Build) 12487.50 cop[tikv] not(isnull(test.t1.a))", + " │ └─IndexRangeScan 12500.00 cop[tikv] table:t1, index:a(a) range: decided by [eq(test.t1.a, test.t2.a)], keep order:false, stats:pseudo", + " └─TableRowIDScan(Probe) 12487.50 cop[tikv] table:t1 keep order:false, stats:pseudo" + ], + "Warning": null + }, + { + "SQL": "select /*+ leading(t4, t3, t2, t1), no_hash_join(t2, t3) */ * from t1, t2, t3, t4", + "Plan": [ + "Projection 10000000000000000.00 root test.t1.a, test.t1.b, test.t2.a, test.t2.b, test.t3.a, test.t3.b, test.t4.a, test.t4.b", + "└─HashJoin 10000000000000000.00 root CARTESIAN inner join", + " ├─TableReader(Build) 10000.00 root data:TableFullScan", + " │ └─TableFullScan 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo", + " └─MergeJoin(Probe) 1000000000000.00 root inner join", + " ├─TableReader(Build) 10000.00 root data:TableFullScan", + " │ └─TableFullScan 10000.00 cop[tikv] table:t2 keep order:false, stats:pseudo", + " └─MergeJoin(Probe) 100000000.00 root inner join", + " ├─TableReader(Build) 10000.00 root data:TableFullScan", + " │ └─TableFullScan 10000.00 cop[tikv] table:t3 keep order:false, stats:pseudo", + " └─TableReader(Probe) 10000.00 root data:TableFullScan", + " └─TableFullScan 10000.00 cop[tikv] table:t4 keep order:false, stats:pseudo" + ], + "Warning": null + }, + { + "SQL": "select /*+ leading(t1, t2, t3, t4), hash_join(t1, t2), no_hash_join(t3), hash_join(t4) */ * from t1, t2, t3, t4", + "Plan": [ + "HashJoin 10000000000000000.00 root CARTESIAN inner join", + "├─TableReader(Build) 10000.00 root data:TableFullScan", + "│ └─TableFullScan 10000.00 cop[tikv] table:t4 keep order:false, stats:pseudo", + "└─MergeJoin(Probe) 1000000000000.00 root inner join", + " ├─TableReader(Build) 10000.00 root data:TableFullScan", + " │ └─TableFullScan 10000.00 cop[tikv] table:t3 keep order:false, stats:pseudo", + " └─HashJoin(Probe) 100000000.00 root CARTESIAN inner join", + " ├─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" + ], + "Warning": null + } + ] + }, + { + "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 + } + ] + }, + { +>>>>>>> ef27b0ef7a6 (planner: support `no_merge_join` hint on optimizer (#45562)):planner/core/casetest/rule/testdata/join_reorder_suite_out.json "Name": "TestLeadingJoinHint", "Cases": [ { diff --git a/planner/core/exhaust_physical_plans.go b/planner/core/exhaust_physical_plans.go index ae23a2949ca3e..1ca45e415d8e0 100644 --- a/planner/core/exhaust_physical_plans.go +++ b/planner/core/exhaust_physical_plans.go @@ -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 { diff --git a/planner/core/logical_plan_builder.go b/planner/core/logical_plan_builder.go index d2f60866c52bf..584ce3f1d8423 100644 --- a/planner/core/logical_plan_builder.go +++ b/planner/core/logical_plan_builder.go @@ -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" @@ -601,6 +603,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 @@ -3710,6 +3720,10 @@ func (b *PlanBuilder) pushTableHints(hints []*ast.TableOptimizerHint, currentLev hints = b.hintProcessor.GetCurrentStmtHints(hints, currentLevel) var ( sortMergeTables, inljTables, inlhjTables, inlmjTables, hashJoinTables, bcTables []hintTableInfo +<<<<<<< HEAD +======= + noHashJoinTables, noMergeJoinTables []hintTableInfo +>>>>>>> ef27b0ef7a6 (planner: support `no_merge_join` hint on optimizer (#45562)) shuffleJoinTables []hintTableInfo indexHintList, indexMergeHintList []indexHintInfo tiflashTables, tikvTables []hintTableInfo @@ -3724,7 +3738,11 @@ 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 { +<<<<<<< HEAD case TiDBMergeJoin, HintSMJ, TiDBIndexNestedLoopJoin, HintINLJ, HintINLHJ, HintINLMJ, +======= + case TiDBMergeJoin, HintSMJ, TiDBIndexNestedLoopJoin, HintINLJ, HintINLHJ, HintINLMJ, HintNoHashJoin, HintNoMergeJoin, +>>>>>>> ef27b0ef7a6 (planner: support `no_merge_join` hint on optimizer (#45562)) TiDBHashJoin, HintHJ, HintUseIndex, HintIgnoreIndex, HintForceIndex, HintOrderIndex, HintNoOrderIndex, HintIndexMerge, HintLeading: if len(hint.Tables) == 0 { b.pushHintWithoutTableWarning(hint) @@ -3747,6 +3765,13 @@ 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)...) +<<<<<<< HEAD +======= + 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)...) +>>>>>>> ef27b0ef7a6 (planner: support `no_merge_join` hint on optimizer (#45562)) case HintMPP1PhaseAgg: aggHints.preferAggType |= preferMPP1PhaseAgg case HintMPP2PhaseAgg: @@ -3857,6 +3882,11 @@ func (b *PlanBuilder) pushTableHints(hints []*ast.TableOptimizerHint, currentLev shuffleJoinTables: shuffleJoinTables, indexNestedLoopJoinTables: indexNestedLoopJoinTables{inljTables, inlhjTables, inlmjTables}, hashJoinTables: hashJoinTables, +<<<<<<< HEAD +======= + noHashJoinTables: noHashJoinTables, + noMergeJoinTables: noMergeJoinTables, +>>>>>>> ef27b0ef7a6 (planner: support `no_merge_join` hint on optimizer (#45562)) indexHintList: indexHintList, tiflashTables: tiflashTables, tikvTables: tikvTables, @@ -7062,6 +7092,12 @@ func getInnerFromParenthesesAndUnaryPlus(expr ast.ExprNode) ast.ExprNode { // containDifferentJoinTypes checks whether `preferJoinType` contains different // join types. func containDifferentJoinTypes(preferJoinType uint) bool { +<<<<<<< HEAD +======= + preferJoinType &= ^preferNoHashJoin + preferJoinType &= ^preferNoMergeJoin + +>>>>>>> ef27b0ef7a6 (planner: support `no_merge_join` hint on optimizer (#45562)) inlMask := preferRightAsINLJInner ^ preferLeftAsINLJInner inlhjMask := preferRightAsINLHJInner ^ preferLeftAsINLHJInner inlmjMask := preferRightAsINLMJInner ^ preferLeftAsINLMJInner diff --git a/planner/core/logical_plans.go b/planner/core/logical_plans.go index ecfd09446f1cd..c6816b34593c9 100644 --- a/planner/core/logical_plans.go +++ b/planner/core/logical_plans.go @@ -119,6 +119,7 @@ const ( preferHJProbe preferHashJoin preferMergeJoin + preferNoMergeJoin preferBCJoin preferShuffleJoin preferRewriteSemiJoin diff --git a/planner/core/planbuilder.go b/planner/core/planbuilder.go index 8206a615ca76a..7786c83653d6a 100644 --- a/planner/core/planbuilder.go +++ b/planner/core/planbuilder.go @@ -93,6 +93,11 @@ type tableHintInfo struct { broadcastJoinTables []hintTableInfo shuffleJoinTables []hintTableInfo hashJoinTables []hintTableInfo +<<<<<<< HEAD +======= + noHashJoinTables []hintTableInfo + noMergeJoinTables []hintTableInfo +>>>>>>> ef27b0ef7a6 (planner: support `no_merge_join` hint on optimizer (#45562)) indexHintList []indexHintInfo tiflashTables []hintTableInfo tikvTables []hintTableInfo @@ -237,6 +242,17 @@ func (info *tableHintInfo) ifPreferHashJoin(tableNames ...*hintTableInfo) bool { return info.matchTableName(tableNames, info.hashJoinTables) } +<<<<<<< HEAD +======= +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) +} + +>>>>>>> ef27b0ef7a6 (planner: support `no_merge_join` hint on optimizer (#45562)) func (info *tableHintInfo) ifPreferHJBuild(tableNames ...*hintTableInfo) bool { return info.matchTableName(tableNames, info.hjBuildTables) }