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 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(0 8@+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 -”; `(0 8@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(0 8@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(0 8@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(0 8@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=<
Scan
range: <-∞, ∞>
> 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=<
Scan
range: <-∞, ∞>
> 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=<
Scan
range: <-∞, ∞>
> 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=<
Scan
range: <-∞, ∞>
> 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(0 8@_SCAN(<,>) | FILTER coalesce_string(_.A.B.A, promote(@c14 AS STRING)) IS_NULL | MAP (_.ID AS ID)digraph G { +@ }(0 8@_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(0 8@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 +Ѹ@ ӑ(0ƒ8@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=<
Index
I1
> 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=<
Index
I1
> 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 *( 0׊8#@'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=<
Index
I1
> 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=<
Index
I1
> 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=<
Index
I1
> 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=<
Index
I1
> 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=<
Index
I1
> 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=<
Index
IR
> 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}] ...