This is an automated email from the ASF dual-hosted git repository.

yiguolei pushed a commit to branch branch-4.2
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-4.2 by this push:
     new 675875de550 branch-4.2 [fix](fd) Fold function projections over 
uniform constants for predicate pruning (#68205)
675875de550 is described below

commit 675875de550b098d55feb2c2baa7f5e3aaba0d50
Author: minghong <[email protected]>
AuthorDate: Sun Sep 20 11:21:47 2026 +0800

    branch-4.2 [fix](fd) Fold function projections over uniform constants for 
predicate pruning (#68205)
    
    Cherry-pick of #66906 to branch-4.2
    
    [fix](fd) Fold function projections over uniform constants for predicate
    pruning
    
    ---------
    
    Signed-off-by: AurĂ©lien Pupier <[email protected]>
    Co-authored-by: AurĂ©lien Pupier <[email protected]>
---
 .../trees/plans/logical/LogicalLoadProject.java    |   7 ++
 .../trees/plans/logical/LogicalProject.java        |   7 ++
 .../trees/plans/physical/PhysicalProject.java      |   7 ++
 .../apache/doris/nereids/util/ExpressionUtils.java |  25 +++++
 .../rewrite/ConstantProjectionFoldingTest.java     | 101 +++++++++++++++++++++
 5 files changed, 147 insertions(+)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalLoadProject.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalLoadProject.java
index 99d281f40c7..18399fcd5f4 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalLoadProject.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalLoadProject.java
@@ -251,6 +251,13 @@ public class LogicalLoadProject<CHILD_TYPE extends Plan> 
extends LogicalUnary<CH
                 } else if (childTrait.isUniform(slot)) {
                     builder.addUniformSlot(proj.toSlot());
                 }
+            } else {
+                // e.g. project `days_sub(begin_time, 1)` over a uniform 
constant slot `begin_time`:
+                // substitute the constant values so the projected slot also 
becomes a uniform
+                // constant, then downstream constant propagation can fold 
predicates over it.
+                Optional<Expression> constantExpr = 
ExpressionUtils.foldToConstantByUniformValues(
+                        proj.child(0), 
child(0).getLogicalProperties().getTrait());
+                constantExpr.ifPresent(expr -> 
builder.addUniformSlotAndLiteral(proj.toSlot(), expr));
             }
         }
     }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalProject.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalProject.java
index 3d818754b04..cd773d3ef03 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalProject.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalProject.java
@@ -282,6 +282,13 @@ public class LogicalProject<CHILD_TYPE extends Plan> 
extends LogicalUnary<CHILD_
                 } else if (childTrait.isUniform(slot)) {
                     builder.addUniformSlot(proj.toSlot());
                 }
+            } else {
+                // e.g. project `days_sub(begin_time, 1)` over a uniform 
constant slot `begin_time`:
+                // substitute the constant values so the projected slot also 
becomes a uniform
+                // constant, then downstream constant propagation can fold 
predicates over it.
+                Optional<Expression> constantExpr = 
ExpressionUtils.foldToConstantByUniformValues(
+                        proj.child(0), 
child(0).getLogicalProperties().getTrait());
+                constantExpr.ifPresent(expr -> 
builder.addUniformSlotAndLiteral(proj.toSlot(), expr));
             }
         }
     }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalProject.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalProject.java
index eb65a9c9f60..3cfcfabab45 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalProject.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalProject.java
@@ -323,6 +323,13 @@ public class PhysicalProject<CHILD_TYPE extends Plan> 
extends PhysicalUnary<CHIL
                 } else if (childTrait.isUniform(slot)) {
                     builder.addUniformSlot(proj.toSlot());
                 }
+            } else {
+                // e.g. project `days_sub(begin_time, 1)` over a uniform 
constant slot `begin_time`:
+                // substitute the constant values so the projected slot also 
becomes a uniform
+                // constant, then downstream constant propagation can fold 
predicates over it.
+                Optional<Expression> constantExpr = 
ExpressionUtils.foldToConstantByUniformValues(
+                        proj.child(0), 
child(0).getLogicalProperties().getTrait());
+                constantExpr.ifPresent(expr -> 
builder.addUniformSlotAndLiteral(proj.toSlot(), expr));
             }
         }
     }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java
index 836db942c18..07cc3f9292e 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java
@@ -26,6 +26,7 @@ import org.apache.doris.nereids.CascadesContext;
 import org.apache.doris.nereids.analyzer.Scope;
 import org.apache.doris.nereids.analyzer.UnboundSlot;
 import org.apache.doris.nereids.exceptions.AnalysisException;
+import org.apache.doris.nereids.properties.DataTrait;
 import org.apache.doris.nereids.properties.PhysicalProperties;
 import org.apache.doris.nereids.rules.analysis.ExpressionAnalyzer;
 import org.apache.doris.nereids.rules.expression.ExpressionRewriteContext;
@@ -1433,6 +1434,30 @@ public class ExpressionUtils {
         return true;
     }
 
