diff --git a/planner/cascades/testdata/integration_suite_in.json b/planner/cascades/testdata/integration_suite_in.json index 4a19d038c0..38bd3be3fc 100644 --- a/planner/cascades/testdata/integration_suite_in.json +++ b/planner/cascades/testdata/integration_suite_in.json @@ -44,7 +44,8 @@ "select count(case when a > 0 and a <= 1000 then b end) from t", "select count(case when a <= 0 or a > 1000 then null else b end) from t", "select count(distinct case when a > 0 and a <= 1000 then b end) from t", - "select count(b), sum(b), avg(b), b, max(b), min(b), bit_and(b), bit_or(b), bit_xor(b) from t group by a having sum(b) >= 0 and count(b) >= 0 order by b" + "select count(b), sum(b), avg(b), b, max(b), min(b), bit_and(b), bit_or(b), bit_xor(b) from t group by a having sum(b) >= 0 and count(b) >= 0 order by b", + "select group_concat(a, b), min(b), avg(a / b), a from t group by (a+b), a order by a" ] }, { diff --git a/planner/cascades/testdata/integration_suite_out.json b/planner/cascades/testdata/integration_suite_out.json index 2087d996e2..2a3d8f6ab1 100644 --- a/planner/cascades/testdata/integration_suite_out.json +++ b/planner/cascades/testdata/integration_suite_out.json @@ -134,10 +134,10 @@ { "SQL": "select sum(a) from t", "Plan": [ - "HashAgg_12 1.00 root funcs:sum(Column#4)->Column#3", - "└─TableReader_13 1.00 root data:HashAgg_14", - " └─HashAgg_14 1.00 cop[tikv] funcs:sum(test.t.a)->Column#4", - " └─TableFullScan_10 10000.00 cop[tikv] table:t keep order:false, stats:pseudo" + "HashAgg_14 1.00 root funcs:sum(Column#4)->Column#3", + "└─TableReader_15 1.00 root data:HashAgg_16", + " └─HashAgg_16 1.00 cop[tikv] funcs:sum(test.t.a)->Column#4", + " └─TableFullScan_13 10000.00 cop[tikv] table:t keep order:false, stats:pseudo" ], "Result": [ "10" @@ -158,12 +158,12 @@ { "SQL": "select b, avg(a) from t group by b order by b", "Plan": [ - "Projection_10 8000.00 root test.t.b, Column#3", - "└─Sort_19 8000.00 root test.t.b", - " └─HashAgg_12 8000.00 root group by:Column#9, funcs:avg(Column#7)->Column#3, funcs:firstrow(Column#8)->test.t.b", + "Projection_12 8000.00 root test.t.b, Column#3", + "└─Sort_21 8000.00 root test.t.b", + " └─HashAgg_14 8000.00 root group by:test.t.b, funcs:avg(Column#7)->Column#3, funcs:firstrow(test.t.b)->test.t.b", " └─Projection_15 10000.00 root cast(test.t.a, decimal(65,4) BINARY)->Column#7, test.t.b, test.t.b", - " └─TableReader_13 10000.00 root data:TableFullScan_14", - " └─TableFullScan_14 10000.00 cop[tikv] table:t keep order:false, stats:pseudo" + " └─TableReader_16 10000.00 root data:TableFullScan_17", + " └─TableFullScan_17 10000.00 cop[tikv] table:t keep order:false, stats:pseudo" ], "Result": [ "11 1.0000", @@ -175,12 +175,12 @@ { "SQL": "select b, sum(a) from t group by b order by b", "Plan": [ - "Projection_10 8000.00 root test.t.b, Column#3", - "└─Sort_19 8000.00 root test.t.b", - " └─HashAgg_16 8000.00 root group by:test.t.b, funcs:sum(Column#4)->Column#3, funcs:firstrow(test.t.b)->test.t.b", - " └─TableReader_17 8000.00 root data:HashAgg_18", - " └─HashAgg_18 8000.00 cop[tikv] group by:test.t.b, funcs:sum(test.t.a)->Column#4", - " └─TableFullScan_14 10000.00 cop[tikv] table:t keep order:false, stats:pseudo" + "Projection_12 8000.00 root test.t.b, Column#3", + "└─Sort_21 8000.00 root test.t.b", + " └─HashAgg_18 8000.00 root group by:test.t.b, funcs:sum(Column#4)->Column#3, funcs:firstrow(test.t.b)->test.t.b", + " └─TableReader_19 8000.00 root data:HashAgg_20", + " └─HashAgg_20 8000.00 cop[tikv] group by:test.t.b, funcs:sum(test.t.a)->Column#4", + " └─TableFullScan_17 10000.00 cop[tikv] table:t keep order:false, stats:pseudo" ], "Result": [ "11 1", @@ -192,14 +192,14 @@ { "SQL": "select b, avg(a) from t group by b having sum(a) > 1 order by b", "Plan": [ - "Projection_12 6400.00 root test.t.b, Column#3", - "└─Projection_14 6400.00 root test.t.b, Column#3, Column#4", - " └─Sort_27 6400.00 root test.t.b", - " └─Selection_26 6400.00 root gt(Column#4, 1)", - " └─HashAgg_17 8000.00 root group by:Column#14, funcs:avg(Column#11)->Column#3, funcs:sum(Column#12)->Column#4, funcs:firstrow(Column#13)->test.t.b", + "Projection_14 6400.00 root test.t.b, Column#3", + "└─Projection_16 6400.00 root test.t.b, Column#3, Column#4", + " └─Sort_29 6400.00 root test.t.b", + " └─Selection_28 6400.00 root gt(Column#4, 1)", + " └─HashAgg_19 8000.00 root group by:test.t.b, funcs:avg(Column#11)->Column#3, funcs:sum(Column#12)->Column#4, funcs:firstrow(test.t.b)->test.t.b", " └─Projection_20 10000.00 root cast(test.t.a, decimal(65,4) BINARY)->Column#11, cast(test.t.a, decimal(65,0) BINARY)->Column#12, test.t.b, test.t.b", - " └─TableReader_18 10000.00 root data:TableFullScan_19", - " └─TableFullScan_19 10000.00 cop[tikv] table:t keep order:false, stats:pseudo" + " └─TableReader_21 10000.00 root data:TableFullScan_22", + " └─TableFullScan_22 10000.00 cop[tikv] table:t keep order:false, stats:pseudo" ], "Result": [ "22 2.0000", @@ -210,10 +210,10 @@ { "SQL": "select max(a+b) from t", "Plan": [ - "HashAgg_31 1.00 root funcs:max(Column#4)->Column#3", - "└─TableReader_32 1.00 root data:HashAgg_33", - " └─HashAgg_33 1.00 cop[tikv] funcs:max(plus(test.t.a, test.t.b))->Column#4", - " └─TableFullScan_29 10000.00 cop[tikv] table:t keep order:false, stats:pseudo" + "HashAgg_68 1.00 root funcs:max(Column#4)->Column#3", + "└─TableReader_69 1.00 root data:HashAgg_70", + " └─HashAgg_70 1.00 cop[tikv] funcs:max(plus(test.t.a, test.t.b))->Column#4", + " └─TableFullScan_41 10000.00 cop[tikv] table:t keep order:false, stats:pseudo" ], "Result": [ "48" @@ -237,13 +237,13 @@ { "SQL": "select b, sum(a) from t group by b having b > 1 order by b", "Plan": [ - "Projection_14 6400.00 root test.t.b, Column#3", - "└─Sort_24 6400.00 root test.t.b", - " └─HashAgg_21 6400.00 root group by:test.t.b, funcs:sum(Column#4)->Column#3, funcs:firstrow(test.t.b)->test.t.b", - " └─TableReader_22 6400.00 root data:HashAgg_23", - " └─HashAgg_23 6400.00 cop[tikv] group by:test.t.b, funcs:sum(test.t.a)->Column#4", - " └─Selection_18 8000.00 cop[tikv] gt(test.t.b, 1)", - " └─TableFullScan_19 10000.00 cop[tikv] table:t keep order:false, stats:pseudo" + "Projection_16 6400.00 root test.t.b, Column#3", + "└─Sort_26 6400.00 root test.t.b", + " └─HashAgg_23 6400.00 root group by:test.t.b, funcs:sum(Column#4)->Column#3, funcs:firstrow(test.t.b)->test.t.b", + " └─TableReader_24 6400.00 root data:HashAgg_25", + " └─HashAgg_25 6400.00 cop[tikv] group by:test.t.b, funcs:sum(test.t.a)->Column#4", + " └─Selection_21 8000.00 cop[tikv] gt(test.t.b, 1)", + " └─TableFullScan_22 10000.00 cop[tikv] table:t keep order:false, stats:pseudo" ], "Result": [ "11 1", @@ -255,13 +255,13 @@ { "SQL": "select c, sum(a) from (select a+b as c, a from t) t1 group by c having c > 1 order by c", "Plan": [ - "Projection_18 6400.00 root Column#3, Column#4", - "└─Sort_30 6400.00 root Column#3", - " └─HashAgg_26 6400.00 root group by:Column#7, funcs:sum(Column#8)->Column#4, funcs:firstrow(Column#7)->Column#3", - " └─TableReader_27 6400.00 root data:HashAgg_28", - " └─HashAgg_28 6400.00 cop[tikv] group by:plus(test.t.a, test.t.b), funcs:sum(test.t.a)->Column#8", - " └─Selection_23 8000.00 cop[tikv] gt(plus(test.t.a, test.t.b), 1)", - " └─TableFullScan_24 10000.00 cop[tikv] table:t keep order:false, stats:pseudo" + "Projection_23 6400.00 root Column#3, Column#4", + "└─Sort_35 6400.00 root Column#3", + " └─HashAgg_32 6400.00 root group by:Column#7, funcs:sum(Column#8)->Column#4, funcs:firstrow(Column#7)->Column#3", + " └─TableReader_33 6400.00 root data:HashAgg_34", + " └─HashAgg_34 6400.00 cop[tikv] group by:plus(test.t.a, test.t.b), funcs:sum(test.t.a)->Column#8", + " └─Selection_28 8000.00 cop[tikv] gt(plus(test.t.a, test.t.b), 1)", + " └─TableFullScan_29 10000.00 cop[tikv] table:t keep order:false, stats:pseudo" ], "Result": [ "12 1", @@ -286,10 +286,10 @@ { "SQL": "select avg(a.b) from t a left join t b on a.a = b.a", "Plan": [ - "HashAgg_14 1.00 root funcs:avg(Column#6, Column#7)->Column#5", - "└─TableReader_15 1.00 root data:HashAgg_16", - " └─HashAgg_16 1.00 cop[tikv] funcs:count(test.t.b)->Column#6, funcs:sum(test.t.b)->Column#7", - " └─TableFullScan_12 10000.00 cop[tikv] table:a keep order:false, stats:pseudo" + "HashAgg_16 1.00 root funcs:avg(Column#6, Column#7)->Column#5", + "└─TableReader_17 1.00 root data:HashAgg_18", + " └─HashAgg_18 1.00 cop[tikv] funcs:count(test.t.b)->Column#6, funcs:sum(test.t.b)->Column#7", + " └─TableFullScan_15 10000.00 cop[tikv] table:a keep order:false, stats:pseudo" ], "Result": [ "27.5000" @@ -367,10 +367,10 @@ { "SQL": "select sum(case when a > 0 and a <= 1000 then b else 0 end) from t", "Plan": [ - "HashAgg_16 1.00 root funcs:sum(Column#4)->Column#3", - "└─TableReader_17 1.00 root data:HashAgg_18", - " └─HashAgg_18 1.00 cop[tikv] funcs:sum(test.t.b)->Column#4", - " └─TableRangeScan_14 250.00 cop[tikv] table:t range:(0,1000], keep order:false, stats:pseudo" + "HashAgg_18 1.00 root funcs:sum(Column#4)->Column#3", + "└─TableReader_19 1.00 root data:HashAgg_20", + " └─HashAgg_20 1.00 cop[tikv] funcs:sum(test.t.b)->Column#4", + " └─TableRangeScan_17 250.00 cop[tikv] table:t range:(0,1000], keep order:false, stats:pseudo" ], "Result": [ "110" @@ -379,10 +379,10 @@ { "SQL": "select sum(case when a > 0 then (case when a <= 1000 then b end) else 0 end) from t", "Plan": [ - "HashAgg_19 1.00 root funcs:sum(Column#4)->Column#3", - "└─TableReader_20 1.00 root data:HashAgg_21", - " └─HashAgg_21 1.00 cop[tikv] funcs:sum(test.t.b)->Column#4", - " └─TableRangeScan_17 250.00 cop[tikv] table:t range:(0,1000], keep order:false, stats:pseudo" + "HashAgg_21 1.00 root funcs:sum(Column#4)->Column#3", + "└─TableReader_22 1.00 root data:HashAgg_23", + " └─HashAgg_23 1.00 cop[tikv] funcs:sum(test.t.b)->Column#4", + " └─TableRangeScan_20 250.00 cop[tikv] table:t range:(0,1000], keep order:false, stats:pseudo" ], "Result": [ "110" @@ -391,10 +391,10 @@ { "SQL": "select sum(case when a <= 0 or a > 1000 then 0.0 else b end) from t", "Plan": [ - "HashAgg_16 1.00 root funcs:sum(Column#4)->Column#3", - "└─TableReader_17 1.00 root data:HashAgg_18", - " └─HashAgg_18 1.00 cop[tikv] funcs:sum(cast(test.t.b))->Column#4", - " └─TableRangeScan_14 250.00 cop[tikv] table:t range:(0,1000], keep order:false, stats:pseudo" + "HashAgg_18 1.00 root funcs:sum(Column#4)->Column#3", + "└─TableReader_19 1.00 root data:HashAgg_20", + " └─HashAgg_20 1.00 cop[tikv] funcs:sum(cast(test.t.b))->Column#4", + " └─TableRangeScan_17 250.00 cop[tikv] table:t range:(0,1000], keep order:false, stats:pseudo" ], "Result": [ "110.0" @@ -451,6 +451,22 @@ "1 33 33.0000 33 33 33 33 33 33", "1 44 44.0000 44 44 44 44 44 44" ] + }, + { + "SQL": "select group_concat(a, b), min(b), avg(a / b), a from t group by (a+b), a order by a", + "Plan": [ + "Sort_15 8000.00 root test.t.a", + "└─HashAgg_11 8000.00 root group by:Column#9, test.t.a, funcs:group_concat(Column#6, Column#7 separator \",\")->Column#3, funcs:min(test.t.b)->Column#4, funcs:avg(Column#8)->Column#5, funcs:firstrow(test.t.a)->test.t.a", + " └─Projection_12 10000.00 root cast(test.t.a, var_string(20))->Column#6, cast(test.t.b, var_string(20))->Column#7, test.t.b, div(cast(test.t.a, decimal(20,0) BINARY), cast(test.t.b, decimal(20,0) BINARY))->Column#8, test.t.a, plus(test.t.a, test.t.b)->Column#9, test.t.a", + " └─TableReader_13 10000.00 root data:TableFullScan_14", + " └─TableFullScan_14 10000.00 cop[tikv] table:t keep order:false, stats:pseudo" + ], + "Result": [ + "111 11 0.09090909 1", + "222 22 0.09090909 2", + "333 33 0.09090909 3", + "444 44 0.09090909 4" + ] } ] }, @@ -460,11 +476,11 @@ { "SQL": "select /*+ HASH_AGG() */ avg(distinct a) from t;", "Plan": [ - "HashAgg_12 1.00 root funcs:avg(distinct Column#8)->Column#5", - "└─Projection_15 8000.00 root cast(test.t.a, decimal(65,4) BINARY)->Column#8", - " └─TableReader_13 8000.00 root data:HashAgg_14", - " └─HashAgg_14 8000.00 cop[tikv] group by:test.t.a, ", - " └─TableFullScan_10 10000.00 cop[tikv] table:t keep order:false, stats:pseudo" + "HashAgg_16 1.00 root funcs:avg(distinct Column#8)->Column#5", + "└─Projection_17 8000.00 root cast(test.t.a, decimal(65,4) BINARY)->Column#8", + " └─TableReader_18 8000.00 root data:HashAgg_19", + " └─HashAgg_19 8000.00 cop[tikv] group by:test.t.a, ", + " └─TableFullScan_15 10000.00 cop[tikv] table:t keep order:false, stats:pseudo" ], "Result": [ "1.5000" @@ -486,11 +502,11 @@ { "SQL": "select /*+ HASH_AGG() */ avg(b), c, avg(b), count(distinct A, B), count(distinct A), count(distinct c), sum(b) from t group by c;", "Plan": [ - "Projection_8 8000.00 root Column#5, test.t.c, Column#5, Column#6, Column#7, Column#8, Column#9", - "└─HashAgg_13 8000.00 root group by:test.t.c, funcs:avg(Column#11, Column#12)->Column#5, funcs:count(distinct test.t.a, test.t.b)->Column#6, funcs:count(distinct test.t.a)->Column#7, funcs:count(distinct test.t.c)->Column#8, funcs:sum(Column#13)->Column#9, funcs:firstrow(test.t.c)->test.t.c", - " └─TableReader_14 8000.00 root data:HashAgg_15", - " └─HashAgg_15 8000.00 cop[tikv] group by:test.t.a, test.t.b, test.t.c, funcs:count(test.t.b)->Column#11, funcs:sum(test.t.b)->Column#12, funcs:sum(test.t.b)->Column#13", - " └─TableFullScan_11 10000.00 cop[tikv] table:t keep order:false, stats:pseudo" + "Projection_10 8000.00 root Column#5, test.t.c, Column#5, Column#6, Column#7, Column#8, Column#9", + "└─HashAgg_15 8000.00 root group by:test.t.c, funcs:avg(Column#11, Column#12)->Column#5, funcs:count(distinct test.t.a, test.t.b)->Column#6, funcs:count(distinct test.t.a)->Column#7, funcs:count(distinct test.t.c)->Column#8, funcs:sum(Column#13)->Column#9, funcs:firstrow(test.t.c)->test.t.c", + " └─TableReader_16 8000.00 root data:HashAgg_17", + " └─HashAgg_17 8000.00 cop[tikv] group by:test.t.a, test.t.b, test.t.c, funcs:count(test.t.b)->Column#11, funcs:sum(test.t.b)->Column#12, funcs:sum(test.t.b)->Column#13", + " └─TableFullScan_14 10000.00 cop[tikv] table:t keep order:false, stats:pseudo" ], "Result": [ "1.0000 1 1.0000 1 1 1 1", @@ -570,10 +586,10 @@ { "SQL": "select /*+ HASH_AGG(), AGG_TO_COP() */ avg(distinct a) from t;", "Plan": [ - "HashAgg_6 1.00 root funcs:avg(distinct Column#7)->Column#5", + "HashAgg_8 1.00 root funcs:avg(distinct Column#7)->Column#5", "└─Projection_9 10000.00 root cast(test.t.a, decimal(65,4) BINARY)->Column#7", - " └─TableReader_7 10000.00 root data:TableFullScan_8", - " └─TableFullScan_8 10000.00 cop[tikv] table:t keep order:false, stats:pseudo" + " └─TableReader_10 10000.00 root data:TableFullScan_11", + " └─TableFullScan_11 10000.00 cop[tikv] table:t keep order:false, stats:pseudo" ], "Result": [ "1.5000" @@ -594,11 +610,11 @@ { "SQL": "select /*+ HASH_AGG(), AGG_TO_COP() */ avg(b), c, avg(b), count(distinct A, B), count(distinct A), count(distinct c), sum(b) from t group by c;", "Plan": [ - "Projection_6 8000.00 root Column#5, test.t.c, Column#5, Column#6, Column#7, Column#8, Column#9", - "└─HashAgg_7 8000.00 root group by:Column#18, funcs:avg(Column#11)->Column#5, funcs:count(distinct Column#12, Column#13)->Column#6, funcs:count(distinct Column#14)->Column#7, funcs:count(distinct Column#15)->Column#8, funcs:sum(Column#16)->Column#9, funcs:firstrow(Column#17)->test.t.c", - " └─Projection_10 10000.00 root cast(test.t.b, decimal(65,4) BINARY)->Column#11, test.t.a, test.t.b, test.t.a, test.t.c, cast(test.t.b, decimal(65,0) BINARY)->Column#16, test.t.c, test.t.c", - " └─TableReader_8 10000.00 root data:TableFullScan_9", - " └─TableFullScan_9 10000.00 cop[tikv] table:t keep order:false, stats:pseudo" + "Projection_8 8000.00 root Column#5, test.t.c, Column#5, Column#6, Column#7, Column#8, Column#9", + "└─HashAgg_9 8000.00 root group by:test.t.c, funcs:avg(Column#11)->Column#5, funcs:count(distinct test.t.a, test.t.b)->Column#6, funcs:count(distinct test.t.a)->Column#7, funcs:count(distinct test.t.c)->Column#8, funcs:sum(Column#12)->Column#9, funcs:firstrow(test.t.c)->test.t.c", + " └─Projection_10 10000.00 root cast(test.t.b, decimal(65,4) BINARY)->Column#11, test.t.a, test.t.b, test.t.a, test.t.c, cast(test.t.b, decimal(65,0) BINARY)->Column#12, test.t.c, test.t.c", + " └─TableReader_11 10000.00 root data:TableFullScan_12", + " └─TableFullScan_12 10000.00 cop[tikv] table:t keep order:false, stats:pseudo" ], "Result": [ "1.0000 1 1.0000 1 1 1 1", @@ -995,25 +1011,25 @@ { "SQL": "select sum(a), (select t1.a from t1 where t1.a = t2.a limit 1), (select t1.b from t1 where t1.b = t2.b limit 1) from t2", "Plan": [ - "Projection_28 1.00 root Column#3, test.t1.a, test.t1.b", - "└─Apply_30 1.00 root CARTESIAN left outer join", - " ├─Apply_32(Build) 1.00 root CARTESIAN left outer join", - " │ ├─HashAgg_37(Build) 1.00 root funcs:sum(Column#8)->Column#3, funcs:firstrow(Column#9)->test.t2.a, funcs:firstrow(Column#10)->test.t2.b", - " │ │ └─TableReader_38 1.00 root data:HashAgg_39", - " │ │ └─HashAgg_39 1.00 cop[tikv] funcs:sum(test.t2.a)->Column#8, funcs:firstrow(test.t2.a)->Column#9, funcs:firstrow(test.t2.b)->Column#10", - " │ │ └─TableFullScan_35 10000.00 cop[tikv] table:t2 keep order:false, stats:pseudo", - " │ └─MaxOneRow_40(Probe) 1.00 root ", - " │ └─Limit_41 1.00 root offset:0, count:1", - " │ └─TableReader_42 1.00 root data:Limit_43", - " │ └─Limit_43 1.00 cop[tikv] offset:0, count:1", - " │ └─Selection_44 1.00 cop[tikv] eq(test.t1.a, test.t2.a)", - " │ └─TableFullScan_45 1.00 cop[tikv] table:t1 keep order:false, stats:pseudo", - " └─MaxOneRow_46(Probe) 1.00 root ", - " └─Limit_47 1.00 root offset:0, count:1", - " └─TableReader_48 1.00 root data:Limit_49", - " └─Limit_49 1.00 cop[tikv] offset:0, count:1", - " └─Selection_50 1.00 cop[tikv] eq(test.t1.b, test.t2.b)", - " └─TableFullScan_51 1.00 cop[tikv] table:t1 keep order:false, stats:pseudo" + "Projection_30 1.00 root Column#3, test.t1.a, test.t1.b", + "└─Apply_32 1.00 root CARTESIAN left outer join", + " ├─Apply_34(Build) 1.00 root CARTESIAN left outer join", + " │ ├─HashAgg_39(Build) 1.00 root funcs:sum(Column#8)->Column#3, funcs:firstrow(Column#9)->test.t2.a, funcs:firstrow(Column#10)->test.t2.b", + " │ │ └─TableReader_40 1.00 root data:HashAgg_41", + " │ │ └─HashAgg_41 1.00 cop[tikv] funcs:sum(test.t2.a)->Column#8, funcs:firstrow(test.t2.a)->Column#9, funcs:firstrow(test.t2.b)->Column#10", + " │ │ └─TableFullScan_38 10000.00 cop[tikv] table:t2 keep order:false, stats:pseudo", + " │ └─MaxOneRow_42(Probe) 1.00 root ", + " │ └─Limit_43 1.00 root offset:0, count:1", + " │ └─TableReader_44 1.00 root data:Limit_45", + " │ └─Limit_45 1.00 cop[tikv] offset:0, count:1", + " │ └─Selection_46 1.00 cop[tikv] eq(test.t1.a, test.t2.a)", + " │ └─TableFullScan_47 1.00 cop[tikv] table:t1 keep order:false, stats:pseudo", + " └─MaxOneRow_48(Probe) 1.00 root ", + " └─Limit_49 1.00 root offset:0, count:1", + " └─TableReader_50 1.00 root data:Limit_51", + " └─Limit_51 1.00 cop[tikv] offset:0, count:1", + " └─Selection_52 1.00 cop[tikv] eq(test.t1.b, test.t2.b)", + " └─TableFullScan_53 1.00 cop[tikv] table:t1 keep order:false, stats:pseudo" ], "Result": [ "6 1 11" diff --git a/planner/cascades/testdata/transformation_rules_suite_in.json b/planner/cascades/testdata/transformation_rules_suite_in.json index f68fbd2569..12555b07fd 100644 --- a/planner/cascades/testdata/transformation_rules_suite_in.json +++ b/planner/cascades/testdata/transformation_rules_suite_in.json @@ -205,5 +205,14 @@ "cases": [ "select a from t t1 where exists (select 1 from t t2 where t1.a = t2.b)" ] + }, + { + "name": "TestInjectProj", + "cases": [ + "select * from t order by (a+b) limit 10", + "select max(a), min(b), avg(c) from t group by a+b", + "select max(a), min(b), avg(a / b) from t group by a", + "select max(a), min(b), avg(a / b) from t group by (a+b)" + ] } ] diff --git a/planner/cascades/testdata/transformation_rules_suite_out.json b/planner/cascades/testdata/transformation_rules_suite_out.json index 97244c4524..0be9e41251 100644 --- a/planner/cascades/testdata/transformation_rules_suite_out.json +++ b/planner/cascades/testdata/transformation_rules_suite_out.json @@ -2352,5 +2352,64 @@ ] } ] + }, + { + "Name": "TestInjectProj", + "Cases": [ + { + "SQL": "select * from t order by (a+b) limit 10", + "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]", + " Projection_6 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", + "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,Column#13]", + " TopN_8 input:[Group#2], Column#13, offset:0, count:10", + "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,Column#13]", + " Projection_7 input:[Group#3], 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, plus(test.t.a, test.t.b)->Column#13", + "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]", + " Projection_2 input:[Group#4], 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#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]", + " DataSource_1 table:t" + ] + }, + { + "SQL": "select max(a), min(b), avg(c) from t group by a+b", + "Result": [ + "Group#0 Schema:[Column#13,Column#14,Column#15]", + " Projection_3 input:[Group#1], Column#13, Column#14, Column#15", + "Group#1 Schema:[Column#13,Column#14,Column#15]", + " Aggregation_5 input:[Group#2], group by:Column#17, funcs:max(test.t.a), min(test.t.b), avg(Column#16)", + "Group#2 Schema:[test.t.a,test.t.b,Column#16,Column#17]", + " Projection_4 input:[Group#3], test.t.a, test.t.b, cast(test.t.c, decimal(65,30) BINARY)->Column#16, plus(test.t.a, test.t.b)->Column#17", + "Group#3 Schema:[test.t.a,test.t.b,test.t.c]", + " DataSource_1 table:t" + ] + }, + { + "SQL": "select max(a), min(b), avg(a / b) from t group by a", + "Result": [ + "Group#0 Schema:[Column#13,Column#14,Column#15]", + " Projection_3 input:[Group#1], Column#13, Column#14, Column#15", + "Group#1 Schema:[Column#13,Column#14,Column#15]", + " Aggregation_5 input:[Group#2], group by:test.t.a, funcs:max(test.t.a), min(test.t.b), avg(Column#16)", + "Group#2 Schema:[test.t.a,test.t.b,Column#16,test.t.a]", + " Projection_4 input:[Group#3], test.t.a, test.t.b, div(cast(test.t.a, decimal(20,0) BINARY), cast(test.t.b, decimal(20,0) BINARY))->Column#16, test.t.a", + "Group#3 Schema:[test.t.a,test.t.b]", + " DataSource_1 table:t" + ] + }, + { + "SQL": "select max(a), min(b), avg(a / b) from t group by (a+b)", + "Result": [ + "Group#0 Schema:[Column#13,Column#14,Column#15]", + " Projection_3 input:[Group#1], Column#13, Column#14, Column#15", + "Group#1 Schema:[Column#13,Column#14,Column#15]", + " Aggregation_5 input:[Group#2], group by:Column#17, funcs:max(test.t.a), min(test.t.b), avg(Column#16)", + "Group#2 Schema:[test.t.a,test.t.b,Column#16,Column#17]", + " Projection_4 input:[Group#3], test.t.a, test.t.b, div(cast(test.t.a, decimal(20,0) BINARY), cast(test.t.b, decimal(20,0) BINARY))->Column#16, plus(test.t.a, test.t.b)->Column#17", + "Group#3 Schema:[test.t.a,test.t.b]", + " DataSource_1 table:t" + ] + } + ] } ] diff --git a/planner/cascades/transformation_rules.go b/planner/cascades/transformation_rules.go index 9ea7b8c423..5885c2be15 100644 --- a/planner/cascades/transformation_rules.go +++ b/planner/cascades/transformation_rules.go @@ -139,6 +139,9 @@ var PostTransformationBatch = TransformationRuleBatch{ NewRuleEliminateProjection(), NewRuleMergeAdjacentProjection(), }, + memo.OperandAggregation: { + NewRuleInjectProjectionBelowAgg(), + }, memo.OperandTopN: { NewRuleInjectProjectionBelowTopN(), }, @@ -2222,6 +2225,122 @@ func (r *InjectProjectionBelowTopN) OnTransform(old *memo.ExprIter) (newExprs [] return []*memo.GroupExpr{topProjGroupExpr}, true, false, nil } +// InjectProjectionBelowAgg injects Projection below Agg if Agg's AggFuncDesc.Args or +// Agg's GroupByItem contain ScalarFunctions. +type InjectProjectionBelowAgg struct { + baseRule +} + +// NewRuleInjectProjectionBelowAgg creates a new Transformation NewRuleInjectProjectionBelowAgg. +// It will extract the ScalarFunctions of `AggFuncDesc` and `GroupByItems` into a Projection and injects it below Agg. +// The reason why we need this rule is that, AggExecutor in TiDB does not support ScalarFunction +// as `AggFuncDesc.Arg` and `GroupByItem`. So we have to use a Projection to calculate the ScalarFunctions in advance. +// The pattern of this rule is: a single Aggregation. +func NewRuleInjectProjectionBelowAgg() Transformation { + rule := &InjectProjectionBelowAgg{} + rule.pattern = memo.BuildPattern( + memo.OperandAggregation, + memo.EngineTiDBOnly, + ) + return rule +} + +// Match implements Transformation interface. +func (r *InjectProjectionBelowAgg) Match(expr *memo.ExprIter) bool { + agg := expr.GetExpr().ExprNode.(*plannercore.LogicalAggregation) + return agg.IsCompleteModeAgg() +} + +// OnTransform implements Transformation interface. +// It will convert `Agg -> X` to `Agg -> Proj -> X`. +func (r *InjectProjectionBelowAgg) OnTransform(old *memo.ExprIter) (newExprs []*memo.GroupExpr, eraseOld bool, eraseAll bool, err error) { + agg := old.GetExpr().ExprNode.(*plannercore.LogicalAggregation) + + hasScalarFunc := false + copyFuncs := make([]*aggregation.AggFuncDesc, 0, len(agg.AggFuncs)) + for _, aggFunc := range agg.AggFuncs { + copyFunc := aggFunc.Clone() + //WrapCastForAggArgs will modify AggFunc, so we should clone AggFunc. + copyFunc.WrapCastForAggArgs(agg.SCtx()) + copyFuncs = append(copyFuncs, copyFunc) + for _, arg := range copyFunc.Args { + _, isScalarFunc := arg.(*expression.ScalarFunction) + hasScalarFunc = hasScalarFunc || isScalarFunc + } + } + + for i := 0; !hasScalarFunc && i < len(agg.GroupByItems); i++ { + _, isScalarFunc := agg.GroupByItems[i].(*expression.ScalarFunction) + hasScalarFunc = hasScalarFunc || isScalarFunc + } + if !hasScalarFunc { + return nil, false, false, nil + } + + projSchemaCols := make([]*expression.Column, 0, len(copyFuncs)+len(agg.GroupByItems)) + projExprs := make([]expression.Expression, 0, cap(projSchemaCols)) + + for _, f := range copyFuncs { + for i, arg := range f.Args { + switch expr := arg.(type) { + case *expression.Constant: + continue + case *expression.Column: + projExprs = append(projExprs, expr) + projSchemaCols = append(projSchemaCols, expr) + default: + projExprs = append(projExprs, expr) + newArg := &expression.Column{ + UniqueID: agg.SCtx().GetSessionVars().AllocPlanColumnID(), + RetType: arg.GetType(), + } + projSchemaCols = append(projSchemaCols, newArg) + f.Args[i] = newArg + } + } + } + + newGroupByItems := make([]expression.Expression, len(agg.GroupByItems)) + for i, item := range agg.GroupByItems { + switch expr := item.(type) { + case *expression.Constant: + newGroupByItems[i] = expr + case *expression.Column: + newGroupByItems[i] = expr + projExprs = append(projExprs, expr) + projSchemaCols = append(projSchemaCols, expr) + default: + projExprs = append(projExprs, expr) + newArg := &expression.Column{ + UniqueID: agg.SCtx().GetSessionVars().AllocPlanColumnID(), + RetType: item.GetType(), + } + projSchemaCols = append(projSchemaCols, newArg) + newGroupByItems[i] = newArg + } + } + + // Construct GroupExpr, Group (Agg -> Proj -> Child). + proj := plannercore.LogicalProjection{ + Exprs: projExprs, + }.Init(agg.SCtx(), agg.SelectBlockOffset()) + projSchema := expression.NewSchema(projSchemaCols...) + proj.SetSchema(projSchema) + projExpr := memo.NewGroupExpr(proj) + projExpr.SetChildren(old.GetExpr().Children[0]) + projGroup := memo.NewGroupWithSchema(projExpr, projSchema) + + newAgg := plannercore.LogicalAggregation{ + AggFuncs: copyFuncs, + GroupByItems: newGroupByItems, + }.Init(agg.SCtx(), agg.SelectBlockOffset()) + newAgg.CopyAggHints(agg) + newAggExpr := memo.NewGroupExpr(newAgg) + newAggExpr.SetChildren(projGroup) + + return []*memo.GroupExpr{newAggExpr}, true, false, nil +} + // TransformApplyToJoin transforms a LogicalApply to LogicalJoin if it's // inner children has no correlated columns from it's outer schema. type TransformApplyToJoin struct { diff --git a/planner/cascades/transformation_rules_test.go b/planner/cascades/transformation_rules_test.go index 0bab318c5a..421f815313 100644 --- a/planner/cascades/transformation_rules_test.go +++ b/planner/cascades/transformation_rules_test.go @@ -425,3 +425,28 @@ func (s *testTransformationRuleSuite) TestDecorrelate(c *C) { s.testData.GetTestCases(c, &input, &output) testGroupToString(input, output, s, c) } + +func (s *testTransformationRuleSuite) TestInjectProj(c *C) { + s.optimizer.ResetTransformationRules(map[memo.Operand][]Transformation{ + memo.OperandLimit: { + NewRuleTransformLimitToTopN(), + }, + }, map[memo.Operand][]Transformation{ + memo.OperandAggregation: { + NewRuleInjectProjectionBelowAgg(), + }, + memo.OperandTopN: { + NewRuleInjectProjectionBelowTopN(), + }, + }) + defer func() { + s.optimizer.ResetTransformationRules(DefaultRuleBatches...) + }() + var input []string + var output []struct { + SQL string + Result []string + } + s.testData.GetTestCases(c, &input, &output) + testGroupToString(input, output, s, c) +} diff --git a/planner/implementation/simple_plans.go b/planner/implementation/simple_plans.go index b7941faadf..c20096f828 100644 --- a/planner/implementation/simple_plans.go +++ b/planner/implementation/simple_plans.go @@ -87,8 +87,6 @@ func (agg *TiDBHashAggImpl) CalcCost(outCount float64, children ...memo.Implemen func (agg *TiDBHashAggImpl) AttachChildren(children ...memo.Implementation) memo.Implementation { hashAgg := agg.plan.(*plannercore.PhysicalHashAgg) hashAgg.SetChildren(children[0].GetPlan()) - // Inject extraProjection if the AggFuncs or GroupByItems contain ScalarFunction. - plannercore.InjectProjBelowAgg(hashAgg, hashAgg.AggFuncs, hashAgg.GroupByItems) return agg }