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(<array>, 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]