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 dadb3ab1ed6 branch-4.2: [fix](fe) Collect ARRAY_MAP container metadata 
on the input array #69340 (#70054)
dadb3ab1ed6 is described below

commit dadb3ab1ed6f84f45eafc4ad081382c0e3590ae0
Author: morrySnow <[email protected]>
AuthorDate: Wed Nov 4 08:16:06 2026 +0800

    branch-4.2: [fix](fe) Collect ARRAY_MAP container metadata on the input 
array #69340 (#70054)
    
    picked from master #69340
    
    Co-authored-by: minghong <[email protected]>
---
 .../rewrite/AccessPathExpressionCollector.java     | 101 +++++++++-
 .../rules/rewrite/PruneNestedColumnTest.java       | 101 ++++++++++
 .../array_map_container_metadata_pruning.out       |  57 ++++++
 .../array_map_container_metadata_pruning.groovy    | 220 +++++++++++++++++++++
 4 files changed, 476 insertions(+), 3 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 fcd84cf9d83..d86286acc35 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
@@ -26,6 +26,7 @@ import org.apache.doris.nereids.trees.expressions.Alias;
 import org.apache.doris.nereids.trees.expressions.ArrayItemReference;
 import 
org.apache.doris.nereids.trees.expressions.ArrayItemReference.ArrayItemSlot;
 import org.apache.doris.nereids.trees.expressions.Cast;
+import org.apache.doris.nereids.trees.expressions.ExprId;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.IsNull;
 import org.apache.doris.nereids.trees.expressions.Not;
