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 1fd25254466 fix prune nested column when use union with cast
1fd25254466 is described below

commit 1fd2525446603878b0c86c31118ddf9e41b3efae
Author: 924060929 <[email protected]>
AuthorDate: Mon Oct 27 23:02:56 2025 +0800

    fix prune nested column when use union with cast
---
 .../nereids/rules/rewrite/NestedColumnPruning.java |  12 +-
 .../rules/rewrite/PruneNestedColumnTest.java       | 161 +++++++++++++--------
 .../apache/doris/utframe/TestWithFeService.java    |   3 +-
 3 files changed, 111 insertions(+), 65 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/NestedColumnPruning.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/NestedColumnPruning.java
index 00f79b08eef..0d56260629b 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/NestedColumnPruning.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/NestedColumnPruning.java
@@ -263,7 +263,7 @@ public class NestedColumnPruning implements CustomRewriter {
                     nameMapping.put(s, castNames.get(i++));
                 }
                 List<StructField> mappingFields = new ArrayList<>();
-                StructType originPrunedStructType = (StructType) type;
+                StructType originCastStructType = (StructType) cast.type;
                 for (Entry<String, DataTypeAccessTree> kv : 
children.entrySet()) {
                     String originName = kv.getKey();
                     String mappingName = nameMapping.getOrDefault(originName, 
originName);
@@ -272,9 +272,10 @@ public class NestedColumnPruning implements CustomRewriter 
{
                             origin.children.get(originName),
                             cast.children.get(mappingName)
                     );
-                    StructField field = 
originPrunedStructType.getField(originName);
+                    StructField originCastField = 
originCastStructType.getField(mappingName);
                     mappingFields.add(
-                            new StructField(mappingName, mappingType, 
field.isNullable(), field.getComment())
+                            new StructField(mappingName, mappingType,
+                                    originCastField.isNullable(), 
originCastField.getComment())
                     );
                 }
                 return new StructType(mappingFields);
@@ -283,7 +284,8 @@ public class NestedColumnPruning implements CustomRewriter {
                         children.values().iterator().next().pruneCastType(
                                 origin.children.values().iterator().next(),
                                 cast.children.values().iterator().next()
-                        )
+                        ),
+                        ((ArrayType) cast.type).containsNull()
                 );
             } else if (type instanceof MapType) {
                 return MapType.of(
@@ -291,7 +293,7 @@ public class NestedColumnPruning implements CustomRewriter {
                         
children.get("VALUES").pruneCastType(origin.children.get("VALUES"), 
cast.children.get("VALUES"))
                 );
             } else {
-                return type;
+                return cast.type;
             }
         }
 
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 39d652ab510..934df6bc3f6 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
@@ -20,6 +20,7 @@ package org.apache.doris.nereids.rules.rewrite;
 import org.apache.doris.analysis.SlotDescriptor;
 import org.apache.doris.catalog.Type;
 import org.apache.doris.common.Pair;
+import org.apache.doris.common.Triple;
 import org.apache.doris.nereids.NereidsPlanner;
 import org.apache.doris.nereids.rules.RuleType;
 import org.apache.doris.nereids.trees.expressions.Alias;
@@ -128,11 +129,41 @@ public class PruneNestedColumnTest extends 
TestWithFeService implements MemoPatt
                 ImmutableList.of()
         );
 
-        // assertColumn("select struct_element(s, 'city'), 
struct_element(map_values(struct_element(s, 'data')[0])[0], 'b') from (select * 
from tbl union all select * from tbl2)t",
-        //         "struct<city:text>",
-        //         ImmutableList.of(path("s", "city")),
-        //         ImmutableList.of()
-        // );
+        assertColumn("select struct_element(map_values(struct_element(cast(s 
as struct<k:text,l:array<map<int,struct<x:double,y:double>>>>), 'l')[0])[0], 
'x') from tbl",
+                "struct<data:array<map<int,struct<a:int>>>>",
+                ImmutableList.of(path("s", "data", "*", "VALUES", "a")),
+                ImmutableList.of()
+        );
+
+        assertColumns("select struct_element(s, 'city') from (select * from 
tbl union all select * from tbl2)t",
+                ImmutableList.of(
+                        Triple.of(
+                                "struct<city2:text>",
+                                ImmutableList.of(path("s2", "city2")),
+                                ImmutableList.of()
+                        ),
+                        Triple.of(
+                                "struct<city:text>",
+                                ImmutableList.of(path("s", "city")),
+                                ImmutableList.of()
+                        )
+                )
+        );
+
+        assertColumns("select struct_element(s, 'city'), 
struct_element(map_values(struct_element(s, 'data')[0])[0], 'b') from (select * 
from tbl union all select * from tbl2)t",
+                ImmutableList.of(
+                        Triple.of(
+                                
"struct<city2:text,data2:array<map<int,struct<b2:double>>>>",
+                                ImmutableList.of(path("s2", "city2"), 
path("s2", "data2", "*", "VALUES", "b2")),
+                                ImmutableList.of()
+                        ),
+                        Triple.of(
+                                
"struct<city:text,data:array<map<int,struct<b:double>>>>",
+                                ImmutableList.of(path("s", "city"), path("s", 
"data", "*", "VALUES", "b")),
+                                ImmutableList.of()
+                        )
+                )
+        );
     }
 
     @Test
