Skip to content

Commit c8a5e07

Browse files
authored
planner: eliminate empty projection below aggregation (#29606) (#29636)
close pingcap/tiflash#3389
1 parent 70e5da7 commit c8a5e07

File tree

3 files changed

+91
-55
lines changed

3 files changed

+91
-55
lines changed

planner/core/logical_plan_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,9 @@ func (s *testPlanSuite) TestProjectionEliminator(c *C) {
604604
{
605605
sql: "select 1+num from (select 1+a as num from t) t1;",
606606
best: "DataScan(t)->Projection",
607+
}, {
608+
sql: "select count(*) from t where a in (select b from t2 where a is null);",
609+
best: "Join{DataScan(t)->Dual->Aggr(firstrow(test.t2.b))}(test.t.a,test.t2.b)->Aggr(count(1))->Projection",
607610
},
608611
}
609612

@@ -621,6 +624,29 @@ func (s *testPlanSuite) TestProjectionEliminator(c *C) {
621624
}
622625
}
623626

627+
func (s *testPlanSuite) TestCS3389(c *C) {
628+
defer testleak.AfterTest(c)()
629+
630+
ctx := context.Background()
631+
stmt, err := s.ParseOneStmt("select count(*) from t where a in (select b from t2 where a is null);", "", "")
632+
c.Assert(err, IsNil)
633+
p, _, err := BuildLogicalPlan(ctx, s.ctx, stmt, s.is)
634+
c.Assert(err, IsNil)
635+
p, err = logicalOptimize(context.TODO(), flagBuildKeyInfo|flagPrunColumns|flagPrunColumnsAgain|flagEliminateProjection|flagJoinReOrder, p.(LogicalPlan))
636+
c.Assert(err, IsNil)
637+
638+
// Assert that all Projection is not empty and there is no Projection between Aggregation and Join.
639+
proj, isProj := p.(*LogicalProjection)
640+
c.Assert(isProj, IsTrue)
641+
c.Assert(len(proj.Exprs) > 0, IsTrue)
642+
child := proj.Children()[0]
643+
agg, isAgg := child.(*LogicalAggregation)
644+
c.Assert(isAgg, IsTrue)
645+
child = agg.Children()[0]
646+
_, isJoin := child.(*LogicalJoin)
647+
c.Assert(isJoin, IsTrue)
648+
}
649+
624650
func (s *testPlanSuite) TestAllocID(c *C) {
625651
ctx := MockContext()
626652
pA := DataSource{}.Init(ctx, 0)

planner/core/rule_column_pruning.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,21 @@ func (la *LogicalAggregation) PruneColumns(parentUsedCols []*expression.Column)
147147
la.GroupByItems = []expression.Expression{expression.NewOne()}
148148
}
149149
}
150-
return child.PruneColumns(selfUsedCols)
150+
err := child.PruneColumns(selfUsedCols)
151+
if err != nil {
152+
return err
153+
}
154+
// Do an extra Projection Elimination here. This is specially for empty Projection below Aggregation.
155+
// This kind of Projection would cause some bugs for MPP plan and is safe to be removed.
156+
// This kind of Projection should be removed in Projection Elimination, but currently PrunColumnsAgain is
157+
// the last rule. So we specially handle this case here.
158+
if childProjection, isProjection := child.(*LogicalProjection); isProjection {
159+
if len(childProjection.Exprs) == 0 && childProjection.Schema().Len() == 0 {
160+
childOfChild := childProjection.children[0]
161+
la.SetChildren(childOfChild)
162+
}
163+
}
164+
return nil
151165
}
152166

