Skip to content

Commit 0e4da39

Browse files
authored
planner: clean the one side filters for outer join reorder which is not done in #44409 (#45565) (#53074)
ref #44409, close #45564
1 parent 5e74466 commit 0e4da39

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
@@ -366,21 +366,28 @@ func TestAdditionOtherConditionsRemained4OuterJoin(t *testing.T) {
366366
tk.MustExec("INSERT INTO queries_program(`id`, `identifier_id`) values(8, 13), (9, 14);")
367367
tk.MustExec("INSERT INTO queries_channel(`id`, `identifier_id`) values(5, 13);")
368368

369-
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("" +
370-
"13 i1"))
371-
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("" +
372-
"13 i1"))
373-
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(""+
374-
"Sort 2.50 root test.queries_identifier.id",
375-
"└─Projection 2.50 root test.queries_identifier.id, test.queries_identifier.name",
376-
" └─Selection 2.50 root or(and(eq(test.queries_channel.id, 5), eq(test.queries_program.id, 9)), eq(test.queries_program.id, 8))",
377-
" └─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)",
378-
" ├─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)",
379-
" │ ├─Batch_Point_Get(Build) 2.00 root table:queries_program handle:[8 9], keep order:false, desc:false",
380-
" │ └─TableReader(Probe) 2.00 root data:TableRangeScan",
381-
" │ └─TableRangeScan 2.00 cop[tikv] table:queries_identifier range: decided by [test.queries_program.identifier_id], keep order:false, stats:pseudo",
382-
" └─IndexReader(Probe) 2.50 root index:IndexRangeScan",
383-
" └─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"))
369+
tk.MustExec("create table t(a int)")
370+
tk.MustExec("create table t1(a int, b int)")
371+
tk.MustExec("create table t2(a int, b int, c int)")
372+
tk.MustExec("create table t3(a int, b int)")
373+
tk.MustExec("create table t4(a int, b int)")
374+
375+
testData := plannercore.GetJoinReorderData()
376+
var (
377+
input []string
378+
output []struct {
379+
SQL string
380+
Output []string
381+
}
382+
)
383+
testData.LoadTestCases(t, &input, &output)
384+
for i, sql := range input {
385+
testdata.OnRecord(func() {
386+
output[i].SQL = sql
387+
output[i].Output = testdata.ConvertRowsToStrings(tk.MustQuery(sql).Rows())
388+
})
389+
tk.MustQuery(sql).Check(testkit.Rows(output[i].Output...))
390+
}
384391
}
385392

386393
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
@@ -522,5 +522,15 @@
522522
"select /*+ leading(t2@sel_2, t1) */ t1.a, (select min(t2.a) from t2) from t1 left join t3 on t1.a = t3.a;",
523523
"select /*+ leading(t3, t2@sel_2) */ t1.a, (select min(t2.a) from t2) from t1 right join t3 on t1.a = t3.a;"
524524
]
525+
},
526+
{
527+
"name": "TestAdditionOtherConditionsRemained4OuterJoin",
528+
"cases": [
529+
"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",
530+
"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",
531+
"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;",
532+
// The where clause should be a Selection out of joins.
533+
"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)"
534+
]
525535
}
526536
]

planner/core/testdata/join_reorder_suite_out.json

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9659,5 +9659,63 @@
96599659
]
96609660
}
96619661
]
9662+
},
9663+
{
9664+
"Name": "TestAdditionOtherConditionsRemained4OuterJoin",
9665+
"Cases": [
9666+
{
9667+
"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",
9668+
"Output": [
9669+
"13 i1"
9670+
]
9671+
},
9672+
{
9673+
"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",
9674+
"Output": [
9675+
"13 i1"
9676+
]
9677+
},
9678+
{
9679+
"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;",
9680+
"Output": [
9681+
"Sort 2.50 root test.queries_identifier.id",
9682+
"└─Projection 2.50 root test.queries_identifier.id, test.queries_identifier.name",
9683+
" └─Selection 2.50 root or(and(eq(test.queries_channel.id, 5), eq(test.queries_program.id, 9)), eq(test.queries_program.id, 8))",
9684+
" └─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)",
9685+
" ├─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)",
9686+
" │ ├─Batch_Point_Get(Build) 2.00 root table:queries_program handle:[8 9], keep order:false, desc:false",
9687+
" │ └─TableReader(Probe) 2.00 root data:TableRangeScan",
9688+
" │ └─TableRangeScan 2.00 cop[tikv] table:queries_identifier range: decided by [test.queries_program.identifier_id], keep order:false, stats:pseudo",
9689+
" └─IndexReader(Probe) 2.50 root index:IndexRangeScan",
9690+
" └─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"
9691+
]
9692+
},
9693+
{
9694+
"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)",
9695+
"Output": [
9696+
"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",
9697+
"└─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)))",
9698+
" └─HashJoin 24.39 root left outer join, equal:[eq(test.t2.a, test.t4.a)]",
9699+
" ├─HashJoin(Build) 19.51 root left outer join, equal:[eq(test.t.a, test.t1.a)]",
9700+
" │ ├─HashJoin(Build) 15.61 root inner join, equal:[eq(test.t2.a, test.t.a)]",
9701+
" │ │ ├─HashJoin(Build) 12.49 root left outer join, equal:[eq(test.t2.a, test.t3.a)]",
9702+
" │ │ │ ├─TableReader(Build) 9.99 root data:Selection",
9703+
" │ │ │ │ └─Selection 9.99 cop[tikv] eq(test.t2.c, 100), not(isnull(test.t2.a))",
9704+
" │ │ │ │ └─TableFullScan 10000.00 cop[tikv] table:t2 keep order:false, stats:pseudo",
9705+
" │ │ │ └─TableReader(Probe) 3330.00 root data:Selection",
9706+
" │ │ │ └─Selection 3330.00 cop[tikv] gt(test.t3.b, 1), not(isnull(test.t3.a))",
9707+
" │ │ │ └─TableFullScan 10000.00 cop[tikv] table:t3 keep order:false, stats:pseudo",
9708+
" │ │ └─TableReader(Probe) 9990.00 root data:Selection",
9709+
" │ │ └─Selection 9990.00 cop[tikv] not(isnull(test.t.a))",
9710+
" │ │ └─TableFullScan 10000.00 cop[tikv] table:t keep order:false, stats:pseudo",
9711+
" │ └─TableReader(Probe) 9990.00 root data:Selection",
9712+
" │ └─Selection 9990.00 cop[tikv] not(isnull(test.t1.a))",
9713+
" │ └─TableFullScan 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo",
9714+
" └─TableReader(Probe) 9990.00 root data:Selection",
9715+
" └─Selection 9990.00 cop[tikv] not(isnull(test.t4.a))",
9716+
" └─TableFullScan 10000.00 cop[tikv] table:t4 keep order:false, stats:pseudo"
9717+
]
9718+
}
9719+
]
96629720
}
96639721
]

0 commit comments

Comments
 (0)