@@ -78,6 +79,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Objects;
+import java.util.Set;
 import java.util.Stack;
 
 /**
@@ -289,14 +291,25 @@ public class AccessPathExpressionCollector extends 
DefaultExpressionVisitor<Void
             offsetContext.setType(TAccessPathType.META);
             // cardinality(map_keys(m)) == cardinality(m) == 
cardinality(map_values(m)):
             // all three count map entries, so emit the same [map_col, OFFSET] 
path.
-            Expression effectiveArg = (arg instanceof MapKeys || arg 
instanceof MapValues)
-                    ? arg.child(0) : arg;
+            Expression effectiveArg = normalizeMapArrayProducer(arg);
             return effectiveArg.accept(this, offsetContext);
         }
         // fall through to default
         return visit(cardinality, context);
     }
 
+    /**
+     * map_keys / map_values produce an array whose container shape (length 
and nullness) is the
+     * map's own: their metadata requirement belongs to the map column, not to 
the KEYS / VALUES
+     * sub-column, which has no offsets of its own (the BE rejects e.g. 
`m.VALUES.OFFSET` when the
+     * value is a struct). Returns the map expression for such an array 
producer, the expression
+     * itself otherwise.
+     */
+    private static Expression normalizeMapArrayProducer(Expression expression) 
{
+        return (expression instanceof MapKeys || expression instanceof 
MapValues)
+                ? expression.child(0) : expression;
+    }
+
     @Override
     public Void visitArrayItemSlot(ArrayItemSlot arrayItemSlot, 
CollectorContext context) {
         if (nameToLambdaArguments.isEmpty()) {
@@ -487,11 +500,55 @@ public class AccessPathExpressionCollector extends 
DefaultExpressionVisitor<Void
 
         Expression argument = arrayMap.getArgument(0);
         if ((argument instanceof Lambda)) {
-            return collectArrayPathInLambda((Lambda) argument, context);
+            Lambda lambda = (Lambda) argument;
+            return collectArrayMapPathInLambda(lambda, context);
         }
         return visit(arrayMap, context);
     }
 
+    private Void collectArrayMapPathInLambda(Lambda lambda, CollectorContext 
context) {
+        if (isRootContainerMetadata(context)) {
+            // A metadata requirement on the ARRAY_MAP result itself (OFFSET 
for cardinality(arr),
+            // NULL for arr IS NULL) describes the output ARRAY container, not 
the lambda elements.
+            // The output container is shaped by the input arrays: array_map 
copies the first
+            // input's offsets and its null map is the union of all inputs' 
null maps. Pushing that
+            // context into the lambda body would attach the metadata to the 
element layer instead,
+            // producing e.g. `arr.*.OFFSET`, which the BE rejects for a 
struct element (OFFSET
+            // metadata is only supported at array/map/string level). Route 
the container metadata
+            // to the input arrays, and collect the element paths the lambda 
actually reads with a
+            // fresh data context so that the elements stay available to the 
lambda body.
+            // A map_keys / map_values input is normalized to its map column: 
the produced array's
+            // container is the map itself, so the metadata must not be 
attached to the KEYS /
+            // VALUES sub-column (which has no offsets of its own, e.g. for a 
struct value).
+            for (ArrayItemReference lambdaArgument : 
lambda.getLambdaArguments()) {
+                continueCollectAccessPath(
+                        
normalizeMapArrayProducer(lambdaArgument.getArrayExpression()),
+                        copyContext(context));
+            }
+            return collectArrayPathInLambda(lambda,
+                    new CollectorContext(context.statementContext, 
context.bottomFilter));
+        }
+        // Only a root request is routed to the input containers. A prefixed 
request such as the
+        // `[*, OFFSET]` of cardinality(element_at(array_map(...), 1)) 
addresses an element of the
+        // ARRAY_MAP result, whose shape is produced by the lambda and is 
unrelated to the input
+        // array's containers. Keep the lambda traversal for it: the 
unrepresentable metadata is
+        // dropped and the element data the lambda reads is preserved.
+        return collectArrayPathInLambda(lambda, context);
+    }
+
+    /**
+     * Whether the pending metadata requirement addresses the current 
expression's own container,
+     * e.g. the `[OFFSET]` of cardinality(arr) or the `[NULL]` of arr IS NULL, 
rather than one of
+     * its descendants such as the `[*, OFFSET]` produced by 
cardinality(element_at(&lt;array&gt;, 1)).
+     * Every metadata request is created together with exactly one OFFSET / 
NULL suffix (see
+     * visitCardinality / visitLength / visitMapSize / visitIsNull), and the 
visitors only prepend
+     * the components they descend through, so a single-component metadata 
path is a root request.
+     */
+    private static boolean isRootContainerMetadata(CollectorContext context) {
+        return context.getType() == TAccessPathType.META
+                && context.getAccessPathBuilder().getPathList().size() == 1;
+    }
+
     @Override
     public Void visitArraySort(ArraySort arraySort, CollectorContext context) {
         // ARRAY_SORT(lambda, <arr>)
@@ -732,6 +789,44 @@ public class AccessPathExpressionCollector extends 
DefaultExpressionVisitor<Void
         } finally {
             nameToLambdaArguments.pop();
         }
+
+        // After visiting the lambda body, for any bound array whose lambda 
variable
+        // was NOT referenced in the body (e.g. x -> true where x never 
appears),
+        // visitArrayItemSlot was never called and the array column's access 
path is
+        // missing. This gap is exposed when an is-null or offset-only path 
has been
+        // registered for the same slot — NestedColumnPruning then incorrectly 
prunes
+        // the complex column to null-only / offset-only instead of reading 
full data.
+        //
+        // Detect usage by scanning the lambda body for ArrayItemSlots 
matching the
+        // argument name, which is more reliable than getInputSlots() that 
deliberately
+        // excludes ArrayItemSlot and may falsely match outer slots.
+        //
+        // Must use a fresh context: when the body DOES reference some 
variables
+        // (e.g. (x,y) -> x > 0), visitArrayItemSlot mutates 
context.accessPathBuilder
+        // in-place (addPrefix without cleanup). A fresh context isolates the 
fallback
+        // path for unreferenced variables from pollution by referenced ones.
+        for (Expression argument : arguments) {
+            if (argument instanceof ArrayItemReference) {
+                ExprId argExprId = ((ArrayItemReference) argument).getExprId();
+                Set<ArrayItemSlot> arrayItemSlots = arguments.get(0)
+                        .<ArrayItemSlot>collect(e -> e instanceof 
ArrayItemSlot);
+                boolean isReferenced = false;
+                for (ArrayItemSlot slot : arrayItemSlots) {
+                    if (slot.getExprId().equals(argExprId)) {
+                        isReferenced = true;
+                        break;
+                    }
+                }
+                if (!isReferenced) {
+                    Expression boundArray = argument.child(0);
+                    CollectorContext fullAccessCtx = new CollectorContext(
+                            context.statementContext, context.bottomFilter);
+                    
fullAccessCtx.accessPathBuilder.addPrefix(AccessPathInfo.ACCESS_ALL);
+                    continueCollectAccessPath(boundArray, fullAccessCtx);
+                }
+            }
+        }
+
         return null;
     }
 
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 1afe9326e94..fb0f89738eb 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
@@ -145,6 +145,16 @@ public class PruneNestedColumnTest extends 
TestWithFeService implements MemoPatt
                 + "  map_arr_col map<string, array<int>>\n"
                 + ") properties ('replication_num'='1')");
 