@@ -473,75 +504,87 @@ public class PruneNestedColumnTest extends 
TestWithFeService implements MemoPatt
     private void assertColumn(String sql, String expectType,
             List<TColumnAccessPath> expectAllAccessPaths,
             List<TColumnAccessPath> expectPredicateAccessPaths) throws 
Exception {
+        assertColumns(sql, expectType == null ? null : 
ImmutableList.of(Triple.of(expectType, expectAllAccessPaths, 
expectPredicateAccessPaths)));
+    }
+
+    private void assertColumns(String sql,
+            List<Triple<String, List<TColumnAccessPath>, 
List<TColumnAccessPath>>> expectResults) throws Exception {
         Pair<PhysicalPlan, List<SlotDescriptor>> result = 
collectComplexSlots(sql);
         PhysicalPlan physicalPlan = result.first;
         List<SlotDescriptor> slotDescriptors = result.second;
-        if (expectType == null) {
+        if (expectResults == null) {
             Assertions.assertEquals(0, slotDescriptors.size());
             return;
         }
 
-        Assertions.assertEquals(1, slotDescriptors.size());
-        Assertions.assertEquals(expectType, 
slotDescriptors.get(0).getType().toString());
-
-        TreeSet<TColumnAccessPath> expectAllAccessPathSet = new 
TreeSet<>(expectAllAccessPaths);
-        TreeSet<TColumnAccessPath> actualAllAccessPaths
-                = new TreeSet<>(slotDescriptors.get(0).getAllAccessPaths());
-        Assertions.assertEquals(expectAllAccessPathSet, actualAllAccessPaths);
-
-        TreeSet<TColumnAccessPath> expectPredicateAccessPathSet = new 
TreeSet<>(expectPredicateAccessPaths);
-        TreeSet<TColumnAccessPath> actualPredicateAccessPaths
-                = new 
TreeSet<>(slotDescriptors.get(0).getPredicateAccessPaths());
-        Assertions.assertEquals(expectPredicateAccessPathSet, 
actualPredicateAccessPaths);
-
-        Map<Integer, DataType> slotIdToDataTypes = new LinkedHashMap<>();
-        Consumer<Expression> assertHasSameType = e -> {
-            if (e instanceof NamedExpression) {
-                DataType dataType = slotIdToDataTypes.get(((NamedExpression) 
e).getExprId().asInt());
-                if (dataType != null) {
-                    Assertions.assertEquals(dataType, e.getDataType());
-                } else {
-                    slotIdToDataTypes.put(((NamedExpression) 
e).getExprId().asInt(), e.getDataType());
-                }
-            }
-        };
-
-        // assert same slot id has same type
-        physicalPlan.foreachUp(plan -> {
-            List<? extends Expression> expressions = ((PhysicalPlan) 
plan).getExpressions();
-            for (Expression expression : expressions) {
-                expression.foreach(e -> {
-                    assertHasSameType.accept((Expression) e);
-                    if (e instanceof Alias && e.child(0) instanceof Slot) {
-                        assertHasSameType.accept((Alias) e);
-                    } else if (e instanceof ArrayItemReference) {
-                        assertHasSameType.accept((ArrayItemReference) e);
+        Assertions.assertEquals(expectResults.size(), slotDescriptors.size());
+        int slotIndex = 0;
+        for (Triple<String, List<TColumnAccessPath>, List<TColumnAccessPath>> 
expectResult : expectResults) {
+            String expectType = expectResult.left;
+            List<TColumnAccessPath> expectAllAccessPaths = expectResult.middle;
+            List<TColumnAccessPath> expectPredicateAccessPaths = 
expectResult.right;
+            SlotDescriptor slotDescriptor = slotDescriptors.get(slotIndex++);
+            Assertions.assertEquals(expectType, 
slotDescriptor.getType().toString());
+
+            TreeSet<TColumnAccessPath> expectAllAccessPathSet = new 
TreeSet<>(expectAllAccessPaths);
+            TreeSet<TColumnAccessPath> actualAllAccessPaths
+                    = new TreeSet<>(slotDescriptor.getAllAccessPaths());
+            Assertions.assertEquals(expectAllAccessPathSet, 
actualAllAccessPaths);
+
+            TreeSet<TColumnAccessPath> expectPredicateAccessPathSet = new 
TreeSet<>(expectPredicateAccessPaths);
+            TreeSet<TColumnAccessPath> actualPredicateAccessPaths
+                    = new TreeSet<>(slotDescriptor.getPredicateAccessPaths());
+            Assertions.assertEquals(expectPredicateAccessPathSet, 
actualPredicateAccessPaths);
+
+            Map<Integer, DataType> slotIdToDataTypes = new LinkedHashMap<>();
+            Consumer<Expression> assertHasSameType = e -> {
+                if (e instanceof NamedExpression) {
+                    DataType dataType = 
slotIdToDataTypes.get(((NamedExpression) e).getExprId().asInt());
+                    if (dataType != null) {
+                        Assertions.assertEquals(dataType, e.getDataType());
+                    } else {
+                        slotIdToDataTypes.put(((NamedExpression) 
e).getExprId().asInt(), e.getDataType());
                     }
-                });
-            }
+                }
+            };
+
+            // assert same slot id has same type
+            physicalPlan.foreachUp(plan -> {
+                List<? extends Expression> expressions = ((PhysicalPlan) 
plan).getExpressions();
+                for (Expression expression : expressions) {
+                    expression.foreach(e -> {
+                        assertHasSameType.accept((Expression) e);
+                        if (e instanceof Alias && e.child(0) instanceof Slot) {
+                            assertHasSameType.accept((Alias) e);
+                        } else if (e instanceof ArrayItemReference) {
+                            assertHasSameType.accept((ArrayItemReference) e);
+                        }
+                    });
+                }
 
-            if (plan instanceof PhysicalCTEConsumer) {
-                for (Entry<Slot, Collection<Slot>> kv : ((PhysicalCTEConsumer) 
plan).getProducerToConsumerSlotMap()
-                        .asMap().entrySet()) {
-                    Slot producerSlot = kv.getKey();
-                    for (Slot consumerSlot : kv.getValue()) {
-                        Assertions.assertEquals(producerSlot.getDataType(), 
consumerSlot.getDataType());
+                if (plan instanceof PhysicalCTEConsumer) {
+                    for (Entry<Slot, Collection<Slot>> kv : 
((PhysicalCTEConsumer) plan).getProducerToConsumerSlotMap()
+                            .asMap().entrySet()) {
+                        Slot producerSlot = kv.getKey();
+                        for (Slot consumerSlot : kv.getValue()) {
+                            
Assertions.assertEquals(producerSlot.getDataType(), consumerSlot.getDataType());
+                        }
                     }
-                }
-            } else if (plan instanceof PhysicalUnion) {
-                List<Slot> output = ((PhysicalUnion) plan).getOutput();
-                for (List<SlotReference> regularChildrenOutput : 
((PhysicalUnion) plan).getRegularChildrenOutputs()) {
-                    Assertions.assertEquals(output.size(), 
regularChildrenOutput.size());
-                    for (int i = 0; i < output.size(); i++) {
-                        Assertions.assertEquals(output.get(i).getDataType(), 
regularChildrenOutput.get(i).getDataType());
+                } else if (plan instanceof PhysicalUnion) {
+                    List<Slot> output = ((PhysicalUnion) plan).getOutput();
+                    for (List<SlotReference> regularChildrenOutput : 
((PhysicalUnion) plan).getRegularChildrenOutputs()) {
+                        Assertions.assertEquals(output.size(), 
regularChildrenOutput.size());
+                        for (int i = 0; i < output.size(); i++) {
+                            
Assertions.assertEquals(output.get(i).getDataType(), 
regularChildrenOutput.get(i).getDataType());
+                        }
                     }
                 }
-            }
-        });
+            });
+        }
     }
 
     private Pair<PhysicalPlan, List<SlotDescriptor>> 
collectComplexSlots(String sql) throws Exception {
-        NereidsPlanner planner = (NereidsPlanner) 
getSqlStmtExecutor(sql).planner();
+        NereidsPlanner planner = (NereidsPlanner) 
executeNereidsSql(sql).planner();
         List<SlotDescriptor> complexSlots = new ArrayList<>();
         PhysicalPlan physicalPlan = planner.getPhysicalPlan();
         for (PlanFragment fragment : planner.getFragments()) {
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/utframe/TestWithFeService.java 
b/fe/fe-core/src/test/java/org/apache/doris/utframe/TestWithFeService.java
index bbe3885b637..33eac635ebe 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/utframe/TestWithFeService.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/utframe/TestWithFeService.java
@@ -573,7 +573,7 @@ public abstract class TestWithFeService {
         }
     }
 
-    public void executeNereidsSql(String queryStr) throws Exception {
+    public StmtExecutor executeNereidsSql(String queryStr) throws Exception {
         connectContext.getState().reset();
 
         StatementContext statementContext = new 
StatementContext(connectContext, new OriginStatement(queryStr, 0));
@@ -589,6 +589,7 @@ public abstract class TestWithFeService {
                 || connectContext.getState().getErrorCode() != null) {
             throw new 
IllegalStateException(connectContext.getState().getErrorMessage());
         }
+        return stmtExecutor;
     }
 
     public void createDatabase(String db) throws Exception {


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

Reply via email to