From 684340459cfc36f01b9b3588c042aff7db4e83be Mon Sep 17 00:00:00 2001 From: feiniaofeiafei Date: Mon, 6 Jul 2026 16:49:55 +0800 Subject: [PATCH] [fix](fe) Skip eager aggregation for empty relation context Issue Number: None Related PR: None Problem Summary: Eager aggregation could reach a logical relation with no aggregate functions or group keys and generate an invalid empty aggregate. Return the relation unchanged for this empty push-down context and add a focused unit test covering the visitor path. Prevent eager aggregation rewrite from generating an empty aggregate on a relation. - Test: Unit Test - ./run-fe-ut.sh --run org.apache.doris.nereids.rules.rewrite.eageraggregation.EagerAggRewriterTest - ./run-fe-ut.sh --run org.apache.doris.nereids.rules.rewrite.eageraggregation.EagerAggRewriterTest#testEmptyContextDoesNotAddRelationAggregate - Behavior changed: Yes (empty eager-aggregation contexts now leave relations unchanged) - Does this need documentation: No --- .../eageraggregation/EagerAggRewriter.java | 7 ++++-- .../eageraggregation/PushDownAggContext.java | 3 +++ .../EagerAggRewriterTest.java | 22 +++++++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/EagerAggRewriter.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/EagerAggRewriter.java index 18562508fa484b..cc0e92ff03ce96 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/EagerAggRewriter.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/EagerAggRewriter.java @@ -150,9 +150,9 @@ public Plan visitLogicalJoin(LogicalJoin join, P context.needOutputCount(), rightFuncs); boolean rightNeedOutputCount = needOutputCountForJoinChild(join, toRight, toLeft, context.needOutputCount(), leftFuncs); - Optional leftChildContext = toLeft ? Optional.of(context.forOneBranch(leftFuncs, + Optional leftChildContext = toLeft ? Optional.ofNullable(context.forOneBranch(leftFuncs, leftAliasMap, leftChildGroupByKeys, passThroughBigJoin, leftNeedOutputCount)) : Optional.empty(); - Optional rightChildContext = toRight ? Optional.of(context.forOneBranch(rightFuncs, + Optional rightChildContext = toRight ? Optional.ofNullable(context.forOneBranch(rightFuncs, rightAliasMap, rightChildGroupByKeys, passThroughBigJoin, rightNeedOutputCount)) : Optional.empty(); Plan newLeft = join.left(); @@ -744,6 +744,9 @@ public Plan visitLogicalFilter(LogicalFilter filter, PushDownAgg @Override public Plan visitLogicalRelation(LogicalRelation relation, PushDownAggContext context) { + if (context.aggFuncAndGroupKeyAllEmpty()) { + return relation; + } return genAggregate(relation, context); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/PushDownAggContext.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/PushDownAggContext.java index 0d2832617e642a..b9a4a72b95dc13 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/PushDownAggContext.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/PushDownAggContext.java @@ -153,6 +153,9 @@ public PushDownAggContext withGroupKeys(List groupKeys) { public PushDownAggContext forOneBranch(List branchAggFunctions, Map branchAliasMap, List groupKeys, boolean passThroughBigJoin, boolean needOutputCount) { + if (branchAggFunctions.isEmpty() && groupKeys.isEmpty()) { + return null; + } return new PushDownAggContext(branchAggFunctions, groupKeys, branchAliasMap, cascadesContext, passThroughBigJoin, hasDecomposedAggIf, hasCaseWhen, bilateralState, needOutputCount); diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/EagerAggRewriterTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/EagerAggRewriterTest.java index 758e23a4ba03e9..8ecd5f432e95f9 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/EagerAggRewriterTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/EagerAggRewriterTest.java @@ -29,6 +29,7 @@ import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate; import org.apache.doris.nereids.trees.plans.logical.LogicalFilter; import org.apache.doris.nereids.trees.plans.logical.LogicalJoin; +import org.apache.doris.nereids.trees.plans.logical.LogicalRelation; import org.apache.doris.nereids.types.BigIntType; import org.apache.doris.nereids.util.MemoPatternMatchSupported; import org.apache.doris.nereids.util.PlanChecker; @@ -464,6 +465,27 @@ void testBilateralPushMultiLevelJoin() { } } + @Test + void testEmptyContextDoesNotAddRelationAggregate() { + connectContext.getSessionVariable().setEagerAggregationMode(1); + try { + PlanChecker planChecker = PlanChecker.from(connectContext).analyze("select * from t1"); + Plan analyzedPlan = planChecker.getPlan(); + LogicalRelation relation = findFirstPlan(analyzedPlan, LogicalRelation.class); + Assertions.assertNotNull(relation, analyzedPlan.treeString()); + PushDownAggContext context = new PushDownAggContext( + Collections.emptyList(), Collections.emptyList(), Collections.emptyMap(), + planChecker.getCascadesContext(), + true, false, false, new BilateralState(), false); + + Plan rewritten = relation.accept(new EagerAggRewriter(), context); + + Assertions.assertSame(relation, rewritten, rewritten.treeString()); + } finally { + connectContext.getSessionVariable().setEagerAggregationMode(0); + } + } + @Test void testVolatileJoinConjunctBlocksPushDown() { connectContext.getSessionVariable().setEagerAggregationMode(1);