+        createTable("create table array_map_meta_tbl(\n"
+                + "  id int,\n"
+                + "  a array<struct<v: int>>\n"
+                + ") properties ('replication_num'='1')");
+
+        createTable("create table array_map_map_producer_tbl(\n"
+                + "  id int,\n"
+                + "  m map<string, struct<v: int>>\n"
+                + ") properties ('replication_num'='1')");
+
         createTable("create table map_array_value_tbl(\n"
                 + "  id int,\n"
                 + "  s struct<\n"
@@ -2295,4 +2305,95 @@ public class PruneNestedColumnTest extends 
TestWithFeService implements MemoPatt
                         ImmutableList.of("s", "f", "NULL"), true, 
TAccessPathType.META)),
                 "f is NOT NULL, expected NO [s,f,NULL] META in: " + allPaths);
     }
+
+    @Test
+    public void testCardinalityArrayMapCollectsContainerOffsetPath() throws 
Exception {
+        // ARRAY_MAP keeps the container shape of its inputs, so 
cardinality(array_map(...)) must
+        // collect the input array's OFFSET metadata. Passing the OFFSET 
requirement into the lambda
+        // body instead yields `a.*.OFFSET`, an OFFSET at the struct element 
level that the BE
+        // rejects; the element data the lambda reads must stay collected as 
well.
+        assertColumn("select cardinality(array_map(x -> x, a)) from 
array_map_meta_tbl",
+                "array<struct<v:int>>",
+                ImmutableList.of(path("a", "*"), metaPath("a", "OFFSET")),
+                ImmutableList.of());
+    }
+
+    @Test
+    public void 
testCardinalityArrayMapOnNestedArrayCollectsContainerOffsetPath() throws 
Exception {
+        assertColumn("select cardinality(array_map(x -> x, element_at(s, 
'arr'))) "
+                        + "from nested_container_tbl",
+                "struct<arr:array<struct<str_field:text,int_field:int>>>",
+                ImmutableList.of(
+                        path("s", "arr", "*"),
+                        metaPath("s", "arr", "OFFSET")),
+                ImmutableList.of());
+    }
+
+    @Test
+    public void testCardinalityArrayMapOnMapValueCollectsValueOffsetPath() 
throws Exception {
+        assertAllAccessPathsContain(
+                "select cardinality(array_map(x -> x, element_at(element_at(s, 
'm'), 'k'))) "
+                        + "from map_array_value_tbl",
+                ImmutableList.of(
+                        path("s", "m", "KEYS"),
+                        path("s", "m", "VALUES", "*"),
+                        metaPath("s", "m", "VALUES", "OFFSET")),
+                ImmutableList.of(
+                        metaPath("s", "m", "VALUES", "*", "OFFSET")));
+    }
+
+    @Test
+    public void testIsNullArrayMapCollectsContainerNullPath() throws Exception 
{
+        assertAllAccessPathsContain(
+                "select array_map(x -> x, a) is null from array_map_meta_tbl",
+                ImmutableList.of(metaPath("a", "NULL")),
+                ImmutableList.of(metaPath("a", "*", "NULL")));
+    }
+
+    @Test
+    public void testCardinalityArrayMapKeepsElementFieldTheLambdaReads() 
throws Exception {
+        assertColumn("select cardinality(array_map(x -> x.v + 1, a)) from 
array_map_meta_tbl",
+                "array<struct<v:int>>",
+                ImmutableList.of(path("a", "*", "v"), metaPath("a", "OFFSET")),
+                ImmutableList.of());
+    }
+
+    @Test
+    public void testArrayMapResultElementMetadataKeepsLambdaPayloadOnly() 
throws Exception {
+        // cardinality() of a selected ARRAY_MAP element addresses an element 
of the ARRAY_MAP
+        // result, whose shape is produced by the lambda, not the input array 
container. That
+        // metadata cannot be routed to the input array's element: doing so 
emitted the invalid
+        // `a.*.OFFSET` on a struct element, so only the element data the 
lambda reads is kept.
+        assertColumn("select cardinality(element_at(array_map(x -> array(x.v), 
a), 1)) "
+                        + "from array_map_meta_tbl",
+                "array<struct<v:int>>",
+                ImmutableList.of(path("a", "*", "v")),
+                ImmutableList.of());
+
+        assertColumn("select element_at(array_map(x -> array(x.v), a), 1) is 
null "
+                        + "from array_map_meta_tbl",
+                "array<struct<v:int>>",
+                ImmutableList.of(path("a", "*", "v")),
+                ImmutableList.of());
+    }
+
+    @Test
+    public void testArrayMapOnMapProducerUsesMapContainerMetadata() throws 
Exception {
+        // map_values / map_keys produce an array whose container is the map 
itself: its length is
+        // the map's entry count. The OFFSET requirement must be collected on 
the map column, as
+        // cardinality(map_values(m)) does, instead of the VALUES / KEYS 
sub-column, which has no
+        // offsets of its own when the value is a struct (`m.VALUES.OFFSET` 
fails in the BE).
+        assertColumn("select cardinality(array_map(x -> x.v + 1, 
map_values(m))) "
+                        + "from array_map_map_producer_tbl",
+                "map<text,struct<v:int>>",
+                ImmutableList.of(path("m", "VALUES", "v"), metaPath("m", 
"OFFSET")),
+                ImmutableList.of());
+
+        assertColumn("select cardinality(array_map(x -> 1, map_keys(m))) "
+                        + "from array_map_map_producer_tbl",
+                "map<text,struct<v:int>>",
+                ImmutableList.of(path("m", "KEYS"), metaPath("m", "OFFSET")),
+                ImmutableList.of());
+    }
+
 }
