From dbb6fdf51df10aba86eed7ba67c052ea6a97bf20 Mon Sep 17 00:00:00 2001 From: Qiannan Date: Thu, 9 Jan 2020 22:56:50 +1300 Subject: [PATCH] planner/cascades: add transformation rule PushTopNDownOuterJoin (#14249) --- .../transformation_rules_suite_in.json | 7 + .../transformation_rules_suite_out.json | 145 ++++++++++++++++++ planner/cascades/transformation_rules.go | 84 ++++++++++ planner/cascades/transformation_rules_test.go | 1 + 4 files changed, 237 insertions(+) diff --git a/planner/cascades/testdata/transformation_rules_suite_in.json b/planner/cascades/testdata/transformation_rules_suite_in.json index e7a9c174f0..28350cfa38 100644 --- a/planner/cascades/testdata/transformation_rules_suite_in.json +++ b/planner/cascades/testdata/transformation_rules_suite_in.json @@ -39,6 +39,13 @@ "select a, b, c from t t1 where t1.a in (select t2.a as a from t t2 where t2.b > t1.b order by t1.b limit 1)", "select a, b, c from t t1 where t1.a in (select a from (select t2.a as a, t1.b as b from t t2 where t2.b > t1.b) x order by b limit 1)", "select a, b from (select @i as a, @i := @i+1 as b from t) t order by a desc limit 1", + "select * from t t1 left join t t2 on t1.b = t2.b order by t1.b limit 1", + "select * from t t1 left join t t2 on t1.b = t2.b order by t1.a, t1.c limit 1", + "select * from t t1 left join t t2 on t1.b = t2.b order by t2.a, t2.c limit 1", + "select * from t t1 left join t t2 on t1.b = t2.b order by t1.a, t2.c limit 1", + "select * from t t1 right join t t2 on t1.b = t2.b order by t1.a, t1.c limit 1", + "select * from t t1 right join t t2 on t1.b = t2.b order by t2.a, t2.c limit 1", + "select * from t t1 right join t t2 on t1.b = t2.b order by t1.a, t2.c limit 1", "(select a from t) union all (select b from t) order by a limit 2;", "(select a from t) union all (select b from t) limit 2;", "(select a from t) union all (select b from t) limit 2 offset 5;", diff --git a/planner/cascades/testdata/transformation_rules_suite_out.json b/planner/cascades/testdata/transformation_rules_suite_out.json index c26c943d60..aabd085071 100644 --- a/planner/cascades/testdata/transformation_rules_suite_out.json +++ b/planner/cascades/testdata/transformation_rules_suite_out.json @@ -506,6 +506,151 @@ " IndexScan_8 table:t, index:c, d, e" ] }, + { + "SQL": "select * from t t1 left join t t2 on t1.b = t2.b order by t1.b limit 1", + "Result": [ + "Group#0 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date,test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " Projection_4 input:[Group#1], test.t.a, test.t.b, test.t.c, test.t.d, test.t.e, test.t.c_str, test.t.d_str, test.t.e_str, test.t.f, test.t.g, test.t.h, test.t.i_date, test.t.a, test.t.b, test.t.c, test.t.d, test.t.e, test.t.c_str, test.t.d_str, test.t.e_str, test.t.f, test.t.g, test.t.h, test.t.i_date", + "Group#1 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date,test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TopN_12 input:[Group#2], test.t.b:asc, offset:0, count:1", + "Group#2 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date,test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " Join_3 input:[Group#3,Group#4], left outer join, equal:[eq(test.t.b, test.t.b)]", + "Group#3 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TopN_13 input:[Group#5], test.t.b:asc, offset:0, count:1", + "Group#5 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TiKVSingleGather_8 input:[Group#6], table:t1", + "Group#6 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TopN_14 input:[Group#7], test.t.b:asc, offset:0, count:1", + "Group#7 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TableScan_7 table:t1, pk col:test.t.a", + "Group#4 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TiKVSingleGather_10 input:[Group#8], table:t2", + "Group#8 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TableScan_9 table:t2, pk col:test.t.a" + ] + }, + { + "SQL": "select * from t t1 left join t t2 on t1.b = t2.b order by t1.a, t1.c limit 1", + "Result": [ + "Group#0 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date,test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " Projection_4 input:[Group#1], test.t.a, test.t.b, test.t.c, test.t.d, test.t.e, test.t.c_str, test.t.d_str, test.t.e_str, test.t.f, test.t.g, test.t.h, test.t.i_date, test.t.a, test.t.b, test.t.c, test.t.d, test.t.e, test.t.c_str, test.t.d_str, test.t.e_str, test.t.f, test.t.g, test.t.h, test.t.i_date", + "Group#1 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date,test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TopN_12 input:[Group#2], test.t.a:asc, test.t.c:asc, offset:0, count:1", + "Group#2 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date,test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " Join_3 input:[Group#3,Group#4], left outer join, equal:[eq(test.t.b, test.t.b)]", + "Group#3 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TopN_13 input:[Group#5], test.t.a:asc, test.t.c:asc, offset:0, count:1", + "Group#5 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TiKVSingleGather_8 input:[Group#6], table:t1", + "Group#6 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TopN_14 input:[Group#7], test.t.a:asc, test.t.c:asc, offset:0, count:1", + "Group#7 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TableScan_7 table:t1, pk col:test.t.a", + "Group#4 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TiKVSingleGather_10 input:[Group#8], table:t2", + "Group#8 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TableScan_9 table:t2, pk col:test.t.a" + ] + }, + { + "SQL": "select * from t t1 left join t t2 on t1.b = t2.b order by t2.a, t2.c limit 1", + "Result": [ + "Group#0 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date,test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " Projection_4 input:[Group#1], test.t.a, test.t.b, test.t.c, test.t.d, test.t.e, test.t.c_str, test.t.d_str, test.t.e_str, test.t.f, test.t.g, test.t.h, test.t.i_date, test.t.a, test.t.b, test.t.c, test.t.d, test.t.e, test.t.c_str, test.t.d_str, test.t.e_str, test.t.f, test.t.g, test.t.h, test.t.i_date", + "Group#1 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date,test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TopN_12 input:[Group#2], test.t.a:asc, test.t.c:asc, offset:0, count:1", + "Group#2 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date,test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " Join_3 input:[Group#3,Group#4], left outer join, equal:[eq(test.t.b, test.t.b)]", + "Group#3 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TiKVSingleGather_8 input:[Group#5], table:t1", + "Group#5 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TableScan_7 table:t1, pk col:test.t.a", + "Group#4 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TiKVSingleGather_10 input:[Group#6], table:t2", + "Group#6 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TableScan_9 table:t2, pk col:test.t.a" + ] + }, + { + "SQL": "select * from t t1 left join t t2 on t1.b = t2.b order by t1.a, t2.c limit 1", + "Result": [ + "Group#0 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date,test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " Projection_4 input:[Group#1], test.t.a, test.t.b, test.t.c, test.t.d, test.t.e, test.t.c_str, test.t.d_str, test.t.e_str, test.t.f, test.t.g, test.t.h, test.t.i_date, test.t.a, test.t.b, test.t.c, test.t.d, test.t.e, test.t.c_str, test.t.d_str, test.t.e_str, test.t.f, test.t.g, test.t.h, test.t.i_date", + "Group#1 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date,test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TopN_12 input:[Group#2], test.t.a:asc, test.t.c:asc, offset:0, count:1", + "Group#2 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date,test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " Join_3 input:[Group#3,Group#4], left outer join, equal:[eq(test.t.b, test.t.b)]", + "Group#3 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TiKVSingleGather_8 input:[Group#5], table:t1", + "Group#5 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TableScan_7 table:t1, pk col:test.t.a", + "Group#4 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TiKVSingleGather_10 input:[Group#6], table:t2", + "Group#6 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TableScan_9 table:t2, pk col:test.t.a" + ] + }, + { + "SQL": "select * from t t1 right join t t2 on t1.b = t2.b order by t1.a, t1.c limit 1", + "Result": [ + "Group#0 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date,test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " Projection_4 input:[Group#1], test.t.a, test.t.b, test.t.c, test.t.d, test.t.e, test.t.c_str, test.t.d_str, test.t.e_str, test.t.f, test.t.g, test.t.h, test.t.i_date, test.t.a, test.t.b, test.t.c, test.t.d, test.t.e, test.t.c_str, test.t.d_str, test.t.e_str, test.t.f, test.t.g, test.t.h, test.t.i_date", + "Group#1 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date,test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TopN_12 input:[Group#2], test.t.a:asc, test.t.c:asc, offset:0, count:1", + "Group#2 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date,test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " Join_3 input:[Group#3,Group#4], right outer join, equal:[eq(test.t.b, test.t.b)]", + "Group#3 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TiKVSingleGather_8 input:[Group#5], table:t1", + "Group#5 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TableScan_7 table:t1, pk col:test.t.a", + "Group#4 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TiKVSingleGather_10 input:[Group#6], table:t2", + "Group#6 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TableScan_9 table:t2, pk col:test.t.a" + ] + }, + { + "SQL": "select * from t t1 right join t t2 on t1.b = t2.b order by t2.a, t2.c limit 1", + "Result": [ + "Group#0 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date,test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " Projection_4 input:[Group#1], test.t.a, test.t.b, test.t.c, test.t.d, test.t.e, test.t.c_str, test.t.d_str, test.t.e_str, test.t.f, test.t.g, test.t.h, test.t.i_date, test.t.a, test.t.b, test.t.c, test.t.d, test.t.e, test.t.c_str, test.t.d_str, test.t.e_str, test.t.f, test.t.g, test.t.h, test.t.i_date", + "Group#1 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date,test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TopN_12 input:[Group#2], test.t.a:asc, test.t.c:asc, offset:0, count:1", + "Group#2 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date,test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " Join_3 input:[Group#3,Group#4], right outer join, equal:[eq(test.t.b, test.t.b)]", + "Group#3 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TiKVSingleGather_8 input:[Group#5], table:t1", + "Group#5 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TableScan_7 table:t1, pk col:test.t.a", + "Group#4 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TopN_13 input:[Group#6], test.t.a:asc, test.t.c:asc, offset:0, count:1", + "Group#6 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TiKVSingleGather_10 input:[Group#7], table:t2", + "Group#7 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TopN_14 input:[Group#8], test.t.a:asc, test.t.c:asc, offset:0, count:1", + "Group#8 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TableScan_9 table:t2, pk col:test.t.a" + ] + }, + { + "SQL": "select * from t t1 right join t t2 on t1.b = t2.b order by t1.a, t2.c limit 1", + "Result": [ + "Group#0 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date,test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " Projection_4 input:[Group#1], test.t.a, test.t.b, test.t.c, test.t.d, test.t.e, test.t.c_str, test.t.d_str, test.t.e_str, test.t.f, test.t.g, test.t.h, test.t.i_date, test.t.a, test.t.b, test.t.c, test.t.d, test.t.e, test.t.c_str, test.t.d_str, test.t.e_str, test.t.f, test.t.g, test.t.h, test.t.i_date", + "Group#1 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date,test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TopN_12 input:[Group#2], test.t.a:asc, test.t.c:asc, offset:0, count:1", + "Group#2 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date,test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " Join_3 input:[Group#3,Group#4], right outer join, equal:[eq(test.t.b, test.t.b)]", + "Group#3 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TiKVSingleGather_8 input:[Group#5], table:t1", + "Group#5 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TableScan_7 table:t1, pk col:test.t.a", + "Group#4 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TiKVSingleGather_10 input:[Group#6], table:t2", + "Group#6 Schema:[test.t.a,test.t.b,test.t.c,test.t.d,test.t.e,test.t.c_str,test.t.d_str,test.t.e_str,test.t.f,test.t.g,test.t.h,test.t.i_date]", + " TableScan_9 table:t2, pk col:test.t.a" + ] + }, { "SQL": "(select a from t) union all (select b from t) order by a limit 2;", "Result": [ diff --git a/planner/cascades/transformation_rules.go b/planner/cascades/transformation_rules.go index 75233b9bac..0d30601a1a 100644 --- a/planner/cascades/transformation_rules.go +++ b/planner/cascades/transformation_rules.go @@ -76,6 +76,7 @@ var defaultTransformationMap = map[memo.Operand][]Transformation{ }, memo.OperandTopN: { NewRulePushTopNDownProjection(), + NewRulePushTopNDownOuterJoin(), NewRulePushTopNDownUnionAll(), NewRulePushTopNDownTiKVSingleGather(), }, @@ -1035,6 +1036,89 @@ func (r *MergeAdjacentProjection) OnTransform(old *memo.ExprIter) (newExprs []*m return []*memo.GroupExpr{newProjExpr}, true, false, nil } +// PushTopNDownOuterJoin pushes topN to outer join. +type PushTopNDownOuterJoin struct { + baseRule +} + +// NewRulePushTopNDownOuterJoin creates a new Transformation PushTopNDownOuterJoin. +// The pattern of this rule is: `TopN -> Join`. +func NewRulePushTopNDownOuterJoin() Transformation { + rule := &PushTopNDownOuterJoin{} + rule.pattern = memo.BuildPattern( + memo.OperandTopN, + memo.EngineTiDBOnly, + memo.NewPattern(memo.OperandJoin, memo.EngineTiDBOnly), + ) + return rule +} + +// Match implements Transformation interface. +// Use appliedRuleSet in GroupExpr to avoid re-apply rules. +func (r *PushTopNDownOuterJoin) Match(expr *memo.ExprIter) bool { + if expr.GetExpr().HasAppliedRule(r) { + return false + } + join := expr.Children[0].GetExpr().ExprNode.(*plannercore.LogicalJoin) + switch join.JoinType { + case plannercore.LeftOuterJoin, plannercore.LeftOuterSemiJoin, plannercore.AntiLeftOuterSemiJoin, plannercore.RightOuterJoin: + return true + default: + return false + } +} + +func pushTopNDownOuterJoinToChild(topN *plannercore.LogicalTopN, outerGroup *memo.Group) *memo.Group { + for _, by := range topN.ByItems { + cols := expression.ExtractColumns(by.Expr) + for _, col := range cols { + if !outerGroup.Prop.Schema.Contains(col) { + return outerGroup + } + } + } + + newTopN := plannercore.LogicalTopN{ + Count: topN.Count + topN.Offset, + ByItems: make([]*plannercore.ByItems, len(topN.ByItems)), + }.Init(topN.SCtx(), topN.SelectBlockOffset()) + + for i := range topN.ByItems { + newTopN.ByItems[i] = topN.ByItems[i].Clone() + } + newTopNGroup := memo.NewGroupExpr(newTopN) + newTopNGroup.SetChildren(outerGroup) + newChild := memo.NewGroupWithSchema(newTopNGroup, outerGroup.Prop.Schema) + return newChild +} + +// OnTransform implements Transformation interface. +// This rule will transform `TopN->OuterJoin->(OuterChild, InnerChild)` to `TopN->OuterJoin->(TopN->OuterChild, InnerChild)` +func (r *PushTopNDownOuterJoin) OnTransform(old *memo.ExprIter) (newExprs []*memo.GroupExpr, eraseOld bool, eraseAll bool, err error) { + topN := old.GetExpr().ExprNode.(*plannercore.LogicalTopN) + joinExpr := old.Children[0].GetExpr() + join := joinExpr.ExprNode.(*plannercore.LogicalJoin) + joinSchema := old.Children[0].Group.Prop.Schema + leftGroup := joinExpr.Children[0] + rightGroup := joinExpr.Children[1] + + switch join.JoinType { + case plannercore.LeftOuterJoin, plannercore.LeftOuterSemiJoin, plannercore.AntiLeftOuterSemiJoin: + leftGroup = pushTopNDownOuterJoinToChild(topN, leftGroup) + case plannercore.RightOuterJoin: + rightGroup = pushTopNDownOuterJoinToChild(topN, rightGroup) + default: + return nil, false, false, nil + } + + newJoinExpr := memo.NewGroupExpr(join) + newJoinExpr.SetChildren(leftGroup, rightGroup) + newTopNExpr := memo.NewGroupExpr(topN) + newTopNExpr.SetChildren(memo.NewGroupWithSchema(newJoinExpr, joinSchema)) + newTopNExpr.AddAppliedRule(r) + return []*memo.GroupExpr{newTopNExpr}, true, false, nil +} + // PushTopNDownProjection pushes TopN to Projection. type PushTopNDownProjection struct { baseRule diff --git a/planner/cascades/transformation_rules_test.go b/planner/cascades/transformation_rules_test.go index 7999ef5b45..4fe79fd9fc 100644 --- a/planner/cascades/transformation_rules_test.go +++ b/planner/cascades/transformation_rules_test.go @@ -158,6 +158,7 @@ func (s *testTransformationRuleSuite) TestTopNRules(c *C) { }, memo.OperandTopN: { NewRulePushTopNDownProjection(), + NewRulePushTopNDownOuterJoin(), NewRulePushTopNDownUnionAll(), NewRulePushTopNDownTiKVSingleGather(), },