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

huajianlan pushed a commit to branch nested_column_prune
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/nested_column_prune by this 
push:
     new 70cb7f63446 push down project through join/union
70cb7f63446 is described below

commit 70cb7f63446715fba86989229e9b67517e8632bc
Author: 924060929 <[email protected]>
AuthorDate: Wed Oct 22 20:55:11 2025 +0800

    push down project through join/union
---
 .idea/vcs.xml                                      |  52 ++--
 .../org/apache/doris/nereids/rules/RuleSet.java    |   2 +
 .../org/apache/doris/nereids/rules/RuleType.java   |   3 +
 .../rewrite/AccessPathExpressionCollector.java     |   7 +
 .../rules/rewrite/PushDownNestedColumn.java        | 299 +++++++++++++++++++++
 .../trees/expressions/PreferPushDownProject.java   |  41 +++
 .../expressions/functions/scalar/ArrayMap.java     |   3 +-
 .../expressions/functions/scalar/ElementAt.java    |   3 +-
 .../functions/scalar/HighOrderFunction.java        |   3 +-
 .../functions/scalar/MapContainsEntry.java         |   3 +-
 .../functions/scalar/MapContainsKey.java           |   3 +-
 .../functions/scalar/MapContainsValue.java         |   3 +-
 .../expressions/functions/scalar/MapEntries.java   |   3 +-
 .../expressions/functions/scalar/MapKeys.java      |   3 +-
 .../expressions/functions/scalar/MapValues.java    |   3 +-
 .../functions/scalar/StructElement.java            |   3 +-
 .../rules/rewrite/PruneNestedColumnTest.java       |  59 +++-
 17 files changed, 450 insertions(+), 43 deletions(-)

diff --git a/.idea/vcs.xml b/.idea/vcs.xml
index 0a185d9bdc7..8b083db2f94 100644
--- a/.idea/vcs.xml
+++ b/.idea/vcs.xml
@@ -1,34 +1,22 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!--
-   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.
-  -->
 <project version="4">
-    <component name="IssueNavigationConfiguration">
-        <option name="links">
-            <list>
-                <IssueNavigationLink>
-                    <option name="issueRegexp" value="#(\d+)" />
-                    <option name="linkRegexp" 
value="https://github.com/apache/doris/pull/$1"; />
-                </IssueNavigationLink>
-            </list>
-        </option>
-    </component>
-    <component name="VcsDirectoryMappings">
-        <mapping directory="$PROJECT_DIR$" vcs="Git" />
-        <mapping directory="$PROJECT_DIR$/be/src/apache-orc" vcs="Git" />
-        <mapping directory="$PROJECT_DIR$/be/src/clucene" vcs="Git" />
-    </component>
-</project>
+  <component name="IssueNavigationConfiguration">
+    <option name="links">
+      <list>
+        <IssueNavigationLink>
+          <option name="issueRegexp" value="#(\d+)" />
+          <option name="linkRegexp" 
value="https://github.com/apache/doris/pull/$1"; />
+        </IssueNavigationLink>
+      </list>
+    </option>
+  </component>
+  <component name="VcsDirectoryMappings">
+    <mapping directory="$PROJECT_DIR$" vcs="Git" />
+    <mapping directory="$PROJECT_DIR$/be/src/apache-orc" vcs="Git" />
+    <mapping directory="$PROJECT_DIR$/be/src/clucene" vcs="Git" />
+    <mapping directory="$PROJECT_DIR$/contrib/apache-orc" vcs="Git" />
+    <mapping directory="$PROJECT_DIR$/contrib/clucene" vcs="Git" />
+    <mapping directory="$PROJECT_DIR$/contrib/faiss" vcs="Git" />
+    <mapping directory="$PROJECT_DIR$/contrib/openblas" vcs="Git" />
+  </component>
+</project>
\ No newline at end of file
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleSet.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleSet.java
index 6e47834d6c9..f78f0bec970 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleSet.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleSet.java
@@ -114,6 +114,7 @@ import 
org.apache.doris.nereids.rules.rewrite.PushDownFilterThroughSort;
 import org.apache.doris.nereids.rules.rewrite.PushDownFilterThroughWindow;
 import org.apache.doris.nereids.rules.rewrite.PushDownJoinOtherCondition;
 import org.apache.doris.nereids.rules.rewrite.PushDownLimitDistinctThroughJoin;
