From aa37fbac88a0d379d2ba3946aa70add0a89658bb Mon Sep 17 00:00:00 2001 From: Yuanjia Zhang Date: Tue, 25 Jul 2023 23:16:34 +0800 Subject: [PATCH 1/4] This is an automated cherry-pick of #45562 Signed-off-by: ti-chi-bot --- planner/core/casetest/rule/BUILD.bazel | 35 ++ .../casetest/rule/rule_join_reorder_test.go | 327 ++++++++++++++++++ planner/core/exhaust_physical_plans.go | 9 + planner/core/logical_plan_builder.go | 18 +- planner/core/logical_plans.go | 1 + planner/core/planbuilder.go | 5 + .../core/testdata/join_reorder_suite_in.json | 13 + .../core/testdata/join_reorder_suite_out.json | 113 ++++++ 8 files changed, 519 insertions(+), 2 deletions(-) create mode 100644 planner/core/casetest/rule/BUILD.bazel create mode 100644 planner/core/casetest/rule/rule_join_reorder_test.go 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/rule_join_reorder_test.go b/planner/core/casetest/rule/rule_join_reorder_test.go new file mode 100644 index 0000000000000..aa3c49eae8cfb --- /dev/null +++ b/planner/core/casetest/rule/rule_join_reorder_test.go @@ -0,0 +1,327 @@ +// Copyright 2023 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package rule + +import ( + "testing" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/domain" + "github.com/pingcap/tidb/parser/model" + "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/testkit/testdata" + "github.com/stretchr/testify/require" +) + +func runJoinReorderTestData(t *testing.T, tk *testkit.TestKit, name string) { + var input []string + var output []struct { + SQL string + Plan []string + Warning []string + } + joinReorderSuiteData := GetJoinReorderSuiteData() + joinReorderSuiteData.LoadTestCasesByName(name, t, &input, &output) + require.Equal(t, len(input), len(output)) + for i := range input { + testdata.OnRecord(func() { + output[i].SQL = input[i] + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + input[i]).Rows()) + output[i].Warning = testdata.ConvertRowsToStrings(tk.MustQuery("show warnings").Rows()) + }) + tk.MustQuery("explain format = 'brief' " + input[i]).Check(testkit.Rows(output[i].Plan...)) + tk.MustQuery("show warnings").Check(testkit.Rows(output[i].Warning...)) + } +} + +func TestStraightJoinHint(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("drop table if exists t, t1, t2, t3, t4;") + tk.MustExec("create table t(a int, b int, key(a));") + 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, "TestStraightJoinHint") +} + +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") +} + +func TestLeadingJoinHint(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("drop table if exists t, t1, t2, t3, t4, t5, t6, t7, t8;") + tk.MustExec("create table t(a int, b int, key(a));") + 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));") + tk.MustExec("create table t5(a int, b int, key(a));") + tk.MustExec("create table t6(a int, b int, key(a));") + tk.MustExec("create table t7(a int, b int, key(a));") + tk.MustExec("create table t8(a int, b int, key(a));") + runJoinReorderTestData(t, tk, "TestLeadingJoinHint") + + // test cases for multiple leading hints + tk.MustExec("select /*+ leading(t1) leading(t2) */ * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 We can only use one leading hint at most, when multiple leading hints are used, all leading hints will be invalid")) +} + +func TestJoinOrderHint(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t, t1, t2, t3, t4, t5, t6, t7, t8;") + tk.MustExec("create table t(a int, b int, key(a));") + 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));") + tk.MustExec("create table t5(a int, b int, key(a));") + tk.MustExec("create table t6(a int, b int, key(a));") + tk.MustExec("create table t7(a int, b int, key(a));") + tk.MustExec("create table t8(a int, b int, key(a));") + + // test cases for using the leading hint and straight_join hint at the same time + tk.MustExec("select /*+ leading(t1) straight_join() */ * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 We can only use the straight_join hint, when we use the leading hint and straight_join hint at the same time, all leading hints will be invalid")) + + tk.MustExec("select /*+ straight_join() leading(t1) */ * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 We can only use the straight_join hint, when we use the leading hint and straight_join hint at the same time, all leading hints will be invalid")) + + // more join order hints appear in the same time + tk.MustExec("select /*+ leading(t1) leading(t1) */ * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 We can only use one leading hint at most, when multiple leading hints are used, all leading hints will be invalid")) + + tk.MustExec("select /*+ leading(t1) leading(t2) */ * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 We can only use one leading hint at most, when multiple leading hints are used, all leading hints will be invalid")) + + tk.MustExec("select /*+ straight_join() straight_join() */ * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 STRAIGHT_JOIN() is defined more than once, only the last definition takes effect")) + + // test cases for table name in hint + // the same table appears in the leading hint + tk.MustExec("select /*+ leading(t1, t1) */ * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 There are no matching table names for (t1) in optimizer hint /*+ LEADING(t1, t1) */. Maybe you can use the table alias name", + "Warning 1815 leading hint is inapplicable, check if the leading hint table is valid")) + + tk.MustExec("select /*+ leading(t1, t2, t1) */ * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 There are no matching table names for (t1) in optimizer hint /*+ LEADING(t1, t2, t1) */. Maybe you can use the table alias name", + "Warning 1815 leading hint is inapplicable, check if the leading hint table is valid")) + + // the wrong table appears in the leading hint + tk.MustExec("select /*+ leading(t) */ * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 There are no matching table names for (t) in optimizer hint /*+ LEADING(t) */. Maybe you can use the table alias name")) + + tk.MustExec("select /*+ leading(t1, t2, t) */ * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 There are no matching table names for (t) in optimizer hint /*+ LEADING(t1, t2, t) */. Maybe you can use the table alias name", + "Warning 1815 leading hint is inapplicable, check if the leading hint table is valid")) + + // table alias in the leading hint + tk.MustExec("select /*+ leading(t) */ * from t1 t join t2 on t.a=t2.a join t3 on t2.b=t3.b") + tk.MustQuery("show warnings").Check(testkit.Rows()) + + tk.MustExec("select /*+ leading(t1) */ * from t1 t join t2 on t.a=t2.a join t3 on t2.b=t3.b") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 There are no matching table names for (t1) in optimizer hint /*+ LEADING(t1) */. Maybe you can use the table alias name")) + + tk.MustExec("select /*+ leading(t2, t) */ * from t1 t join t2 on t.a=t2.a join t3 on t2.b=t3.b") + tk.MustQuery("show warnings").Check(testkit.Rows()) + + tk.MustExec("select /*+ leading(t2, t1) */ * from t1 t join t2 on t.a=t2.a join t3 on t2.b=t3.b") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 There are no matching table names for (t1) in optimizer hint /*+ LEADING(t2, t1) */. Maybe you can use the table alias name", + "Warning 1815 leading hint is inapplicable, check if the leading hint table is valid")) + + // table name in leading hint cross query block + // Todo: Can not handle this case yet. Because when we extract the join group, it will get the join group {t1, t2, t3}. + // So the table 't4' can not be used. + tk.MustExec("select /*+ leading(t4) */ * from (select t2.b from t1 join t2 on t1.a=t2.a) t4 join t3 on t4.b=t3.b") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 leading hint is inapplicable, check if the leading hint table is valid")) + + tk.MustExec("select /*+ leading(t3, t2@sel_2) */ * from (select t2.b from t1 join t2 on t1.a=t2.a) t4 join t3 on t4.b=t3.b") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 There are no matching table names for (t2) in optimizer hint /*+ LEADING(t3, t2) */. Maybe you can use the table alias name")) + + tk.MustExec("select * from (select /*+ leading(t1, t3@sel_1) */ t2.b from t1 join t2 on t1.a=t2.a) t4 join t3 on t4.b=t3.b") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 There are no matching table names for (t3) in optimizer hint /*+ LEADING(t1, t3) */. Maybe you can use the table alias name")) + + tk.MustExec("select /*+ leading(t3) */ * from (select /*+ leading(t1) */ t2.b from t1 join t2 on t1.a=t2.a) t4 join t3 on t4.b=t3.b") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 We can only use one leading hint at most, when multiple leading hints are used, all leading hints will be invalid")) + + runJoinReorderTestData(t, tk, "TestJoinOrderHint") +} + +func TestJoinOrderHint4StaticPartitionTable(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("drop table if exists t, t1, t2, t3;") + tk.MustExec(`create table t(a int, b int) partition by hash(a) partitions 3`) + tk.MustExec(`create table t1(a int, b int) partition by hash(a) partitions 4`) + tk.MustExec(`create table t2(a int, b int) partition by hash(a) partitions 5`) + tk.MustExec(`create table t3(a int, b int) partition by hash(b) partitions 3`) + tk.MustExec(`create table t4(a int, b int) partition by hash(a) partitions 4`) + tk.MustExec(`create table t5(a int, b int) partition by hash(a) partitions 5`) + tk.MustExec(`create table t6(a int, b int) partition by hash(b) partitions 3`) + + tk.MustExec(`set @@tidb_partition_prune_mode="static"`) + tk.MustExec("set @@tidb_enable_outer_join_reorder=true") + runJoinReorderTestData(t, tk, "TestJoinOrderHint4StaticPartitionTable") +} + +func TestJoinOrderHint4DynamicPartitionTable(t *testing.T) { + failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t, t1, t2, t3;") + tk.MustExec(`create table t(a int, b int) partition by hash(a) partitions 3`) + tk.MustExec(`create table t1(a int, b int) partition by hash(a) partitions 4`) + tk.MustExec(`create table t2(a int, b int) partition by hash(a) partitions 5`) + tk.MustExec(`create table t3(a int, b int) partition by hash(b) partitions 3`) + tk.MustExec(`create table t4(a int, b int) partition by hash(a) partitions 4`) + tk.MustExec(`create table t5(a int, b int) partition by hash(a) partitions 5`) + tk.MustExec(`create table t6(a int, b int) partition by hash(b) partitions 3`) + + tk.MustExec(`set @@tidb_partition_prune_mode="dynamic"`) + tk.MustExec("set @@tidb_enable_outer_join_reorder=true") + runJoinReorderTestData(t, tk, "TestJoinOrderHint4DynamicPartitionTable") +} + +func TestJoinOrderHint4DifferentJoinType(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("drop table if exists t, t1, t2, t3, t4, t5, t6, t7, t8;") + tk.MustExec("create table t(a int, b int, key(a));") + 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));") + tk.MustExec("create table t5(a int, b int, key(a));") + tk.MustExec("create table t6(a int, b int, key(a));") + tk.MustExec("create table t7(a int, b int, key(a));") + tk.MustExec("create table t8(a int, b int, key(a));") + tk.MustExec("set @@tidb_enable_outer_join_reorder=true") + + runJoinReorderTestData(t, tk, "TestJoinOrderHint4DifferentJoinType") +} + +func TestJoinOrderHint4TiFlash(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t, t1, t2, t3;") + tk.MustExec("create table t(a int, b int, key(a));") + 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));") + tk.MustExec("create table t5(a int, b int, key(a));") + tk.MustExec("create table t6(a int, b int, key(a));") + tk.MustExec("set @@tidb_enable_outer_join_reorder=true") + + // Create virtual tiflash replica info. + dom := domain.GetDomain(tk.Session()) + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + tableName := tblInfo.Name.L + if tableName == "t" || tableName == "t1" || tableName == "t2" || tableName == "t3" || tableName == "t4" || tableName == "t5" || tableName == "t6" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + + tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1;") + runJoinReorderTestData(t, tk, "TestJoinOrderHint4TiFlash") +} + +func TestJoinOrderHint4Subquery(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("drop table if exists t, t1, t2, t3, t4, t5, t6, t7, t8;") + tk.MustExec("create table t(a int, b int, key(a));") + 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));") + tk.MustExec("create table t5(a int, b int, key(a));") + tk.MustExec("create table t6(a int, b int, key(a));") + tk.MustExec("create table t7(a int, b int, key(a));") + tk.MustExec("create table t8(a int, b int, key(a));") + tk.MustExec("insert into t3 values(1, 1), (2, 2), (3, 3);") + tk.MustExec("analyze table t3;") + + runJoinReorderTestData(t, tk, "TestJoinOrderHint4Subquery") +} + +func TestLeadingJoinHint4OuterJoin(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("drop table if exists t, t1, t2, t3, t4, t5, t6, t7, t8;") + tk.MustExec("create table t(a int, b int, key(a));") + 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));") + tk.MustExec("create table t5(a int, b int, key(a));") + tk.MustExec("create table t6(a int, b int, key(a));") + tk.MustExec("create table t7(a int, b int, key(a));") + tk.MustExec("create table t8(a int, b int, key(a));") + tk.MustExec("set @@tidb_enable_outer_join_reorder=true") + runJoinReorderTestData(t, tk, "TestLeadingJoinHint4OuterJoin") +} diff --git a/planner/core/exhaust_physical_plans.go b/planner/core/exhaust_physical_plans.go index 028c83d180928..05776d1927327 100644 --- a/planner/core/exhaust_physical_plans.go +++ b/planner/core/exhaust_physical_plans.go @@ -227,6 +227,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 ff42798e77467..a83295cdbf35c 100644 --- a/planner/core/logical_plan_builder.go +++ b/planner/core/logical_plan_builder.go @@ -72,6 +72,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 @@ -3698,7 +3708,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 @@ -3713,7 +3723,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) @@ -3738,6 +3748,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: @@ -3849,6 +3861,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, @@ -7064,6 +7077,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 diff --git a/planner/core/logical_plans.go b/planner/core/logical_plans.go index 5156d519631dd..b7dcbe0befc3c 100644 --- a/planner/core/logical_plans.go +++ b/planner/core/logical_plans.go @@ -119,6 +119,7 @@ const ( preferHashJoin preferNoHashJoin preferMergeJoin + preferNoMergeJoin preferBCJoin preferShuffleJoin preferRewriteSemiJoin diff --git a/planner/core/planbuilder.go b/planner/core/planbuilder.go index 1257e82b3e91c..5047071e63db5 100644 --- a/planner/core/planbuilder.go +++ b/planner/core/planbuilder.go @@ -94,6 +94,7 @@ type tableHintInfo struct { shuffleJoinTables []hintTableInfo hashJoinTables []hintTableInfo noHashJoinTables []hintTableInfo + noMergeJoinTables []hintTableInfo indexHintList []indexHintInfo tiflashTables []hintTableInfo tikvTables []hintTableInfo @@ -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) } diff --git a/planner/core/testdata/join_reorder_suite_in.json b/planner/core/testdata/join_reorder_suite_in.json index 12b93ca3d00e8..de51183a5e4c0 100644 --- a/planner/core/testdata/join_reorder_suite_in.json +++ b/planner/core/testdata/join_reorder_suite_in.json @@ -47,6 +47,19 @@ "select /*+ no_hash_join(t2) */ * from t1 right join t2 on t1.a=t2.a" ] }, + { + "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": [ diff --git a/planner/core/testdata/join_reorder_suite_out.json b/planner/core/testdata/join_reorder_suite_out.json index 85ec19ba2600a..6a7509a89383a 100644 --- a/planner/core/testdata/join_reorder_suite_out.json +++ b/planner/core/testdata/join_reorder_suite_out.json @@ -747,6 +747,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": [ From 7250dd53f8363e98ef4eeae90b62c4f7a3cd3af4 Mon Sep 17 00:00:00 2001 From: qw4990 Date: Tue, 1 Aug 2023 11:04:00 +0800 Subject: [PATCH 2/4] fixup --- planner/core/casetest/rule/BUILD.bazel | 35 -- .../casetest/rule/rule_join_reorder_test.go | 327 ------------------ 2 files changed, 362 deletions(-) delete mode 100644 planner/core/casetest/rule/BUILD.bazel delete mode 100644 planner/core/casetest/rule/rule_join_reorder_test.go diff --git a/planner/core/casetest/rule/BUILD.bazel b/planner/core/casetest/rule/BUILD.bazel deleted file mode 100644 index 50d121108f1d1..0000000000000 --- a/planner/core/casetest/rule/BUILD.bazel +++ /dev/null @@ -1,35 +0,0 @@ -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/rule_join_reorder_test.go b/planner/core/casetest/rule/rule_join_reorder_test.go deleted file mode 100644 index aa3c49eae8cfb..0000000000000 --- a/planner/core/casetest/rule/rule_join_reorder_test.go +++ /dev/null @@ -1,327 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package rule - -import ( - "testing" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/stretchr/testify/require" -) - -func runJoinReorderTestData(t *testing.T, tk *testkit.TestKit, name string) { - var input []string - var output []struct { - SQL string - Plan []string - Warning []string - } - joinReorderSuiteData := GetJoinReorderSuiteData() - joinReorderSuiteData.LoadTestCasesByName(name, t, &input, &output) - require.Equal(t, len(input), len(output)) - for i := range input { - testdata.OnRecord(func() { - output[i].SQL = input[i] - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + input[i]).Rows()) - output[i].Warning = testdata.ConvertRowsToStrings(tk.MustQuery("show warnings").Rows()) - }) - tk.MustQuery("explain format = 'brief' " + input[i]).Check(testkit.Rows(output[i].Plan...)) - tk.MustQuery("show warnings").Check(testkit.Rows(output[i].Warning...)) - } -} - -func TestStraightJoinHint(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("drop table if exists t, t1, t2, t3, t4;") - tk.MustExec("create table t(a int, b int, key(a));") - 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, "TestStraightJoinHint") -} - -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") -} - -func TestLeadingJoinHint(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("drop table if exists t, t1, t2, t3, t4, t5, t6, t7, t8;") - tk.MustExec("create table t(a int, b int, key(a));") - 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));") - tk.MustExec("create table t5(a int, b int, key(a));") - tk.MustExec("create table t6(a int, b int, key(a));") - tk.MustExec("create table t7(a int, b int, key(a));") - tk.MustExec("create table t8(a int, b int, key(a));") - runJoinReorderTestData(t, tk, "TestLeadingJoinHint") - - // test cases for multiple leading hints - tk.MustExec("select /*+ leading(t1) leading(t2) */ * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 We can only use one leading hint at most, when multiple leading hints are used, all leading hints will be invalid")) -} - -func TestJoinOrderHint(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t, t1, t2, t3, t4, t5, t6, t7, t8;") - tk.MustExec("create table t(a int, b int, key(a));") - 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));") - tk.MustExec("create table t5(a int, b int, key(a));") - tk.MustExec("create table t6(a int, b int, key(a));") - tk.MustExec("create table t7(a int, b int, key(a));") - tk.MustExec("create table t8(a int, b int, key(a));") - - // test cases for using the leading hint and straight_join hint at the same time - tk.MustExec("select /*+ leading(t1) straight_join() */ * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 We can only use the straight_join hint, when we use the leading hint and straight_join hint at the same time, all leading hints will be invalid")) - - tk.MustExec("select /*+ straight_join() leading(t1) */ * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 We can only use the straight_join hint, when we use the leading hint and straight_join hint at the same time, all leading hints will be invalid")) - - // more join order hints appear in the same time - tk.MustExec("select /*+ leading(t1) leading(t1) */ * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 We can only use one leading hint at most, when multiple leading hints are used, all leading hints will be invalid")) - - tk.MustExec("select /*+ leading(t1) leading(t2) */ * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 We can only use one leading hint at most, when multiple leading hints are used, all leading hints will be invalid")) - - tk.MustExec("select /*+ straight_join() straight_join() */ * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 STRAIGHT_JOIN() is defined more than once, only the last definition takes effect")) - - // test cases for table name in hint - // the same table appears in the leading hint - tk.MustExec("select /*+ leading(t1, t1) */ * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 There are no matching table names for (t1) in optimizer hint /*+ LEADING(t1, t1) */. Maybe you can use the table alias name", - "Warning 1815 leading hint is inapplicable, check if the leading hint table is valid")) - - tk.MustExec("select /*+ leading(t1, t2, t1) */ * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 There are no matching table names for (t1) in optimizer hint /*+ LEADING(t1, t2, t1) */. Maybe you can use the table alias name", - "Warning 1815 leading hint is inapplicable, check if the leading hint table is valid")) - - // the wrong table appears in the leading hint - tk.MustExec("select /*+ leading(t) */ * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 There are no matching table names for (t) in optimizer hint /*+ LEADING(t) */. Maybe you can use the table alias name")) - - tk.MustExec("select /*+ leading(t1, t2, t) */ * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 There are no matching table names for (t) in optimizer hint /*+ LEADING(t1, t2, t) */. Maybe you can use the table alias name", - "Warning 1815 leading hint is inapplicable, check if the leading hint table is valid")) - - // table alias in the leading hint - tk.MustExec("select /*+ leading(t) */ * from t1 t join t2 on t.a=t2.a join t3 on t2.b=t3.b") - tk.MustQuery("show warnings").Check(testkit.Rows()) - - tk.MustExec("select /*+ leading(t1) */ * from t1 t join t2 on t.a=t2.a join t3 on t2.b=t3.b") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 There are no matching table names for (t1) in optimizer hint /*+ LEADING(t1) */. Maybe you can use the table alias name")) - - tk.MustExec("select /*+ leading(t2, t) */ * from t1 t join t2 on t.a=t2.a join t3 on t2.b=t3.b") - tk.MustQuery("show warnings").Check(testkit.Rows()) - - tk.MustExec("select /*+ leading(t2, t1) */ * from t1 t join t2 on t.a=t2.a join t3 on t2.b=t3.b") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 There are no matching table names for (t1) in optimizer hint /*+ LEADING(t2, t1) */. Maybe you can use the table alias name", - "Warning 1815 leading hint is inapplicable, check if the leading hint table is valid")) - - // table name in leading hint cross query block - // Todo: Can not handle this case yet. Because when we extract the join group, it will get the join group {t1, t2, t3}. - // So the table 't4' can not be used. - tk.MustExec("select /*+ leading(t4) */ * from (select t2.b from t1 join t2 on t1.a=t2.a) t4 join t3 on t4.b=t3.b") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 leading hint is inapplicable, check if the leading hint table is valid")) - - tk.MustExec("select /*+ leading(t3, t2@sel_2) */ * from (select t2.b from t1 join t2 on t1.a=t2.a) t4 join t3 on t4.b=t3.b") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 There are no matching table names for (t2) in optimizer hint /*+ LEADING(t3, t2) */. Maybe you can use the table alias name")) - - tk.MustExec("select * from (select /*+ leading(t1, t3@sel_1) */ t2.b from t1 join t2 on t1.a=t2.a) t4 join t3 on t4.b=t3.b") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 There are no matching table names for (t3) in optimizer hint /*+ LEADING(t1, t3) */. Maybe you can use the table alias name")) - - tk.MustExec("select /*+ leading(t3) */ * from (select /*+ leading(t1) */ t2.b from t1 join t2 on t1.a=t2.a) t4 join t3 on t4.b=t3.b") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 We can only use one leading hint at most, when multiple leading hints are used, all leading hints will be invalid")) - - runJoinReorderTestData(t, tk, "TestJoinOrderHint") -} - -func TestJoinOrderHint4StaticPartitionTable(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("drop table if exists t, t1, t2, t3;") - tk.MustExec(`create table t(a int, b int) partition by hash(a) partitions 3`) - tk.MustExec(`create table t1(a int, b int) partition by hash(a) partitions 4`) - tk.MustExec(`create table t2(a int, b int) partition by hash(a) partitions 5`) - tk.MustExec(`create table t3(a int, b int) partition by hash(b) partitions 3`) - tk.MustExec(`create table t4(a int, b int) partition by hash(a) partitions 4`) - tk.MustExec(`create table t5(a int, b int) partition by hash(a) partitions 5`) - tk.MustExec(`create table t6(a int, b int) partition by hash(b) partitions 3`) - - tk.MustExec(`set @@tidb_partition_prune_mode="static"`) - tk.MustExec("set @@tidb_enable_outer_join_reorder=true") - runJoinReorderTestData(t, tk, "TestJoinOrderHint4StaticPartitionTable") -} - -func TestJoinOrderHint4DynamicPartitionTable(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t, t1, t2, t3;") - tk.MustExec(`create table t(a int, b int) partition by hash(a) partitions 3`) - tk.MustExec(`create table t1(a int, b int) partition by hash(a) partitions 4`) - tk.MustExec(`create table t2(a int, b int) partition by hash(a) partitions 5`) - tk.MustExec(`create table t3(a int, b int) partition by hash(b) partitions 3`) - tk.MustExec(`create table t4(a int, b int) partition by hash(a) partitions 4`) - tk.MustExec(`create table t5(a int, b int) partition by hash(a) partitions 5`) - tk.MustExec(`create table t6(a int, b int) partition by hash(b) partitions 3`) - - tk.MustExec(`set @@tidb_partition_prune_mode="dynamic"`) - tk.MustExec("set @@tidb_enable_outer_join_reorder=true") - runJoinReorderTestData(t, tk, "TestJoinOrderHint4DynamicPartitionTable") -} - -func TestJoinOrderHint4DifferentJoinType(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("drop table if exists t, t1, t2, t3, t4, t5, t6, t7, t8;") - tk.MustExec("create table t(a int, b int, key(a));") - 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));") - tk.MustExec("create table t5(a int, b int, key(a));") - tk.MustExec("create table t6(a int, b int, key(a));") - tk.MustExec("create table t7(a int, b int, key(a));") - tk.MustExec("create table t8(a int, b int, key(a));") - tk.MustExec("set @@tidb_enable_outer_join_reorder=true") - - runJoinReorderTestData(t, tk, "TestJoinOrderHint4DifferentJoinType") -} - -func TestJoinOrderHint4TiFlash(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t, t1, t2, t3;") - tk.MustExec("create table t(a int, b int, key(a));") - 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));") - tk.MustExec("create table t5(a int, b int, key(a));") - tk.MustExec("create table t6(a int, b int, key(a));") - tk.MustExec("set @@tidb_enable_outer_join_reorder=true") - - // Create virtual tiflash replica info. - dom := domain.GetDomain(tk.Session()) - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - tableName := tblInfo.Name.L - if tableName == "t" || tableName == "t1" || tableName == "t2" || tableName == "t3" || tableName == "t4" || tableName == "t5" || tableName == "t6" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - - tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1;") - runJoinReorderTestData(t, tk, "TestJoinOrderHint4TiFlash") -} - -func TestJoinOrderHint4Subquery(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("drop table if exists t, t1, t2, t3, t4, t5, t6, t7, t8;") - tk.MustExec("create table t(a int, b int, key(a));") - 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));") - tk.MustExec("create table t5(a int, b int, key(a));") - tk.MustExec("create table t6(a int, b int, key(a));") - tk.MustExec("create table t7(a int, b int, key(a));") - tk.MustExec("create table t8(a int, b int, key(a));") - tk.MustExec("insert into t3 values(1, 1), (2, 2), (3, 3);") - tk.MustExec("analyze table t3;") - - runJoinReorderTestData(t, tk, "TestJoinOrderHint4Subquery") -} - -func TestLeadingJoinHint4OuterJoin(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("drop table if exists t, t1, t2, t3, t4, t5, t6, t7, t8;") - tk.MustExec("create table t(a int, b int, key(a));") - 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));") - tk.MustExec("create table t5(a int, b int, key(a));") - tk.MustExec("create table t6(a int, b int, key(a));") - tk.MustExec("create table t7(a int, b int, key(a));") - tk.MustExec("create table t8(a int, b int, key(a));") - tk.MustExec("set @@tidb_enable_outer_join_reorder=true") - runJoinReorderTestData(t, tk, "TestLeadingJoinHint4OuterJoin") -} From 20b80d0c8323ea0eb3abdaf8bfe8cbc38a9b494a Mon Sep 17 00:00:00 2001 From: qw4990 Date: Tue, 1 Aug 2023 11:05:54 +0800 Subject: [PATCH 3/4] fixup --- planner/core/rule_join_reorder_test.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/planner/core/rule_join_reorder_test.go b/planner/core/rule_join_reorder_test.go index 681d88c911a5c..28e60340111cf 100644 --- a/planner/core/rule_join_reorder_test.go +++ b/planner/core/rule_join_reorder_test.go @@ -73,6 +73,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) From b090c7492221e65af4a7ec17bdfb86e564703f5d Mon Sep 17 00:00:00 2001 From: qw4990 Date: Tue, 1 Aug 2023 11:10:53 +0800 Subject: [PATCH 4/4] fixup --- .../core/testdata/join_reorder_suite_in.json | 4 +-- .../core/testdata/join_reorder_suite_out.json | 36 ------------------- 2 files changed, 1 insertion(+), 39 deletions(-) diff --git a/planner/core/testdata/join_reorder_suite_in.json b/planner/core/testdata/join_reorder_suite_in.json index de51183a5e4c0..1f8210bc17245 100644 --- a/planner/core/testdata/join_reorder_suite_in.json +++ b/planner/core/testdata/join_reorder_suite_in.json @@ -55,9 +55,7 @@ "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" + "select /*+ no_merge_join(t2) */ * from t1 right join t2 on t1.a=t2.a" ] }, { diff --git a/planner/core/testdata/join_reorder_suite_out.json b/planner/core/testdata/join_reorder_suite_out.json index 6a7509a89383a..b6eb157d8c782 100644 --- a/planner/core/testdata/join_reorder_suite_out.json +++ b/planner/core/testdata/join_reorder_suite_out.json @@ -821,42 +821,6 @@ " └─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 } ] },