Skip to content

Commit ed1988d

Browse files
authored
planner: clean the one side filters for outer join reorder which is not done in #44409 (#45565) (#45585)
ref #44409, close #45564
1 parent e650558 commit ed1988d

File tree

5 files changed

+101
-16
lines changed

5 files changed

+101
-16
lines changed

planner/core/main_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ func TestMain(m *testing.M) {
5151
testDataMap.LoadTestSuiteData("testdata", "flat_plan_suite")
5252
testDataMap.LoadTestSuiteData("testdata", "binary_plan_suite")
5353
testDataMap.LoadTestSuiteData("testdata", "json_plan_suite")
54+
testDataMap.LoadTestSuiteData("testdata", "join_reorder_suite")
5455

5556
indexMergeSuiteData = testDataMap["index_merge_suite"]
5657
planSuiteUnexportedData = testDataMap["plan_suite_unexported"]
@@ -139,3 +140,7 @@ func GetIndexMergeSuiteData() testdata.TestData {
139140
func GetJSONPlanSuiteData() testdata.TestData {
140141
return testDataMap["json_plan_suite"]
141142
}
143+
144+
func GetJoinReorderData() testdata.TestData {
145+
return testDataMap["join_reorder_suite"]
146+
}

planner/core/rule_join_reorder.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -426,12 +426,17 @@ func (s *baseSingleGroupJoinOrderSolver) makeJoin(leftPlan, rightPlan LogicalPla
426426
remainOtherConds, otherConds = expression.FilterOutInPlace(remainOtherConds, func(expr expression.Expression) bool {
427427
return expression.ExprFromSchema(expr, mergedSchema)
428428
})
429-
if (joinType.JoinType == LeftOuterJoin || joinType.JoinType == RightOuterJoin || joinType.JoinType == LeftOuterSemiJoin || joinType.JoinType == AntiLeftOuterSemiJoin) && len(otherConds) > 0 {
429+
430+
if joinType.JoinType == LeftOuterJoin || joinType.JoinType == RightOuterJoin || joinType.JoinType == LeftOuterSemiJoin || joinType.JoinType == AntiLeftOuterSemiJoin {
430431
// the original outer join's other conditions has been bound to the outer join Edge,
431432
// these remained other condition here shouldn't be appended to it because on-mismatch
432433
// logic will produce more append-null rows which is banned in original semantic.
433434
remainOtherConds = append(remainOtherConds, otherConds...) // nozero
435+
remainOtherConds = append(remainOtherConds, leftConds...) // nozero
436+
remainOtherConds = append(remainOtherConds, rightConds...) // nozero
434437
otherConds = otherConds[:0]
438+
leftConds = leftConds[:0]
439+
rightConds = rightConds[:0]
435440
}
436441
if len(joinType.outerBindCondition) > 0 {
437442
remainOBOtherConds := make([]expression.Expression, len(joinType.outerBindCondition))

planner/core/rule_join_reorder_test.go

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -400,21 +400,28 @@ func TestAdditionOtherConditionsRemained4OuterJoin(t *testing.T) {
400400
tk.MustExec("INSERT INTO queries_program(`id`, `identifier_id`) values(8, 13), (9, 14);")
401401
tk.MustExec("INSERT INTO queries_channel(`id`, `identifier_id`) values(5, 13);")
402402

403-
tk.MustQuery("SELECT `queries_identifier`.`id`, `queries_identifier`.`name` FROM `queries_identifier` LEFT OUTER JOIN `queries_channel` ON (`queries_identifier`.`id` = `queries_channel`.`identifier_id`) INNER JOIN `queries_program` ON (`queries_identifier`.`id` = `queries_program`.`identifier_id`) WHERE ((`queries_channel`.`id` = 5 AND `queries_program`.`id` = 9) OR `queries_program`.`id` = 8) ORDER BY `queries_identifier`.`id` ASC;").Check(testkit.Rows("" +
404-
"13 i1"))
405-
tk.MustQuery("SELECT `queries_identifier`.`id`, `queries_identifier`.`name` FROM `queries_identifier` RIGHT OUTER JOIN `queries_channel` ON (`queries_identifier`.`id` = `queries_channel`.`identifier_id`) INNER JOIN `queries_program` ON (`queries_identifier`.`id` = `queries_program`.`identifier_id`) WHERE ((`queries_channel`.`id` = 5 AND `queries_program`.`id` = 9) OR `queries_program`.`id` = 8) ORDER BY `queries_identifier`.`id` ASC;").Check(testkit.Rows("" +
406-
"13 i1"))
407-
tk.MustQuery("explain format = 'brief' SELECT `queries_identifier`.`id`, `queries_identifier`.`name` FROM `queries_identifier` LEFT OUTER JOIN `queries_channel` ON (`queries_identifier`.`id` = `queries_channel`.`identifier_id`) INNER JOIN `queries_program` ON (`queries_identifier`.`id` = `queries_program`.`identifier_id`) WHERE ((`queries_channel`.`id` = 5 AND `queries_program`.`id` = 9) OR `queries_program`.`id` = 8) ORDER BY `queries_identifier`.`id` ASC;").Check(testkit.Rows(""+
408-
"Sort 2.50 root test.queries_identifier.id",
409-
"└─Projection 2.50 root test.queries_identifier.id, test.queries_identifier.name",
410-
" └─Selection 2.50 root or(and(eq(test.queries_channel.id, 5), eq(test.queries_program.id, 9)), eq(test.queries_program.id, 8))",
411-
" └─IndexJoin 3.12 root left outer join, inner:IndexReader, outer key:test.queries_identifier.id, inner key:test.queries_channel.identifier_id, equal cond:eq(test.queries_identifier.id, test.queries_channel.identifier_id)",
412-
" ├─IndexHashJoin(Build) 2.50 root inner join, inner:TableReader, outer key:test.queries_program.identifier_id, inner key:test.queries_identifier.id, equal cond:eq(test.queries_program.identifier_id, test.queries_identifier.id)",
413-
" │ ├─Batch_Point_Get(Build) 2.00 root table:queries_program handle:[8 9], keep order:false, desc:false",
414-
" │ └─TableReader(Probe) 2.00 root data:TableRangeScan",
415-
" │ └─TableRangeScan 2.00 cop[tikv] table:queries_identifier range: decided by [test.queries_program.identifier_id], keep order:false, stats:pseudo",
416-
" └─IndexReader(Probe) 2.50 root index:IndexRangeScan",
417-
" └─IndexRangeScan 2.50 cop[tikv] table:queries_channel, index:identifier_id(identifier_id) range: decided by [eq(test.queries_channel.identifier_id, test.queries_identifier.id)], keep order:false, stats:pseudo"))
403+
tk.MustExec("create table t(a int)")
404+
tk.MustExec("create table t1(a int, b int)")
405+
tk.MustExec("create table t2(a int, b int, c int)")
406+
tk.MustExec("create table t3(a int, b int)")
407+
tk.MustExec("create table t4(a int, b int)")
408+
409+
testData := plannercore.GetJoinReorderData()
410+
var (
411+
input []string
412+
output []struct {
413+
SQL string
414+
Output []string
415+
}
416+
)
417+
testData.LoadTestCases(t, &input, &output)
418+
for i, sql := range input {
419+
testdata.OnRecord(func() {
420+
output[i].SQL = sql
421+
output[i].Output = testdata.ConvertRowsToStrings(tk.MustQuery(sql).Rows())
422+
})
423+
tk.MustQuery(sql).Check(testkit.Rows(output[i].Output...))
424+
}
418425
}
419426

420427
func TestOuterJoinWIthEqCondCrossInnerJoin(t *testing.T) {

planner/core/testdata/join_reorder_suite_in.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,5 +505,15 @@
505505
"select /*+ leading(t2@sel_2, t1) */ t1.a, (select min(t2.a) from t2) from t1 left join t3 on t1.a = t3.a;",
506506
"select /*+ leading(t3, t2@sel_2) */ t1.a, (select min(t2.a) from t2) from t1 right join t3 on t1.a = t3.a;"
507507
]
508+
},
509+
{
510+
"name": "TestAdditionOtherConditionsRemained4OuterJoin",
511+
"cases": [
512+
"SELECT `queries_identifier`.`id`, `queries_identifier`.`name` FROM `queries_identifier` LEFT OUTER JOIN `queries_channel` ON (`queries_identifier`.`id` = `queries_channel`.`identifier_id`) INNER JOIN `queries_program` ON (`queries_identifier`.`id` = `queries_program`.`identifier_id`) WHERE ((`queries_channel`.`id` = 5 AND `queries_program`.`id` = 9) OR `queries_program`.`id` = 8) ORDER BY `queries_identifier`.`id` ASC",
513+
"SELECT `queries_identifier`.`id`, `queries_identifier`.`name` FROM `queries_identifier` RIGHT OUTER JOIN `queries_channel` ON (`queries_identifier`.`id` = `queries_channel`.`identifier_id`) INNER JOIN `queries_program` ON (`queries_identifier`.`id` = `queries_program`.`identifier_id`) WHERE ((`queries_channel`.`id` = 5 AND `queries_program`.`id` = 9) OR `queries_program`.`id` = 8) ORDER BY `queries_identifier`.`id` ASC",
514+
"explain format = 'brief' SELECT `queries_identifier`.`id`, `queries_identifier`.`name` FROM `queries_identifier` LEFT OUTER JOIN `queries_channel` ON (`queries_identifier`.`id` = `queries_channel`.`identifier_id`) INNER JOIN `queries_program` ON (`queries_identifier`.`id` = `queries_program`.`identifier_id`) WHERE ((`queries_channel`.`id` = 5 AND `queries_program`.`id` = 9) OR `queries_program`.`id` = 8) ORDER BY `queries_identifier`.`id` ASC;",
515+
// The where clause should be a Selection out of joins.
516+
"explain format='brief' select * from t left join t1 on t.a=t1.a inner join t2 on t.a=t2.a and t2.c = 100 left join t3 on t2.a=t3.a and t3.b > 1 left join t4 on t2.a = t4.a where (t2.b > 100 or t.a > 10 or t1.b < 10)"
517+
]
508518
}
509519
]

planner/core/testdata/join_reorder_suite_out.json

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10043,5 +10043,63 @@
1004310043
]
1004410044
}
1004510045
]
10046+
},
10047+
{
10048+
"Name": "TestAdditionOtherConditionsRemained4OuterJoin",
10049+
"Cases": [
10050+
{
10051+
"SQL": "SELECT `queries_identifier`.`id`, `queries_identifier`.`name` FROM `queries_identifier` LEFT OUTER JOIN `queries_channel` ON (`queries_identifier`.`id` = `queries_channel`.`identifier_id`) INNER JOIN `queries_program` ON (`queries_identifier`.`id` = `queries_program`.`identifier_id`) WHERE ((`queries_channel`.`id` = 5 AND `queries_program`.`id` = 9) OR `queries_program`.`id` = 8) ORDER BY `queries_identifier`.`id` ASC",
10052+
"Output": [
10053+
"13 i1"
10054+
]
10055+
},
10056+
{
10057+
"SQL": "SELECT `queries_identifier`.`id`, `queries_identifier`.`name` FROM `queries_identifier` RIGHT OUTER JOIN `queries_channel` ON (`queries_identifier`.`id` = `queries_channel`.`identifier_id`) INNER JOIN `queries_program` ON (`queries_identifier`.`id` = `queries_program`.`identifier_id`) WHERE ((`queries_channel`.`id` = 5 AND `queries_program`.`id` = 9) OR `queries_program`.`id` = 8) ORDER BY `queries_identifier`.`id` ASC",
10058+
"Output": [
10059+
"13 i1"
10060+
]
10061+
},
10062+
{
10063+
"SQL": "explain format = 'brief' SELECT `queries_identifier`.`id`, `queries_identifier`.`name` FROM `queries_identifier` LEFT OUTER JOIN `queries_channel` ON (`queries_identifier`.`id` = `queries_channel`.`identifier_id`) INNER JOIN `queries_program` ON (`queries_identifier`.`id` = `queries_program`.`identifier_id`) WHERE ((`queries_channel`.`id` = 5 AND `queries_program`.`id` = 9) OR `queries_program`.`id` = 8) ORDER BY `queries_identifier`.`id` ASC;",
10064+
"Output": [
10065+
"Sort 2.50 root test.queries_identifier.id",
10066+
"└─Projection 2.50 root test.queries_identifier.id, test.queries_identifier.name",
10067+
" └─Selection 2.50 root or(and(eq(test.queries_channel.id, 5), eq(test.queries_program.id, 9)), eq(test.queries_program.id, 8))",
10068+
" └─IndexJoin 3.12 root left outer join, inner:IndexReader, outer key:test.queries_identifier.id, inner key:test.queries_channel.identifier_id, equal cond:eq(test.queries_identifier.id, test.queries_channel.identifier_id)",
10069+
" ├─IndexHashJoin(Build) 2.50 root inner join, inner:TableReader, outer key:test.queries_program.identifier_id, inner key:test.queries_identifier.id, equal cond:eq(test.queries_program.identifier_id, test.queries_identifier.id)",
10070+
" │ ├─Batch_Point_Get(Build) 2.00 root table:queries_program handle:[8 9], keep order:false, desc:false",
10071+
" │ └─TableReader(Probe) 2.00 root data:TableRangeScan",
10072+
" │ └─TableRangeScan 2.00 cop[tikv] table:queries_identifier range: decided by [test.queries_program.identifier_id], keep order:false, stats:pseudo",
10073+
" └─IndexReader(Probe) 2.50 root index:IndexRangeScan",
10074+
" └─IndexRangeScan 2.50 cop[tikv] table:queries_channel, index:identifier_id(identifier_id) range: decided by [eq(test.queries_channel.identifier_id, test.queries_identifier.id)], keep order:false, stats:pseudo"
10075+
]
10076+
},
10077+
{
10078+
"SQL": "explain format='brief' select * from t left join t1 on t.a=t1.a inner join t2 on t.a=t2.a and t2.c = 100 left join t3 on t2.a=t3.a and t3.b > 1 left join t4 on t2.a = t4.a where (t2.b > 100 or t.a > 10 or t1.b < 10)",
10079+
"Output": [
10080+
"Projection 19.51 root test.t.a, test.t1.a, test.t1.b, test.t2.a, test.t2.b, test.t2.c, test.t3.a, test.t3.b, test.t4.a, test.t4.b",
10081+
"└─Selection 19.51 root or(gt(test.t2.b, 100), or(gt(test.t.a, 10), lt(test.t1.b, 10))), or(gt(test.t2.b, 100), or(gt(test.t2.a, 10), lt(test.t1.b, 10)))",
10082+
" └─HashJoin 24.39 root left outer join, equal:[eq(test.t2.a, test.t4.a)]",
10083+
" ├─HashJoin(Build) 19.51 root left outer join, equal:[eq(test.t.a, test.t1.a)]",
10084+
" │ ├─HashJoin(Build) 15.61 root inner join, equal:[eq(test.t2.a, test.t.a)]",
10085+
" │ │ ├─HashJoin(Build) 12.49 root left outer join, equal:[eq(test.t2.a, test.t3.a)]",
10086+
" │ │ │ ├─TableReader(Build) 9.99 root data:Selection",
10087+
" │ │ │ │ └─Selection 9.99 cop[tikv] eq(test.t2.c, 100), not(isnull(test.t2.a))",
10088+
" │ │ │ │ └─TableFullScan 10000.00 cop[tikv] table:t2 keep order:false, stats:pseudo",
10089+
" │ │ │ └─TableReader(Probe) 3330.00 root data:Selection",
10090+
" │ │ │ └─Selection 3330.00 cop[tikv] gt(test.t3.b, 1), not(isnull(test.t3.a))",
10091+
" │ │ │ └─TableFullScan 10000.00 cop[tikv] table:t3 keep order:false, stats:pseudo",
10092+
" │ │ └─TableReader(Probe) 9990.00 root data:Selection",
10093+
" │ │ └─Selection 9990.00 cop[tikv] not(isnull(test.t.a))",
10094+
" │ │ └─TableFullScan 10000.00 cop[tikv] table:t keep order:false, stats:pseudo",
10095+
" │ └─TableReader(Probe) 9990.00 root data:Selection",
10096+
" │ └─Selection 9990.00 cop[tikv] not(isnull(test.t1.a))",
10097+
" │ └─TableFullScan 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo",
10098+
" └─TableReader(Probe) 9990.00 root data:Selection",
10099+
" └─Selection 9990.00 cop[tikv] not(isnull(test.t4.a))",
10100+
" └─TableFullScan 10000.00 cop[tikv] table:t4 keep order:false, stats:pseudo"
10101+
]
10102+
}
10103+
]
1004610104
}
1004710105
]

0 commit comments

Comments
 (0)