diff --git 
a/regression-test/data/nereids_rules_p0/column_pruning/array_map_container_metadata_pruning.out
 
b/regression-test/data/nereids_rules_p0/column_pruning/array_map_container_metadata_pruning.out
new file mode 100644
index 00000000000..4402d6ad189
--- /dev/null
+++ 
b/regression-test/data/nereids_rules_p0/column_pruning/array_map_container_metadata_pruning.out
@@ -0,0 +1,57 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !root_array_map_cardinality --
+1      2       2
+2      0       0
+3      \N      \N
+4      2       2
+
+-- !struct_field_array_map_cardinality --
+1      2       2
+2      0       0
+3      \N      \N
+4      2       2
+
+-- !map_value_array_map_cardinality --
+1      2       2
+2      0       0
+3      \N      \N
+4      2       2
+
+-- !multi_array_array_map_cardinality --
+1      2
+2      0
+3      \N
+4      2
+
+-- !array_map_is_null --
+1      false   false
+2      false   false
+3      true    true
+4      false   false
+
+-- !lambda_reads_element_field --
+1      2
+2      0
+3      \N
+4      2
+
+-- !array_map_result_element_cardinality --
+1      1
+2      \N
+3      \N
+4      1
+
+-- !array_map_result_element_is_null --
+1      false
+2      true
+3      true
+4      false
+
+-- !map_values_array_map --
+1      [{"v":1}, {"v":3}]      [2, 4]  2
+2      [{"v":10}]      [11]    1
+
+-- !map_keys_array_map --
+1      2
+2      1
+
diff --git 
a/regression-test/suites/nereids_rules_p0/column_pruning/array_map_container_metadata_pruning.groovy
 
