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]

Reply via email to