diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/ExpressionPropertiesMap.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/ExpressionPropertiesMap.java
index cae9883950..21b64722d4 100644
--- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/ExpressionPropertiesMap.java
+++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/ExpressionPropertiesMap.java
@@ -285,8 +285,11 @@ public
Map propertyValueForPlans(@Nonnull final Expressi
public static ExpressionPropertiesMap defaultForRewritePhase() {
return new ExpressionPropertiesMap<>(RelationalExpression.class,
ImmutableSet.of(),
- ImmutableSet.of(ExpressionCountProperty.selectCount(), ExpressionCountProperty.tableFunctionCount(),
- PredicateComplexityProperty.predicateComplexity()),
+ ImmutableSet.of(
+ ExpressionCountProperty.selectCount(),
+ ExpressionCountProperty.tableFunctionCount(),
+ PredicateComplexityProperty.predicateComplexity()
+ ),
ImmutableList.of());
}
}
diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/RewritingCostModel.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/RewritingCostModel.java
index 8ebdc95851..14970af183 100644
--- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/RewritingCostModel.java
+++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/RewritingCostModel.java
@@ -24,19 +24,21 @@
import com.apple.foundationdb.annotation.SpotBugsSuppressWarnings;
import com.apple.foundationdb.record.query.plan.RecordQueryPlannerConfiguration;
import com.apple.foundationdb.record.query.plan.cascades.expressions.RelationalExpression;
+import com.apple.foundationdb.record.query.plan.cascades.properties.NormalizedResidualPredicateProperty;
+import com.apple.foundationdb.record.query.plan.cascades.properties.PredicateCountByLevelProperty;
import javax.annotation.Nonnull;
import static com.apple.foundationdb.record.query.plan.cascades.properties.ExpressionCountProperty.selectCount;
import static com.apple.foundationdb.record.query.plan.cascades.properties.ExpressionCountProperty.tableFunctionCount;
-import static com.apple.foundationdb.record.query.plan.cascades.properties.PredicateComplexityProperty.predicateComplexity;
-import static com.apple.foundationdb.record.query.plan.cascades.properties.PredicateHeightProperty.predicateHeight;
+import static com.apple.foundationdb.record.query.plan.cascades.properties.PredicateCountByLevelProperty.predicateCountByLevel;
/**
* Cost model for {@link PlannerPhase#REWRITING}. TODO To be fleshed out whe we have actual rules.
*/
@API(API.Status.EXPERIMENTAL)
@SpotBugsSuppressWarnings("SE_COMPARATOR_SHOULD_BE_SERIALIZABLE")
+@SuppressWarnings("PMD.TooManyStaticImports")
public class RewritingCostModel implements CascadesCostModel {
@Nonnull
private final RecordQueryPlannerConfiguration configuration;
@@ -72,21 +74,26 @@ public int compare(final RelationalExpression a, final RelationalExpression b) {
}
//
- // Pick the expression where predicates have been pushed down as far as they can go
+ // Pick the expression which has the least number of conjuncts in the normalized form of
+ // its combined query predicates.
//
- int aPredicateHeight = predicateHeight().evaluate(a);
- int bPredicateHeight = predicateHeight().evaluate(b);
- if (aPredicateHeight != bPredicateHeight) {
- return Integer.compare(aPredicateHeight, bPredicateHeight);
+ final long aNormalizedConjuncts = NormalizedResidualPredicateProperty.countNormalizedConjuncts(a);
+ final long bNormalizedConjuncts = NormalizedResidualPredicateProperty.countNormalizedConjuncts(b);
+ if (aNormalizedConjuncts != bNormalizedConjuncts) {
+ return Long.compare(aNormalizedConjuncts, bNormalizedConjuncts);
}
//
- // Choose the expression with the simplest predicate.
+ // Pick the expression that has a higher number of query predicates at a deeper level of
+ // the expression tree.
//
- int aPredicateComplexity = predicateComplexity().evaluate(a);
- int bPredicateComplexity = predicateComplexity().evaluate(b);
- if (aPredicateComplexity != bPredicateComplexity) {
- return Integer.compare(aPredicateComplexity, bPredicateComplexity);
+ PredicateCountByLevelProperty.PredicateCountByLevelInfo aPredicateCountByLevel = predicateCountByLevel().evaluate(a);
+ PredicateCountByLevelProperty.PredicateCountByLevelInfo bPredicateCountByLevel = predicateCountByLevel().evaluate(b);
+ final int predicateCountByLevelComparison = PredicateCountByLevelProperty.PredicateCountByLevelInfo.compare(
+ bPredicateCountByLevel, aPredicateCountByLevel);
+ if (predicateCountByLevelComparison != 0) {
+ // The expression that has more predicates at a deeper level wins
+ return predicateCountByLevelComparison;
}
//
diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/properties/PredicateCountByLevelProperty.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/properties/PredicateCountByLevelProperty.java
new file mode 100644
index 0000000000..da840d367a
--- /dev/null
+++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/properties/PredicateCountByLevelProperty.java
@@ -0,0 +1,228 @@
+/*
+ * PredicateCountByLevelProperty.java
+ *
+ * This source file is part of the FoundationDB open source project
+ *
+ * Copyright 2015-2025 Apple Inc. and the FoundationDB project authors
+ *
+ * Licensed 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.
+ */
+
+package com.apple.foundationdb.record.query.plan.cascades.properties;
+
+import com.apple.foundationdb.record.query.plan.cascades.ExpressionProperty;
+import com.apple.foundationdb.record.query.plan.cascades.Reference;
+import com.apple.foundationdb.record.query.plan.cascades.SimpleExpressionVisitor;
+import com.apple.foundationdb.record.query.plan.cascades.expressions.RelationalExpression;
+import com.apple.foundationdb.record.query.plan.cascades.expressions.RelationalExpressionWithPredicates;
+import com.google.common.base.Verify;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSortedMap;
+import com.google.common.collect.Iterables;
+
+import javax.annotation.Nonnull;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.SortedMap;
+import java.util.stream.Collectors;
+
+/**
+ *
+ * This property traverses a {@link RelationalExpression} to find the total number of
+ * {@link com.apple.foundationdb.record.query.plan.cascades.predicates.QueryPredicate}s associated with
+ * {@link RelationalExpressionWithPredicates} implementations at each level of the expression tree.
+ *
+ *
+ * Information about the number of predicates at each level of the tree is encoded in instances of
+ * {@link PredicateCountByLevelInfo}, which can be compared using the method {@link PredicateCountByLevelInfo#compare}
+ * to determine which expression trees contain more predicates at a deeper level.
+ *
+ *
+ * See also {@link com.apple.foundationdb.record.query.plan.cascades.rules.PredicatePushDownRule}.
+ *
+ */
+public class PredicateCountByLevelProperty implements ExpressionProperty {
+ @Nonnull
+ private static final PredicateCountByLevelProperty PREDICATE_COUNT_BY_LEVEL = new PredicateCountByLevelProperty();
+
+ private PredicateCountByLevelProperty() {
+ // prevent outside instantiation
+ }
+
+ /**
+ * Returns the singleton instance of {@link PredicateCountByLevelProperty}.
+ *
+ * @return the singleton instance of {@link PredicateCountByLevelProperty}
+ */
+ @Nonnull
+ public static PredicateCountByLevelProperty predicateCountByLevel() {
+ return PREDICATE_COUNT_BY_LEVEL;
+ }
+
+ /**
+ * Creates a {@link SimpleExpressionVisitor} that can traverse a {@link RelationalExpression}
+ * to find the total number of {@link com.apple.foundationdb.record.query.plan.cascades.predicates.QueryPredicate}
+ * associated with {@link RelationalExpressionWithPredicates} implementations at each level of the expression tree.
+ *
+ * @return a {@link SimpleExpressionVisitor} that produces {@link PredicateCountByLevelInfo} results.
+ */
+ @Nonnull
+ @Override
+ public SimpleExpressionVisitor createVisitor() {
+ return PredicateCountByLevelVisitor.VISITOR;
+ }
+
+ /**
+ * Evaluate this property for over the given {@link RelationalExpression}.
+ *
+ * @param expression The root of the expression tree to traverse.
+ * @return a {@link PredicateCountByLevelInfo} containing the predicate count at each level
+ * of the expression tree.
+ */
+ @Nonnull
+ public PredicateCountByLevelInfo evaluate(RelationalExpression expression) {
+ return Objects.requireNonNull(expression.acceptVisitor(createVisitor()));
+ }
+
+ /**
+ *
+ * An object that contains information about the number of query predicates at each level
+ * of a {@link RelationalExpression} tree. Level numbers in instances of this class
+ * start from 1 for leaf nodes and increase towards the root, with the root node having the highest
+ * level number, which can be retrieved via {@link #getHighestLevel()}.
+ *
+ *
+ * Instances of this class are can be created for a {@link RelationalExpression} using the method
+ * {@link PredicateCountByLevelProperty#evaluate(RelationalExpression)}.
+ *
+ */
+ public static final class PredicateCountByLevelInfo {
+ private final ImmutableSortedMap levelToPredicateCount;
+
+ public PredicateCountByLevelInfo(Map levelToPredicateCount) {
+ this.levelToPredicateCount = ImmutableSortedMap.copyOf(levelToPredicateCount);
+ }
+
+ /**
+ * Combine a list of {@link PredicateCountByLevelInfo} instances by summing the number of query predicates
+ * at the same level across all instances.
+ *
+ * @param heightInfos a collection of {@link PredicateCountByLevelInfo} instances.
+ * @return a new instance of {@link PredicateCountByLevelInfo} with the combined count.
+ */
+ @Nonnull
+ public static PredicateCountByLevelInfo combine(Collection heightInfos) {
+ return new PredicateCountByLevelInfo(heightInfos
+ .stream()
+ .flatMap(heightInfo -> heightInfo.getLevelToPredicateCount().entrySet().stream())
+ .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, Integer::sum)));
+ }
+
+ /**
+ *
+ * Get a view of the query predicate count at each level as a {@link SortedMap}.
+ *
+ *
+ * Level heights, which are the keys of the returned {@link SortedMap}, start from 1 for leaf nodes in the
+ * {@link RelationalExpression} used to create this instance, and increase towards the root, with the root node
+ * having the highest level number.
+ *
+ *
+ * @return a {@link SortedMap} of level heights to the count of query predicates at that level.
+ */
+ @Nonnull
+ public SortedMap getLevelToPredicateCount() {
+ return levelToPredicateCount;
+ }
+
+ /**
+ * Retrieves the highest level height in the {@link RelationalExpression} tree used to create
+ * used to create this instance, which corresponds to the level of the root node of the expression tree.
+ *
+ * @return an integer the height of the highest level number in the tree, or 0 if no levels have been recorded.
+ */
+ public int getHighestLevel() {
+ return levelToPredicateCount.isEmpty() ? 0 : levelToPredicateCount.lastKey();
+ }
+
+ /**
+ *
+ * Compares two {@link PredicateCountByLevelInfo} instances level by level.
+ *
+ *
+ * This comparison is done by comparing the number of
+ * {@link com.apple.foundationdb.record.query.plan.cascades.predicates.QueryPredicate}s at each level recorded
+ * within the provided {@link PredicateCountByLevelInfo} instances, starting from the deepest level (representing
+ * the leaf nodes of the {@link RelationalExpression} tree used to create those instances) to the highest level
+ * (representing the root node) and returns the integer comparison between the first non-equal query predicate
+ * counts. If the number of query predicates is equal at each level, the integer comparison between the highest
+ * level in each {@link PredicateCountByLevelInfo} instance is returned instead.
+ *
+ * @param a the first {@link PredicateCountByLevelInfo} to compare
+ * @param b the second {@link PredicateCountByLevelInfo} to compare
+ *
+ * @return the value {@code 0} if {@code a} have the same number of predicates at each level as {@code b};
+ * a value less than {@code 0} if {@code a} has fewer predicates than {@code b} at a
+ * deeper level or if {@code a} has fewer levels than {@code b}; and
+ * a value greater than {@code 0} if {@code a} has more predicates than {@code b} at a
+ * deeper level or if {@code a} has more levels than {@code b}.
+ */
+ public static int compare(final PredicateCountByLevelInfo a, final PredicateCountByLevelInfo b) {
+ final SortedMap aLevelToPredicateCount = a.getLevelToPredicateCount();
+ final SortedMap bLevelToPredicateCount = b.getLevelToPredicateCount();
+ for (final var entry : aLevelToPredicateCount.entrySet()) {
+ final int aPredicateCountAtLevel = entry.getValue();
+ final int bPredicateCountAtLevel = bLevelToPredicateCount.getOrDefault(entry.getKey(), 0);
+ if (aPredicateCountAtLevel != bPredicateCountAtLevel) {
+ return Integer.compare(aPredicateCountAtLevel, bPredicateCountAtLevel);
+ }
+ }
+ return Integer.compare(a.getHighestLevel(), b.getHighestLevel());
+ }
+ }
+
+ private static final class PredicateCountByLevelVisitor implements SimpleExpressionVisitor {
+ @Nonnull
+ private static final PredicateCountByLevelVisitor VISITOR = new PredicateCountByLevelVisitor();
+
+ private PredicateCountByLevelVisitor() {
+ // prevent outside instantiation
+ }
+
+ @Nonnull
+ @Override
+ public PredicateCountByLevelInfo evaluateAtExpression(@Nonnull final RelationalExpression expression,
+ @Nonnull final List childResults) {
+ final var newLevelToPredicateCountMap = ImmutableMap.builder()
+ .putAll(PredicateCountByLevelInfo.combine(childResults).getLevelToPredicateCount());
+ final var currentLevel = childResults
+ .stream().mapToInt(PredicateCountByLevelInfo::getHighestLevel).max().orElse(0) + 1;
+ final int currentLevelPredicates;
+ if (expression instanceof RelationalExpressionWithPredicates) {
+ currentLevelPredicates = ((RelationalExpressionWithPredicates)expression).getPredicates().size();
+ } else {
+ currentLevelPredicates = 0;
+ }
+ return new PredicateCountByLevelInfo(newLevelToPredicateCountMap.put(currentLevel, currentLevelPredicates).build());
+ }
+
+ @Nonnull
+ @Override
+ public PredicateCountByLevelInfo evaluateAtRef(@Nonnull final Reference reference, @Nonnull final List memberResults) {
+ Verify.verify(memberResults.size() == 1);
+ return Iterables.getOnlyElement(memberResults);
+ }
+ }
+}
diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/properties/PredicateHeightProperty.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/properties/PredicateHeightProperty.java
deleted file mode 100644
index 193253172a..0000000000
--- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/properties/PredicateHeightProperty.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * PredicateHeightProperty.java
- *
- * This source file is part of the FoundationDB open source project
- *
- * Copyright 2015-2025 Apple Inc. and the FoundationDB project authors
- *
- * Licensed 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.
- */
-
-package com.apple.foundationdb.record.query.plan.cascades.properties;
-
-import com.apple.foundationdb.record.query.plan.cascades.ExpressionProperty;
-import com.apple.foundationdb.record.query.plan.cascades.Reference;
-import com.apple.foundationdb.record.query.plan.cascades.SimpleExpressionVisitor;
-import com.apple.foundationdb.record.query.plan.cascades.expressions.RelationalExpression;
-import com.apple.foundationdb.record.query.plan.cascades.expressions.RelationalExpressionVisitor;
-import com.apple.foundationdb.record.query.plan.cascades.expressions.RelationalExpressionWithPredicates;
-import com.google.common.collect.Iterables;
-
-import javax.annotation.Nonnull;
-import java.util.Collection;
-import java.util.List;
-
-public class PredicateHeightProperty implements ExpressionProperty {
- private static final PredicateHeightProperty PREDICATE_HEIGHT = new PredicateHeightProperty();
-
- private PredicateHeightProperty() {
- // prevent outside instantiation
- }
-
- @Nonnull
- @Override
- public RelationalExpressionVisitor createVisitor() {
- return PredicateHeightVisitor.VISITOR;
- }
-
- public int evaluate(RelationalExpression expression) {
- return createVisitor().visit(expression).getPredicateHeight();
- }
-
- @Nonnull
- public static PredicateHeightProperty predicateHeight() {
- return PREDICATE_HEIGHT;
- }
-
- public static final class PredicateHeightInfo {
- private final int height;
- private final int predicateHeight;
-
- private PredicateHeightInfo(int height, int predicateHeight) {
- this.height = height;
- this.predicateHeight = predicateHeight;
- }
-
- public static PredicateHeightInfo combine(Collection extends PredicateHeightInfo> heightInfos) {
- int newHeight = 0;
- int newPredicateHeight = 0;
- for (PredicateHeightInfo heightInfo : heightInfos) {
- newHeight = Math.max(newHeight, heightInfo.height);
- newPredicateHeight = Math.max(newPredicateHeight, heightInfo.predicateHeight);
- }
- return new PredicateHeightInfo(newHeight, newPredicateHeight);
- }
-
- public int getHeight() {
- return height;
- }
-
- public int getPredicateHeight() {
- return predicateHeight;
- }
- }
-
- private static final class PredicateHeightVisitor implements SimpleExpressionVisitor {
- private static final PredicateHeightVisitor VISITOR = new PredicateHeightVisitor();
-
- @Nonnull
- @Override
- public PredicateHeightInfo evaluateAtExpression(@Nonnull final RelationalExpression expression, @Nonnull final List childResults) {
- int newHeight = childResults.stream().mapToInt(PredicateHeightInfo::getHeight).max().orElse(0) + 1;
- if (expression instanceof RelationalExpressionWithPredicates) {
- var predicateExpression = (RelationalExpressionWithPredicates) expression;
- if (!predicateExpression.getPredicates().isEmpty()) {
- return new PredicateHeightInfo(newHeight, newHeight);
- }
- }
- int newPredicateHeight = childResults.stream().mapToInt(PredicateHeightInfo::getPredicateHeight).max().orElse(0);
- return new PredicateHeightInfo(newHeight, newPredicateHeight);
- }
-
- @Nonnull
- @Override
- public PredicateHeightInfo evaluateAtRef(@Nonnull final Reference ref, @Nonnull final List memberResults) {
- if (memberResults.size() == 1) {
- return Iterables.getOnlyElement(memberResults);
- }
- return PredicateHeightInfo.combine(memberResults);
- }
- }
-}
diff --git a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/query/plan/cascades/RewritingCostModelTest.java b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/query/plan/cascades/RewritingCostModelTest.java
new file mode 100644
index 0000000000..a949fc68e1
--- /dev/null
+++ b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/query/plan/cascades/RewritingCostModelTest.java
@@ -0,0 +1,215 @@
+/*
+ * RewritingCostModelTest.java
+ *
+ * This source file is part of the FoundationDB open source project
+ *
+ * Copyright 2015-2025 Apple Inc. and the FoundationDB project authors
+ *
+ * Licensed 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.
+ */
+
+package com.apple.foundationdb.record.query.plan.cascades;
+
+import com.apple.foundationdb.record.query.expressions.Comparisons;
+import com.apple.foundationdb.record.query.plan.RecordQueryPlannerConfiguration;
+import com.apple.foundationdb.record.query.plan.cascades.expressions.SelectExpression;
+import com.apple.foundationdb.record.query.plan.cascades.predicates.AndPredicate;
+import com.apple.foundationdb.record.query.plan.cascades.predicates.ConstantPredicate;
+import com.apple.foundationdb.record.query.plan.cascades.predicates.ExistsPredicate;
+import com.apple.foundationdb.record.query.plan.cascades.predicates.OrPredicate;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+import static com.apple.foundationdb.record.provider.foundationdb.query.FDBQueryGraphTestHelpers.column;
+import static com.apple.foundationdb.record.provider.foundationdb.query.FDBQueryGraphTestHelpers.exists;
+import static com.apple.foundationdb.record.provider.foundationdb.query.FDBQueryGraphTestHelpers.fieldPredicate;
+import static com.apple.foundationdb.record.provider.foundationdb.query.FDBQueryGraphTestHelpers.forEach;
+import static com.apple.foundationdb.record.provider.foundationdb.query.FDBQueryGraphTestHelpers.selectWithPredicates;
+import static com.apple.foundationdb.record.query.plan.cascades.RuleTestHelper.EQUALS_42;
+import static com.apple.foundationdb.record.query.plan.cascades.RuleTestHelper.baseT;
+import static com.apple.foundationdb.record.query.plan.cascades.RuleTestHelper.baseTau;
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+
+class RewritingCostModelTest {
+ /**
+ * Test that the push-down of a single query predicate with other predicates left at the same level is preferred.
+ * The following query:
+ *
+ * {@code
+ * SELECT a FROM sq (SELECT a, b FROM T WHERE a = 42) WHERE EXISTS (SELECT alpha FROM TAU)
+ * }
+ * should be preferred over the query:
+ * {@code
+ * SELECT a FROM sq (SELECT a, b FROM T) WHERE a = 42 AND EXISTS (SELECT alpha FROM TAU)
+ * }
+ *
+ */
+ @Test
+ void costModelsPrefersPushedDownPredicates() {
+ final Quantifier baseQuantifier = baseT();
+
+ final Quantifier innerQuantifierA = forEach(selectWithPredicates(baseQuantifier, List.of("a", "b")));
+ final Quantifier existentialQun = exists(selectWithPredicates(baseTau(), List.of("alpha")));
+ final GraphExpansion.Builder graphABuilder = GraphExpansion.builder().addQuantifier(innerQuantifierA).addQuantifier(existentialQun);
+ graphABuilder.addResultColumn(column(innerQuantifierA, "a", "a"));
+ graphABuilder.addAllPredicates(List.of(
+ fieldPredicate(innerQuantifierA, "a", EQUALS_42),
+ new ExistsPredicate(existentialQun.getAlias())
+ ));
+ final SelectExpression expressionA = graphABuilder.build().buildSelect();
+
+ final Quantifier innerQuantifierB = forEach(selectWithPredicates(
+ baseQuantifier, List.of("a", "b"),
+ fieldPredicate(baseQuantifier, "a", EQUALS_42)
+ ));
+ final GraphExpansion.Builder graphBBuilder = GraphExpansion.builder()
+ .addQuantifier(innerQuantifierB).addQuantifier(existentialQun);
+ graphBBuilder.addResultColumn(column(innerQuantifierB, "a", "a"));
+ graphBBuilder.addAllPredicates(List.of(new ExistsPredicate(existentialQun.getAlias())));
+ final SelectExpression expressionB = graphBBuilder.build().buildSelect();
+
+ assertThat(PlannerPhase.REWRITING
+ .createCostModel(RecordQueryPlannerConfiguration.defaultPlannerConfiguration())
+ .compare(expressionB, expressionA)).isNegative();
+ }
+
+ /**
+ * Test that an expression with a simplified query predicate is preferred.
+ * The following query:
+ *
{@code
+ * SELECT a FROM T WHERE a = 42
+ * }
+ *
+ * should be preferred over the query:
+ *
+ * {@code
+ * SELECT a FROM T WHERE a = 42 and a = 42
+ * }
+ */
+ @Test
+ void costModelsPrefersSimplifiedPredicates() {
+ final Quantifier baseQuantifier = baseT();
+ final GraphExpansion.Builder graphABuilder = GraphExpansion.builder().addQuantifier(baseQuantifier);
+ graphABuilder.addResultColumn(column(baseQuantifier, "a", "a"));
+ graphABuilder.addAllPredicates(List.of(
+ fieldPredicate(baseQuantifier, "a", EQUALS_42),
+ fieldPredicate(baseQuantifier, "a", EQUALS_42)
+ ));
+ final SelectExpression expressionA = graphABuilder.build().buildSelect();
+
+ final GraphExpansion.Builder graphBBuilder = GraphExpansion.builder().addQuantifier(baseQuantifier);
+ graphBBuilder.addResultColumn(column(baseQuantifier, "b", "b"));
+ graphBBuilder.addAllPredicates(List.of(fieldPredicate(baseQuantifier, "a", EQUALS_42)));
+ final SelectExpression expressionB = graphBBuilder.build().buildSelect();
+
+ assertThat(PlannerPhase.REWRITING
+ .createCostModel(RecordQueryPlannerConfiguration.defaultPlannerConfiguration())
+ .compare(expressionB, expressionA)).isNegative();
+ }
+
+ /**
+ * Test that an expression with a tautology query predicate is preferred.
+ * The following query:
+ * {@code
+ * SELECT a FROM T WHERE false
+ * }
+ *
+ * should be preferred over the query:
+ *
+ * {@code
+ * SELECT a FROM T WHERE a IS NULL
+ * }
+ */
+ @Test
+ void costModelPrefersEliminatedPredicates() {
+ final Quantifier baseQuantifier = baseT();
+ final GraphExpansion.Builder graphABuilder = GraphExpansion.builder().addQuantifier(baseQuantifier);
+ graphABuilder.addResultColumn(column(baseQuantifier, "a", "a"));
+ graphABuilder.addAllPredicates(List.of(
+ fieldPredicate(baseQuantifier, "a", new Comparisons.NullComparison(Comparisons.Type.IS_NULL))
+ ));
+ final SelectExpression expressionA = graphABuilder.build().buildSelect();
+
+ final GraphExpansion.Builder graphBBuilder = GraphExpansion.builder().addQuantifier(baseQuantifier);
+ graphBBuilder.addResultColumn(column(baseQuantifier, "b", "b"));
+ graphBBuilder.addAllPredicates(List.of(ConstantPredicate.FALSE));
+ final SelectExpression expressionB = graphBBuilder.build().buildSelect();
+
+ assertThat(PlannerPhase.REWRITING
+ .createCostModel(RecordQueryPlannerConfiguration.defaultPlannerConfiguration())
+ .compare(expressionB, expressionA)).isNegative();
+ }
+
+ /**
+ * Test that an expression with a simplified query predicate at a deeper level is preferred.
+ * The following query:
+ *
+ * {@code
+ * SELECT a FROM sq (SELECT a, b FROM T WHERE a = 42) WHERE EXISTS (SELECT alpha FROM TAU)
+ * }
+ * should be preferred over the query:
+ * {@code
+ * SELECT a
+ * FROM sq (
+ * SELECT a, b
+ * FROM T
+ * WHERE a = 42 OR (a = 43 AND false) OR (a is null AND false)
+ * )
+ * WHERE EXISTS (SELECT alpha FROM TAU).
+ * }
+ *
+ */
+ @Test
+ void costModelPrefersSimplifiedPredicatesAtDeeperLevels() {
+ final Quantifier baseQuantifier = baseT();
+ final Quantifier innerQuantifierA = forEach(selectWithPredicates(
+ baseQuantifier, List.of("a", "b"),
+ OrPredicate.or(
+ fieldPredicate(baseQuantifier, "a", EQUALS_42),
+ AndPredicate.and(
+ fieldPredicate(baseQuantifier, "a",
+ new Comparisons.SimpleComparison(Comparisons.Type.EQUALS, 43L)),
+ ConstantPredicate.FALSE
+ ),
+ AndPredicate.and(
+ fieldPredicate(baseQuantifier, "a",
+ new Comparisons.SimpleComparison(Comparisons.Type.EQUALS, 44L)),
+ ConstantPredicate.FALSE
+ )
+ )
+ ));
+ final Quantifier existentialQun = exists(selectWithPredicates(baseTau(), List.of("alpha")));
+ final GraphExpansion.Builder graphABuilder = GraphExpansion.builder().addQuantifier(innerQuantifierA).addQuantifier(existentialQun);
+ graphABuilder.addResultColumn(column(innerQuantifierA, "a", "a"));
+ graphABuilder.addAllPredicates(List.of(
+ fieldPredicate(innerQuantifierA, "a", EQUALS_42),
+ new ExistsPredicate(existentialQun.getAlias())
+ ));
+ final SelectExpression expressionA = graphABuilder.build().buildSelect();
+
+ final Quantifier innerQuantifierB = forEach(selectWithPredicates(
+ baseQuantifier, List.of("a", "b"),
+ fieldPredicate(baseQuantifier, "a", EQUALS_42)
+ ));
+ final GraphExpansion.Builder graphBBuilder = GraphExpansion.builder()
+ .addQuantifier(innerQuantifierB).addQuantifier(existentialQun);
+ graphBBuilder.addResultColumn(column(innerQuantifierB, "a", "a"));
+ graphBBuilder.addAllPredicates(List.of(new ExistsPredicate(existentialQun.getAlias())));
+ final SelectExpression expressionB = graphBBuilder.build().buildSelect();
+
+ assertThat(PlannerPhase.REWRITING
+ .createCostModel(RecordQueryPlannerConfiguration.defaultPlannerConfiguration())
+ .compare(expressionB, expressionA)).isNegative();
+ }
+}
diff --git a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/query/plan/cascades/properties/PredicateCountByLevelPropertyTest.java b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/query/plan/cascades/properties/PredicateCountByLevelPropertyTest.java
new file mode 100644
index 0000000000..eefd60b417
--- /dev/null
+++ b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/query/plan/cascades/properties/PredicateCountByLevelPropertyTest.java
@@ -0,0 +1,209 @@
+/*
+ * PredicateCountByLevelPropertyTest.java
+ *
+ * This source file is part of the FoundationDB open source project
+ *
+ * Copyright 2015-2025 Apple Inc. and the FoundationDB project authors
+ *
+ * Licensed 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.
+ */
+
+package com.apple.foundationdb.record.query.plan.cascades.properties;
+
+import com.apple.foundationdb.record.query.plan.cascades.GraphExpansion;
+import com.apple.foundationdb.record.query.plan.cascades.Quantifier;
+import com.apple.foundationdb.record.query.plan.cascades.expressions.SelectExpression;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import org.junit.jupiter.api.Test;
+
+import java.util.Map;
+
+import static com.apple.foundationdb.record.provider.foundationdb.query.FDBQueryGraphTestHelpers.column;
+import static com.apple.foundationdb.record.provider.foundationdb.query.FDBQueryGraphTestHelpers.fieldPredicate;
+import static com.apple.foundationdb.record.provider.foundationdb.query.FDBQueryGraphTestHelpers.forEach;
+import static com.apple.foundationdb.record.provider.foundationdb.query.FDBQueryGraphTestHelpers.selectWithPredicates;
+import static com.apple.foundationdb.record.query.plan.cascades.RuleTestHelper.EQUALS_42;
+import static com.apple.foundationdb.record.query.plan.cascades.RuleTestHelper.baseT;
+import static com.apple.foundationdb.record.query.plan.cascades.RuleTestHelper.baseTau;
+import static com.apple.foundationdb.record.query.plan.cascades.properties.PredicateCountByLevelProperty.predicateCountByLevel;
+import static org.assertj.core.api.Assertions.assertThat;
+
+class PredicateCountByLevelPropertyTest {
+ /**
+ * Test that a simple expression with one predicate is evaluated correctly.
+ * Query:
+ *
+ * {@code
+ * SELECT a FROM T WHERE a = 42
+ * }
+ */
+ @Test
+ void selectWithOnePredicateIsEvaluatedCorrectly() {
+ final Quantifier baseQuantifier = baseT();
+ final GraphExpansion.Builder graphBuilder = GraphExpansion.builder().addQuantifier(baseQuantifier);
+ graphBuilder.addResultColumn(column(baseQuantifier, "a", "a"));
+ graphBuilder.addAllPredicates(ImmutableList.of(fieldPredicate(baseQuantifier, "a", EQUALS_42)));
+ final SelectExpression expression = graphBuilder.build().buildSelect();
+
+ final var info = predicateCountByLevel().evaluate(expression);
+
+ assertThat(info.getLevelToPredicateCount().lastKey()).isEqualTo(3);
+ assertThat(info.getLevelToPredicateCount()).containsExactly(
+ Map.entry(1, 0), // corresponds to the level with the FullUnorderedScanExpression
+ Map.entry(2, 0), // corresponds to the level with the LogicalTypeFilterExpression
+ Map.entry(3, 1)
+ );
+ }
+
+ /**
+ * Test that expressions with predicates at different levels are evaluated correctly.
+ * Query:
+ *
+ * {@code
+ * SELECT a
+ * FROM
+ * (
+ * SELECT a, b
+ * FROM (SELECT a, b FROM T WHERE a = 42) isq
+ * WHERE a = 42 AND b = 42
+ * ) sq
+ * WHERE
+ * sq.b = 42
+ * }
+ */
+ @Test
+ void predicatesAtMultipleLevelsAreCountedCorrectly() {
+ final Quantifier baseQuantifier = baseT();
+ final Quantifier isqQuantifier = forEach(selectWithPredicates(
+ baseQuantifier, ImmutableList.of("a", "b"),
+ fieldPredicate(baseQuantifier, "a", EQUALS_42)
+ ));
+ final Quantifier sqQuantifier = forEach(selectWithPredicates(
+ isqQuantifier, ImmutableList.of("a", "b"),
+ fieldPredicate(baseQuantifier, "a", EQUALS_42),
+ fieldPredicate(baseQuantifier, "b", EQUALS_42)
+ ));
+ final GraphExpansion.Builder graphBuilder = GraphExpansion.builder().addQuantifier(sqQuantifier);
+ graphBuilder.addResultColumn(column(sqQuantifier, "a", "a"));
+ graphBuilder.addAllPredicates(ImmutableList.of(fieldPredicate(sqQuantifier, "b", EQUALS_42)));
+ final SelectExpression expression = graphBuilder.build().buildSelect();
+
+ final var info = predicateCountByLevel().evaluate(expression);
+
+ // Should have 2 predicates total (1 at each level)
+ assertThat(info.getLevelToPredicateCount().lastKey()).isEqualTo(5);
+ assertThat(info.getLevelToPredicateCount()).containsExactly(
+ Map.entry(1, 0), // corresponds to the level with the FullUnorderedScanExpression
+ Map.entry(2, 0), // corresponds to the level with the LogicalTypeFilterExpression
+ Map.entry(3, 1),
+ Map.entry(4, 2),
+ Map.entry(5, 1)
+ );
+ }
+
+ /**
+ * Test that predicate counts for different expressions at the same level are added.
+ * Query:
+ *
+ * {@code
+ * SELECT
+ * sq1.a
+ * FROM
+ * (SELECT a, b FROM T WHERE a = 42 AND b = 42) sq1,
+ * (SELECT alpha from TAU WHERE alpha = 42) sq2
+ * WHERE
+ * sq1.b = 42
+ * }
+ */
+ @Test
+ void predicatesAtTheSameLevelForDifferentExpressionsAreCountedCorrectly() {
+ final Quantifier sq1BaseQuantifier = baseT();
+ final Quantifier sq2BaseQuantifier = baseTau();
+ final Quantifier sq1Quantifier = forEach(selectWithPredicates(
+ sq1BaseQuantifier, ImmutableList.of("a", "b"),
+ fieldPredicate(sq1BaseQuantifier, "a", EQUALS_42),
+ fieldPredicate(sq1BaseQuantifier, "b", EQUALS_42)
+ ));
+ final Quantifier sq2Quantifier = forEach(selectWithPredicates(
+ sq2BaseQuantifier, ImmutableList.of("alpha"),
+ fieldPredicate(sq2BaseQuantifier, "alpha", EQUALS_42)
+ ));
+
+ final GraphExpansion.Builder graphBuilder = GraphExpansion.builder().addQuantifier(sq1Quantifier).addQuantifier(sq2Quantifier);
+ graphBuilder.addResultColumn(column(sq1Quantifier, "a", "a"));
+ graphBuilder.addAllPredicates(ImmutableList.of(fieldPredicate(sq1Quantifier, "b", EQUALS_42)));
+ final SelectExpression expression = graphBuilder.build().buildSelect();
+
+ final var info = predicateCountByLevel().evaluate(expression);
+
+ assertThat(info.getLevelToPredicateCount().lastKey()).isEqualTo(4);
+ assertThat(info.getLevelToPredicateCount()).containsExactly(
+ Map.entry(1, 0), // corresponds to the level with the FullUnorderedScanExpression
+ Map.entry(2, 0), // corresponds to the level with the LogicalTypeFilterExpression
+ Map.entry(3, 3),
+ Map.entry(4, 1)
+ );
+ }
+
+ @Test
+ void predicateCountByLevelInfoInstancesAreCombinedCorrectly() {
+ final var aInfo = new PredicateCountByLevelProperty.PredicateCountByLevelInfo(
+ ImmutableMap.of(1, 1, 2, 1, 3, 2)
+ );
+ final var bInfo = new PredicateCountByLevelProperty.PredicateCountByLevelInfo(
+ ImmutableMap.of(1, 0, 2, 1, 3, 1, 4, 4)
+ );
+
+ final var combinedInfo =
+ PredicateCountByLevelProperty.PredicateCountByLevelInfo.combine(ImmutableList.of(aInfo, bInfo));
+
+ assertThat(combinedInfo.getLevelToPredicateCount().lastKey()).isEqualTo(4);
+ assertThat(combinedInfo.getLevelToPredicateCount()).containsExactly(
+ Map.entry(1, 1),
+ Map.entry(2, 2),
+ Map.entry(3, 3),
+ Map.entry(4, 4)
+ );
+ }
+
+ @Test
+ void compareReturnsComparisonBetweenFirstNonEqualLevel() {
+ final PredicateCountByLevelProperty.PredicateCountByLevelInfo aInfo =
+ new PredicateCountByLevelProperty.PredicateCountByLevelInfo(ImmutableMap.of(1, 1, 2, 3, 3, 1));
+ final PredicateCountByLevelProperty.PredicateCountByLevelInfo bInfo =
+ new PredicateCountByLevelProperty.PredicateCountByLevelInfo(ImmutableMap.of(1, 1, 2, 2, 3, 1));
+
+ assertThat(PredicateCountByLevelProperty.PredicateCountByLevelInfo.compare(aInfo, bInfo)).isPositive();
+ }
+
+ @Test
+ void compareReturnsInfoWithMoreLevelsInCaseOfEquality() {
+ final PredicateCountByLevelProperty.PredicateCountByLevelInfo aInfo =
+ new PredicateCountByLevelProperty.PredicateCountByLevelInfo(ImmutableMap.of(1, 1, 2, 3, 3, 1));
+ final PredicateCountByLevelProperty.PredicateCountByLevelInfo bInfo =
+ new PredicateCountByLevelProperty.PredicateCountByLevelInfo(ImmutableMap.of(1, 1, 2, 3, 3, 1, 4, 1));
+
+ assertThat(PredicateCountByLevelProperty.PredicateCountByLevelInfo.compare(aInfo, bInfo)).isNegative();
+ }
+
+ @Test
+ void compareReturnsZeroForEqualPredicateCounts() {
+ final PredicateCountByLevelProperty.PredicateCountByLevelInfo aInfo =
+ new PredicateCountByLevelProperty.PredicateCountByLevelInfo(ImmutableMap.of(1, 1, 2, 3, 3, 1, 4, 1));
+ final PredicateCountByLevelProperty.PredicateCountByLevelInfo bInfo =
+ new PredicateCountByLevelProperty.PredicateCountByLevelInfo(ImmutableMap.of(1, 1, 2, 3, 3, 1, 4, 1));
+
+ assertThat(PredicateCountByLevelProperty.PredicateCountByLevelInfo.compare(aInfo, bInfo)).isZero();
+ }
+}
diff --git a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/query/plan/cascades/rules/PredicatePushDownRuleTest.java b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/query/plan/cascades/rules/PredicatePushDownRuleTest.java
index 2d40afaa52..2abf1fab2f 100644
--- a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/query/plan/cascades/rules/PredicatePushDownRuleTest.java
+++ b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/query/plan/cascades/rules/PredicatePushDownRuleTest.java
@@ -23,6 +23,7 @@
import com.apple.foundationdb.record.provider.foundationdb.query.FDBQueryGraphTestHelpers;
import com.apple.foundationdb.record.query.expressions.Comparisons;
import com.apple.foundationdb.record.query.plan.cascades.AliasMap;
+import com.apple.foundationdb.record.query.plan.cascades.GraphExpansion;
import com.apple.foundationdb.record.query.plan.cascades.OrderingPart;
import com.apple.foundationdb.record.query.plan.cascades.PlannerStage;
import com.apple.foundationdb.record.query.plan.cascades.Quantifier;
@@ -128,6 +129,53 @@ void pushDownSimplePredicate() {
testHelper.assertYields(higher, newHigher);
}
+ /**
+ * Test a simple rewrite of a single parameter predicate with the existence of other predicates. It should go from:
+ * {@code
+ * SELECT a FROM (SELECT a, b, d FROM T) WHERE a = 42 AND EXISTS (SELECT alpha FROM TAU)
+ * }
+ *
+ * And become:
+ *
+ * {@code
+ * SELECT a FROM (SELECT a, b FROM T WHERE a = 42) WHERE EXISTS (SELECT alpha FROM TAU)
+ * }
+ */
+ @Test
+ void pushDownOnePredicateOfMultiple() {
+ Quantifier baseQun = baseT();
+
+ Quantifier lowerQun = forEach(selectWithPredicates(
+ baseQun, ImmutableList.of("a", "b")
+ ));
+ Quantifier existentialQun = exists(selectWithPredicates(
+ baseTau(), ImmutableList.of("alpha")
+ ));
+
+ GraphExpansion.Builder builder = GraphExpansion.builder().addQuantifier(lowerQun).addQuantifier(existentialQun);
+ builder.addResultColumn(column(lowerQun, "b", "b"));
+ builder.addAllPredicates(ImmutableList.of(
+ fieldPredicate(lowerQun, "a", EQUALS_42),
+ new ExistsPredicate(existentialQun.getAlias())
+ ));
+ SelectExpression higher = builder.build().buildSelect();
+
+
+ Quantifier newLowerQun = forEach(selectWithPredicates(
+ baseQun, ImmutableList.of("a", "b"),
+ fieldPredicate(baseQun, "a", EQUALS_42)
+ ));
+
+ GraphExpansion.Builder newBuilder = GraphExpansion.builder().addQuantifier(newLowerQun).addQuantifier(existentialQun);
+ newBuilder.addResultColumn(column(newLowerQun, "b", "b"));
+ newBuilder.addAllPredicates(ImmutableList.of(
+ new ExistsPredicate(existentialQun.getAlias())
+ ));
+ SelectExpression newHigher = newBuilder.build().buildSelect();
+
+ testHelper.assertYields(higher, newHigher);
+ }
+
@Test
void pushSimplePredicateIntoLogicalFilter() {
Quantifier baseQun = baseT();
diff --git a/yaml-tests/src/test/resources/nested-with-nulls-proto.metrics.binpb b/yaml-tests/src/test/resources/nested-with-nulls-proto.metrics.binpb
index 973695d9bf..81e9ba7fce 100644
--- a/yaml-tests/src/test/resources/nested-with-nulls-proto.metrics.binpb
+++ b/yaml-tests/src/test/resources/nested-with-nulls-proto.metrics.binpb
@@ -1,7 +1,8 @@
N
nested-with-nulls-proto-tests-EXPLAIN select id from t1 where a.a.a IS NULL
-م@ I(08@+SCAN(<,>) | FILTER false | MAP (_.ID AS ID)digraph G {
+@ F(0
+8@+SCAN(<,>) | FILTER false | MAP (_.ID AS ID)digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -15,7 +16,7 @@ N
}
N
nested-with-nulls-proto-tests-EXPLAIN select id from t1 where a.a.b IS NULL
-; 1(0љ8@5SCAN(<,>) | FILTER _.A.A.B IS_NULL | MAP (_.ID AS ID)digraph G {
+; z(08@5SCAN(<,>) | FILTER _.A.A.B IS_NULL | MAP (_.ID AS ID)digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -29,7 +30,7 @@ N
}
N
nested-with-nulls-proto-tests-EXPLAIN select id from t1 where a.b.a IS NULL
-; O(08@5SCAN(<,>) | FILTER _.A.B.A IS_NULL | MAP (_.ID AS ID)digraph G {
+ȟ; ҍT(08@5SCAN(<,>) | FILTER _.A.B.A IS_NULL | MAP (_.ID AS ID)digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -43,7 +44,7 @@ N
}
N
nested-with-nulls-proto-tests-EXPLAIN select id from t1 where a.b.b IS NULL
-; 1(08@5SCAN(<,>) | FILTER _.A.B.B IS_NULL | MAP (_.ID AS ID)digraph G {
+ǽ; :(08@5SCAN(<,>) | FILTER _.A.B.B IS_NULL | MAP (_.ID AS ID)digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -57,7 +58,7 @@ N
}
N
nested-with-nulls-proto-tests-EXPLAIN select id from t1 where b.a.a IS NULL
-ֺ; +(08@5SCAN(<,>) | FILTER _.B.A.A IS_NULL | MAP (_.ID AS ID)digraph G {
+; y(0
8@5SCAN(<,>) | FILTER _.B.A.A IS_NULL | MAP (_.ID AS ID)digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -71,7 +72,7 @@ N
}
N
nested-with-nulls-proto-tests-EXPLAIN select id from t1 where b.a.b IS NULL
-ϫ; F(0 8@5SCAN(<,>) | FILTER _.B.A.B IS_NULL | MAP (_.ID AS ID)digraph G {
+; ](08@5SCAN(<,>) | FILTER _.B.A.B IS_NULL | MAP (_.ID AS ID)digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -85,7 +86,7 @@ N
}
N
nested-with-nulls-proto-tests-EXPLAIN select id from t1 where b.b.a IS NULL
-; `(08@5SCAN(<,>) | FILTER _.B.B.A IS_NULL | MAP (_.ID AS ID)digraph G {
+Ï; ͛u(0
8@5SCAN(<,>) | FILTER _.B.B.A IS_NULL | MAP (_.ID AS ID)digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -96,10 +97,10 @@ N
3 -> 2 [ label=< q2> label="q2" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
4 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
2 -> 1 [ label=< q22> label="q22" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
-}
+}
N
-nested-with-nulls-proto-tests-EXPLAIN select id from t1 where b.b.b IS NULL
-; ޛ(0Ü8@5SCAN(<,>) | FILTER _.B.B.B IS_NULL | MAP (_.ID AS ID)digraph G {
+nested-with-nulls-proto-tests-EXPLAIN select id from t1 where b.b.b IS NULL
+Մ; 0(08@5SCAN(<,>) | FILTER _.B.B.B IS_NULL | MAP (_.ID AS ID)digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -113,7 +114,8 @@ N
}
R
nested-with-nulls-proto-tests1EXPLAIN select id from t1 where a.a.a IS NOT NULL
-@ i(0ŷ8@SCAN(<,>) | MAP (_.ID AS ID)digraph G {
+쭝@ Q(0
+8@SCAN(<,>) | MAP (_.ID AS ID)digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -125,7 +127,7 @@ R
}
R
nested-with-nulls-proto-tests1EXPLAIN select id from t1 where a.a.b IS NOT NULL
-; m(08@6SCAN(<,>) | FILTER _.A.A.B NOT_NULL | MAP (_.ID AS ID)digraph G {
+; A(0 8@6SCAN(<,>) | FILTER _.A.A.B NOT_NULL | MAP (_.ID AS ID)digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -136,10 +138,10 @@ R
3 -> 2 [ label=< q2> label="q2" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
4 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
2 -> 1 [ label=< q22> label="q22" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
-}
+}
R
-nested-with-nulls-proto-tests1EXPLAIN select id from t1 where a.b.a IS NOT NULL
-; h(0
8@6SCAN(<,>) | FILTER _.A.B.A NOT_NULL | MAP (_.ID AS ID)digraph G {
+nested-with-nulls-proto-tests1EXPLAIN select id from t1 where a.b.a IS NOT NULL
+{; ֎.(08@6SCAN(<,>) | FILTER _.A.B.A NOT_NULL | MAP (_.ID AS ID)digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -150,10 +152,10 @@ R
3 -> 2 [ label=< q2> label="q2" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
4 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
2 -> 1 [ label=< q22> label="q22" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
-}
+}
R
-nested-with-nulls-proto-tests1EXPLAIN select id from t1 where a.b.b IS NOT NULL
-; (08@6SCAN(<,>) | FILTER _.A.B.B NOT_NULL | MAP (_.ID AS ID)digraph G {
+nested-with-nulls-proto-tests1EXPLAIN select id from t1 where a.b.b IS NOT NULL
+֙; I(0Ӂ 8@6SCAN(<,>) | FILTER _.A.B.B NOT_NULL | MAP (_.ID AS ID)digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -167,7 +169,7 @@ R
}
R
nested-with-nulls-proto-tests1EXPLAIN select id from t1 where b.a.a IS NOT NULL
-̔; 2(08@6SCAN(<,>) | FILTER _.B.A.A NOT_NULL | MAP (_.ID AS ID)digraph G {
+ԇ; 6(08@6SCAN(<,>) | FILTER _.B.A.A NOT_NULL | MAP (_.ID AS ID)digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -181,7 +183,7 @@ R
}
R
nested-with-nulls-proto-tests1EXPLAIN select id from t1 where b.a.b IS NOT NULL
-; g(08@6SCAN(<,>) | FILTER _.B.A.B NOT_NULL | MAP (_.ID AS ID)digraph G {
+Ѥ; P(08@6SCAN(<,>) | FILTER _.B.A.B NOT_NULL | MAP (_.ID AS ID)digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -192,10 +194,10 @@ R
3 -> 2 [ label=< q2> label="q2" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
4 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
2 -> 1 [ label=< q22> label="q22" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
-}
+}
R
-nested-with-nulls-proto-tests1EXPLAIN select id from t1 where b.b.a IS NOT NULL
-; \(0 8@6SCAN(<,>) | FILTER _.B.B.A NOT_NULL | MAP (_.ID AS ID)digraph G {
+nested-with-nulls-proto-tests1EXPLAIN select id from t1 where b.b.a IS NOT NULL
+^; )(08@6SCAN(<,>) | FILTER _.B.B.A NOT_NULL | MAP (_.ID AS ID)digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -206,10 +208,10 @@ R
3 -> 2 [ label=< q2> label="q2" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
4 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
2 -> 1 [ label=< q22> label="q22" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
-}
+}
R
-nested-with-nulls-proto-tests1EXPLAIN select id from t1 where b.b.b IS NOT NULL
-; (08@6SCAN(<,>) | FILTER _.B.B.B NOT_NULL | MAP (_.ID AS ID)digraph G {
+nested-with-nulls-proto-tests1EXPLAIN select id from t1 where b.b.b IS NOT NULL
+; =(08@6SCAN(<,>) | FILTER _.B.B.B NOT_NULL | MAP (_.ID AS ID)digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -220,10 +222,10 @@ R
3 -> 2 [ label=< q2> label="q2" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
4 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
2 -> 1 [ label=< q22> label="q22" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
-}
+}
_
-nested-with-nulls-proto-tests>EXPLAIN select id from t1 where coalesce(a.a.a, 'blah') = 'a1'
-; :(08@PSCAN(<,>) | FILTER coalesce_string(_.A.A.A, @c14) EQUALS @c17 | MAP (_.ID AS ID)digraph G {
+nested-with-nulls-proto-tests>EXPLAIN select id from t1 where coalesce(a.a.a, 'blah') = 'a1'
+ȷ; (08@PSCAN(<,>) | FILTER coalesce_string(_.A.A.A, @c14) EQUALS @c17 | MAP (_.ID AS ID)digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -234,10 +236,10 @@ _
3 -> 2 [ label=< q2> label="q2" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
4 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
2 -> 1 [ label=< q22> label="q22" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
-}
+}
`
-nested-with-nulls-proto-tests?EXPLAIN select id from t1 where coalesce(a.a.a, 'blah') = 'a1p'
-; :(08@PSCAN(<,>) | FILTER coalesce_string(_.A.A.A, @c14) EQUALS @c17 | MAP (_.ID AS ID)digraph G {
+nested-with-nulls-proto-tests?EXPLAIN select id from t1 where coalesce(a.a.a, 'blah') = 'a1p'
+ȷ; (08@PSCAN(<,>) | FILTER coalesce_string(_.A.A.A, @c14) EQUALS @c17 | MAP (_.ID AS ID)digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -248,38 +250,34 @@ _
3 -> 2 [ label=< q2> label="q2" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
4 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
2 -> 1 [ label=< q22> label="q22" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
-}
+}
d
-nested-with-nulls-proto-testsCEXPLAIN select id from t1 where coalesce(a.a.a, 'blah') IS NOT NULL
-ڥ@ {(08@MSCAN(<,>) | FILTER coalesce_string(_.A.A.A, @c14) NOT_NULL | MAP (_.ID AS ID)digraph G {
+nested-with-nulls-proto-testsCEXPLAIN select id from t1 where coalesce(a.a.a, 'blah') IS NOT NULL
+@ n(08@SCAN(<,>) | MAP (_.ID AS ID)digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
- 1 [ label=<| Value Computation |
| MAP (q23.ID AS ID) |
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS ID)" ];
- 2 [ label=<| Predicate Filter |
| WHERE coalesce_string(q2.A.A.A, @c14) NOT_NULL |
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS ID, STRING AS A, LONG AS B AS A, STRING AS A, LONG AS B AS B AS A, STRING AS A, LONG AS B AS A, STRING AS A, LONG AS B AS B AS B)" ];
- 3 [ label=<> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS ID, STRING AS A, LONG AS B AS A, STRING AS A, LONG AS B AS B AS A, STRING AS A, LONG AS B AS A, STRING AS A, LONG AS B AS B AS B)" ];
- 4 [ label=<| Primary Storage |
| record types: [T1] |
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS ID, STRING AS A, LONG AS B AS A, STRING AS A, LONG AS B AS B AS A, STRING AS A, LONG AS B AS A, STRING AS A, LONG AS B AS B AS B)" ];
- 3 -> 2 [ label=< q2> label="q2" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
- 4 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
- 2 -> 1 [ label=< q23> label="q23" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
-}
+ 1 [ label=<| Value Computation |
| MAP (q2.ID AS ID) |
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS ID)" ];
+ 2 [ label=<> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS ID, STRING AS A, LONG AS B AS A, STRING AS A, LONG AS B AS B AS A, STRING AS A, LONG AS B AS A, STRING AS A, LONG AS B AS B AS B)" ];
+ 3 [ label=<| Primary Storage |
| record types: [T1] |
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS ID, STRING AS A, LONG AS B AS A, STRING AS A, LONG AS B AS B AS A, STRING AS A, LONG AS B AS A, STRING AS A, LONG AS B AS B AS B)" ];
+ 3 -> 2 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
+ 2 -> 1 [ label=< q2> label="q2" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
+}
b
-nested-with-nulls-proto-testsAEXPLAIN select id from t1 where coalesce(a.a.a, null) IS NOT NULL
-ب@ m(0̖8@`SCAN(<,>) | FILTER coalesce_string(promote(_.A.A.A AS STRING), NULL) NOT_NULL | MAP (_.ID AS ID)digraph G {
+nested-with-nulls-proto-testsAEXPLAIN select id from t1 where coalesce(a.a.a, null) IS NOT NULL
+@ Y(08@SCAN(<,>) | MAP (_.ID AS ID)digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
- 1 [ label=<| Value Computation |
| MAP (q23.ID AS ID) |
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS ID)" ];
- 2 [ label=<| Predicate Filter |
| WHERE coalesce_string(promote(q2.A.A.A AS STRING), NULL) NOT_NULL |
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS ID, STRING AS A, LONG AS B AS A, STRING AS A, LONG AS B AS B AS A, STRING AS A, LONG AS B AS A, STRING AS A, LONG AS B AS B AS B)" ];
- 3 [ label=<> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS ID, STRING AS A, LONG AS B AS A, STRING AS A, LONG AS B AS B AS A, STRING AS A, LONG AS B AS A, STRING AS A, LONG AS B AS B AS B)" ];
- 4 [ label=<| Primary Storage |
| record types: [T1] |
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS ID, STRING AS A, LONG AS B AS A, STRING AS A, LONG AS B AS B AS A, STRING AS A, LONG AS B AS A, STRING AS A, LONG AS B AS B AS B)" ];
- 3 -> 2 [ label=< q2> label="q2" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
- 4 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
- 2 -> 1 [ label=< q23> label="q23" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
+ 1 [ label=<| Value Computation |
| MAP (q2.ID AS ID) |
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS ID)" ];
+ 2 [ label=<> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS ID, STRING AS A, LONG AS B AS A, STRING AS A, LONG AS B AS B AS A, STRING AS A, LONG AS B AS A, STRING AS A, LONG AS B AS B AS B)" ];
+ 3 [ label=<| Primary Storage |
| record types: [T1] |
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS ID, STRING AS A, LONG AS B AS A, STRING AS A, LONG AS B AS B AS A, STRING AS A, LONG AS B AS A, STRING AS A, LONG AS B AS B AS B)" ];
+ 3 -> 2 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
+ 2 -> 1 [ label=< q2> label="q2" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
}
]
nested-with-nulls-proto-tests) | FILTER coalesce_string(_.A.B.A, promote(@c14 AS STRING)) EQUALS promote(@c14 AS STRING) | MAP (_.ID AS ID)digraph G {
+@ (0
8@vSCAN(<,>) | FILTER coalesce_string(_.A.B.A, promote(@c14 AS STRING)) EQUALS promote(@c14 AS STRING) | MAP (_.ID AS ID)digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -293,7 +291,7 @@ b
}
_
nested-with-nulls-proto-tests>EXPLAIN select id from t1 where coalesce(a.b.a, 'foo') = 'foo'
-@ Ξ(08@vSCAN(<,>) | FILTER coalesce_string(_.A.B.A, promote(@c14 AS STRING)) EQUALS promote(@c14 AS STRING) | MAP (_.ID AS ID)digraph G {
+@ (0
8@vSCAN(<,>) | FILTER coalesce_string(_.A.B.A, promote(@c14 AS STRING)) EQUALS promote(@c14 AS STRING) | MAP (_.ID AS ID)digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -307,7 +305,7 @@ _
}
_
nested-with-nulls-proto-tests>EXPLAIN select id from t1 where coalesce(a.b.a, 'foo') IS NULL
-@ s(08@_SCAN(<,>) | FILTER coalesce_string(_.A.B.A, promote(@c14 AS STRING)) IS_NULL | MAP (_.ID AS ID)digraph G {
+@ }(08@_SCAN(<,>) | FILTER coalesce_string(_.A.B.A, promote(@c14 AS STRING)) IS_NULL | MAP (_.ID AS ID)digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -318,10 +316,10 @@ _
3 -> 2 [ label=< q2> label="q2" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
4 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
2 -> 1 [ label=< q23> label="q23" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
-}
+}
^
-nested-with-nulls-proto-tests=EXPLAIN select id from t1 where coalesce(a.b.a, null) IS NULL
-@ S(08@LSCAN(<,>) | FILTER coalesce_string(_.A.B.A, NULL) IS_NULL | MAP (_.ID AS ID)digraph G {
+nested-with-nulls-proto-tests=EXPLAIN select id from t1 where coalesce(a.b.a, null) IS NULL
+Ѹ@ ӑ(08@LSCAN(<,>) | FILTER coalesce_string(_.A.B.A, NULL) IS_NULL | MAP (_.ID AS ID)digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -332,10 +330,10 @@ _
3 -> 2 [ label=< q2> label="q2" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
4 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
2 -> 1 [ label=< q23> label="q23" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
-}
+}
c
-nested-with-nulls-proto-testsBEXPLAIN select id from t1 where coalesce(a.b.a, 'foo') IS NOT NULL
-´@ @(08@`SCAN(<,>) | FILTER coalesce_string(_.A.B.A, promote(@c14 AS STRING)) NOT_NULL | MAP (_.ID AS ID)digraph G {
+nested-with-nulls-proto-testsBEXPLAIN select id from t1 where coalesce(a.b.a, 'foo') IS NOT NULL
+@ ڝ(08@`SCAN(<,>) | FILTER coalesce_string(_.A.B.A, promote(@c14 AS STRING)) NOT_NULL | MAP (_.ID AS ID)digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -346,10 +344,11 @@ c
3 -> 2 [ label=< q2> label="q2" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
4 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
2 -> 1 [ label=< q23> label="q23" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
-}
+}
W
-nested-with-nulls-proto-tests6EXPLAIN select id from t1 where coalesce(b.a.b, 3) = 3
-; (08@pSCAN(<,>) | FILTER coalesce_long(_.B.A.B, promote(@c14 AS LONG)) EQUALS promote(@c14 AS LONG) | MAP (_.ID AS ID)digraph G {
+nested-with-nulls-proto-tests6EXPLAIN select id from t1 where coalesce(b.a.b, 3) = 3
+; آm(0
+8@pSCAN(<,>) | FILTER coalesce_long(_.B.A.B, promote(@c14 AS LONG)) EQUALS promote(@c14 AS LONG) | MAP (_.ID AS ID)digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -360,10 +359,11 @@ W
3 -> 2 [ label=< q2> label="q2" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
4 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
2 -> 1 [ label=< q22> label="q22" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
-}
+}
Y
-nested-with-nulls-proto-tests8EXPLAIN select id from t1 where coalesce(b.a.b, 42) = 42
-; (08@pSCAN(<,>) | FILTER coalesce_long(_.B.A.B, promote(@c14 AS LONG)) EQUALS promote(@c14 AS LONG) | MAP (_.ID AS ID)digraph G {
+nested-with-nulls-proto-tests8EXPLAIN select id from t1 where coalesce(b.a.b, 42) = 42
+; آm(0
+8@pSCAN(<,>) | FILTER coalesce_long(_.B.A.B, promote(@c14 AS LONG)) EQUALS promote(@c14 AS LONG) | MAP (_.ID AS ID)digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -377,7 +377,7 @@ Y
}
\
nested-with-nulls-proto-tests;EXPLAIN select id from t1 where coalesce(b.a.b, 42) IS NULL
-; .(0͌8@[SCAN(<,>) | FILTER coalesce_long(_.B.A.B, promote(@c14 AS LONG)) IS_NULL | MAP (_.ID AS ID)digraph G {
+; /(08@[SCAN(<,>) | FILTER coalesce_long(_.B.A.B, promote(@c14 AS LONG)) IS_NULL | MAP (_.ID AS ID)digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -391,7 +391,7 @@ Y
}
`
nested-with-nulls-proto-tests?EXPLAIN select id from t1 where coalesce(b.a.b, 42) IS NOT NULL
-; v(0
8@\SCAN(<,>) | FILTER coalesce_long(_.B.A.B, promote(@c14 AS LONG)) NOT_NULL | MAP (_.ID AS ID)digraph G {
+Ɯ; W(08@\SCAN(<,>) | FILTER coalesce_long(_.B.A.B, promote(@c14 AS LONG)) NOT_NULL | MAP (_.ID AS ID)digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
diff --git a/yaml-tests/src/test/resources/nested-with-nulls-proto.metrics.yaml b/yaml-tests/src/test/resources/nested-with-nulls-proto.metrics.yaml
index 99d2815b51..fee4625693 100644
--- a/yaml-tests/src/test/resources/nested-with-nulls-proto.metrics.yaml
+++ b/yaml-tests/src/test/resources/nested-with-nulls-proto.metrics.yaml
@@ -12,9 +12,9 @@ nested-with-nulls-proto-tests:
- query: EXPLAIN select id from t1 where a.a.b IS NULL
explain: SCAN(<,>) | FILTER _.A.A.B IS_NULL | MAP (_.ID AS ID)
task_count: 206
- task_total_time_ms: 2
+ task_total_time_ms: 8
transform_count: 59
- transform_time_ms: 0
+ transform_time_ms: 2
transform_yield_count: 15
insert_time_ms: 0
insert_new_count: 18
@@ -22,7 +22,7 @@ nested-with-nulls-proto-tests:
- query: EXPLAIN select id from t1 where a.b.a IS NULL
explain: SCAN(<,>) | FILTER _.A.B.A IS_NULL | MAP (_.ID AS ID)
task_count: 206
- task_total_time_ms: 3
+ task_total_time_ms: 4
transform_count: 59
transform_time_ms: 1
transform_yield_count: 15
@@ -32,7 +32,7 @@ nested-with-nulls-proto-tests:
- query: EXPLAIN select id from t1 where a.b.b IS NULL
explain: SCAN(<,>) | FILTER _.A.B.B IS_NULL | MAP (_.ID AS ID)
task_count: 206
- task_total_time_ms: 2
+ task_total_time_ms: 3
transform_count: 59
transform_time_ms: 0
transform_yield_count: 15
@@ -42,9 +42,9 @@ nested-with-nulls-proto-tests:
- query: EXPLAIN select id from t1 where b.a.a IS NULL
explain: SCAN(<,>) | FILTER _.B.A.A IS_NULL | MAP (_.ID AS ID)
task_count: 206
- task_total_time_ms: 2
+ task_total_time_ms: 7
transform_count: 59
- transform_time_ms: 0
+ transform_time_ms: 1
transform_yield_count: 15
insert_time_ms: 0
insert_new_count: 18
@@ -52,7 +52,7 @@ nested-with-nulls-proto-tests:
- query: EXPLAIN select id from t1 where b.a.b IS NULL
explain: SCAN(<,>) | FILTER _.B.A.B IS_NULL | MAP (_.ID AS ID)
task_count: 206
- task_total_time_ms: 3
+ task_total_time_ms: 2
transform_count: 59
transform_time_ms: 1
transform_yield_count: 15
@@ -62,7 +62,7 @@ nested-with-nulls-proto-tests:
- query: EXPLAIN select id from t1 where b.b.a IS NULL
explain: SCAN(<,>) | FILTER _.B.B.A IS_NULL | MAP (_.ID AS ID)
task_count: 206
- task_total_time_ms: 6
+ task_total_time_ms: 8
transform_count: 59
transform_time_ms: 1
transform_yield_count: 15
@@ -72,9 +72,9 @@ nested-with-nulls-proto-tests:
- query: EXPLAIN select id from t1 where b.b.b IS NULL
explain: SCAN(<,>) | FILTER _.B.B.B IS_NULL | MAP (_.ID AS ID)
task_count: 206
- task_total_time_ms: 9
+ task_total_time_ms: 2
transform_count: 59
- transform_time_ms: 2
+ transform_time_ms: 0
transform_yield_count: 15
insert_time_ms: 0
insert_new_count: 18
@@ -82,7 +82,7 @@ nested-with-nulls-proto-tests:
- query: EXPLAIN select id from t1 where a.a.a IS NOT NULL
explain: SCAN(<,>) | MAP (_.ID AS ID)
task_count: 208
- task_total_time_ms: 8
+ task_total_time_ms: 6
transform_count: 64
transform_time_ms: 1
transform_yield_count: 17
@@ -92,7 +92,7 @@ nested-with-nulls-proto-tests:
- query: EXPLAIN select id from t1 where a.a.b IS NOT NULL
explain: SCAN(<,>) | FILTER _.A.A.B NOT_NULL | MAP (_.ID AS ID)
task_count: 206
- task_total_time_ms: 6
+ task_total_time_ms: 2
transform_count: 59
transform_time_ms: 1
transform_yield_count: 15
@@ -102,9 +102,9 @@ nested-with-nulls-proto-tests:
- query: EXPLAIN select id from t1 where a.b.a IS NOT NULL
explain: SCAN(<,>) | FILTER _.A.B.A NOT_NULL | MAP (_.ID AS ID)
task_count: 206
- task_total_time_ms: 5
+ task_total_time_ms: 2
transform_count: 59
- transform_time_ms: 1
+ transform_time_ms: 0
transform_yield_count: 15
insert_time_ms: 0
insert_new_count: 18
@@ -112,9 +112,9 @@ nested-with-nulls-proto-tests:
- query: EXPLAIN select id from t1 where a.b.b IS NOT NULL
explain: SCAN(<,>) | FILTER _.A.B.B NOT_NULL | MAP (_.ID AS ID)
task_count: 206
- task_total_time_ms: 9
+ task_total_time_ms: 4
transform_count: 59
- transform_time_ms: 2
+ transform_time_ms: 1
transform_yield_count: 15
insert_time_ms: 0
insert_new_count: 18
@@ -132,7 +132,7 @@ nested-with-nulls-proto-tests:
- query: EXPLAIN select id from t1 where b.a.b IS NOT NULL
explain: SCAN(<,>) | FILTER _.B.A.B NOT_NULL | MAP (_.ID AS ID)
task_count: 206
- task_total_time_ms: 6
+ task_total_time_ms: 4
transform_count: 59
transform_time_ms: 1
transform_yield_count: 15
@@ -142,9 +142,9 @@ nested-with-nulls-proto-tests:
- query: EXPLAIN select id from t1 where b.b.a IS NOT NULL
explain: SCAN(<,>) | FILTER _.B.B.A NOT_NULL | MAP (_.ID AS ID)
task_count: 206
- task_total_time_ms: 5
+ task_total_time_ms: 1
transform_count: 59
- transform_time_ms: 1
+ transform_time_ms: 0
transform_yield_count: 15
insert_time_ms: 0
insert_new_count: 18
@@ -152,9 +152,9 @@ nested-with-nulls-proto-tests:
- query: EXPLAIN select id from t1 where b.b.b IS NOT NULL
explain: SCAN(<,>) | FILTER _.B.B.B NOT_NULL | MAP (_.ID AS ID)
task_count: 206
- task_total_time_ms: 9
+ task_total_time_ms: 2
transform_count: 59
- transform_time_ms: 2
+ transform_time_ms: 1
transform_yield_count: 15
insert_time_ms: 0
insert_new_count: 18
@@ -163,9 +163,9 @@ nested-with-nulls-proto-tests:
explain: SCAN(<,>) | FILTER coalesce_string(_.A.A.A, @c14) EQUALS @c17 | MAP (_.ID
AS ID)
task_count: 206
- task_total_time_ms: 2
+ task_total_time_ms: 9
transform_count: 59
- transform_time_ms: 0
+ transform_time_ms: 2
transform_yield_count: 15
insert_time_ms: 0
insert_new_count: 18
@@ -174,40 +174,38 @@ nested-with-nulls-proto-tests:
explain: SCAN(<,>) | FILTER coalesce_string(_.A.A.A, @c14) EQUALS @c17 | MAP (_.ID
AS ID)
task_count: 206
- task_total_time_ms: 2
+ task_total_time_ms: 9
transform_count: 59
- transform_time_ms: 0
+ transform_time_ms: 2
transform_yield_count: 15
insert_time_ms: 0
insert_new_count: 18
insert_reused_count: 2
- query: EXPLAIN select id from t1 where coalesce(a.a.a, 'blah') IS NOT NULL
- explain: SCAN(<,>) | FILTER coalesce_string(_.A.A.A, @c14) NOT_NULL | MAP (_.ID
- AS ID)
- task_count: 222
- task_total_time_ms: 9
+ explain: SCAN(<,>) | MAP (_.ID AS ID)
+ task_count: 208
+ task_total_time_ms: 8
transform_count: 64
- transform_time_ms: 2
+ transform_time_ms: 1
transform_yield_count: 17
insert_time_ms: 0
- insert_new_count: 22
+ insert_new_count: 20
insert_reused_count: 2
- query: EXPLAIN select id from t1 where coalesce(a.a.a, null) IS NOT NULL
- explain: SCAN(<,>) | FILTER coalesce_string(promote(_.A.A.A AS STRING), NULL)
- NOT_NULL | MAP (_.ID AS ID)
- task_count: 222
- task_total_time_ms: 6
+ explain: SCAN(<,>) | MAP (_.ID AS ID)
+ task_count: 208
+ task_total_time_ms: 4
transform_count: 64
transform_time_ms: 1
transform_yield_count: 17
insert_time_ms: 0
- insert_new_count: 22
+ insert_new_count: 20
insert_reused_count: 2
- query: EXPLAIN select id from t1 where coalesce(a.b.a, 'a2') = 'a2'
explain: SCAN(<,>) | FILTER coalesce_string(_.A.B.A, promote(@c14 AS STRING))
EQUALS promote(@c14 AS STRING) | MAP (_.ID AS ID)
task_count: 222
- task_total_time_ms: 9
+ task_total_time_ms: 7
transform_count: 64
transform_time_ms: 2
transform_yield_count: 17
@@ -218,7 +216,7 @@ nested-with-nulls-proto-tests:
explain: SCAN(<,>) | FILTER coalesce_string(_.A.B.A, promote(@c14 AS STRING))
EQUALS promote(@c14 AS STRING) | MAP (_.ID AS ID)
task_count: 222
- task_total_time_ms: 9
+ task_total_time_ms: 7
transform_count: 64
transform_time_ms: 2
transform_yield_count: 17
@@ -229,9 +227,9 @@ nested-with-nulls-proto-tests:
explain: SCAN(<,>) | FILTER coalesce_string(_.A.B.A, promote(@c14 AS STRING))
IS_NULL | MAP (_.ID AS ID)
task_count: 222
- task_total_time_ms: 6
+ task_total_time_ms: 8
transform_count: 64
- transform_time_ms: 1
+ transform_time_ms: 2
transform_yield_count: 17
insert_time_ms: 0
insert_new_count: 22
@@ -240,9 +238,9 @@ nested-with-nulls-proto-tests:
explain: SCAN(<,>) | FILTER coalesce_string(_.A.B.A, NULL) IS_NULL | MAP (_.ID
AS ID)
task_count: 222
- task_total_time_ms: 4
+ task_total_time_ms: 9
transform_count: 64
- transform_time_ms: 1
+ transform_time_ms: 2
transform_yield_count: 17
insert_time_ms: 0
insert_new_count: 22
@@ -251,9 +249,9 @@ nested-with-nulls-proto-tests:
explain: SCAN(<,>) | FILTER coalesce_string(_.A.B.A, promote(@c14 AS STRING))
NOT_NULL | MAP (_.ID AS ID)
task_count: 222
- task_total_time_ms: 2
+ task_total_time_ms: 9
transform_count: 64
- transform_time_ms: 1
+ transform_time_ms: 2
transform_yield_count: 17
insert_time_ms: 0
insert_new_count: 22
@@ -262,9 +260,9 @@ nested-with-nulls-proto-tests:
explain: SCAN(<,>) | FILTER coalesce_long(_.B.A.B, promote(@c14 AS LONG)) EQUALS
promote(@c14 AS LONG) | MAP (_.ID AS ID)
task_count: 206
- task_total_time_ms: 9
+ task_total_time_ms: 6
transform_count: 59
- transform_time_ms: 2
+ transform_time_ms: 1
transform_yield_count: 15
insert_time_ms: 0
insert_new_count: 18
@@ -273,9 +271,9 @@ nested-with-nulls-proto-tests:
explain: SCAN(<,>) | FILTER coalesce_long(_.B.A.B, promote(@c14 AS LONG)) EQUALS
promote(@c14 AS LONG) | MAP (_.ID AS ID)
task_count: 206
- task_total_time_ms: 9
+ task_total_time_ms: 6
transform_count: 59
- transform_time_ms: 2
+ transform_time_ms: 1
transform_yield_count: 15
insert_time_ms: 0
insert_new_count: 18
@@ -295,7 +293,7 @@ nested-with-nulls-proto-tests:
explain: SCAN(<,>) | FILTER coalesce_long(_.B.A.B, promote(@c14 AS LONG)) NOT_NULL
| MAP (_.ID AS ID)
task_count: 206
- task_total_time_ms: 6
+ task_total_time_ms: 4
transform_count: 59
transform_time_ms: 1
transform_yield_count: 15
diff --git a/yaml-tests/src/test/resources/nested-with-nulls-proto.yamsql b/yaml-tests/src/test/resources/nested-with-nulls-proto.yamsql
index ec1efd7731..310cb44a6d 100644
--- a/yaml-tests/src/test/resources/nested-with-nulls-proto.yamsql
+++ b/yaml-tests/src/test/resources/nested-with-nulls-proto.yamsql
@@ -124,12 +124,12 @@ test_block:
-
# Neither a.a.a nor 'blah' are nullable, so this predicate could be simplified to TRUE
- query: select id from t1 where coalesce(a.a.a, 'blah') IS NOT NULL
- - explain: "SCAN(<,>) | FILTER coalesce_string(_.A.A.A, @c14) NOT_NULL | MAP (_.ID AS ID)"
+ - explain: "SCAN(<,>) | MAP (_.ID AS ID)"
- unorderedResult: [{ ID: 100 }, { ID: 101 }, { ID: 102 }, { ID: 103 }, { ID: 104 }, { ID: 105 }, { ID: 106 }, { ID: 107 }]
-
# As a.a.a is not nullable, this predicate could be simplified to TRUE
- query: select id from t1 where coalesce(a.a.a, null) IS NOT NULL
- - explain: "SCAN(<,>) | FILTER coalesce_string(promote(_.A.A.A AS STRING), NULL) NOT_NULL | MAP (_.ID AS ID)"
+ - explain: "SCAN(<,>) | MAP (_.ID AS ID)"
- unorderedResult: [{ ID: 100 }, { ID: 101 }, { ID: 102 }, { ID: 103 }, { ID: 104 }, { ID: 105 }, { ID: 106 }, { ID: 107 }]
-
- query: select id from t1 where coalesce(a.b.a, 'a2') = 'a2'
diff --git a/yaml-tests/src/test/resources/standard-tests.metrics.binpb b/yaml-tests/src/test/resources/standard-tests.metrics.binpb
index 262e4db92f..0bf70195f9 100644
--- a/yaml-tests/src/test/resources/standard-tests.metrics.binpb
+++ b/yaml-tests/src/test/resources/standard-tests.metrics.binpb
@@ -1,7 +1,7 @@
standard-testsnEXPLAIN select id, case when col1 = 10 then 100 when col2 in (6,7,8,9) then 200 else 300 end as NEWCOL from T1
-6 (0@8@ISCAN(I1 <,>) | MAP (_.ID AS ID, pick(ConditionSelector(_.COL1 equals @c8, _.COL2 IN promote(@c14 AS ARRAY(LONG)), TRUE), @c10, @c24, @c26) AS NEWCOL)digraph G {
+Ӡ6 (0)8@ISCAN(I1 <,>) | MAP (_.ID AS ID, pick(ConditionSelector(_.COL1 equals @c8, _.COL2 IN promote(@c14 AS ARRAY(LONG)), TRUE), @c10, @c24, @c26) AS NEWCOL)digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -13,7 +13,7 @@
}
w
standard-testseEXPLAIN select id, case when col1 = 10 then 100 when col2 in (6,7,8,9) then 200 end as NEWCOL from T1
-6 ʭ(0E8@ISCAN(I1 <,>) | MAP (_.ID AS ID, pick(ConditionSelector(_.COL1 equals @c8, _.COL2 IN promote(@c14 AS ARRAY(LONG))), @c10, @c24) AS NEWCOL)digraph G {
+ȅ6 (0׆,8@ISCAN(I1 <,>) | MAP (_.ID AS ID, pick(ConditionSelector(_.COL1 equals @c8, _.COL2 IN promote(@c14 AS ARRAY(LONG))), @c10, @c24) AS NEWCOL)digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -22,10 +22,10 @@ w
3 [ label=<> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS ID, LONG AS COL1, LONG AS COL2)" ];
3 -> 2 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
2 -> 1 [ label=< q2> label="q2" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
-}
+}
:
-standard-tests(EXPLAIN select * from T1 where COL1 = 20
-ڍAK ,(08@'ISCAN(I1 [EQUALS promote(@c8 AS LONG)])digraph G {
+standard-tests(EXPLAIN select * from T1 where COL1 = 20
+ K (0'8@'ISCAN(I1 [EQUALS promote(@c8 AS LONG)])digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -36,7 +36,7 @@ w
I
standard-tests7EXPLAIN select * from T1 where COL1 >= 10 OR COL1 <= 20
-, (O08@COVERING(I1 [[GREATER_THAN_OR_EQUALS promote(@c9 AS LONG)]] -> [COL1: KEY[0], ID: KEY[2]]) ⊎ COVERING(I1 [[LESS_THAN_OR_EQUALS promote(@c14 AS LONG)]] -> [COL1: KEY[0], ID: KEY[2]]) | DISTINCT BY PK | FETCHdigraph G {
+/ (O08@COVERING(I1 [[GREATER_THAN_OR_EQUALS promote(@c9 AS LONG)]] -> [COL1: KEY[0], ID: KEY[2]]) ⊎ COVERING(I1 [[LESS_THAN_OR_EQUALS promote(@c14 AS LONG)]] -> [COL1: KEY[0], ID: KEY[2]]) | DISTINCT BY PK | FETCHdigraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -56,7 +56,7 @@ I
}
W
standard-testsEEXPLAIN select * from T1 where COL1 >= 10 OR COL1 <= 20 ORDER BY COL1
-( ҝ(>08}@ISCAN(I1 [[GREATER_THAN_OR_EQUALS promote(@c9 AS LONG)]]) ∪ ISCAN(I1 [[LESS_THAN_OR_EQUALS promote(@c14 AS LONG)]]) COMPARE BY (_.COL1, recordType(_), _.ID)digraph G {
+ (>08}@ISCAN(I1 [[GREATER_THAN_OR_EQUALS promote(@c9 AS LONG)]]) ∪ ISCAN(I1 [[LESS_THAN_OR_EQUALS promote(@c14 AS LONG)]]) COMPARE BY (_.COL1, recordType(_), _.ID)digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -72,7 +72,7 @@ W
}
J
standard-tests8EXPLAIN select * from T1 where COL1 >= 10 AND COL1 <= 20
-L (0Ɯ+8@fISCAN(I1 [[GREATER_THAN_OR_EQUALS promote(@c9 AS LONG) && LESS_THAN_OR_EQUALS promote(@c14 AS LONG)]])digraph G {
+L (0#8@fISCAN(I1 [[GREATER_THAN_OR_EQUALS promote(@c9 AS LONG) && LESS_THAN_OR_EQUALS promote(@c14 AS LONG)]])digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -82,17 +82,17 @@ J
}
X
standard-testsFEXPLAIN select * from T1 where COL1 >= 10 AND COL1 <= 20 ORDER BY COL1
-=A *(0ގ|8@fISCAN(I1 [[GREATER_THAN_OR_EQUALS promote(@c9 AS LONG) && LESS_THAN_OR_EQUALS promote(@c14 AS LONG)]])digraph G {
+A ؛(08@fISCAN(I1 [[GREATER_THAN_OR_EQUALS promote(@c9 AS LONG) && LESS_THAN_OR_EQUALS promote(@c14 AS LONG)]])digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
1 [ label=<| Index Scan |
| comparisons: [[GREATER_THAN_OR_EQUALS promote(@c9 AS LONG) && LESS_THAN_OR_EQUALS promote(@c14 AS LONG)]] |
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS ID, LONG AS COL1, LONG AS COL2)" ];
2 [ label=<> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS ID, LONG AS COL1, LONG AS COL2)" ];
2 -> 1 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
-}
+}
G
-standard-tests5EXPLAIN select * from T1 where COL1 = 20 OR COL1 = 20
-ѵAP *( 08#@'ISCAN(I1 [EQUALS promote(@c8 AS LONG)])digraph G {
+standard-tests5EXPLAIN select * from T1 where COL1 = 20 OR COL1 = 20
+P ( 0"8#@'ISCAN(I1 [EQUALS promote(@c8 AS LONG)])digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -102,28 +102,28 @@ G
}
U
standard-testsCEXPLAIN select * from T1 where COL1 = 20 OR COL1 = 20 ORDER BY COL1
-F 檌(0"8@'ISCAN(I1 [EQUALS promote(@c8 AS LONG)])digraph G {
+މF (0+8@'ISCAN(I1 [EQUALS promote(@c8 AS LONG)])digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
1 [ label=<| Index Scan |
| comparisons: [EQUALS promote(@c8 AS LONG)] |
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS ID, LONG AS COL1, LONG AS COL2)" ];
2 [ label=<> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS ID, LONG AS COL1, LONG AS COL2)" ];
2 -> 1 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
-}
+}
H
-standard-tests6EXPLAIN select * from T1 where COL1 = 20 AND COL1 = 20
-AK +(08@'ISCAN(I1 [EQUALS promote(@c8 AS LONG)])digraph G {
+standard-tests6EXPLAIN select * from T1 where COL1 = 20 AND COL1 = 20
+ K (0:8@'ISCAN(I1 [EQUALS promote(@c8 AS LONG)])digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
1 [ label=<| Index Scan |
| comparisons: [EQUALS promote(@c8 AS LONG)] |
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS ID, LONG AS COL1, LONG AS COL2)" ];
2 [ label=<> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS ID, LONG AS COL1, LONG AS COL2)" ];
2 -> 1 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
-}
+}
f
-standard-testsTEXPLAIN select * from T1 where (COL1 = 20 OR COL1 = 10) AND (COL1 = 20 OR COL1 = 10)
-e (0ڽ
-8@,COVERING(I1 [EQUALS promote(@c9 AS LONG)] -> [COL1: KEY[0], ID: KEY[2]]) ⊎ COVERING(I1 [EQUALS promote(@c13 AS LONG)] -> [COL1: KEY[0], ID: KEY[2]]) | DISTINCT BY PK | FETCHdigraph G {
+standard-testsTEXPLAIN select * from T1 where (COL1 = 20 OR COL1 = 10) AND (COL1 = 20 OR COL1 = 10)
+
+ ױ(S08@COVERING(I1 [EQUALS promote(@c9 AS LONG)] -> [COL1: KEY[0], ID: KEY[2]]) ⊎ COVERING(I1 [EQUALS promote(@c13 AS LONG)] -> [COL1: KEY[0], ID: KEY[2]]) | DISTINCT BY PK | FETCHdigraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -134,16 +134,17 @@ f
5 [ label=<| Covering Index Scan |
| comparisons: [EQUALS promote(@c13 AS LONG)] |
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS ID, LONG AS COL1, LONG AS COL2)" ];
6 [ label=<> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS ID, LONG AS COL1, LONG AS COL2)" ];
7 [ label=<> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS ID, LONG AS COL1, LONG AS COL2)" ];
- 3 -> 2 [ label=< q296> label="q296" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
- 4 -> 3 [ label=< q276> label="q276" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
- 5 -> 3 [ label=< q278> label="q278" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
+ 3 -> 2 [ label=< q138> label="q138" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
+ 4 -> 3 [ label=< q118> label="q118" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
+ 5 -> 3 [ label=< q120> label="q120" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
6 -> 4 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
7 -> 5 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
- 2 -> 1 [ label=< q298> label="q298" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
+ 2 -> 1 [ label=< q140> label="q140" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
}
t
standard-testsbEXPLAIN select * from T1 where (COL1 = 20 OR COL1 = 10) AND (COL1 = 20 OR COL1 = 10) ORDER BY COL1
-J (i08@*}ISCAN(I1 [EQUALS promote(@c9 AS LONG)]) ∪ ISCAN(I1 [EQUALS promote(@c13 AS LONG)]) COMPARE BY (_.COL1, recordType(_), _.ID)digraph G {
+
+(@08@}ISCAN(I1 [EQUALS promote(@c9 AS LONG)]) ∪ ISCAN(I1 [EQUALS promote(@c13 AS LONG)]) COMPARE BY (_.COL1, recordType(_), _.ID)digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -153,13 +154,13 @@ t
4 [ label=<| Index Scan |
| comparisons: [EQUALS promote(@c13 AS LONG)] |
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS ID, LONG AS COL1, LONG AS COL2)" ];
5 [ label=<> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS ID, LONG AS COL1, LONG AS COL2)" ];
3 -> 2 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
- 2 -> 1 [ label=< q283> label="q283" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
+ 2 -> 1 [ label=< q129> label="q129" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
5 -> 4 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
- 4 -> 1 [ label=< q285> label="q285" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
+ 4 -> 1 [ label=< q131> label="q131" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
}
k
standard-testsYEXPLAIN select * from T1 where (COL1 >= 10 AND COL1 <= 20) OR (COL1 >= 10 AND COL1 <= 20)
-Q ( 0O8#@gISCAN(I1 [[GREATER_THAN_OR_EQUALS promote(@c10 AS LONG) && LESS_THAN_OR_EQUALS promote(@c15 AS LONG)]])digraph G {
+َQ ( 0*8#@gISCAN(I1 [[GREATER_THAN_OR_EQUALS promote(@c10 AS LONG) && LESS_THAN_OR_EQUALS promote(@c15 AS LONG)]])digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -169,8 +170,7 @@ k
}
y
standard-testsgEXPLAIN select * from T1 where (COL1 >= 10 AND COL1 <= 20) OR (COL1 >= 10 AND COL1 <= 20) ORDER BY COL1
-
-F (0#8@gISCAN(I1 [[GREATER_THAN_OR_EQUALS promote(@c10 AS LONG) && LESS_THAN_OR_EQUALS promote(@c15 AS LONG)]])digraph G {
+F (0N8@gISCAN(I1 [[GREATER_THAN_OR_EQUALS promote(@c10 AS LONG) && LESS_THAN_OR_EQUALS promote(@c15 AS LONG)]])digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -180,7 +180,7 @@ y
}
b
standard-testsPEXPLAIN select * from (select * from (select * from T1) as x where ID = 5) as y;
-@^ $($082@aCOVERING(I1 <,> -> [COL1: KEY[0], ID: KEY[2]]) | FILTER _.ID EQUALS promote(@c19 AS LONG) | FETCHdigraph G {
+^ ($082@aCOVERING(I1 <,> -> [COL1: KEY[0], ID: KEY[2]]) | FILTER _.ID EQUALS promote(@c19 AS LONG) | FETCHdigraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -191,10 +191,10 @@ b
3 -> 2 [ label=< q46> label="q46" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
4 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
2 -> 1 [ label=< q48> label="q48" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
-}
+}
b
-standard-testsPEXPLAIN select * from (select * from (select * from T1) as x) as y where ID = 5;
-Ag $((08;@aCOVERING(I1 <,> -> [COL1: KEY[0], ID: KEY[2]]) | FILTER _.ID EQUALS promote(@c22 AS LONG) | FETCHdigraph G {
+standard-testsPEXPLAIN select * from (select * from (select * from T1) as x) as y where ID = 5;
+g ((0Z8;@aCOVERING(I1 <,> -> [COL1: KEY[0], ID: KEY[2]]) | FILTER _.ID EQUALS promote(@c22 AS LONG) | FETCHdigraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -208,7 +208,7 @@ b
}
standard-testsmEXPLAIN select count(*) from (select * from (select * from (select * from T1 where ID = 5) as x) as y) as z;
- ()0y8D@COVERING(I1 <,> -> [COL1: KEY[0], ID: KEY[2]]) | FILTER _.ID EQUALS promote(@c23 AS LONG) | FETCH | MAP (_ AS _0) | AGG (count_star(*) AS _0) | ON EMPTY NULL | MAP (coalesce_long(_._0._0, promote(0l AS LONG)) AS _0)digraph G {
+щ ()0T8D@COVERING(I1 <,> -> [COL1: KEY[0], ID: KEY[2]]) | FILTER _.ID EQUALS promote(@c23 AS LONG) | FETCH | MAP (_ AS _0) | AGG (count_star(*) AS _0) | ON EMPTY NULL | MAP (coalesce_long(_._0._0, promote(0l AS LONG)) AS _0)digraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
@@ -227,10 +227,10 @@ b
7 -> 6 [ label=< q56> label="q56" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
8 -> 7 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
2 -> 1 [ label=< q12> label="q12" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
-}
+}
y
-standard-testsgEXPLAIN select * from (select * from (select * from (select * from T1 where ID > 10) as x) as y) as z;
-@Y )(#0ԛ8/@gCOVERING(I1 <,> -> [COL1: KEY[0], ID: KEY[2]]) | FILTER _.ID GREATER_THAN promote(@c20 AS LONG) | FETCHdigraph G {
+standard-testsgEXPLAIN select * from (select * from (select * from (select * from T1 where ID > 10) as x) as y) as z;
+ Y (#088/@gCOVERING(I1 <,> -> [COL1: KEY[0], ID: KEY[2]]) | FILTER _.ID GREATER_THAN promote(@c20 AS LONG) | FETCHdigraph G {
fontname=courier;
rankdir=BT;
splines=polyline;
diff --git a/yaml-tests/src/test/resources/standard-tests.metrics.yaml b/yaml-tests/src/test/resources/standard-tests.metrics.yaml
index 4e54152a8c..6c7624b276 100644
--- a/yaml-tests/src/test/resources/standard-tests.metrics.yaml
+++ b/yaml-tests/src/test/resources/standard-tests.metrics.yaml
@@ -4,11 +4,11 @@ standard-tests:
explain: ISCAN(I1 <,>) | MAP (_.ID AS ID, pick(ConditionSelector(_.COL1 equals
@c8, _.COL2 IN promote(@c14 AS ARRAY(LONG)), TRUE), @c10, @c24, @c26) AS NEWCOL)
task_count: 213
- task_total_time_ms: 23
+ task_total_time_ms: 13
transform_count: 54
- transform_time_ms: 11
+ transform_time_ms: 6
transform_yield_count: 23
- insert_time_ms: 1
+ insert_time_ms: 0
insert_new_count: 22
insert_reused_count: 4
- query: EXPLAIN select id, case when col1 = 10 then 100 when col2 in (6,7,8,9)
@@ -16,21 +16,21 @@ standard-tests:
explain: ISCAN(I1 <,>) | MAP (_.ID AS ID, pick(ConditionSelector(_.COL1 equals
@c8, _.COL2 IN promote(@c14 AS ARRAY(LONG))), @c10, @c24) AS NEWCOL)
task_count: 213
- task_total_time_ms: 23
+ task_total_time_ms: 16
transform_count: 54
- transform_time_ms: 11
+ transform_time_ms: 7
transform_yield_count: 23
- insert_time_ms: 1
+ insert_time_ms: 0
insert_new_count: 22
insert_reused_count: 4
- query: EXPLAIN select * from T1 where COL1 = 20
explain: ISCAN(I1 [EQUALS promote(@c8 AS LONG)])
task_count: 311
- task_total_time_ms: 136
+ task_total_time_ms: 20
transform_count: 75
- transform_time_ms: 92
+ transform_time_ms: 8
transform_yield_count: 30
- insert_time_ms: 2
+ insert_time_ms: 0
insert_new_count: 31
insert_reused_count: 4
- query: EXPLAIN select * from T1 where COL1 >= 10 OR COL1 <= 20
@@ -38,11 +38,11 @@ standard-tests:
KEY[0], ID: KEY[2]]) ⊎ COVERING(I1 [[LESS_THAN_OR_EQUALS promote(@c14 AS LONG)]]
-> [COL1: KEY[0], ID: KEY[2]]) | DISTINCT BY PK | FETCH'
task_count: 1285
- task_total_time_ms: 94
+ task_total_time_ms: 98
transform_count: 260
- transform_time_ms: 26
+ transform_time_ms: 36
transform_yield_count: 79
- insert_time_ms: 6
+ insert_time_ms: 5
insert_new_count: 159
insert_reused_count: 23
- query: EXPLAIN select * from T1 where COL1 >= 10 OR COL1 <= 20 ORDER BY COL1
@@ -50,20 +50,20 @@ standard-tests:
[[LESS_THAN_OR_EQUALS promote(@c14 AS LONG)]]) COMPARE BY (_.COL1, recordType(_),
_.ID)
task_count: 1080
- task_total_time_ms: 84
+ task_total_time_ms: 47
transform_count: 224
- transform_time_ms: 34
+ transform_time_ms: 14
transform_yield_count: 62
- insert_time_ms: 4
+ insert_time_ms: 2
insert_new_count: 125
insert_reused_count: 14
- query: EXPLAIN select * from T1 where COL1 >= 10 AND COL1 <= 20
explain: ISCAN(I1 [[GREATER_THAN_OR_EQUALS promote(@c9 AS LONG) && LESS_THAN_OR_EQUALS
promote(@c14 AS LONG)]])
task_count: 311
- task_total_time_ms: 14
+ task_total_time_ms: 15
transform_count: 76
- transform_time_ms: 9
+ transform_time_ms: 7
transform_yield_count: 30
insert_time_ms: 0
insert_new_count: 31
@@ -72,29 +72,29 @@ standard-tests:
explain: ISCAN(I1 [[GREATER_THAN_OR_EQUALS promote(@c9 AS LONG) && LESS_THAN_OR_EQUALS
promote(@c14 AS LONG)]])
task_count: 263
- task_total_time_ms: 129
+ task_total_time_ms: 12
transform_count: 65
- transform_time_ms: 88
+ transform_time_ms: 4
transform_yield_count: 26
- insert_time_ms: 2
+ insert_time_ms: 0
insert_new_count: 24
insert_reused_count: 2
- query: EXPLAIN select * from T1 where COL1 = 20 OR COL1 = 20
explain: ISCAN(I1 [EQUALS promote(@c8 AS LONG)])
task_count: 327
- task_total_time_ms: 137
+ task_total_time_ms: 16
transform_count: 80
- transform_time_ms: 89
+ transform_time_ms: 6
transform_yield_count: 32
- insert_time_ms: 2
+ insert_time_ms: 0
insert_new_count: 35
insert_reused_count: 4
- query: EXPLAIN select * from T1 where COL1 = 20 OR COL1 = 20 ORDER BY COL1
explain: ISCAN(I1 [EQUALS promote(@c8 AS LONG)])
task_count: 279
- task_total_time_ms: 17
+ task_total_time_ms: 23
transform_count: 70
- transform_time_ms: 6
+ transform_time_ms: 11
transform_yield_count: 28
insert_time_ms: 0
insert_new_count: 28
@@ -102,11 +102,11 @@ standard-tests:
- query: EXPLAIN select * from T1 where COL1 = 20 AND COL1 = 20
explain: ISCAN(I1 [EQUALS promote(@c8 AS LONG)])
task_count: 311
- task_total_time_ms: 137
+ task_total_time_ms: 20
transform_count: 75
- transform_time_ms: 90
+ transform_time_ms: 8
transform_yield_count: 30
- insert_time_ms: 3
+ insert_time_ms: 0
insert_new_count: 31
insert_reused_count: 4
- query: EXPLAIN select * from T1 where (COL1 = 20 OR COL1 = 10) AND (COL1 = 20
@@ -114,36 +114,36 @@ standard-tests:
explain: 'COVERING(I1 [EQUALS promote(@c9 AS LONG)] -> [COL1: KEY[0], ID: KEY[2]])
⊎ COVERING(I1 [EQUALS promote(@c13 AS LONG)] -> [COL1: KEY[0], ID: KEY[2]])
| DISTINCT BY PK | FETCH'
- task_count: 2476
- task_total_time_ms: 212
- transform_count: 467
- transform_time_ms: 67
- transform_yield_count: 143
- insert_time_ms: 21
- insert_new_count: 315
- insert_reused_count: 44
+ task_count: 1325
+ task_total_time_ms: 51
+ transform_count: 270
+ transform_time_ms: 18
+ transform_yield_count: 83
+ insert_time_ms: 3
+ insert_new_count: 165
+ insert_reused_count: 24
- query: EXPLAIN select * from T1 where (COL1 = 20 OR COL1 = 10) AND (COL1 = 20
OR COL1 = 10) ORDER BY COL1
explain: ISCAN(I1 [EQUALS promote(@c9 AS LONG)]) ∪ ISCAN(I1 [EQUALS promote(@c13
AS LONG)]) COMPARE BY (_.COL1, recordType(_), _.ID)
- task_count: 2218
- task_total_time_ms: 155
- transform_count: 418
- transform_time_ms: 50
- transform_yield_count: 105
- insert_time_ms: 12
- insert_new_count: 247
- insert_reused_count: 42
+ task_count: 1192
+ task_total_time_ms: 59
+ transform_count: 241
+ transform_time_ms: 21
+ transform_yield_count: 64
+ insert_time_ms: 2
+ insert_new_count: 132
+ insert_reused_count: 21
- query: EXPLAIN select * from T1 where (COL1 >= 10 AND COL1 <= 20) OR (COL1 >=
10 AND COL1 <= 20)
explain: ISCAN(I1 [[GREATER_THAN_OR_EQUALS promote(@c10 AS LONG) && LESS_THAN_OR_EQUALS
promote(@c15 AS LONG)]])
task_count: 327
- task_total_time_ms: 27
+ task_total_time_ms: 14
transform_count: 81
- transform_time_ms: 13
+ transform_time_ms: 6
transform_yield_count: 32
- insert_time_ms: 1
+ insert_time_ms: 0
insert_new_count: 35
insert_reused_count: 4
- query: EXPLAIN select * from T1 where (COL1 >= 10 AND COL1 <= 20) OR (COL1 >=
@@ -151,11 +151,11 @@ standard-tests:
explain: ISCAN(I1 [[GREATER_THAN_OR_EQUALS promote(@c10 AS LONG) && LESS_THAN_OR_EQUALS
promote(@c15 AS LONG)]])
task_count: 279
- task_total_time_ms: 21
+ task_total_time_ms: 23
transform_count: 70
- transform_time_ms: 8
+ transform_time_ms: 12
transform_yield_count: 28
- insert_time_ms: 0
+ insert_time_ms: 1
insert_new_count: 28
insert_reused_count: 2
- query: EXPLAIN select * from (select * from (select * from T1) as x where ID =
@@ -163,11 +163,11 @@ standard-tests:
explain: 'COVERING(I1 <,> -> [COL1: KEY[0], ID: KEY[2]]) | FILTER _.ID EQUALS
promote(@c19 AS LONG) | FETCH'
task_count: 399
- task_total_time_ms: 135
+ task_total_time_ms: 29
transform_count: 94
- transform_time_ms: 76
+ transform_time_ms: 11
transform_yield_count: 36
- insert_time_ms: 9
+ insert_time_ms: 3
insert_new_count: 50
insert_reused_count: 6
- query: EXPLAIN select * from (select * from (select * from T1) as x) as y where
@@ -175,11 +175,11 @@ standard-tests:
explain: 'COVERING(I1 <,> -> [COL1: KEY[0], ID: KEY[2]]) | FILTER _.ID EQUALS
promote(@c22 AS LONG) | FETCH'
task_count: 441
- task_total_time_ms: 137
+ task_total_time_ms: 23
transform_count: 103
- transform_time_ms: 76
+ transform_time_ms: 8
transform_yield_count: 40
- insert_time_ms: 8
+ insert_time_ms: 1
insert_new_count: 59
insert_reused_count: 7
- query: EXPLAIN select count(*) from (select * from (select * from (select * from
@@ -188,9 +188,9 @@ standard-tests:
promote(@c23 AS LONG) | FETCH | MAP (_ AS _0) | AGG (count_star(*) AS _0)
| ON EMPTY NULL | MAP (coalesce_long(_._0._0, promote(0l AS LONG)) AS _0)'
task_count: 603
- task_total_time_ms: 48
+ task_total_time_ms: 35
transform_count: 141
- transform_time_ms: 18
+ transform_time_ms: 12
transform_yield_count: 41
insert_time_ms: 1
insert_new_count: 68
@@ -200,10 +200,10 @@ standard-tests:
explain: 'COVERING(I1 <,> -> [COL1: KEY[0], ID: KEY[2]]) | FILTER _.ID GREATER_THAN
promote(@c20 AS LONG) | FETCH'
task_count: 392
- task_total_time_ms: 135
+ task_total_time_ms: 20
transform_count: 89
- transform_time_ms: 86
+ transform_time_ms: 7
transform_yield_count: 35
- insert_time_ms: 3
+ insert_time_ms: 0
insert_new_count: 47
insert_reused_count: 5
diff --git a/yaml-tests/src/test/resources/subquery-tests.metrics.binpb b/yaml-tests/src/test/resources/subquery-tests.metrics.binpb
index 3ee181bdd0..ba01f06eab 100644
--- a/yaml-tests/src/test/resources/subquery-tests.metrics.binpb
+++ b/yaml-tests/src/test/resources/subquery-tests.metrics.binpb
@@ -197,4 +197,25 @@ p
rankDir=LR;
2 -> 5 [ color="red" style="invis" ];
}
+}
+
+subquery-testsEXPLAIN select sq.idr, sq.z from (select * from r where idr = 1) sq, (select f from sq.nr where f > 10) sq2 where sq.z = 10 AND sq2.f is not null
+Њ7 Џ(J08@ISCAN(IR [EQUALS @c18, EQUALS promote(@c32 AS INT)]) | FLATMAP q0 -> { EXPLODE q0.NR | FILTER _.F GREATER_THAN @c32 AND _.F NOT_NULL AS q1 RETURN (q0.IDR AS IDR, q0.Z AS Z) }digraph G {
+ fontname=courier;
+ rankdir=BT;
+ splines=polyline;
+ 1 [ label=<| Nested Loop Join |
| FLATMAP (q4.IDR AS IDR, q4.Z AS Z) |
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS IDR, INT AS Z)" ];
+ 2 [ label=<| Index Scan |
| comparisons: [EQUALS @c18, EQUALS promote(@c32 AS INT)] |
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS IDR, INT AS Z, ARRAY(INT AS F) AS NR)" ];
+ 3 [ label=<> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(INT AS IDR, INT AS Z, ARRAY(INT AS F) AS NR)" ];
+ 4 [ label=<| Predicate Filter |
| WHERE q6.F GREATER_THAN @c32 AND q6.F NOT_NULL |
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS F)" ];
+ 5 [ label=<| Value Computation |
| EXPLODE q4.NR |
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS F)" ];
+ 3 -> 2 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
+ 2 -> 1 [ label=< q4> label="q4" color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
+ 5 -> 4 [ label=< q6> label="q6" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
+ 4 -> 1 [ label=< q6> label="q6" color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
+ {
+ rank=same;
+ rankDir=LR;
+ 2 -> 4 [ color="red" style="invis" ];
+ }
}
\ No newline at end of file
diff --git a/yaml-tests/src/test/resources/subquery-tests.metrics.yaml b/yaml-tests/src/test/resources/subquery-tests.metrics.yaml
index 1de2147caa..bb68e60297 100644
--- a/yaml-tests/src/test/resources/subquery-tests.metrics.yaml
+++ b/yaml-tests/src/test/resources/subquery-tests.metrics.yaml
@@ -79,3 +79,16 @@ subquery-tests:
insert_time_ms: 0
insert_new_count: 59
insert_reused_count: 3
+- query: EXPLAIN select sq.idr, sq.z from (select * from r where idr = 1) sq, (select
+ f from sq.nr where f > 10) sq2 where sq.z = 10 AND sq2.f is not null
+ explain: ISCAN(IR [EQUALS @c18, EQUALS promote(@c32 AS INT)]) | FLATMAP q0 ->
+ { EXPLODE q0.NR | FILTER _.F GREATER_THAN @c32 AND _.F NOT_NULL AS q1 RETURN
+ (q0.IDR AS IDR, q0.Z AS Z) }
+ task_count: 878
+ task_total_time_ms: 115
+ transform_count: 233
+ transform_time_ms: 33
+ transform_yield_count: 74
+ insert_time_ms: 11
+ insert_new_count: 172
+ insert_reused_count: 21
diff --git a/yaml-tests/src/test/resources/subquery-tests.yamsql b/yaml-tests/src/test/resources/subquery-tests.yamsql
index 9d8540c3ce..37ffc22ee2 100644
--- a/yaml-tests/src/test/resources/subquery-tests.yamsql
+++ b/yaml-tests/src/test/resources/subquery-tests.yamsql
@@ -19,19 +19,20 @@
---
schema_template:
- create table a(ida integer, x integer, primary key(ida))
- create table x(idx integer, y integer, primary key(idx))
- CREATE TYPE AS STRUCT s(f integer)
- create table r(idr integer, nr s array, primary key(idr))
- create table b(idb integer, q integer, r integer, primary key(idb))
- create index ib as select q from b
- create index ir as select sq.f from r, (select f from r.nr) sq;
+ create table a(ida integer, x integer, primary key(ida))
+ create table x(idx integer, y integer, primary key(idx))
+ CREATE TYPE AS STRUCT s(f integer)
+ create table r(idr integer, z integer, nr s array, primary key(idr))
+ create table b(idb integer, q integer, r integer, primary key(idb))
+ create index ib as select q from b
+ create index ir as select idr, z from r order by idr, z
+ create index ir_f as select sq.f from r, (select f from r.nr) sq;
---
setup:
steps:
- query: INSERT INTO A VALUES (1, 1), (2, 2), (3, 3)
- query: INSERT INTO X VALUES (4, 10), (5, 20), (6, 30)
- - query: INSERT INTO R VALUES (1, [(11), (12), (13)]), (2, [(21), (22), (23)]), (3, [(31), (32), (33)])
+ - query: INSERT INTO R VALUES (1, 10, [(11), (12), (13)]), (2, 20, [(21), (22), (23)]), (3, 30, [(31), (32), (33)])
- query: INSERT INTO B VALUES (1, 10, 100), (2, 20, 200), (3, 30, 300)
---
test_block:
@@ -81,4 +82,9 @@ test_block:
- query: select x from a where exists (select max(a.x), max(idb) from b where q > x group by q)
- explain: "SCAN(<,>) | TFILTER A | FLATMAP q0 -> { ISCAN(IB [[GREATER_THAN q0.X]]) | MAP (_ AS _0) | AGG (max_i(q0.X) AS _0, max_i(_._0.IDB) AS _1) GROUP BY (_._0.Q AS _0) | MAP (_._1._0 AS _0, _._1._1 AS _1) | DEFAULT NULL | FILTER _ NOT_NULL AS q0 RETURN (q0.X AS X) }"
- result: [{1}, {2}, {3}]
+ -
+ # plans with pushed-down predicates should be preferred
+ - query: select sq.idr, sq.z from (select * from r where idr = 1) sq, (select f from sq.nr where f > 10) sq2 where sq.z = 10 AND sq2.f is not null
+ - explain: "ISCAN(IR [EQUALS @c18, EQUALS promote(@c32 AS INT)]) | FLATMAP q0 -> { EXPLODE q0.NR | FILTER _.F GREATER_THAN @c32 AND _.F NOT_NULL AS q1 RETURN (q0.IDR AS IDR, q0.Z AS Z) }"
+ - result: [{1, 10}, {1, 10}, {1, 10}]
...