+    /**
+     * Try to substitute the uniform constant values of {@code childTrait} 
into {@code expr}. If all
+     * input slots of {@code expr} have a known uniform constant value in 
{@code childTrait} and the
+     * substituted expression is a constant, return it. e.g. for a project 
expression
+     * `days_sub(begin_time, 1)` over a child where `begin_time` is a uniform 
constant slot, returns
+     * `days_sub('2026-07-28 00:00:00', 1)`, so the projected slot can also be 
registered as a
+     * uniform constant and downstream constant propagation can fold 
predicates over it.
+     */
+    public static Optional<Expression> 
foldToConstantByUniformValues(Expression expr, DataTrait childTrait) {
+        Set<Slot> inputSlots = expr.getInputSlots();
+        if (inputSlots.isEmpty()) {
+            return Optional.empty();
+        }
+        Map<Expression, Expression> replaceMap = new HashMap<>();
+        for (Slot slot : inputSlots) {
+            if (!childTrait.isUniformAndHasConstValue(slot)) {
+                return Optional.empty();
+            }
+            replaceMap.put(slot, childTrait.getUniformValue(slot).get());
+        }
+        Expression constantExpr = replace(expr, replaceMap);
+        return constantExpr.isConstant() ? Optional.of(constantExpr) : 
Optional.empty();
+    }
+
     /** check constant value the expression */
     public static Optional<Literal> checkConstantExpr(Expression expr, 
Optional<ExpressionRewriteContext> context) {
         if (expr instanceof Literal) {
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/ConstantProjectionFoldingTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/ConstantProjectionFoldingTest.java
new file mode 100644
index 00000000000..758f76c951f
--- /dev/null
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/ConstantProjectionFoldingTest.java
@@ -0,0 +1,101 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.rules.rewrite;
+
+import org.apache.doris.catalog.OlapTable;
+import org.apache.doris.common.FeConstants;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan;
+import org.apache.doris.nereids.util.MemoPatternMatchSupported;
+import org.apache.doris.nereids.util.PlanChecker;
+import org.apache.doris.utframe.TestWithFeService;
+
+import com.google.common.collect.Sets;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Test that a project expression whose input slots are all uniform constants 
(e.g.
+ * `date_sub(dt, INTERVAL 1 DAY)` where `dt` is a uniform constant slot) is 
folded and the
+ * projected slot is registered as a uniform constant, so downstream constant 
propagation can
+ * fold predicates over the projected slot and partition pruning on the 
referenced tables works.
+ *
+ * <p>The uniform constant source here is a filter predicate (`WHERE dt = 
'2026-07-28'`), which
+ * is independent of constant CTEs.
+ */
+class ConstantProjectionFoldingTest extends TestWithFeService implements 
MemoPatternMatchSupported {
+
+    // the subquery projects `date_sub(dt, INTERVAL 1 DAY)`; `dt` is a uniform 
constant there
+    // (from `WHERE dt = '2026-07-28'`), so the projected slot must fold to a 
uniform constant
+    // and the outer join predicate `t1.dt = prev_dt` folds to `t1.dt = 
'2026-07-27'`, letting
+    // the outer scan prune to a single partition.
+    private static final String SQL = "SELECT * FROM cte_prune_t t1\n"
+            + "JOIN (\n"
+            + "    SELECT dt, date_sub(dt, INTERVAL 1 DAY) AS prev_dt\n"
+            + "    FROM cte_prune_t\n"
+            + "    WHERE dt = '2026-07-28'\n"
+            + ") p ON t1.dt = p.prev_dt";
+
+    @Override
+    protected void runBeforeAll() throws Exception {
+        createDatabase("test");
+        useDatabase("test");
+        createTable("CREATE TABLE `test`.`cte_prune_t` (\n"
+                + "  `dt` DATE NULL COMMENT \"\",\n"
+                + "  `sn` VARCHAR(50) NULL COMMENT \"\",\n"
+                + "  `v` DOUBLE NULL COMMENT \"\"\n"
+                + ") DUPLICATE KEY(`dt`, `sn`)\n"
+                + "PARTITION BY RANGE(`dt`)\n"
+                + "(PARTITION p20260101 VALUES [(\"2026-01-01\"), 
(\"2026-01-02\")),\n"
+                + " PARTITION p20260726 VALUES [(\"2026-07-26\"), 
(\"2026-07-27\")),\n"
+                + " PARTITION p20260727 VALUES [(\"2026-07-27\"), 
(\"2026-07-28\")),\n"
+                + " PARTITION p20260728 VALUES [(\"2026-07-28\"), 
(\"2026-07-29\")),\n"
+                + " PARTITION p20260729 VALUES [(\"2026-07-29\"), 
(\"2026-07-30\")),\n"
+                + " PARTITION p20260901 VALUES [(\"2026-09-01\"), 
(\"2026-09-02\")))\n"
+                + "DISTRIBUTED BY HASH(`sn`) BUCKETS 3\n"
+                + "PROPERTIES('replication_num' = '1');");
+        FeConstants.runningUnitTest = true;
+    }
+
+    @Test
+    void testUniformConstantFoldThroughFunctionProjection() {
+        PlanChecker planChecker = PlanChecker.from(connectContext)
+                .analyze(SQL)
+                .rewrite();
+        Plan plan = planChecker.getCascadesContext().getRewritePlan();
+        String planString = plan.treeString();
+
+        List<LogicalOlapScan> scans = 
plan.collectToList(LogicalOlapScan.class::isInstance);
+        Assertions.assertEquals(2, scans.size(),
+                "both the outer table and the subquery should scan 
cte_prune_t, plan: " + planString);
+        Set<String> selectedPartitions = Sets.newHashSet();
+        for (LogicalOlapScan scan : scans) {
+            // the outer scan (t1) should prune to p20260727, the subquery 
scan to p20260728
+            Assertions.assertEquals(1, scan.getSelectedPartitionIds().size(),
+                    "scan on cte_prune_t should prune to exactly one 
partition, plan: " + planString);
+            selectedPartitions.add(((OlapTable) scan.getTable())
+                    
.getPartition(scan.getSelectedPartitionIds().get(0)).getName());
+        }
+        Assertions.assertEquals(Sets.newHashSet("p20260727", "p20260728"), 
selectedPartitions,
+                "outer scan should prune to p20260727 and subquery scan to 
p20260728, plan: "
+                        + planString);
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to