diff --git a/planner/core/casetest/rule/BUILD.bazel b/planner/core/casetest/rule/BUILD.bazel index e69de29bb2d1d..2d0740d4d3065 100644 --- a/planner/core/casetest/rule/BUILD.bazel +++ b/planner/core/casetest/rule/BUILD.bazel @@ -0,0 +1,38 @@ +<<<<<<< HEAD +======= +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 = 23, + 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", + ], +) +>>>>>>> c266a9791c5 (planner: support `no_index_join`, `no_index_hash_join`, `no_index_merge_join` hints (#45633)) diff --git a/planner/core/casetest/rule_join_reorder_test.go b/planner/core/casetest/rule_join_reorder_test.go index 770af4bdfd6a1..2cfb28739e2cf 100644 --- a/planner/core/casetest/rule_join_reorder_test.go +++ b/planner/core/casetest/rule_join_reorder_test.go @@ -72,6 +72,32 @@ func TestNoHashJoinHint(t *testing.T) { runJoinReorderTestData(t, tk, "TestNoHashJoinHint") } +<<<<<<< HEAD:planner/core/casetest/rule_join_reorder_test.go +======= +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 TestNoIndexJoinHint(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec(`set tidb_enable_index_merge_join=true`) + 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, "TestNoIndexJoinHint") +} + +>>>>>>> c266a9791c5 (planner: support `no_index_join`, `no_index_hash_join`, `no_index_merge_join` hints (#45633)):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 6c5750386e87a..a0428f098cf16 100644 --- a/planner/core/casetest/testdata/join_reorder_suite_in.json +++ b/planner/core/casetest/testdata/join_reorder_suite_in.json @@ -50,6 +50,38 @@ ] }, { +<<<<<<< HEAD:planner/core/casetest/testdata/join_reorder_suite_in.json +======= + "name": "TestNoIndexJoinHint", + "cases": [ + "select /*+ no_merge_join(t1), no_hash_join(t1), no_index_hash_join(t1), no_index_merge_join(t1) */ * from t1, t2 where t1.a=t2.a", + "select /*+ no_merge_join(t1), no_hash_join(t1), no_index_hash_join(t1), no_index_merge_join(t2) */ * from t1, t2 where t1.a=t2.a", + "select /*+ no_merge_join(t1), no_hash_join(t1), no_index_hash_join(t1, t2), no_index_merge_join(t2, t1) */ * from t1, t2 where t1.a=t2.a", + "select /*+ no_merge_join(t1), no_hash_join(t1), no_index_join(t1), no_index_merge_join(t1) */ * from t1, t2 where t1.a=t2.a", + "select /*+ no_merge_join(t1), no_hash_join(t1), no_index_join(t1), no_index_hash_join(t1) */ * from t1, t2 where t1.a=t2.a", + "select /*+ no_merge_join(t1), no_hash_join(t1), inl_join(t1), no_index_merge_join(t1), no_index_hash_join(t1) */ * from t1, t2 where t1.a=t2.a", + "select /*+ inl_join(t1), no_index_join(t1) */ * from t1, t2 where t1.a=t2.a", + "select /*+ inl_hash_join(t1), no_index_hash_join(t2) */ * from t1, t2 where t1.a=t2.a", + "select /*+ inl_merge_join(t1), no_index_merge_join(t1, t2) */ * from t1, t2 where t1.a=t2.a", + "select /*+ inl_join(t1), no_index_hash_join(t1) */ * from t1, t2 where t1.a=t2.a", + "select /*+ inl_join(t1), no_index_merge_join(t1) */ * from t1, t2 where 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" + ] + }, + { +>>>>>>> c266a9791c5 (planner: support `no_index_join`, `no_index_hash_join`, `no_index_merge_join` hints (#45633)):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 e62971942bb4b..48c8aa2e3a1de 100644 --- a/planner/core/casetest/testdata/join_reorder_suite_out.json +++ b/planner/core/casetest/testdata/join_reorder_suite_out.json @@ -783,6 +783,265 @@ ] }, { +<<<<<<< HEAD:planner/core/casetest/testdata/join_reorder_suite_out.json +======= + "Name": "TestNoIndexJoinHint", + "Cases": [ + { + "SQL": "select /*+ no_merge_join(t1), no_hash_join(t1), no_index_hash_join(t1), no_index_merge_join(t1) */ * from t1, t2 where t1.a=t2.a", + "Plan": [ + "IndexJoin 12487.50 root inner join, inner:IndexReader, outer key:test.t1.a, inner key:test.t2.a, equal cond: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) 12487.50 root index:Selection", + " └─Selection 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" + ], + "Warning": null + }, + { + "SQL": "select /*+ no_merge_join(t1), no_hash_join(t1), no_index_hash_join(t1), no_index_merge_join(t2) */ * from t1, t2 where t1.a=t2.a", + "Plan": [ + "IndexJoin 12487.50 root inner join, inner:IndexReader, outer key:test.t1.a, inner key:test.t2.a, equal cond: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) 12487.50 root index:Selection", + " └─Selection 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" + ], + "Warning": null + }, + { + "SQL": "select /*+ no_merge_join(t1), no_hash_join(t1), no_index_hash_join(t1, t2), no_index_merge_join(t2, t1) */ * from t1, t2 where t1.a=t2.a", + "Plan": [ + "IndexJoin 12487.50 root inner join, inner:IndexReader, outer key:test.t1.a, inner key:test.t2.a, equal cond: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) 12487.50 root index:Selection", + " └─Selection 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" + ], + "Warning": null + }, + { + "SQL": "select /*+ no_merge_join(t1), no_hash_join(t1), no_index_join(t1), no_index_merge_join(t1) */ * from t1, t2 where t1.a=t2.a", + "Plan": [ + "IndexHashJoin 12487.50 root inner join, inner:IndexReader, outer key:test.t1.a, inner key:test.t2.a, equal cond: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) 12487.50 root index:Selection", + " └─Selection 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" + ], + "Warning": null + }, + { + "SQL": "select /*+ no_merge_join(t1), no_hash_join(t1), no_index_join(t1), no_index_hash_join(t1) */ * from t1, t2 where t1.a=t2.a", + "Plan": [ + "IndexMergeJoin 12487.50 root inner join, inner:IndexReader, outer key:test.t1.a, inner key: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) 12487.50 root index:Selection", + " └─Selection 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:true, stats:pseudo" + ], + "Warning": null + }, + { + "SQL": "select /*+ no_merge_join(t1), no_hash_join(t1), inl_join(t1), no_index_merge_join(t1), no_index_hash_join(t1) */ * from t1, t2 where t1.a=t2.a", + "Plan": [ + "IndexJoin 12487.50 root inner join, inner:IndexReader, outer key:test.t2.a, inner key:test.t1.a, equal cond:eq(test.t2.a, test.t1.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) 12487.50 root index:Selection", + " └─Selection 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" + ], + "Warning": null + }, + { + "SQL": "select /*+ inl_join(t1), no_index_join(t1) */ * from t1, t2 where t1.a=t2.a", + "Plan": [ + "IndexJoin 12487.50 root inner join, inner:IndexReader, outer key:test.t2.a, inner key:test.t1.a, equal cond:eq(test.t2.a, test.t1.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) 12487.50 root index:Selection", + " └─Selection 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" + ], + "Warning": [ + "Warning 1815 Some INL_JOIN and NO_INDEX_JOIN hints conflict, NO_INDEX_JOIN may be ignored" + ] + }, + { + "SQL": "select /*+ inl_hash_join(t1), no_index_hash_join(t2) */ * from t1, t2 where t1.a=t2.a", + "Plan": [ + "IndexHashJoin 12487.50 root inner join, inner:IndexReader, outer key:test.t2.a, inner key:test.t1.a, equal cond:eq(test.t2.a, test.t1.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) 12487.50 root index:Selection", + " └─Selection 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" + ], + "Warning": [ + "Warning 1815 Some INL_HASH_JOIN and NO_INDEX_HASH_JOIN hints conflict, NO_INDEX_HASH_JOIN may be ignored" + ] + }, + { + "SQL": "select /*+ inl_merge_join(t1), no_index_merge_join(t1, t2) */ * from t1, t2 where t1.a=t2.a", + "Plan": [ + "IndexMergeJoin 12487.50 root inner join, inner:IndexReader, outer key:test.t2.a, inner key:test.t1.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) 12487.50 root index:Selection", + " └─Selection 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:true, stats:pseudo" + ], + "Warning": [ + "Warning 1815 Some INL_MERGE_JOIN and NO_INDEX_MERGE_JOIN hints conflict, NO_INDEX_MERGE_JOIN may be ignored" + ] + }, + { + "SQL": "select /*+ inl_join(t1), no_index_hash_join(t1) */ * from t1, t2 where t1.a=t2.a", + "Plan": [ + "IndexJoin 12487.50 root inner join, inner:IndexReader, outer key:test.t2.a, inner key:test.t1.a, equal cond:eq(test.t2.a, test.t1.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) 12487.50 root index:Selection", + " └─Selection 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" + ], + "Warning": null + }, + { + "SQL": "select /*+ inl_join(t1), no_index_merge_join(t1) */ * from t1, t2 where t1.a=t2.a", + "Plan": [ + "IndexJoin 12487.50 root inner join, inner:IndexReader, outer key:test.t2.a, inner key:test.t1.a, equal cond:eq(test.t2.a, test.t1.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) 12487.50 root index:Selection", + " └─Selection 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" + ], + "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 + } + ] + }, + { +>>>>>>> c266a9791c5 (planner: support `no_index_join`, `no_index_hash_join`, `no_index_merge_join` hints (#45633)):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 19ee783376851..ed7ceab9bd220 100644 --- a/planner/core/exhaust_physical_plans.go +++ b/planner/core/exhaust_physical_plans.go @@ -2056,6 +2056,7 @@ func (p *LogicalJoin) tryToGetIndexJoin(prop *property.PhysicalProperty) (indexJ } } if supportRightOuter { +<<<<<<< HEAD allRightOuterJoins = p.getIndexJoinByOuterIdx(prop, 1) forcedRightOuterJoins = make([]PhysicalPlan, 0, len(allRightOuterJoins)) for _, j := range allRightOuterJoins { @@ -2073,6 +2074,64 @@ func (p *LogicalJoin) tryToGetIndexJoin(prop *property.PhysicalProperty) (indexJ forcedRightOuterJoins = append(forcedRightOuterJoins, j) } } +======= + candidates = append(candidates, p.getIndexJoinByOuterIdx(prop, 1)...) + } + + // Handle hints and variables about index join. + // The priority is: force hints like TIDB_INLJ > filter hints like NO_INDEX_JOIN > variables. + // Handle hints conflict first. + stmtCtx := p.SCtx().GetSessionVars().StmtCtx + if p.preferAny(preferLeftAsINLJInner, preferRightAsINLJInner) && p.preferAny(preferNoIndexJoin) { + stmtCtx.AppendWarning(ErrInternal.GenWithStack("Some INL_JOIN and NO_INDEX_JOIN hints conflict, NO_INDEX_JOIN may be ignored")) + } + if p.preferAny(preferLeftAsINLHJInner, preferRightAsINLHJInner) && p.preferAny(preferNoIndexHashJoin) { + stmtCtx.AppendWarning(ErrInternal.GenWithStack("Some INL_HASH_JOIN and NO_INDEX_HASH_JOIN hints conflict, NO_INDEX_HASH_JOIN may be ignored")) + } + if p.preferAny(preferLeftAsINLMJInner, preferRightAsINLMJInner) && p.preferAny(preferNoIndexMergeJoin) { + stmtCtx.AppendWarning(ErrInternal.GenWithStack("Some INL_MERGE_JOIN and NO_INDEX_MERGE_JOIN hints conflict, NO_INDEX_MERGE_JOIN may be ignored")) + } + + candidates, canForced = p.handleForceIndexJoinHints(prop, candidates) + if canForced { + return candidates, canForced + } + candidates = p.handleFilterIndexJoinHints(candidates) + return filterIndexJoinBySessionVars(p.SCtx(), candidates), false +} + +func (p *LogicalJoin) handleFilterIndexJoinHints(candidates []PhysicalPlan) []PhysicalPlan { + if !p.preferAny(preferNoIndexJoin, preferNoIndexHashJoin, preferNoIndexMergeJoin) { + return candidates // no filter index join hints + } + filtered := make([]PhysicalPlan, 0, len(candidates)) + for _, candidate := range candidates { + _, joinMethod, ok := p.getIndexJoinSideAndMethod(candidate) + if !ok { + continue + } + if (p.preferAny(preferNoIndexJoin) && joinMethod == indexJoinMethod) || + (p.preferAny(preferNoIndexHashJoin) && joinMethod == indexHashJoinMethod) || + (p.preferAny(preferNoIndexMergeJoin) && joinMethod == indexMergeJoinMethod) { + continue + } + filtered = append(filtered, candidate) + } + return filtered +} + +// handleForceIndexJoinHints handles the force index join hints and returns all plans that can satisfy the hints. +func (p *LogicalJoin) handleForceIndexJoinHints(prop *property.PhysicalProperty, candidates []PhysicalPlan) (indexJoins []PhysicalPlan, canForced bool) { + if !p.preferAny(preferRightAsINLJInner, preferRightAsINLHJInner, preferRightAsINLMJInner, + preferLeftAsINLJInner, preferLeftAsINLHJInner, preferLeftAsINLMJInner) { + return candidates, false // no force index join hints + } + forced := make([]PhysicalPlan, 0, len(candidates)) + for _, candidate := range candidates { + innerSide, joinMethod, ok := p.getIndexJoinSideAndMethod(candidate) + if !ok { + continue +>>>>>>> c266a9791c5 (planner: support `no_index_join`, `no_index_hash_join`, `no_index_merge_join` hints (#45633)) } switch { case len(forcedRightOuterJoins) == 0 && !supportLeftOuter: diff --git a/planner/core/logical_plan_builder.go b/planner/core/logical_plan_builder.go index f47b74758ccfb..54663f57341cd 100644 --- a/planner/core/logical_plan_builder.go +++ b/planner/core/logical_plan_builder.go @@ -94,6 +94,12 @@ const ( HintINLHJ = "inl_hash_join" // HintINLMJ is hint enforce index nested loop merge join. HintINLMJ = "inl_merge_join" + // HintNoIndexJoin is the hint to enforce the query not to use index join. + HintNoIndexJoin = "no_index_join" + // HintNoIndexHashJoin is the hint to enforce the query not to use index hash join. + HintNoIndexHashJoin = "no_index_hash_join" + // HintNoIndexMergeJoin is the hint to enforce the query not to use index merge join. + HintNoIndexMergeJoin = "no_index_merge_join" // TiDBHashJoin is hint enforce hash join. TiDBHashJoin = "tidb_hj" // HintNoHashJoin is the hint to enforce the query not to use hash join. @@ -659,6 +665,30 @@ func (p *LogicalJoin) setPreferredJoinTypeAndOrder(hintInfo *tableHintInfo) { p.preferJoinType |= preferRightAsINLMJInner p.rightPreferJoinType |= preferINLMJ } + if hintInfo.ifPreferNoIndexJoin(lhsAlias) { + p.preferJoinType |= preferNoIndexJoin + p.leftPreferJoinType |= preferNoIndexJoin + } + if hintInfo.ifPreferNoIndexJoin(rhsAlias) { + p.preferJoinType |= preferNoIndexJoin + p.rightPreferJoinType |= preferNoIndexJoin + } + if hintInfo.ifPreferNoIndexHashJoin(lhsAlias) { + p.preferJoinType |= preferNoIndexHashJoin + p.leftPreferJoinType |= preferNoIndexHashJoin + } + if hintInfo.ifPreferNoIndexHashJoin(rhsAlias) { + p.preferJoinType |= preferNoIndexHashJoin + p.rightPreferJoinType |= preferNoIndexHashJoin + } + if hintInfo.ifPreferNoIndexMergeJoin(lhsAlias) { + p.preferJoinType |= preferNoIndexMergeJoin + p.leftPreferJoinType |= preferNoIndexMergeJoin + } + if hintInfo.ifPreferNoIndexMergeJoin(rhsAlias) { + p.preferJoinType |= preferNoIndexMergeJoin + p.rightPreferJoinType |= preferNoIndexMergeJoin + } if hintInfo.ifPreferHJBuild(lhsAlias) { p.preferJoinType |= preferLeftAsHJBuild p.leftPreferJoinType |= preferHJBuild @@ -3720,7 +3750,12 @@ func (b *PlanBuilder) pushTableHints(hints []*ast.TableOptimizerHint, currentLev hints = b.hintProcessor.GetCurrentStmtHints(hints, currentLevel) var ( sortMergeTables, inljTables, inlhjTables, inlmjTables, hashJoinTables, bcTables []hintTableInfo +<<<<<<< HEAD noHashJoinTables []hintTableInfo +======= + noIndexJoinTables, noIndexHashJoinTables, noIndexMergeJoinTables []hintTableInfo + noHashJoinTables, noMergeJoinTables []hintTableInfo +>>>>>>> c266a9791c5 (planner: support `no_index_join`, `no_index_hash_join`, `no_index_merge_join` hints (#45633)) shuffleJoinTables []hintTableInfo indexHintList, indexMergeHintList []indexHintInfo tiflashTables, tikvTables []hintTableInfo @@ -3760,6 +3795,17 @@ 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)...) +<<<<<<< HEAD +======= + case HintNoMergeJoin: + noMergeJoinTables = append(noMergeJoinTables, tableNames2HintTableInfo(b.ctx, hint.HintName.L, hint.Tables, b.hintProcessor, currentLevel)...) + case HintNoIndexJoin: + noIndexJoinTables = append(noIndexJoinTables, tableNames2HintTableInfo(b.ctx, hint.HintName.L, hint.Tables, b.hintProcessor, currentLevel)...) + case HintNoIndexHashJoin: + noIndexHashJoinTables = append(noIndexHashJoinTables, tableNames2HintTableInfo(b.ctx, hint.HintName.L, hint.Tables, b.hintProcessor, currentLevel)...) + case HintNoIndexMergeJoin: + noIndexMergeJoinTables = append(noIndexMergeJoinTables, tableNames2HintTableInfo(b.ctx, hint.HintName.L, hint.Tables, b.hintProcessor, currentLevel)...) +>>>>>>> c266a9791c5 (planner: support `no_index_join`, `no_index_hash_join`, `no_index_merge_join` hints (#45633)) case HintMPP1PhaseAgg: aggHints.preferAggType |= preferMPP1PhaseAgg case HintMPP2PhaseAgg: @@ -3869,6 +3915,7 @@ func (b *PlanBuilder) pushTableHints(hints []*ast.TableOptimizerHint, currentLev broadcastJoinTables: bcTables, shuffleJoinTables: shuffleJoinTables, indexNestedLoopJoinTables: indexNestedLoopJoinTables{inljTables, inlhjTables, inlmjTables}, + noIndexJoinTables: indexNestedLoopJoinTables{noIndexJoinTables, noIndexHashJoinTables, noIndexMergeJoinTables}, hashJoinTables: hashJoinTables, noHashJoinTables: noHashJoinTables, indexHintList: indexHintList, @@ -7077,6 +7124,13 @@ func getInnerFromParenthesesAndUnaryPlus(expr ast.ExprNode) ast.ExprNode { // join types. func containDifferentJoinTypes(preferJoinType uint) bool { preferJoinType &= ^preferNoHashJoin +<<<<<<< HEAD +======= + preferJoinType &= ^preferNoMergeJoin + preferJoinType &= ^preferNoIndexJoin + preferJoinType &= ^preferNoIndexHashJoin + preferJoinType &= ^preferNoIndexMergeJoin +>>>>>>> c266a9791c5 (planner: support `no_index_join`, `no_index_hash_join`, `no_index_merge_join` hints (#45633)) inlMask := preferRightAsINLJInner ^ preferLeftAsINLJInner inlhjMask := preferRightAsINLHJInner ^ preferLeftAsINLHJInner diff --git a/planner/core/logical_plans.go b/planner/core/logical_plans.go index 1131da8142fc3..a2f25a9d5e976 100644 --- a/planner/core/logical_plans.go +++ b/planner/core/logical_plans.go @@ -120,6 +120,13 @@ const ( preferHashJoin preferNoHashJoin preferMergeJoin +<<<<<<< HEAD +======= + preferNoMergeJoin + preferNoIndexJoin + preferNoIndexHashJoin + preferNoIndexMergeJoin +>>>>>>> c266a9791c5 (planner: support `no_index_join`, `no_index_hash_join`, `no_index_merge_join` hints (#45633)) preferBCJoin preferShuffleJoin preferRewriteSemiJoin diff --git a/planner/core/planbuilder.go b/planner/core/planbuilder.go index 1068c110f5bbf..1206f8823b896 100644 --- a/planner/core/planbuilder.go +++ b/planner/core/planbuilder.go @@ -89,6 +89,7 @@ type indexNestedLoopJoinTables struct { type tableHintInfo struct { indexNestedLoopJoinTables + noIndexJoinTables indexNestedLoopJoinTables sortMergeJoinTables []hintTableInfo broadcastJoinTables []hintTableInfo shuffleJoinTables []hintTableInfo @@ -262,6 +263,18 @@ func (info *tableHintInfo) ifPreferINLMJ(tableNames ...*hintTableInfo) bool { return info.matchTableName(tableNames, info.indexNestedLoopJoinTables.inlmjTables) } +func (info *tableHintInfo) ifPreferNoIndexJoin(tableNames ...*hintTableInfo) bool { + return info.matchTableName(tableNames, info.noIndexJoinTables.inljTables) +} + +func (info *tableHintInfo) ifPreferNoIndexHashJoin(tableNames ...*hintTableInfo) bool { + return info.matchTableName(tableNames, info.noIndexJoinTables.inlhjTables) +} + +func (info *tableHintInfo) ifPreferNoIndexMergeJoin(tableNames ...*hintTableInfo) bool { + return info.matchTableName(tableNames, info.noIndexJoinTables.inlmjTables) +} + func (info *tableHintInfo) ifPreferTiFlash(tableName *hintTableInfo) *hintTableInfo { if tableName == nil { return nil