diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveLogicalPlanner.java b/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveLogicalPlanner.java index 56383aca916dc..04fbd820fe373 100644 --- a/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveLogicalPlanner.java +++ b/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveLogicalPlanner.java @@ -84,6 +84,7 @@ import static com.facebook.presto.SystemSessionProperties.OPTIMIZE_METADATA_QUERIES_IGNORE_STATS; import static com.facebook.presto.SystemSessionProperties.PUSHDOWN_DEREFERENCE_ENABLED; import static com.facebook.presto.SystemSessionProperties.PUSHDOWN_SUBFIELDS_ENABLED; +import static com.facebook.presto.SystemSessionProperties.PUSHDOWN_SUBFIELDS_FOR_MAP_SUBSET; import static com.facebook.presto.common.function.OperatorType.EQUAL; import static com.facebook.presto.common.predicate.Domain.create; import static com.facebook.presto.common.predicate.Domain.multipleValues; @@ -1461,6 +1462,44 @@ public void testPushdownSubfields() assertUpdate("DROP TABLE test_pushdown_struct_subfields"); } + @Test + public void testPushdownSubfieldsForMapSubset() + { + Session mapSubset = Session.builder(getSession()).setSystemProperty(PUSHDOWN_SUBFIELDS_FOR_MAP_SUBSET, "true").build(); + assertUpdate("CREATE TABLE test_pushdown_map_subfields(id integer, x map(integer, double))"); + assertPushdownSubfields(mapSubset, "SELECT t.id, map_subset(x, array[1, 2, 3]) FROM test_pushdown_map_subfields t", "test_pushdown_map_subfields", + ImmutableMap.of("x", toSubfields("x[1]", "x[2]", "x[3]"))); + assertPushdownSubfields(mapSubset, "SELECT t.id, map_subset(x, array[-1, -2, 3]) FROM test_pushdown_map_subfields t", "test_pushdown_map_subfields", + ImmutableMap.of("x", toSubfields("x[-1]", "x[-2]", "x[3]"))); + assertPushdownSubfields(mapSubset, "SELECT t.id, map_subset(x, array[1, 2, null]) FROM test_pushdown_map_subfields t", "test_pushdown_map_subfields", + ImmutableMap.of("x", toSubfields())); + assertPushdownSubfields(mapSubset, "SELECT t.id, map_subset(x, array[1, 2, 3, id]) FROM test_pushdown_map_subfields t", "test_pushdown_map_subfields", + ImmutableMap.of("x", toSubfields())); + assertPushdownSubfields(mapSubset, "SELECT t.id, map_subset(x, array[id]) FROM test_pushdown_map_subfields t", "test_pushdown_map_subfields", + ImmutableMap.of("x", toSubfields())); + assertUpdate("DROP TABLE test_pushdown_map_subfields"); + + assertUpdate("CREATE TABLE test_pushdown_map_subfields(id integer, x array(map(integer, double)))"); + assertPushdownSubfields(mapSubset, "SELECT t.id, transform(x, mp -> map_subset(mp, array[1, 2, 3])) FROM test_pushdown_map_subfields t", "test_pushdown_map_subfields", + ImmutableMap.of("x", toSubfields("x[*][1]", "x[*][2]", "x[*][3]"))); + assertPushdownSubfields(mapSubset, "SELECT t.id, transform(x, mp -> map_subset(mp, array[1, 2, null])) FROM test_pushdown_map_subfields t", "test_pushdown_map_subfields", + ImmutableMap.of("x", toSubfields())); + assertPushdownSubfields(mapSubset, "SELECT t.id, transform(x, mp -> map_subset(mp, array[1, 2, id])) FROM test_pushdown_map_subfields t", "test_pushdown_map_subfields", + ImmutableMap.of("x", toSubfields())); + assertUpdate("DROP TABLE test_pushdown_map_subfields"); + + assertUpdate("CREATE TABLE test_pushdown_map_subfields(id varchar, x map(varchar, double))"); + assertPushdownSubfields(mapSubset, "SELECT t.id, map_subset(x, array['ab', 'c', 'd']) FROM test_pushdown_map_subfields t", "test_pushdown_map_subfields", + ImmutableMap.of("x", toSubfields("x[\"ab\"]", "x[\"c\"]", "x[\"d\"]"))); + assertPushdownSubfields(mapSubset, "SELECT t.id, map_subset(x, array['ab', 'c', null]) FROM test_pushdown_map_subfields t", "test_pushdown_map_subfields", + ImmutableMap.of("x", toSubfields())); + assertPushdownSubfields(mapSubset, "SELECT t.id, map_subset(x, array['ab', 'c', 'd', id]) FROM test_pushdown_map_subfields t", "test_pushdown_map_subfields", + ImmutableMap.of("x", toSubfields())); + assertPushdownSubfields(mapSubset, "SELECT t.id, map_subset(x, array[id]) FROM test_pushdown_map_subfields t", "test_pushdown_map_subfields", + ImmutableMap.of("x", toSubfields())); + assertUpdate("DROP TABLE test_pushdown_map_subfields"); + } + @Test public void testPushdownSubfieldsAssorted() { diff --git a/presto-main-base/src/main/java/com/facebook/presto/SystemSessionProperties.java b/presto-main-base/src/main/java/com/facebook/presto/SystemSessionProperties.java index 2953460c3a461..2c2cc27afad64 100644 --- a/presto-main-base/src/main/java/com/facebook/presto/SystemSessionProperties.java +++ b/presto-main-base/src/main/java/com/facebook/presto/SystemSessionProperties.java @@ -335,6 +335,7 @@ public final class SystemSessionProperties public static final String QUERY_CLIENT_TIMEOUT = "query_client_timeout"; public static final String REWRITE_MIN_MAX_BY_TO_TOP_N = "rewrite_min_max_by_to_top_n"; public static final String ADD_DISTINCT_BELOW_SEMI_JOIN_BUILD = "add_distinct_below_semi_join_build"; + public static final String PUSHDOWN_SUBFIELDS_FOR_MAP_SUBSET = "pushdown_subfields_for_map_subset"; // TODO: Native execution related session properties that are temporarily put here. They will be relocated in the future. public static final String NATIVE_AGGREGATION_SPILL_ALL = "native_aggregation_spill_all"; @@ -1910,6 +1911,10 @@ public SystemSessionProperties( "Optimize out APPROX_DISTINCT operations over constant conditionals", featuresConfig.isOptimizeConditionalApproxDistinct(), false), + booleanProperty(PUSHDOWN_SUBFIELDS_FOR_MAP_SUBSET, + "Enable subfield pruning for map_subset function", + featuresConfig.isPushdownSubfieldForMapSubset(), + false), new PropertyMetadata<>( QUERY_CLIENT_TIMEOUT, "Configures how long the query runs without contact from the client application, such as the CLI, before it's abandoned", @@ -3259,6 +3264,11 @@ public static boolean isEnabledAddExchangeBelowGroupId(Session session) return session.getSystemProperty(ADD_EXCHANGE_BELOW_PARTIAL_AGGREGATION_OVER_GROUP_ID, Boolean.class); } + public static boolean isPushSubfieldsForMapSubsetEnabled(Session session) + { + return session.getSystemProperty(PUSHDOWN_SUBFIELDS_FOR_MAP_SUBSET, Boolean.class); + } + public static boolean isAddDistinctBelowSemiJoinBuildEnabled(Session session) { return session.getSystemProperty(ADD_DISTINCT_BELOW_SEMI_JOIN_BUILD, Boolean.class); diff --git a/presto-main-base/src/main/java/com/facebook/presto/sql/analyzer/FeaturesConfig.java b/presto-main-base/src/main/java/com/facebook/presto/sql/analyzer/FeaturesConfig.java index a92a1ab8c7faf..ed358d2459548 100644 --- a/presto-main-base/src/main/java/com/facebook/presto/sql/analyzer/FeaturesConfig.java +++ b/presto-main-base/src/main/java/com/facebook/presto/sql/analyzer/FeaturesConfig.java @@ -306,6 +306,7 @@ public class FeaturesConfig private String expressionOptimizerName = DEFAULT_EXPRESSION_OPTIMIZER_NAME; private boolean addExchangeBelowPartialAggregationOverGroupId; private boolean addDistinctBelowSemiJoinBuild; + private boolean pushdownSubfieldForMapSubset = true; public enum PartitioningPrecisionStrategy { @@ -3056,4 +3057,17 @@ public boolean isAddDistinctBelowSemiJoinBuild() { return addDistinctBelowSemiJoinBuild; } + + @Config("optimizer.pushdown-subfield-for-map-subset") + @ConfigDescription("Enable subfield pruning for map_subset function") + public FeaturesConfig setPushdownSubfieldForMapSubset(boolean pushdownSubfieldForMapSubset) + { + this.pushdownSubfieldForMapSubset = pushdownSubfieldForMapSubset; + return this; + } + + public boolean isPushdownSubfieldForMapSubset() + { + return pushdownSubfieldForMapSubset; + } } diff --git a/presto-main-base/src/main/java/com/facebook/presto/sql/planner/optimizations/PushdownSubfields.java b/presto-main-base/src/main/java/com/facebook/presto/sql/planner/optimizations/PushdownSubfields.java index 0334f8833390d..82850bd2eef53 100644 --- a/presto-main-base/src/main/java/com/facebook/presto/sql/planner/optimizations/PushdownSubfields.java +++ b/presto-main-base/src/main/java/com/facebook/presto/sql/planner/optimizations/PushdownSubfields.java @@ -18,9 +18,11 @@ import com.facebook.presto.common.Subfield; import com.facebook.presto.common.Subfield.NestedField; import com.facebook.presto.common.Subfield.PathElement; +import com.facebook.presto.common.block.Block; import com.facebook.presto.common.type.ArrayType; import com.facebook.presto.common.type.MapType; import com.facebook.presto.common.type.RowType; +import com.facebook.presto.common.type.Type; import com.facebook.presto.expressions.DefaultRowExpressionTraversalVisitor; import com.facebook.presto.metadata.FunctionAndTypeManager; import com.facebook.presto.metadata.Metadata; @@ -89,10 +91,12 @@ import java.util.stream.IntStream; import static com.facebook.presto.SystemSessionProperties.isLegacyUnnest; +import static com.facebook.presto.SystemSessionProperties.isPushSubfieldsForMapSubsetEnabled; import static com.facebook.presto.SystemSessionProperties.isPushdownSubfieldsEnabled; import static com.facebook.presto.SystemSessionProperties.isPushdownSubfieldsFromArrayLambdasEnabled; import static com.facebook.presto.common.Subfield.allSubscripts; import static com.facebook.presto.common.Subfield.noSubfield; +import static com.facebook.presto.common.type.TypeUtils.readNativeValue; import static com.facebook.presto.common.type.Varchars.isVarcharType; import static com.facebook.presto.metadata.BuiltInTypeAndFunctionNamespaceManager.JAVA_BUILTIN_NAMESPACE; import static com.facebook.presto.spi.relation.SpecialFormExpression.Form.DEREFERENCE; @@ -151,7 +155,7 @@ private static class Rewriter { private final Session session; private final Metadata metadata; - private final StandardFunctionResolution functionResolution; + private final FunctionResolution functionResolution; private final ExpressionOptimizer expressionOptimizer; private final SubfieldExtractor subfieldExtractor; private static final QualifiedObjectName ARBITRARY_AGGREGATE_FUNCTION = QualifiedObjectName.valueOf(JAVA_BUILTIN_NAMESPACE, "arbitrary"); @@ -169,7 +173,7 @@ public Rewriter(Session session, Metadata metadata, ExpressionOptimizerProvider expressionOptimizer, session.toConnectorSession(), metadata.getFunctionAndTypeManager(), - isPushdownSubfieldsFromArrayLambdasEnabled(session)); + session); } public boolean isPlanChanged() @@ -307,9 +311,9 @@ public PlanNode visitProject(ProjectNode node, RewriteContext context) continue; } - Optional subfield = toSubfield(expression, functionResolution, expressionOptimizer, session.toConnectorSession(), metadata.getFunctionAndTypeManager()); + Optional> subfield = toSubfield(expression, functionResolution, expressionOptimizer, session.toConnectorSession(), metadata.getFunctionAndTypeManager(), isPushSubfieldsForMapSubsetEnabled(session)); if (subfield.isPresent()) { - context.get().addAssignment(variable, subfield.get()); + subfield.get().forEach(element -> context.get().addAssignment(variable, element)); continue; } @@ -570,17 +574,18 @@ private static String getColumnName(Session session, Metadata metadata, TableHan return metadata.getColumnMetadata(session, tableHandle, columnHandle).getName(); } - private static Optional toSubfield( + private static Optional> toSubfield( RowExpression expression, - StandardFunctionResolution functionResolution, + FunctionResolution functionResolution, ExpressionOptimizer expressionOptimizer, ConnectorSession connectorSession, - FunctionAndTypeManager functionAndTypeManager) + FunctionAndTypeManager functionAndTypeManager, + boolean isPushdownSubfieldsForMapSubsetEnabled) { ImmutableList.Builder elements = ImmutableList.builder(); while (true) { if (expression instanceof VariableReferenceExpression) { - return Optional.of(new Subfield(((VariableReferenceExpression) expression).getName(), elements.build().reverse())); + return Optional.of(ImmutableList.of(new Subfield(((VariableReferenceExpression) expression).getName(), elements.build().reverse()))); } if (expression instanceof CallExpression) { ComplexTypeFunctionDescriptor functionDescriptor = functionAndTypeManager.getFunctionMetadata(((CallExpression) expression).getFunctionHandle()).getDescriptor(); @@ -650,6 +655,29 @@ private static Optional toSubfield( } return Optional.empty(); } + // map_subset(feature, constant_array) is only accessing fields specified in feature map. + // For example map_subset(feature, array[1, 2]) is equivalent to calling element_at(feature, 1) and element_at(feature, 2) for subfield extraction + if (isPushdownSubfieldsForMapSubsetEnabled && expression instanceof CallExpression && isMapSubSetWithConstantArray((CallExpression) expression, functionResolution)) { + CallExpression call = (CallExpression) expression; + ConstantExpression constantArray = (ConstantExpression) call.getArguments().get(1); + checkState(constantArray.getValue() instanceof Block && constantArray.getType() instanceof ArrayType); + Block arrayValue = (Block) constantArray.getValue(); + Type arrayElementType = ((ArrayType) constantArray.getType()).getElementType(); + ImmutableList.Builder arguments = ImmutableList.builder(); + for (int i = 0; i < arrayValue.getPositionCount(); ++i) { + Object mapKey = readNativeValue(arrayElementType, arrayValue, i); + if (mapKey == null) { + return Optional.empty(); + } + if (mapKey instanceof Number) { + arguments.add(new Subfield(((VariableReferenceExpression) call.getArguments().get(0)).getName(), ImmutableList.of(new Subfield.LongSubscript(((Number) mapKey).longValue())))); + } + if (isVarcharType(arrayElementType)) { + arguments.add(new Subfield(((VariableReferenceExpression) call.getArguments().get(0)).getName(), ImmutableList.of(new Subfield.StringSubscript(((Slice) mapKey).toStringUtf8())))); + } + } + return Optional.of(arguments.build()); + } return Optional.empty(); } @@ -663,38 +691,41 @@ private static NestedField nestedField(String name) private static final class SubfieldExtractor extends DefaultRowExpressionTraversalVisitor { - private final StandardFunctionResolution functionResolution; + private final FunctionResolution functionResolution; private final ExpressionOptimizer expressionOptimizer; private final ConnectorSession connectorSession; private final FunctionAndTypeManager functionAndTypeManager; private final boolean isPushDownSubfieldsFromLambdasEnabled; + private final boolean isPushdownSubfieldsForMapSubsetEnabled; private SubfieldExtractor( - StandardFunctionResolution functionResolution, + FunctionResolution functionResolution, ExpressionOptimizer expressionOptimizer, ConnectorSession connectorSession, FunctionAndTypeManager functionAndTypeManager, - boolean isPushDownSubfieldsFromLambdasEnabled) + Session session) { this.functionResolution = requireNonNull(functionResolution, "functionResolution is null"); this.expressionOptimizer = requireNonNull(expressionOptimizer, "expressionOptimizer is null"); this.connectorSession = connectorSession; this.functionAndTypeManager = requireNonNull(functionAndTypeManager, "functionAndTypeManager is null"); - this.isPushDownSubfieldsFromLambdasEnabled = isPushDownSubfieldsFromLambdasEnabled; + requireNonNull(session); + this.isPushDownSubfieldsFromLambdasEnabled = isPushdownSubfieldsFromArrayLambdasEnabled(session); + this.isPushdownSubfieldsForMapSubsetEnabled = isPushSubfieldsForMapSubsetEnabled(session); } @Override public Void visitCall(CallExpression call, Context context) { ComplexTypeFunctionDescriptor functionDescriptor = functionAndTypeManager.getFunctionMetadata(call.getFunctionHandle()).getDescriptor(); - if (isSubscriptOrElementAtFunction(call, functionResolution, functionAndTypeManager)) { - Optional subfield = toSubfield(call, functionResolution, expressionOptimizer, connectorSession, functionAndTypeManager); + if (isSubscriptOrElementAtFunction(call, functionResolution, functionAndTypeManager) || isMapSubSetWithConstantArray(call, functionResolution)) { + Optional> subfield = toSubfield(call, functionResolution, expressionOptimizer, connectorSession, functionAndTypeManager, isPushdownSubfieldsForMapSubsetEnabled); if (subfield.isPresent()) { if (context.isPruningLambdaSubfieldsPossible()) { - addRequiredLambdaSubfields(context, subfield.get()); + subfield.get().forEach(item -> addRequiredLambdaSubfields(context, item)); } else { - context.subfields.add(subfield.get()); + context.subfields.addAll(subfield.get()); } } else { @@ -847,14 +878,14 @@ else if (specialForm.getForm() != DEREFERENCE) { return null; } - Optional subfield = toSubfield(specialForm, functionResolution, expressionOptimizer, connectorSession, functionAndTypeManager); + Optional> subfield = toSubfield(specialForm, functionResolution, expressionOptimizer, connectorSession, functionAndTypeManager, isPushdownSubfieldsForMapSubsetEnabled); if (subfield.isPresent()) { if (context.isPruningLambdaSubfieldsPossible()) { - addRequiredLambdaSubfields(context, subfield.get()); + subfield.get().forEach(item -> addRequiredLambdaSubfields(context, item)); } else { - context.subfields.add(subfield.get()); + context.subfields.addAll(subfield.get()); } } else { @@ -887,7 +918,7 @@ private void addRequiredLambdaSubfields(Context context, Subfield input) public Void visitVariableReference(VariableReferenceExpression reference, Context context) { if (context.isPruningLambdaSubfieldsPossible()) { - addRequiredLambdaSubfields(context, toSubfield(reference, functionResolution, expressionOptimizer, connectorSession, functionAndTypeManager).get()); + toSubfield(reference, functionResolution, expressionOptimizer, connectorSession, functionAndTypeManager, isPushdownSubfieldsForMapSubsetEnabled).get().forEach(item -> addRequiredLambdaSubfields(context, item)); return null; } context.variables.add(reference); @@ -978,4 +1009,11 @@ private static boolean isSubscriptOrElementAtFunction(CallExpression expression, functionAndTypeManager.getFunctionAndTypeResolver().getFunctionMetadata(expression.getFunctionHandle()).getName() .equals(functionAndTypeManager.getFunctionAndTypeResolver().qualifyObjectName(QualifiedName.of("element_at"))); } + + private static boolean isMapSubSetWithConstantArray(CallExpression expression, FunctionResolution functionResolution) + { + return functionResolution.isMapSubSetFunction(expression.getFunctionHandle()) + && expression.getArguments().get(0) instanceof VariableReferenceExpression + && expression.getArguments().get(1) instanceof ConstantExpression; + } } diff --git a/presto-main-base/src/main/java/com/facebook/presto/sql/relational/FunctionResolution.java b/presto-main-base/src/main/java/com/facebook/presto/sql/relational/FunctionResolution.java index 94b00e4812a25..50a053f03ca1a 100644 --- a/presto-main-base/src/main/java/com/facebook/presto/sql/relational/FunctionResolution.java +++ b/presto-main-base/src/main/java/com/facebook/presto/sql/relational/FunctionResolution.java @@ -426,6 +426,11 @@ public boolean isWindowValueFunction(FunctionHandle functionHandle) return windowValueFunctions.contains(functionAndTypeResolver.getFunctionMetadata(functionHandle).getName()); } + public boolean isMapSubSetFunction(FunctionHandle functionHandle) + { + return functionAndTypeResolver.getFunctionMetadata(functionHandle).getName().equals(functionAndTypeResolver.qualifyObjectName(QualifiedName.of("map_subset"))); + } + @Override public FunctionHandle lookupBuiltInFunction(String functionName, List inputTypes) { diff --git a/presto-main-base/src/test/java/com/facebook/presto/sql/analyzer/TestFeaturesConfig.java b/presto-main-base/src/test/java/com/facebook/presto/sql/analyzer/TestFeaturesConfig.java index db27a9c9a0a07..62fe7a840a9a6 100644 --- a/presto-main-base/src/test/java/com/facebook/presto/sql/analyzer/TestFeaturesConfig.java +++ b/presto-main-base/src/test/java/com/facebook/presto/sql/analyzer/TestFeaturesConfig.java @@ -259,6 +259,7 @@ public void testDefaults() .setExcludeInvalidWorkerSessionProperties(false) .setAddExchangeBelowPartialAggregationOverGroupId(false) .setAddDistinctBelowSemiJoinBuild(false) + .setPushdownSubfieldForMapSubset(true) .setInnerJoinPushdownEnabled(false) .setBroadcastSemiJoinForDelete(true) .setInEqualityJoinPushdownEnabled(false) @@ -473,6 +474,7 @@ public void testExplicitPropertyMappings() .put("expression-optimizer-name", "custom") .put("exclude-invalid-worker-session-properties", "true") .put("optimizer.add-distinct-below-semi-join-build", "true") + .put("optimizer.pushdown-subfield-for-map-subset", "false") .put("optimizer.add-exchange-below-partial-aggregation-over-group-id", "true") .build(); @@ -678,6 +680,7 @@ public void testExplicitPropertyMappings() .setExcludeInvalidWorkerSessionProperties(true) .setAddExchangeBelowPartialAggregationOverGroupId(true) .setAddDistinctBelowSemiJoinBuild(true) + .setPushdownSubfieldForMapSubset(false) .setInEqualityJoinPushdownEnabled(true) .setBroadcastSemiJoinForDelete(false) .setRewriteMinMaxByToTopNEnabled(true) diff --git a/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestQueries.java b/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestQueries.java index 914642b118be8..cd82a34152968 100644 --- a/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestQueries.java +++ b/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestQueries.java @@ -75,6 +75,8 @@ import static com.facebook.presto.SystemSessionProperties.PREFILTER_FOR_GROUPBY_LIMIT_TIMEOUT_MS; import static com.facebook.presto.SystemSessionProperties.PRE_PROCESS_METADATA_CALLS; import static com.facebook.presto.SystemSessionProperties.PULL_EXPRESSION_FROM_LAMBDA_ENABLED; +import static com.facebook.presto.SystemSessionProperties.PUSHDOWN_SUBFIELDS_ENABLED; +import static com.facebook.presto.SystemSessionProperties.PUSHDOWN_SUBFIELDS_FOR_MAP_SUBSET; import static com.facebook.presto.SystemSessionProperties.PUSH_DOWN_FILTER_EXPRESSION_EVALUATION_THROUGH_CROSS_JOIN; import static com.facebook.presto.SystemSessionProperties.PUSH_REMOTE_EXCHANGE_THROUGH_GROUP_ID; import static com.facebook.presto.SystemSessionProperties.QUICK_DISTINCT_LIMIT_ENABLED; @@ -8227,6 +8229,56 @@ public void testMinMaxByToWindowFunction() assertQueryWithSameQueryRunner(enabled, sql, disabled); } + @Test + public void testPushdownSubfieldForMapSubset() + { + Session enabled = Session.builder(getSession()) + .setSystemProperty(PUSHDOWN_SUBFIELDS_FOR_MAP_SUBSET, "true") + .setSystemProperty(PUSHDOWN_SUBFIELDS_ENABLED, "true") + .build(); + Session disabled = Session.builder(getSession()) + .setSystemProperty(PUSHDOWN_SUBFIELDS_FOR_MAP_SUBSET, "false") + .setSystemProperty(PUSHDOWN_SUBFIELDS_ENABLED, "false") + .build(); + @Language("SQL") String sql = "with t as (SELECT * FROM ( VALUES (3, '2025-01-08', MAP(ARRAY[2, 1], ARRAY[0.34, 0.92])), (1, '2025-01-02', MAP(ARRAY[1, 3], ARRAY[0.23, 0.5])), " + + "(7, '2025-01-17', MAP(ARRAY[6, 8], ARRAY[0.60, 0.70])), (2, '2025-01-06', MAP(ARRAY[2, 3, 5, 7], ARRAY[0.75, 0.32, 0.19, 0.46])), " + + "(5, '2025-01-14', MAP(ARRAY[8, 4, 6], ARRAY[0.88, 0.99, 0.00])), (4, '2025-01-12', MAP(ARRAY[7, 3, 2], ARRAY[0.33, 0.44, 0.55])), " + + "(8, '2025-01-20', MAP(ARRAY[1, 7, 6], ARRAY[0.35, 0.45, 0.55])), (6, '2025-01-16', MAP(ARRAY[9, 1, 3], ARRAY[0.30, 0.40, 0.50])), " + + "(2, '2025-01-05', MAP(ARRAY[3, 4], ARRAY[0.98, 0.21])), (1, '2025-01-04', MAP(ARRAY[1, 2], ARRAY[0.45, 0.67])), (7, '2025-01-18', MAP(ARRAY[4, 2, 9], ARRAY[0.80, 0.90, 0.10])), " + + "(3, '2025-01-10', MAP(ARRAY[4, 1, 8, 6], ARRAY[0.85, 0.13, 0.42, 0.91])), (8, '2025-01-19', MAP(ARRAY[3, 5], ARRAY[0.15, 0.25])), " + + "(4, '2025-01-11', MAP(ARRAY[5, 6], ARRAY[0.11, 0.22])), (5, '2025-01-13', MAP(ARRAY[1, 9], ARRAY[0.66, 0.77])), (6, '2025-01-15', MAP(ARRAY[2, 5], ARRAY[0.10, 0.20])) ) " + + "t(id, ds, feature)) select map_subset(feature, array[1, 3, 9]) from t"; + assertQueryWithSameQueryRunner(enabled, sql, disabled); + + sql = "with t as (SELECT * FROM ( VALUES (3, '2025-01-08', MAP(ARRAY[2, 1], ARRAY[0.34, 0.92]), MAP(ARRAY['a', 'b'], ARRAY[0.12, 0.88])), " + + "(1, '2025-01-02', MAP(ARRAY[1, 3], ARRAY[0.23, 0.5]), MAP(ARRAY['x', 'y'], ARRAY[0.45, 0.55])), (7, '2025-01-17', MAP(ARRAY[6, 8], ARRAY[0.60, 0.70]), MAP(ARRAY['m', 'n'], ARRAY[0.21, 0.79])), " + + "(2, '2025-01-06', MAP(ARRAY[2, 3, 5, 7], ARRAY[0.75, 0.32, 0.19, 0.46]), MAP(ARRAY['p', 'q', 'r'], ARRAY[0.11, 0.22, 0.67])), (5, '2025-01-14', MAP(ARRAY[8, 4, 6], ARRAY[0.88, 0.99, 0.00]), MAP(ARRAY['s', 't', 'u'], ARRAY[0.33, 0.44, 0.23])), " + + "(4, '2025-01-12', MAP(ARRAY[7, 3, 2], ARRAY[0.33, 0.44, 0.55]), MAP(ARRAY['v', 'w'], ARRAY[0.66, 0.34])), (8, '2025-01-20', MAP(ARRAY[1, 7, 6], ARRAY[0.35, 0.45, 0.55]), MAP(ARRAY['i', 'j', 'k'], ARRAY[0.78, 0.89, 0.12])), " + + "(6, '2025-01-16', MAP(ARRAY[9, 1, 3], ARRAY[0.30, 0.40, 0.50]), MAP(ARRAY['c', 'd'], ARRAY[0.90, 0.10])), (2, '2025-01-05', MAP(ARRAY[3, 4], ARRAY[0.98, 0.21]), MAP(ARRAY['e', 'f'], ARRAY[0.56, 0.44])), " + + "(1, '2025-01-04', MAP(ARRAY[1, 2], ARRAY[0.45, 0.67]), MAP(ARRAY['g', 'h'], ARRAY[0.23, 0.77])) ) t(id, ds, feature, extra_feature)) " + + "select map_subset(feature, array[-2, 1, 0]), map_subset(extra_feature, array['a', 'x']) from t"; + assertQueryWithSameQueryRunner(enabled, sql, disabled); + + sql = "with t as (SELECT * FROM ( VALUES (3, '2025-01-08', MAP(ARRAY[-2, -1], ARRAY[0.34, 0.92])), (1, '2025-01-02', MAP(ARRAY[1, 3], ARRAY[0.23, 0.5])), " + + "(7, '2025-01-17', MAP(ARRAY[6, 8], ARRAY[0.60, 0.70])), (2, '2025-01-06', MAP(ARRAY[2, 3, 5, 7], ARRAY[0.75, 0.32, 0.19, 0.46])), " + + "(5, '2025-01-14', MAP(ARRAY[-8, 4, 6], ARRAY[0.88, 0.99, 0.00])), (4, '2025-01-12', MAP(ARRAY[7, 3, 2], ARRAY[0.33, 0.44, 0.55])), " + + "(8, '2025-01-20', MAP(ARRAY[-1, 7, 6], ARRAY[0.35, 0.45, 0.55])), (6, '2025-01-16', MAP(ARRAY[9, 1, 3], ARRAY[0.30, 0.40, 0.50])), " + + "(2, '2025-01-05', MAP(ARRAY[3, 4], ARRAY[0.98, 0.21])), (1, '2025-01-04', MAP(ARRAY[1, -2], ARRAY[0.45, 0.67])), (7, '2025-01-18', MAP(ARRAY[4, 2, 9], ARRAY[0.80, 0.90, 0.10])), " + + "(3, '2025-01-10', MAP(ARRAY[4, 1, 8, -6], ARRAY[0.85, 0.13, 0.42, 0.91])), (8, '2025-01-19', MAP(ARRAY[3, 5], ARRAY[0.15, 0.25])), " + + "(4, '2025-01-11', MAP(ARRAY[5, 6], ARRAY[0.11, 0.22])), (5, '2025-01-13', MAP(ARRAY[1, 9], ARRAY[0.66, 0.77])), (6, '2025-01-15', MAP(ARRAY[2, 5], ARRAY[0.10, 0.20])) ) " + + "t(id, ds, feature)) select map_subset(feature, array[-1, -2, 5]) from t"; + assertQueryWithSameQueryRunner(enabled, sql, disabled); + + sql = "with t as (SELECT * FROM ( VALUES (3, '2025-01-08', MAP(ARRAY[-2, 1], ARRAY[0.34, 0.92]), MAP(ARRAY['a', 'b'], ARRAY[0.12, 0.88])), " + + "(1, '2025-01-02', MAP(ARRAY[1, 3], ARRAY[0.23, 0.5]), MAP(ARRAY['x', 'y'], ARRAY[0.45, 0.55])), (7, '2025-01-17', MAP(ARRAY[6, 8], ARRAY[0.60, 0.70]), MAP(ARRAY['m', 'n'], ARRAY[0.21, 0.79])), " + + "(2, '2025-01-06', MAP(ARRAY[2, 3, 5, -7], ARRAY[0.75, 0.32, 0.19, 0.46]), MAP(ARRAY['p', 'q', 'r'], ARRAY[0.11, 0.22, 0.67])), (5, '2025-01-14', MAP(ARRAY[8, 4, 6], ARRAY[0.88, 0.99, 0.00]), MAP(ARRAY['s', 't', 'u'], ARRAY[0.33, 0.44, 0.23])), " + + "(4, '2025-01-12', MAP(ARRAY[7, 3, 2], ARRAY[0.33, 0.44, 0.55]), MAP(ARRAY['v', 'w'], ARRAY[0.66, 0.34])), (8, '2025-01-20', MAP(ARRAY[1, 7, 6], ARRAY[0.35, 0.45, 0.55]), MAP(ARRAY['i', 'j', 'k'], ARRAY[0.78, 0.89, 0.12])), " + + "(6, '2025-01-16', MAP(ARRAY[9, 1, 3], ARRAY[0.30, 0.40, 0.50]), MAP(ARRAY['c', 'd'], ARRAY[0.90, 0.10])), (2, '2025-01-05', MAP(ARRAY[3, 4], ARRAY[0.98, 0.21]), MAP(ARRAY['e', 'f'], ARRAY[0.56, 0.44])), " + + "(1, '2025-01-04', MAP(ARRAY[1, -2], ARRAY[0.45, 0.67]), MAP(ARRAY['g', 'h'], ARRAY[0.23, 0.77])) ) t(id, ds, feature, extra_feature)) " + + "select map_subset(feature, array[-2, 1, 0]), map_subset(extra_feature, array['a', 'x']) from t"; + assertQueryWithSameQueryRunner(enabled, sql, disabled); + } + private List getNativeWorkerSessionProperties(List inputRows, String sessionPropertyName) { return inputRows.stream()