From 739a300cd2830a5db68881b94442ad6630f0a756 Mon Sep 17 00:00:00 2001 From: 924060929 Date: Mon, 1 Dec 2025 16:57:50 +0800 Subject: [PATCH 1/3] fix --- .../AccessPathExpressionCollector.java | 48 ++++++++++++++++++- .../rules/rewrite/PruneNestedColumnTest.java | 9 +++- 2 files changed, 55 insertions(+), 2 deletions(-) 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 8fb1472a7d00c1..2a5af640ccdf2d 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 @@ -51,7 +51,9 @@ import org.apache.doris.nereids.trees.expressions.functions.scalar.StructElement; import org.apache.doris.nereids.trees.expressions.literal.Literal; import org.apache.doris.nereids.trees.expressions.visitor.DefaultExpressionVisitor; +import org.apache.doris.nereids.types.ArrayType; import org.apache.doris.nereids.types.DataType; +import org.apache.doris.nereids.types.MapType; import org.apache.doris.nereids.types.NestedColumnPrunable; import org.apache.doris.nereids.types.StructField; import org.apache.doris.nereids.types.StructType; @@ -66,6 +68,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Objects; import java.util.Stack; @@ -136,7 +139,8 @@ public Void visitAlias(Alias alias, CollectorContext context) { public Void visitCast(Cast cast, CollectorContext context) { if (!context.accessPathBuilder.isEmpty() && cast.getDataType() instanceof NestedColumnPrunable - && cast.child().getDataType() instanceof NestedColumnPrunable) { + && cast.child().getDataType() instanceof NestedColumnPrunable + && !mapTypeIsChanged(cast.child().getDataType(), cast.getDataType(), false)) { DataTypeAccessTree castTree = DataTypeAccessTree.of(cast.getDataType(), TAccessPathType.DATA); DataTypeAccessTree originTree = DataTypeAccessTree.of(cast.child().getDataType(), TAccessPathType.DATA); @@ -521,4 +525,46 @@ public int hashCode() { return path.hashCode(); } } + + // if the map type is changed, we can not prune the type, because the map type need distinct the keys, + // e.g. select map_values(cast(map(3.0, 1, 3.1, 2) as map)); + // the result is [1] because the keys: 3.0 and 3.1 will cast to 3 and the first entry remained. + // backend will throw exception because it can not only access the values without the cast keys, + // so we should check whether the map type is changed, if not changed, we can prune the type. + private static boolean mapTypeIsChanged(DataType originType, DataType castType, boolean inMap) { + if (originType.isMapType() && !originType.equals(castType)) { + MapType originMapType = (MapType) originType; + MapType castMapType = (MapType) castType; + if (mapTypeIsChanged(originMapType.getKeyType(), castMapType.getKeyType(), true) + || mapTypeIsChanged(originMapType.getValueType(), castMapType.getValueType(), true)) { + return true; + } + return false; + } else if (originType.isStructType()) { + StructType originStructType = (StructType) originType; + StructType castStructType = (StructType) castType; + List> originFields + = new ArrayList<>(originStructType.getNameToFields().entrySet()); + List> castFields + = new ArrayList<>(castStructType.getNameToFields().entrySet()); + + for (int i = 0; i < originFields.size(); i++) { + DataType originFieldType = originFields.get(i).getValue().getDataType(); + DataType castFieldType = castFields.get(i).getValue().getDataType(); + if (mapTypeIsChanged(originFieldType, castFieldType, inMap)) { + return true; + } + } + return false; + } else if (originType.isArrayType()) { + ArrayType originArrayType = (ArrayType) originType; + ArrayType castArrayType = (ArrayType) castType; + return mapTypeIsChanged(originArrayType.getItemType(), castArrayType.getItemType(), inMap); + } else if (inMap) { + return !originType.equals(castType); + } else { + // other type changed which not in map will not affect the map + return false; + } + } } 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 3d22e1e6c28e24..2649b29cdff970 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 @@ -140,13 +140,20 @@ public void testStruct() throws Throwable { @Test public void testPruneCast() throws Exception { + // the map type is changed, so we can not prune type + assertColumn("select struct_element(cast(s as struct>>>), 'k') from tbl", + "struct>>>", + ImmutableList.of(path("s")), + ImmutableList.of() + ); + assertColumn("select struct_element(cast(s as struct>>>), 'k') from tbl", "struct", ImmutableList.of(path("s", "city")), ImmutableList.of() ); - assertColumn("select struct_element(map_values(struct_element(cast(s as struct>>>), 'l')[0])[0], 'x') from tbl", + assertColumn("select struct_element(map_values(struct_element(cast(s as struct>>>), 'l')[0])[0], 'x') from tbl", "struct>>>", ImmutableList.of(path("s", "data", "*", "VALUES", "a")), ImmutableList.of() From e124cebf6c141eb1194bed31210264c049984f60 Mon Sep 17 00:00:00 2001 From: 924060929 Date: Mon, 1 Dec 2025 17:02:47 +0800 Subject: [PATCH 2/3] fix --- .../nereids/rules/rewrite/AccessPathExpressionCollector.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 2a5af640ccdf2d..e91a07baa3e858 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 @@ -528,7 +528,7 @@ public int hashCode() { // if the map type is changed, we can not prune the type, because the map type need distinct the keys, // e.g. select map_values(cast(map(3.0, 1, 3.1, 2) as map)); - // the result is [1] because the keys: 3.0 and 3.1 will cast to 3 and the first entry remained. + // the result is [2] because the keys: 3.0 and 3.1 will cast to 3 and the second entry remained. // backend will throw exception because it can not only access the values without the cast keys, // so we should check whether the map type is changed, if not changed, we can prune the type. private static boolean mapTypeIsChanged(DataType originType, DataType castType, boolean inMap) { From ced77873b3049be43563e0d15acf96276a4b5f77 Mon Sep 17 00:00:00 2001 From: 924060929 Date: Mon, 1 Dec 2025 17:06:54 +0800 Subject: [PATCH 3/3] fix --- .../nereids/rules/rewrite/AccessPathExpressionCollector.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 e91a07baa3e858..3ad75993e8799a 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 @@ -532,7 +532,7 @@ public int hashCode() { // backend will throw exception because it can not only access the values without the cast keys, // so we should check whether the map type is changed, if not changed, we can prune the type. private static boolean mapTypeIsChanged(DataType originType, DataType castType, boolean inMap) { - if (originType.isMapType() && !originType.equals(castType)) { + if (originType.isMapType()) { MapType originMapType = (MapType) originType; MapType castMapType = (MapType) castType; if (mapTypeIsChanged(originMapType.getKeyType(), castMapType.getKeyType(), true)