153167
func pruneByItems(old []*util.ByItems) (new []*util.ByItems, parentUsedCols []*expression.Column) {

planner/core/testdata/integration_serial_suite_out.json

Lines changed: 50 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -251,59 +251,56 @@
251251
{
252252
"SQL": "explain format = 'brief' select count(*) from fact_t, d1_t where fact_t.d1_k = d1_t.d1_k",
253253
"Plan": [
254-
"HashAgg 1.00 root funcs:count(Column#12)->Column#11",
255-
"└─TableReader 1.00 root data:ExchangeSender",
256-
" └─ExchangeSender 1.00 batchCop[tiflash] ExchangeType: PassThrough",
257-
" └─HashAgg 1.00 batchCop[tiflash] funcs:count(1)->Column#12",
258-
" └─HashJoin 8.00 batchCop[tiflash] inner join, equal:[eq(test.d1_t.d1_k, test.fact_t.d1_k)]",
259-
" ├─ExchangeReceiver(Build) 2.00 batchCop[tiflash] ",
260-
" │ └─ExchangeSender 2.00 batchCop[tiflash] ExchangeType: Broadcast",
261-
" │ └─Selection 2.00 batchCop[tiflash] not(isnull(test.d1_t.d1_k))",
262-
" │ └─TableFullScan 2.00 batchCop[tiflash] table:d1_t keep order:false",
263-
" └─Selection(Probe) 8.00 batchCop[tiflash] not(isnull(test.fact_t.d1_k))",
264-
" └─TableFullScan 8.00 batchCop[tiflash] table:fact_t keep order:false"
254+
"StreamAgg 1.00 root funcs:count(1)->Column#11",
255+
"└─TableReader 8.00 root data:ExchangeSender",
256+
" └─ExchangeSender 8.00 cop[tiflash] ExchangeType: PassThrough",
257+
" └─HashJoin 8.00 cop[tiflash] inner join, equal:[eq(test.d1_t.d1_k, test.fact_t.d1_k)]",
258+
" ├─ExchangeReceiver(Build) 2.00 cop[tiflash] ",
259+
" │ └─ExchangeSender 2.00 cop[tiflash] ExchangeType: Broadcast",
260+
" │ └─Selection 2.00 cop[tiflash] not(isnull(test.d1_t.d1_k))",
261+
" │ └─TableFullScan 2.00 cop[tiflash] table:d1_t keep order:false",
262+
" └─Selection(Probe) 8.00 cop[tiflash] not(isnull(test.fact_t.d1_k))",
263+
" └─TableFullScan 8.00 cop[tiflash] table:fact_t keep order:false"
265264
]
266265
},
267266
{
268267
"SQL": "explain format = 'brief' select count(*) from fact_t, d1_t, d2_t, d3_t where fact_t.d1_k = d1_t.d1_k and fact_t.d2_k = d2_t.d2_k and fact_t.d3_k = d3_t.d3_k",
269268
"Plan": [
270-
"HashAgg 1.00 root funcs:count(Column#18)->Column#17",
271-
"└─TableReader 1.00 root data:ExchangeSender",
272-
" └─ExchangeSender 1.00 batchCop[tiflash] ExchangeType: PassThrough",
273-
" └─HashAgg 1.00 batchCop[tiflash] funcs:count(1)->Column#18",
274-
" └─HashJoin 8.00 batchCop[tiflash] inner join, equal:[eq(test.fact_t.d3_k, test.d3_t.d3_k)]",
275-
" ├─ExchangeReceiver(Build) 2.00 batchCop[tiflash] ",
276-
" │ └─ExchangeSender 2.00 batchCop[tiflash] ExchangeType: Broadcast",
277-
" │ └─Selection 2.00 batchCop[tiflash] not(isnull(test.d3_t.d3_k))",
278-
" │ └─TableFullScan 2.00 batchCop[tiflash] table:d3_t keep order:false",
279-
" └─HashJoin(Probe) 8.00 batchCop[tiflash] inner join, equal:[eq(test.fact_t.d2_k, test.d2_t.d2_k)]",
280-
" ├─ExchangeReceiver(Build) 2.00 batchCop[tiflash] ",
281-
" │ └─ExchangeSender 2.00 batchCop[tiflash] ExchangeType: Broadcast",
282-
" │ └─Selection 2.00 batchCop[tiflash] not(isnull(test.d2_t.d2_k))",
283-
" │ └─TableFullScan 2.00 batchCop[tiflash] table:d2_t keep order:false",
284-
" └─HashJoin(Probe) 8.00 batchCop[tiflash] inner join, equal:[eq(test.d1_t.d1_k, test.fact_t.d1_k)]",
285-
" ├─ExchangeReceiver(Build) 2.00 batchCop[tiflash] ",
286-
" │ └─ExchangeSender 2.00 batchCop[tiflash] ExchangeType: Broadcast",
287-
" │ └─Selection 2.00 batchCop[tiflash] not(isnull(test.d1_t.d1_k))",
288-
" │ └─TableFullScan 2.00 batchCop[tiflash] table:d1_t keep order:false",
289-
" └─Selection(Probe) 8.00 batchCop[tiflash] not(isnull(test.fact_t.d1_k)), not(isnull(test.fact_t.d2_k)), not(isnull(test.fact_t.d3_k))",
290-
" └─TableFullScan 8.00 batchCop[tiflash] table:fact_t keep order:false"
269+
"StreamAgg 1.00 root funcs:count(1)->Column#17",
270+
"└─TableReader 8.00 root data:ExchangeSender",
271+
" └─ExchangeSender 8.00 cop[tiflash] ExchangeType: PassThrough",
272+
" └─HashJoin 8.00 cop[tiflash] inner join, equal:[eq(test.fact_t.d3_k, test.d3_t.d3_k)]",
273+
" ├─ExchangeReceiver(Build) 2.00 cop[tiflash] ",
274+
" │ └─ExchangeSender 2.00 cop[tiflash] ExchangeType: Broadcast",
275+
" │ └─Selection 2.00 cop[tiflash] not(isnull(test.d3_t.d3_k))",
276+
" │ └─TableFullScan 2.00 cop[tiflash] table:d3_t keep order:false",
277+
" └─HashJoin(Probe) 8.00 cop[tiflash] inner join, equal:[eq(test.fact_t.d2_k, test.d2_t.d2_k)]",
278+
" ├─ExchangeReceiver(Build) 2.00 cop[tiflash] ",
279+
" │ └─ExchangeSender 2.00 cop[tiflash] ExchangeType: Broadcast",
280+
" │ └─Selection 2.00 cop[tiflash] not(isnull(test.d2_t.d2_k))",
281+
" │ └─TableFullScan 2.00 cop[tiflash] table:d2_t keep order:false",
282+
" └─HashJoin(Probe) 8.00 cop[tiflash] inner join, equal:[eq(test.d1_t.d1_k, test.fact_t.d1_k)]",
283+
" ├─ExchangeReceiver(Build) 2.00 cop[tiflash] ",
284+
" │ └─ExchangeSender 2.00 cop[tiflash] ExchangeType: Broadcast",
285+
" │ └─Selection 2.00 cop[tiflash] not(isnull(test.d1_t.d1_k))",
286+
" │ └─TableFullScan 2.00 cop[tiflash] table:d1_t keep order:false",
287+
" └─Selection(Probe) 8.00 cop[tiflash] not(isnull(test.fact_t.d1_k)), not(isnull(test.fact_t.d2_k)), not(isnull(test.fact_t.d3_k))",
288+
" └─TableFullScan 8.00 cop[tiflash] table:fact_t keep order:false"
291289
]
292290
},
293291
{
294292
"SQL": "explain format = 'brief' select count(*) from fact_t, d1_t where fact_t.d1_k = d1_t.d1_k",
295293
"Plan": [
296-
"HashAgg 1.00 root funcs:count(Column#12)->Column#11",
297-
"└─TableReader 1.00 root data:ExchangeSender",
298-
" └─ExchangeSender 1.00 batchCop[tiflash] ExchangeType: PassThrough",
299-
" └─HashAgg 1.00 batchCop[tiflash] funcs:count(1)->Column#12",
300-
" └─HashJoin 8.00 batchCop[tiflash] inner join, equal:[eq(test.d1_t.d1_k, test.fact_t.d1_k)]",
301-
" ├─ExchangeReceiver(Build) 2.00 batchCop[tiflash] ",
302-
" │ └─ExchangeSender 2.00 batchCop[tiflash] ExchangeType: Broadcast",
303-
" │ └─Selection 2.00 batchCop[tiflash] not(isnull(test.d1_t.d1_k))",
304-
" │ └─TableFullScan 2.00 batchCop[tiflash] table:d1_t keep order:false",
305-
" └─Selection(Probe) 8.00 batchCop[tiflash] not(isnull(test.fact_t.d1_k))",
306-
" └─TableFullScan 8.00 batchCop[tiflash] table:fact_t keep order:false"
294+
"StreamAgg 1.00 root funcs:count(1)->Column#11",
295+
"└─TableReader 8.00 root data:ExchangeSender",
296+
" └─ExchangeSender 8.00 cop[tiflash] ExchangeType: PassThrough",
297+
" └─HashJoin 8.00 cop[tiflash] inner join, equal:[eq(test.d1_t.d1_k, test.fact_t.d1_k)]",
298+
" ├─ExchangeReceiver(Build) 2.00 cop[tiflash] ",
299+
" │ └─ExchangeSender 2.00 cop[tiflash] ExchangeType: Broadcast",
300+
" │ └─Selection 2.00 cop[tiflash] not(isnull(test.d1_t.d1_k))",
301+
" │ └─TableFullScan 2.00 cop[tiflash] table:d1_t keep order:false",
302+
" └─Selection(Probe) 8.00 cop[tiflash] not(isnull(test.fact_t.d1_k))",
303+
" └─TableFullScan 8.00 cop[tiflash] table:fact_t keep order:false"
307304
]
308305
},
309306
{
@@ -337,17 +334,16 @@
337334
{
338335
"SQL": "explain format = 'brief' select count(*) from fact_t join d1_t on fact_t.d1_k = d1_t.d1_k and fact_t.col1 > d1_t.value",
339336
"Plan": [
340-
"HashAgg 1.00 root funcs:count(Column#12)->Column#11",
341-
"└─TableReader 1.00 root data:ExchangeSender",
342-
" └─ExchangeSender 1.00 batchCop[tiflash] ExchangeType: PassThrough",
343-
" └─HashAgg 1.00 batchCop[tiflash] funcs:count(1)->Column#12",
344-
" └─HashJoin 8.00 batchCop[tiflash] inner join, equal:[eq(test.d1_t.d1_k, test.fact_t.d1_k)], other cond:gt(test.fact_t.col1, test.d1_t.value)",
345-
" ├─ExchangeReceiver(Build) 2.00 batchCop[tiflash] ",
346-
" │ └─ExchangeSender 2.00 batchCop[tiflash] ExchangeType: Broadcast",
347-
" │ └─Selection 2.00 batchCop[tiflash] not(isnull(test.d1_t.d1_k)), not(isnull(test.d1_t.value))",
348-
" │ └─TableFullScan 2.00 batchCop[tiflash] table:d1_t keep order:false",
349-
" └─Selection(Probe) 8.00 batchCop[tiflash] not(isnull(test.fact_t.col1)), not(isnull(test.fact_t.d1_k))",
350-
" └─TableFullScan 8.00 batchCop[tiflash] table:fact_t keep order:false"
337+
"StreamAgg 1.00 root funcs:count(1)->Column#11",
338+
"└─TableReader 8.00 root data:ExchangeSender",
339+
" └─ExchangeSender 8.00 cop[tiflash] ExchangeType: PassThrough",
340+
" └─HashJoin 8.00 cop[tiflash] inner join, equal:[eq(test.d1_t.d1_k, test.fact_t.d1_k)], other cond:gt(test.fact_t.col1, test.d1_t.value)",
341+
" ├─ExchangeReceiver(Build) 2.00 cop[tiflash] ",
342+
" │ └─ExchangeSender 2.00 cop[tiflash] ExchangeType: Broadcast",
343+
" │ └─Selection 2.00 cop[tiflash] not(isnull(test.d1_t.d1_k)), not(isnull(test.d1_t.value))",
344+
" │ └─TableFullScan 2.00 cop[tiflash] table:d1_t keep order:false",
345+
" └─Selection(Probe) 8.00 cop[tiflash] not(isnull(test.fact_t.col1)), not(isnull(test.fact_t.d1_k))",
346+
" └─TableFullScan 8.00 cop[tiflash] table:fact_t keep order:false"
351347
]
352348
},
353349
{

0 commit comments

Comments
 (0)