This is an automated email from the ASF dual-hosted git repository.
englefly pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new 23d894d9519 [fix](nereids) fix empty layer in CommonSubExpressionOpt
when WhenClause is the only common sub-expression (#60435)
23d894d9519 is described below
commit 23d894d951995a413b0dc251ecf52db5b550929d
Author: minghong <[email protected]>
AuthorDate: Wed Feb 4 18:10:16 2026 +0800
[fix](nereids) fix empty layer in CommonSubExpressionOpt when WhenClause is
the only common sub-expression (#60435)
### What problem does this PR solve?
Move WhenClause filtering from extraction phase (CommonSubExpressionOpt)
to collection phase (CommonSubExpressionCollector) to prevent
inconsistency that produces empty intermediate projection layers.
---
.../post/CommonSubExpressionCollector.java | 2 +
.../processor/post/CommonSubExpressionOpt.java | 16 ++-----
.../postprocess/CommonSubExpressionTest.java | 50 ++++++++++++++++++++++
3 files changed, 56 insertions(+), 12 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/CommonSubExpressionCollector.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/CommonSubExpressionCollector.java
index 11d8ca00744..cccf9dbba07 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/CommonSubExpressionCollector.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/CommonSubExpressionCollector.java
@@ -21,6 +21,7 @@ import
org.apache.doris.nereids.trees.expressions.ArrayItemReference;
import
org.apache.doris.nereids.trees.expressions.ArrayItemReference.ArrayItemSlot;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.SessionVarGuardExpr;
+import org.apache.doris.nereids.trees.expressions.WhenClause;
import org.apache.doris.nereids.trees.expressions.functions.scalar.Lambda;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
@@ -74,6 +75,7 @@ public class CommonSubExpressionCollector extends
ExpressionVisitor<Integer, Boo
// TODO: could not extract common expression when expression contains
same lambda expression
// because ArrayItemSlot in Lambda are not same.
if (expressions.contains(expr)
+ && !(expr instanceof WhenClause)
&& !(inLambda && expr.containsType(ArrayItemSlot.class,
ArrayItemReference.class))) {
Set<Expression> commonExpression =
getExpressionsFromDepthMap(depth, commonExprByDepth);
commonExpression.add(expr);
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/CommonSubExpressionOpt.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/CommonSubExpressionOpt.java
index 86dabb057da..0d9e3abc25a 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/CommonSubExpressionOpt.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/CommonSubExpressionOpt.java
@@ -22,7 +22,6 @@ import org.apache.doris.nereids.trees.expressions.Alias;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.NamedExpression;
import org.apache.doris.nereids.trees.expressions.Slot;
-import org.apache.doris.nereids.trees.expressions.WhenClause;
import
org.apache.doris.nereids.trees.expressions.visitor.DefaultExpressionRewriter;
import org.apache.doris.nereids.trees.plans.Plan;
import org.apache.doris.nereids.trees.plans.physical.PhysicalProject;
@@ -78,17 +77,10 @@ public class CommonSubExpressionOpt extends
PlanPostProcessor {
Set<Expression> exprsInDepth = CommonSubExpressionCollector
.getExpressionsFromDepthMap(i,
collector.commonExprByDepth);
exprsInDepth.forEach(expr -> {
- if (!(expr instanceof WhenClause)) {
- // case whenClause1 whenClause2 END
- // whenClause should not be regarded as
common-sub-expression, because
- // cse will be replaced by a slot, after rewrite the
case clause becomes:
- // 'case slot whenClause2 END'
- // This is illegal.
- Expression rewritten =
expr.accept(ExpressionReplacer.INSTANCE, aliasMap);
- // if rewritten is already alias, use it directly,
because in materialized view rewriting
- // Should keep out slot immutably after rewritten
successfully
- aliasMap.put(expr, rewritten instanceof Alias ?
(Alias) rewritten : new Alias(rewritten));
- }
+ Expression rewritten =
expr.accept(ExpressionReplacer.INSTANCE, aliasMap);
+ // if rewritten is already alias, use it directly, because
in materialized view rewriting
+ // Should keep out slot immutably after rewritten
successfully
+ aliasMap.put(expr, rewritten instanceof Alias ? (Alias)
rewritten : new Alias(rewritten));
});
for (Alias alias : aliasMap.values()) {
if (previousAlias.contains(alias)) {
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/postprocess/CommonSubExpressionTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/postprocess/CommonSubExpressionTest.java
index c9a20f65e69..1d5ac1dc24a 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/postprocess/CommonSubExpressionTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/postprocess/CommonSubExpressionTest.java
@@ -44,6 +44,7 @@ import org.junit.jupiter.api.Test;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -150,4 +151,53 @@ public class CommonSubExpressionTest extends
ExpressionRewriteTestHelper {
}
}
+ @Test
+ public void testCaseWhenSubExprExtracted() {
+ // a+b appears 4 times inside CASE WHEN, should be collected as common
sub-expression
+ // WhenClause itself should NOT be collected
+ List<NamedExpression> exprs = parseProjections(
+ "CASE WHEN (a+b > 10) THEN (a+b) WHEN (a+b > 5) THEN (a+b)
ELSE 0 END");
+ CommonSubExpressionCollector collector = new
CommonSubExpressionCollector();
+ exprs.forEach(expr -> collector.collect(expr));
+
+ // a+b is at depth 1 and appears multiple times → should be in
commonExprByDepth
+ Assertions.assertFalse(collector.commonExprByDepth.isEmpty(),
+ "a+b should be collected as common sub-expression");
+ Set<Expression> depth1 = collector.commonExprByDepth.get(1);
+ Assertions.assertNotNull(depth1, "common expressions at depth 1 should
exist");
+ Assertions.assertEquals(1, depth1.size());
+ assertExpression(depth1.iterator().next(), "a+b");
+
+ // no WhenClause should appear in commonExprByDepth at any depth
+ for (Set<Expression> exprsAtDepth :
collector.commonExprByDepth.values()) {
+ for (Expression e : exprsAtDepth) {
+ Assertions.assertFalse(e instanceof
org.apache.doris.nereids.trees.expressions.WhenClause,
+ "WhenClause should not be collected as common
sub-expression");
+ }
+ }
+ }
+
+ @Test
+ public void testEmptyLayerWhenOnlyWhenClauseIsCommon() throws Exception {
+ // two identical WhenClause(false, null), no slot references
+ List<NamedExpression> exprs = parseProjections(
+ "CASE WHEN false THEN null WHEN false THEN null ELSE 1 END");
+ Set<Slot> inputSlots = new HashSet<>();
+
+ CommonSubExpressionOpt opt = new CommonSubExpressionOpt();
+ Method method = CommonSubExpressionOpt.class
+ .getDeclaredMethod("computeMultiLayerProjections", Set.class,
List.class);
+ method.setAccessible(true);
+
+ List<List<NamedExpression>> multiLayers =
+ (List<List<NamedExpression>>) method.invoke(opt, inputSlots,
exprs);
+
+ // Bug: multiLayers = [[], [CASE...]] — l0 is empty
+ // Expected: either multiLayers is empty (no useful CSE),
+ // or every layer is non-empty
+ for (List<NamedExpression> layer : multiLayers) {
+ Assertions.assertFalse(layer.isEmpty(),
+ "intermediate layer should not be empty");
+ }
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]