+import org.apache.doris.nereids.rules.rewrite.PushDownNestedColumn;
 import org.apache.doris.nereids.rules.rewrite.PushDownProjectThroughLimit;
 import org.apache.doris.nereids.rules.rewrite.PushDownTopNDistinctThroughJoin;
 import org.apache.doris.nereids.rules.rewrite.PushDownTopNThroughJoin;
@@ -165,6 +166,7 @@ public class RuleSet {
             new PushDownFilterThroughSetOperation(),
             new PushDownFilterThroughGenerate(),
             new PushDownProjectThroughLimit(),
+            new PushDownNestedColumn(),
             new EliminateOuterJoin(),
             new MergeProjectable(),
             new MergeFilters(),
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java
index 163c334290a..efdcc150b7b 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java
@@ -194,6 +194,9 @@ public enum RuleType {
     PUSH_DOWN_PREDICATE_THROUGH_AGGREGATION(RuleTypeClass.REWRITE),
     PUSH_DOWN_PREDICATE_THROUGH_REPEAT(RuleTypeClass.REWRITE),
     PUSH_DOWN_EXPRESSIONS_IN_HASH_CONDITIONS(RuleTypeClass.REWRITE),
+    // Pushdown nested column project
+    PUSH_DOWN_NESTED_COLUMN_THROUGH_JOIN(RuleTypeClass.REWRITE),
+    PUSH_DOWN_NESTED_COLUMN_THROUGH_UNION(RuleTypeClass.REWRITE),
     // Pushdown filter
     PUSH_DOWN_FILTER_THROUGH_JOIN(RuleTypeClass.REWRITE),
     PUSH_DOWN_FILTER_THROUGH_LEFT_SEMI_JOIN(RuleTypeClass.REWRITE),
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/AccessPathExpressionCollector.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/AccessPathExpressionCollector.java
index 7caa3e59c45..8bd794f5422 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/AccessPathExpressionCollector.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/AccessPathExpressionCollector.java
@@ -40,6 +40,7 @@ import 
org.apache.doris.nereids.trees.expressions.functions.scalar.ArraySortBy;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.ArraySplit;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.ElementAt;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.Lambda;
+import 
org.apache.doris.nereids.trees.expressions.functions.scalar.MapContainsEntry;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.MapContainsKey;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.MapContainsValue;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.MapKeys;
@@ -198,6 +199,12 @@ public class AccessPathExpressionCollector extends 
DefaultExpressionVisitor<Void
         return continueCollectAccessPath(mapContainsValue.getArgument(0), 
context);
     }
 
+    @Override
+    public Void visitMapContainsEntry(MapContainsEntry mapContainsEntry, 
CollectorContext context) {
+        context.accessPathBuilder.addPrefix("*");
+        return continueCollectAccessPath(mapContainsEntry.getArgument(0), 
context);
+    }
+
     @Override
     public Void visitArrayMap(ArrayMap arrayMap, CollectorContext context) {
         // ARRAY_MAP(lambda, <arr> [ , <arr> ... ] )
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PushDownNestedColumn.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PushDownNestedColumn.java
new file mode 100644
index 00000000000..a6e790820a5
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PushDownNestedColumn.java
@@ -0,0 +1,299 @@
+// 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.common.Pair;
+import org.apache.doris.nereids.StatementContext;
+import org.apache.doris.nereids.pattern.MatchingContext;
+import org.apache.doris.nereids.rules.Rule;
+import org.apache.doris.nereids.rules.RuleType;
+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.PreferPushDownProject;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.algebra.SetOperation.Qualifier;
+import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
+import org.apache.doris.nereids.trees.plans.logical.LogicalOneRowRelation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+import org.apache.doris.nereids.trees.plans.logical.LogicalUnion;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Multimap;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Function;
+
+/** PushDownNestedColumnThroughJoin */
+public class PushDownNestedColumn implements RewriteRuleFactory {
+    @Override
+    public List<Rule> buildRules() {
+        return ImmutableList.of(
+            RuleType.PUSH_DOWN_NESTED_COLUMN_THROUGH_JOIN.build(
+                logicalProject(logicalJoin()).thenApply(this::pushThroughJoin)
+            ),
+            RuleType.PUSH_DOWN_NESTED_COLUMN_THROUGH_UNION.build(
+                logicalProject(
+                        logicalUnion().when(u -> u.getQualifier() == 
Qualifier.ALL)
+                ).thenApply(this::pushThroughUnion)
+            )
+        );
+    }
+
+    private Plan 
pushThroughJoin(MatchingContext<LogicalProject<LogicalJoin<Plan, Plan>>> ctx) {
+        LogicalProject<LogicalJoin<Plan, Plan>> project = ctx.root;
+        LogicalJoin<Plan, Plan> join = project.child();
+        PushdownProjectHelper pushdownProjectHelper
+                = new PushdownProjectHelper(ctx.statementContext, join);
+
+        Pair<Boolean, List<NamedExpression>> pushProjects
+                = 
pushdownProjectHelper.pushDownExpressions(project.getProjects());
+
+        if (pushProjects.first) {
+            List<Plan> newJoinChildren = 
pushdownProjectHelper.buildNewChildren();
+            return new LogicalProject<>(
+                    pushProjects.second,
+                    join.withChildren(newJoinChildren)
+            );
+        }
+        return project;
+    }
+
+    private Plan 
pushThroughUnion(MatchingContext<LogicalProject<LogicalUnion>> ctx) {
+        LogicalProject<LogicalUnion> project = ctx.root;
+        LogicalUnion union = project.child();
+        PushdownProjectHelper pushdownProjectHelper
+                = new PushdownProjectHelper(ctx.statementContext, project);
+
+        Pair<Boolean, List<NamedExpression>> pushProjects
+                = 
pushdownProjectHelper.pushDownExpressions(project.getProjects());
+        if (pushProjects.first) {
+            List<NamedExpression> unionOutputs = union.getOutputs();
+            Map<Slot, Integer> slotToColumnIndex = new LinkedHashMap<>();
+            for (int i = 0; i < unionOutputs.size(); i++) {
+                NamedExpression output = unionOutputs.get(i);
+                slotToColumnIndex.put(output.toSlot(), i);
+            }
+
+            Collection<NamedExpression> pushDownProjections
+                    = pushdownProjectHelper.childToPushDownProjects.values();
+            List<Plan> newChildren = new ArrayList<>();
+            List<List<SlotReference>> newChildrenOutputs = new ArrayList<>();
+            for (Plan child : union.children()) {
+                List<NamedExpression> pushedOutput = replaceSlot(
+                        ctx.statementContext,
+                        pushDownProjections,
+                        slot -> {
+                            Integer sourceColumnIndex = 
slotToColumnIndex.get(slot);
+                            if (sourceColumnIndex != null) {
+                                return 
child.getOutput().get(sourceColumnIndex).toSlot();
+                            }
+                            return slot;
+                        }
+                );
+
+                LogicalProject<Plan> newChild = new LogicalProject<>(
+                        ImmutableList.<NamedExpression>builder()
+                                .addAll(child.getOutput())
+                                .addAll(pushedOutput)
+                                .build(),
+                        child
+                );
+
+                newChildrenOutputs.add((List) newChild.getOutput());
+                newChildren.add(newChild);
+            }
+
+            for (List<NamedExpression> originConstantExprs : 
union.getConstantExprsList()) {
+                List<NamedExpression> pushedOutput = replaceSlot(
+                        ctx.statementContext,
+                        pushDownProjections,
+                        slot -> {
+                            Integer sourceColumnIndex = 
slotToColumnIndex.get(slot);
+                            if (sourceColumnIndex != null) {
+                                return 
originConstantExprs.get(sourceColumnIndex).toSlot();
+                            }
+                            return slot;
+                        }
+                );
+
+                LogicalOneRowRelation originOneRowRelation = new 
LogicalOneRowRelation(
+                        ctx.statementContext.getNextRelationId(),
+                        originConstantExprs
+                );
+
+                LogicalProject<Plan> newChild = new LogicalProject<>(
+                        ImmutableList.<NamedExpression>builder()
+                                .addAll(originOneRowRelation.getOutput())
+                                .addAll(pushedOutput)
+                                .build(),
+                        originOneRowRelation
+                );
+
+                newChildrenOutputs.add((List) newChild.getOutput());
+                newChildren.add(newChild);
+            }
+
+            List<NamedExpression> newUnionOutputs = new 
ArrayList<>(union.getOutputs());
+            for (NamedExpression projection : pushDownProjections) {
+                newUnionOutputs.add(projection.toSlot());
+            }
+
+            return new LogicalProject<>(
+                    pushProjects.second,
+                    new LogicalUnion(
+                            union.getQualifier(),
+                            newUnionOutputs,
+                            newChildrenOutputs,
+                            ImmutableList.of(),
+                            union.hasPushedFilter(),
+                            newChildren
+                    )
+            );
+        }
+        return project;
+    }
+
+    private List<NamedExpression> replaceSlot(
+            StatementContext statementContext,
+            Collection<NamedExpression> pushDownProjections,
+            Function<Slot, Slot> slotReplace) {
+        List<NamedExpression> pushedOutput = new ArrayList<>();
+        for (NamedExpression projection : pushDownProjections) {
+            NamedExpression newOutput = (NamedExpression) 
projection.rewriteUp(e -> {
+                if (e instanceof Slot) {
+                    Slot newSlot = slotReplace.apply((Slot) e);
+                    if (newSlot != null) {
+                        return newSlot;
+                    }
+                }
+                return e;
+            });
+            if (newOutput instanceof Alias) {
+                pushedOutput.add(new Alias(statementContext.getNextExprId(), 
newOutput.child(0)));
+            } else {
+                pushedOutput.add(new Alias(statementContext.getNextExprId(), 
newOutput));
+            }
+        }
+        return pushedOutput;
+    }
+
+    private static class PushdownProjectHelper {
+        private final Plan plan;
+        private final StatementContext statementContext;
+        private final Map<Expression, Pair<Slot, Plan>> exprToChildAndSlot;
+        private final Multimap<Plan, NamedExpression> childToPushDownProjects;
+
+        public PushdownProjectHelper(StatementContext statementContext, Plan 
plan) {
+            this.statementContext = statementContext;
+            this.plan = plan;
+            this.exprToChildAndSlot = new LinkedHashMap<>();
+            this.childToPushDownProjects = ArrayListMultimap.create();
+        }
+
+        public <C extends Collection<E>, E extends Expression> Pair<Boolean, 
C> pushDownExpressions(C expressions) {
+            ImmutableCollection.Builder<E> builder;
+            if (expressions instanceof List) {
+                builder = 
ImmutableList.builderWithExpectedSize(expressions.size());
+            } else {
+                builder = 
ImmutableSet.builderWithExpectedSize(expressions.size());
+            }
+
+            boolean extracted = false;
+            for (E expression : expressions) {
+                Optional<E> result = pushDownExpression(expression);
+                if (!result.isPresent()) {
+                    builder.add(expression);
+                } else {
+                    extracted = true;
+                    builder.add(result.get());
+                }
+            }
+
+            if (extracted) {
+                return Pair.of(true, (C) builder.build());
+            } else {
+                return Pair.of(false, expressions);
+            }
+        }
+
+        public <E extends Expression> Optional<E> pushDownExpression(E 
expression) {
+            if (!(expression instanceof PreferPushDownProject
+                    || (expression instanceof Alias && expression.child(0) 
instanceof PreferPushDownProject))) {
+                return Optional.empty();
+            }
+            Pair<Slot, Plan> existPushdown = 
exprToChildAndSlot.get(expression);
+            if (existPushdown != null) {
+                return Optional.of((E) existPushdown.first);
+            }
+
+            Alias pushDownAlias = null;
+            if (expression instanceof Alias) {
+                pushDownAlias = (Alias) expression;
+            } else {
+                pushDownAlias = new Alias(statementContext.getNextExprId(), 
expression);
+            }
+
+            Set<Slot> inputSlots = expression.getInputSlots();
+            for (Plan child : plan.children()) {
+                if (child.getOutputSet().containsAll(inputSlots)) {
+                    Slot remaimSlot = pushDownAlias.toSlot();
+                    exprToChildAndSlot.put(expression, Pair.of(remaimSlot, 
child));
+                    childToPushDownProjects.put(child, pushDownAlias);
+                    return Optional.of((E) remaimSlot);
+                }
+            }
+            return Optional.empty();
+        }
+
+        public List<Plan> buildNewChildren() {
+            if (childToPushDownProjects.isEmpty()) {
+                return plan.children();
+            }
+            ImmutableList.Builder<Plan> newChildren = 
ImmutableList.builderWithExpectedSize(plan.arity());
+            for (Plan child : plan.children()) {
+                Collection<NamedExpression> newProject = 
childToPushDownProjects.get(child);
+                if (newProject.isEmpty()) {
+                    newChildren.add(child);
+                } else {
+                    newChildren.add(
+                            new LogicalProject<>(
+                                    ImmutableList.<NamedExpression>builder()
+                                            .addAll(child.getOutput())
+                                            .addAll(newProject)
+                                            .build(),
+                                    child
+                            )
+                    );
+                }
+            }
+            return newChildren.build();
+        }
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/PreferPushDownProject.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/PreferPushDownProject.java
new file mode 100644
index 00000000000..4c9c3bebe5a
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/PreferPushDownProject.java
@@ -0,0 +1,41 @@
+// 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.trees.expressions;
+
+import org.apache.doris.nereids.trees.expressions.functions.ExpressionTrait;
+
+/**
+ * some expressions prefer push down project under the plan,
+ *
+ * e.g. the element_at function prefer push down to a project in under the 
plan:
+ *    Project(element_at(left.column, 'id') as a)
+ *               |
+ *              Join
+ *           /       |
+ *         left      right
+ *
+ * we will push down project through the Join, so we can prune the column 
which has complex type:
+ *
+ *                                Join
+ *      /                                                     \
+ *  Project(element_at(left.column, 'id') as a, ...)         right
+ *       |
+ *    left
+ */
+public interface PreferPushDownProject extends ExpressionTrait {
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayMap.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayMap.java
index 5d74f5e1457..1a1a246cea6 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayMap.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayMap.java
@@ -20,6 +20,7 @@ package 
org.apache.doris.nereids.trees.expressions.functions.scalar;
 import org.apache.doris.catalog.FunctionSignature;
 import org.apache.doris.nereids.trees.expressions.ArrayItemReference;
 import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.PreferPushDownProject;
 import 
org.apache.doris.nereids.trees.expressions.functions.ImplicitlyCastableSignature;
 import org.apache.doris.nereids.trees.expressions.functions.PropagateNullable;
 import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
@@ -37,7 +38,7 @@ import java.util.List;
  * ScalarFunction 'array_map'.
  */
 public class ArrayMap extends ScalarFunction
-        implements ImplicitlyCastableSignature, PropagateNullable {
+        implements ImplicitlyCastableSignature, PropagateNullable, 
PreferPushDownProject {
 
     public static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
             FunctionSignature.ret(new 
FollowToAnyDataType(0)).args(LambdaType.INSTANCE)
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ElementAt.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ElementAt.java
index 99f63bab15c..10d7cfe1d94 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ElementAt.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ElementAt.java
@@ -19,6 +19,7 @@ package 
org.apache.doris.nereids.trees.expressions.functions.scalar;
 
 import org.apache.doris.catalog.FunctionSignature;
 import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.PreferPushDownProject;
 import org.apache.doris.nereids.trees.expressions.functions.AlwaysNullable;
 import 
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
 import org.apache.doris.nereids.trees.expressions.shape.BinaryExpression;
@@ -40,7 +41,7 @@ import java.util.List;
  * ScalarFunction 'element_at'. This class is generated by GenerateFunction.
  */
 public class ElementAt extends ScalarFunction
-        implements BinaryExpression, ExplicitlyCastableSignature, 
AlwaysNullable {
+        implements BinaryExpression, ExplicitlyCastableSignature, 
AlwaysNullable, PreferPushDownProject {
 
     public static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
             FunctionSignature.ret(new FollowToAnyDataType(0))
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/HighOrderFunction.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/HighOrderFunction.java
index a929f2dd5d5..e7a054fc864 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/HighOrderFunction.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/HighOrderFunction.java
@@ -18,6 +18,7 @@
 package org.apache.doris.nereids.trees.expressions.functions.scalar;
 
 import org.apache.doris.catalog.FunctionSignature;
+import org.apache.doris.nereids.trees.expressions.PreferPushDownProject;
 import 
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
 import org.apache.doris.nereids.types.LambdaType;
 import org.apache.doris.nereids.types.coercion.AnyDataType;
@@ -29,7 +30,7 @@ import java.util.List;
 /**
  * Interface of HighOrderFunction, provide default FunctionSignature of 
LambdaType argument
  */
-public interface HighOrderFunction extends ExplicitlyCastableSignature {
+public interface HighOrderFunction extends ExplicitlyCastableSignature, 
PreferPushDownProject {
     @Override
     default List<FunctionSignature> getSignatures() {
         return new ImmutableList.Builder<FunctionSignature>()
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MapContainsEntry.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MapContainsEntry.java
index b8ecc8667c7..517bfaa7748 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MapContainsEntry.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MapContainsEntry.java
@@ -19,6 +19,7 @@ package 
org.apache.doris.nereids.trees.expressions.functions.scalar;
 
 import org.apache.doris.catalog.FunctionSignature;
 import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.PreferPushDownProject;
 import 
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
 import org.apache.doris.nereids.trees.expressions.shape.TernaryExpression;
 import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
@@ -36,7 +37,7 @@ import java.util.List;
  * ScalarFunction 'map_contains_entry'.
  */
 public class MapContainsEntry extends ScalarFunction
-        implements TernaryExpression, ExplicitlyCastableSignature {
+        implements TernaryExpression, ExplicitlyCastableSignature, 
PreferPushDownProject {
 
     /**
      * Basic signature, forcing search key/value type to convert to the same 
type as map's key/value type.
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MapContainsKey.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MapContainsKey.java
index 4fc4c32f233..845e84f71b3 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MapContainsKey.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MapContainsKey.java
@@ -19,6 +19,7 @@ package 
org.apache.doris.nereids.trees.expressions.functions.scalar;
 
 import org.apache.doris.catalog.FunctionSignature;
 import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.PreferPushDownProject;
 import 
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
 import org.apache.doris.nereids.trees.expressions.shape.BinaryExpression;
 import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
@@ -36,7 +37,7 @@ import java.util.List;
  * ScalarFunction 'map_contains_key'. This class is generated by 
GenerateFunction.
  */
 public class MapContainsKey extends ScalarFunction
-        implements BinaryExpression, ExplicitlyCastableSignature {
+        implements BinaryExpression, ExplicitlyCastableSignature, 
PreferPushDownProject {
 
     public static final List<FunctionSignature> FOLLOW_DATATYPE_SIGNATURE = 
ImmutableList.of(
             FunctionSignature.ret(BooleanType.INSTANCE)
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MapContainsValue.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MapContainsValue.java
index a60f6653cde..fd42c42fb65 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MapContainsValue.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MapContainsValue.java
@@ -19,6 +19,7 @@ package 
org.apache.doris.nereids.trees.expressions.functions.scalar;
 
 import org.apache.doris.catalog.FunctionSignature;
 import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.PreferPushDownProject;
 import 
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
 import org.apache.doris.nereids.trees.expressions.shape.BinaryExpression;
 import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
@@ -36,7 +37,7 @@ import java.util.List;
  * ScalarFunction 'map_contains_value'. This class is generated by 
GenerateFunction.
  */
 public class MapContainsValue extends ScalarFunction
-        implements BinaryExpression, ExplicitlyCastableSignature {
+        implements BinaryExpression, ExplicitlyCastableSignature, 
PreferPushDownProject {
 
     public static final List<FunctionSignature> FOLLOW_DATATYPE_SIGNATURE = 
ImmutableList.of(
             FunctionSignature.ret(BooleanType.INSTANCE)
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MapEntries.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MapEntries.java
index 4be48638e5e..22a14c31df3 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MapEntries.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MapEntries.java
@@ -19,6 +19,7 @@ package 
org.apache.doris.nereids.trees.expressions.functions.scalar;
 
 import org.apache.doris.catalog.FunctionSignature;
 import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.PreferPushDownProject;
 import org.apache.doris.nereids.trees.expressions.functions.CustomSignature;
 import org.apache.doris.nereids.trees.expressions.functions.PropagateNullable;
 import org.apache.doris.nereids.trees.expressions.functions.SearchSignature;
@@ -41,7 +42,7 @@ import java.util.List;
  * fields 'key' and 'value'.
  */
 public class MapEntries extends ScalarFunction
-        implements UnaryExpression, CustomSignature, PropagateNullable {
+        implements UnaryExpression, CustomSignature, PropagateNullable, 
PreferPushDownProject {
 
     /**
      * constructor with 1 argument.
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MapKeys.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MapKeys.java
index 957dae2844a..a3123d29cb0 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MapKeys.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MapKeys.java
@@ -19,6 +19,7 @@ package 
org.apache.doris.nereids.trees.expressions.functions.scalar;
 
 import org.apache.doris.catalog.FunctionSignature;
 import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.PreferPushDownProject;
 import 
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
 import org.apache.doris.nereids.trees.expressions.functions.PropagateNullable;
 import org.apache.doris.nereids.trees.expressions.shape.UnaryExpression;
@@ -37,7 +38,7 @@ import java.util.List;
  * ScalarFunction 'map_keys'.
  */
 public class MapKeys extends ScalarFunction
-        implements UnaryExpression, ExplicitlyCastableSignature, 
PropagateNullable {
+        implements UnaryExpression, ExplicitlyCastableSignature, 
PropagateNullable, PreferPushDownProject {
 
     public static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
             FunctionSignature.ret(ArrayType.of(new 
FollowToAnyDataType(0))).args(MapType.of(
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MapValues.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MapValues.java
index e7ca7f48dd3..1622b277156 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MapValues.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MapValues.java
@@ -19,6 +19,7 @@ package 
org.apache.doris.nereids.trees.expressions.functions.scalar;
 
 import org.apache.doris.catalog.FunctionSignature;
 import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.PreferPushDownProject;
 import 
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
 import org.apache.doris.nereids.trees.expressions.functions.PropagateNullable;
 import org.apache.doris.nereids.trees.expressions.shape.UnaryExpression;
@@ -37,7 +38,7 @@ import java.util.List;
  * ScalarFunction 'map_values'.
  */
 public class MapValues extends ScalarFunction
-        implements UnaryExpression, ExplicitlyCastableSignature, 
PropagateNullable {
+        implements UnaryExpression, ExplicitlyCastableSignature, 
PropagateNullable, PreferPushDownProject {
 
     public static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
             FunctionSignature.ret(ArrayType.of(new 
FollowToAnyDataType(0))).args(MapType.of(
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StructElement.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StructElement.java
index 85bb424de19..63169513423 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StructElement.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StructElement.java
@@ -20,6 +20,7 @@ package 
org.apache.doris.nereids.trees.expressions.functions.scalar;
 import org.apache.doris.catalog.FunctionSignature;
 import org.apache.doris.nereids.exceptions.AnalysisException;
 import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.PreferPushDownProject;
 import org.apache.doris.nereids.trees.expressions.functions.AlwaysNullable;
 import 
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
 import 
org.apache.doris.nereids.trees.expressions.functions.PropagateNullLiteral;
@@ -40,7 +41,7 @@ import java.util.List;
  * ScalarFunction 'struct_element'.
  */
 public class StructElement extends ScalarFunction
-        implements ExplicitlyCastableSignature, AlwaysNullable, 
PropagateNullLiteral {
+        implements ExplicitlyCastableSignature, AlwaysNullable, 
PropagateNullLiteral, PreferPushDownProject {
 
     /**
      * constructor with 0 or more arguments.
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PruneNestedColumnTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PruneNestedColumnTest.java
index 468167b68d4..096839b2e57 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PruneNestedColumnTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PruneNestedColumnTest.java
@@ -28,10 +28,14 @@ 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.SlotReference;
+import 
org.apache.doris.nereids.trees.expressions.functions.scalar.StructElement;
+import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalCTEConsumer;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalPlan;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalUnion;
 import org.apache.doris.nereids.types.DataType;
+import org.apache.doris.nereids.util.MemoPatternMatchSupported;
+import org.apache.doris.nereids.util.PlanChecker;
 import org.apache.doris.planner.OlapScanNode;
 import org.apache.doris.planner.PlanFragment;
 import org.apache.doris.thrift.TAccessPathType;
@@ -52,7 +56,7 @@ import java.util.Map.Entry;
 import java.util.TreeSet;
 import java.util.function.Consumer;
 
-public class PruneNestedColumnTest extends TestWithFeService {
+public class PruneNestedColumnTest extends TestWithFeService implements 
MemoPatternMatchSupported {
     @BeforeAll
     public void createTable() throws Exception {
         createDatabase("test");
@@ -297,6 +301,59 @@ public class PruneNestedColumnTest extends 
TestWithFeService {
         );
     }
 
+    @Test
+    public void testPushDownThroughJoin() {
+        PlanChecker.from(connectContext)
+                .analyze("select struct_element(s, 'city') from (select * from 
tbl)a join (select 100 id, 'f1' name)b on a.id=b.id")
+                .rewrite()
+                .matches(
+                    logicalResultSink(
+                        logicalProject(
+                            logicalJoin(
+                                logicalProject(
+                                    logicalFilter(
+                                        logicalOlapScan()
+                                    )
+                                ).when(p -> {
+                                    Assertions.assertEquals(2, 
p.getProjects().size());
+                                    
Assertions.assertTrue(p.getProjects().stream()
+                                            .anyMatch(o -> o instanceof Alias 
&& o.child(0) instanceof StructElement));
+                                    return true;
+                                }),
+                                logicalOneRowRelation()
+                            )
+                        ).when(p -> {
+                            Assertions.assertTrue(p.getProjects().size() == 1 
&& p.getProjects().get(0) instanceof SlotReference);
+                            return true;
+                        })
+                    )
+                );
+    }
+
+    @Test
+    public void testPushDownThroughUnion() {
+        PlanChecker.from(connectContext)
+                .analyze("select struct_element(s, 'city') from (select id, s 
from tbl union all select 1, null) tmp")
+                .rewrite()
+                .matches(
+                    logicalResultSink(
+                        logicalUnion(
+                            logicalProject(
+                                logicalOlapScan()
+                            ).when(p -> {
+                                Assertions.assertEquals(1, 
p.getProjects().size());
+                                
Assertions.assertInstanceOf(StructElement.class, 
p.getProjects().get(0).child(0));
+                                return true;
+                            })
+                        ).when(u -> {
+                            Assertions.assertEquals(1, 
u.getConstantExprsList().size());
+                            Assertions.assertInstanceOf(NullLiteral.class, 
u.getConstantExprsList().get(0).get(0).child(0));
+                            return true;
+                        })
+                    )
+                );
+    }
+
     private void assertColumn(String sql, String expectType,
             List<TColumnNameAccessPath> expectAllAccessPaths,
             List<TColumnNameAccessPath> expectPredicateAccessPaths) throws 
Exception {


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


Reply via email to