b/regression-test/suites/nereids_rules_p0/column_pruning/array_map_container_metadata_pruning.groovy
new file mode 100644
index 00000000000..c0b73b1de6f
--- /dev/null
+++ 
b/regression-test/suites/nereids_rules_p0/column_pruning/array_map_container_metadata_pruning.groovy
@@ -0,0 +1,220 @@
+// 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.
+
+suite("array_map_container_metadata_pruning") {
+    sql """ DROP TABLE IF EXISTS array_map_container_metadata_pruning_tbl """
+    sql """
+        CREATE TABLE array_map_container_metadata_pruning_tbl (
+            id INT,
+            a  ARRAY<STRUCT<v: INT>>,
+            s  STRUCT<arr: ARRAY<STRUCT<v: INT>>>,
+            m  MAP<STRING, ARRAY<STRUCT<v: INT>>>
+        ) ENGINE = OLAP
+        DUPLICATE KEY(id)
+        DISTRIBUTED BY HASH(id) BUCKETS 1
+        PROPERTIES ("replication_allocation" = "tag.location.default: 1")
+    """
+    sql """
+        INSERT INTO array_map_container_metadata_pruning_tbl VALUES
+            (1,
+             array(named_struct('v', 1), named_struct('v', 2)),
+             named_struct('arr', array(named_struct('v', 1), named_struct('v', 
2))),
+             map('k', array(named_struct('v', 1), named_struct('v', 2)))),
+            (2,
+             CAST([] AS ARRAY<STRUCT<v: INT>>),
+             named_struct('arr', CAST([] AS ARRAY<STRUCT<v: INT>>)),
+             map('k', CAST([] AS ARRAY<STRUCT<v: INT>>))),
+            (3,
+             CAST(NULL AS ARRAY<STRUCT<v: INT>>),
+             CAST(NULL AS STRUCT<arr: ARRAY<STRUCT<v: INT>>>),
+             CAST(NULL AS MAP<STRING, ARRAY<STRUCT<v: INT>>>)),
+            (4,
+             array(CAST(NULL AS STRUCT<v: INT>), named_struct('v', 3)),
+             named_struct('arr', array(CAST(NULL AS STRUCT<v: INT>), 
named_struct('v', 3))),
+             map('k', array(CAST(NULL AS STRUCT<v: INT>), named_struct('v', 
3))))
+    """
+
+    // ARRAY_MAP keeps the container shape of its inputs: the result offsets 
come from the first
+    // input array and the result null map is the union of the inputs' null 
maps. A metadata
+    // requirement (OFFSET from cardinality(), NULL from IS NULL) on the 
ARRAY_MAP result therefore
+    // has to be collected on the input array container. Collecting it through 
the lambda element
+    // instead yields paths such as a.*.OFFSET, which the BE rejects when the 
element is a struct.
+    order_qt_root_array_map_cardinality """
+        SELECT id, cardinality(a) AS direct, cardinality(array_map(x -> x, a)) 
AS mapped
+        FROM array_map_container_metadata_pruning_tbl ORDER BY id
+    """
+
+    order_qt_struct_field_array_map_cardinality """
+        SELECT id,
+               cardinality(element_at(s, 'arr')) AS direct,
+               cardinality(array_map(x -> x, element_at(s, 'arr'))) AS mapped
+        FROM array_map_container_metadata_pruning_tbl ORDER BY id
+    """
+
+    order_qt_map_value_array_map_cardinality """
+        SELECT id,
+               cardinality(element_at(m, 'k')) AS direct,
+               cardinality(array_map(x -> x, element_at(m, 'k'))) AS mapped
+        FROM array_map_container_metadata_pruning_tbl ORDER BY id
+    """
+
+    order_qt_multi_array_array_map_cardinality """
+        SELECT id, cardinality(array_map((x, y) -> x.v + y.v, a, a)) AS mapped
+        FROM array_map_container_metadata_pruning_tbl ORDER BY id
+    """
+
+    order_qt_array_map_is_null """
+        SELECT id, a IS NULL AS direct, array_map(x -> x, a) IS NULL AS mapped
+        FROM array_map_container_metadata_pruning_tbl ORDER BY id
+    """
+
+    // The lambda body still reads element data: only the element field it 
references is needed.
+    order_qt_lambda_reads_element_field """
+        SELECT id, cardinality(array_map(x -> x.v + 1, a)) AS mapped
+        FROM array_map_container_metadata_pruning_tbl ORDER BY id
+    """
+
+    // A metadata request that addresses an element of the ARRAY_MAP result 
instead of its own
+    // container (cardinality of a selected result element) is produced by the 
lambda. It must not
+    // be routed to the input array: that emitted `a.*.OFFSET` on the struct 
element and failed
+    // with E-3110. Only the lambda payload is read and the query keeps 
working.
+    order_qt_array_map_result_element_cardinality """
+        SELECT id, cardinality(element_at(array_map(x -> array(x.v), a), 1)) 
AS c
+        FROM array_map_container_metadata_pruning_tbl ORDER BY id
+    """
+
+    order_qt_array_map_result_element_is_null """
+        SELECT id, element_at(array_map(x -> array(x.v), a), 1) IS NULL AS n
+        FROM array_map_container_metadata_pruning_tbl ORDER BY id
+    """
+
+    explain {
+        sql """
+            SELECT cardinality(element_at(array_map(x -> array(x.v), a), 1))
+            FROM array_map_container_metadata_pruning_tbl
+        """
+        contains "a.*.v"
+        notContains "a.*.OFFSET"
+    }
+
+    explain {
+        sql """
+            SELECT element_at(array_map(x -> array(x.v), a), 1) IS NULL
+            FROM array_map_container_metadata_pruning_tbl
+        """
+        contains "a.*.v"
+        notContains "a.*.NULL"
+    }
+
+    // map_values / map_keys produce an array whose container is the map 
itself: its length is the
+    // map's entry count. Cardinality over ARRAY_MAP of such an input must 
collect the map column's
+    // OFFSET, not `m.VALUES.OFFSET` / `m.KEYS.OFFSET`: the KEYS / VALUES 
sub-column has no offsets
+    // of its own when the value is a struct, and the BE rejects that metadata 
path.
+    sql """ DROP TABLE IF EXISTS array_map_map_producer_pruning_tbl """
+    sql """
+        CREATE TABLE array_map_map_producer_pruning_tbl (
+            id INT,
+            m  MAP<STRING, STRUCT<v: INT>>
+        ) ENGINE = OLAP
+        DUPLICATE KEY(id)
+        DISTRIBUTED BY HASH(id) BUCKETS 1
+        PROPERTIES ("replication_allocation" = "tag.location.default: 1")
+    """
+    sql """
+        INSERT INTO array_map_map_producer_pruning_tbl VALUES
+            (1, map('a', named_struct('v', 1), 'b', named_struct('v', 3))),
+            (2, map('c', named_struct('v', 10)))
+    """
+
+    order_qt_map_values_array_map """
+        SELECT id,
+               map_values(m) AS vals,
+               array_map(x -> x.v + 1, map_values(m)) AS mapped,
+               cardinality(array_map(x -> x.v + 1, map_values(m))) AS len
+        FROM array_map_map_producer_pruning_tbl ORDER BY id
+    """
+
+    order_qt_map_keys_array_map """
+        SELECT id, cardinality(array_map(x -> 1, map_keys(m))) AS len
+        FROM array_map_map_producer_pruning_tbl ORDER BY id
+    """
+
+    explain {
+        sql """
+            SELECT cardinality(array_map(x -> x.v + 1, map_values(m)))
+            FROM array_map_map_producer_pruning_tbl
+        """
+        contains "m.OFFSET"
+        notContains "m.VALUES.OFFSET"
+    }
+
+    explain {
+        sql """
+            SELECT cardinality(array_map(x -> 1, map_keys(m)))
+            FROM array_map_map_producer_pruning_tbl
+        """
+        contains "m.OFFSET"
+        notContains "m.KEYS.OFFSET"
+    }
+
+    explain {
+        sql """
+            SELECT cardinality(array_map(x -> x, a))
+            FROM array_map_container_metadata_pruning_tbl
+        """
+        contains "a.OFFSET"
+        contains "a.*"
+        notContains "a.*.OFFSET"
+    }
+
+    explain {
+        sql """
+            SELECT cardinality(array_map(x -> x, element_at(s, 'arr')))
+            FROM array_map_container_metadata_pruning_tbl
+        """
+        contains "s.arr.OFFSET"
+        notContains "s.arr.*.OFFSET"
+    }
+
+    explain {
+        sql """
+            SELECT cardinality(array_map(x -> x, element_at(m, 'k')))
+            FROM array_map_container_metadata_pruning_tbl
+        """
+        contains "m.VALUES.OFFSET"
+        notContains "m.VALUES.*.OFFSET"
+    }
+
+    explain {
+        sql """
+            SELECT array_map(x -> x, a) IS NULL
+            FROM array_map_container_metadata_pruning_tbl
+        """
+        contains "a.NULL"
+        notContains "a.*.NULL"
+    }
+
+    explain {
+        sql """
+            SELECT cardinality(array_map(x -> x.v + 1, a))
+            FROM array_map_container_metadata_pruning_tbl
+        """
+        contains "a.*.v"
+        contains "a.OFFSET"
+        notContains "a.*.OFFSET"
+    }
+}


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

Reply via email to