diff --git a/core/trino-main/src/main/java/io/trino/sql/planner/PlanOptimizers.java b/core/trino-main/src/main/java/io/trino/sql/planner/PlanOptimizers.java index bc8e7b983757..e75d88890c74 100644 --- a/core/trino-main/src/main/java/io/trino/sql/planner/PlanOptimizers.java +++ b/core/trino-main/src/main/java/io/trino/sql/planner/PlanOptimizers.java @@ -154,6 +154,7 @@ import io.trino.sql.planner.iterative.rule.PushDownDereferencesThroughTopNRanking; import io.trino.sql.planner.iterative.rule.PushDownDereferencesThroughWindow; import io.trino.sql.planner.iterative.rule.PushDownProjectionsFromPatternRecognition; +import io.trino.sql.planner.iterative.rule.PushFilterThroughCountAggregation; import io.trino.sql.planner.iterative.rule.PushJoinIntoTableScan; import io.trino.sql.planner.iterative.rule.PushLimitIntoTableScan; import io.trino.sql.planner.iterative.rule.PushLimitThroughMarkDistinct; @@ -560,9 +561,19 @@ public PlanOptimizers( new RemoveEmptyUnionBranches(), new EvaluateEmptyIntersect(), new RemoveEmptyExceptBranches(), - new TransformFilteringSemiJoinToInnerJoin(), - new InlineProjectIntoFilter(metadata), - new SimplifyFilterPredicate(metadata)))); // must run after PredicatePushDown + new TransformFilteringSemiJoinToInnerJoin())), // must run after PredicatePushDown + new IterativeOptimizer( + plannerContext, + ruleStats, + statsCalculator, + costCalculator, + ImmutableSet.>builder() + .add(new InlineProjectIntoFilter(metadata)) + .add(new SimplifyFilterPredicate(metadata)) + .addAll(columnPruningRules) + .add(new InlineProjections(plannerContext, typeAnalyzer)) + .addAll(new PushFilterThroughCountAggregation(plannerContext).rules()) // must run after PredicatePushDown and after TransformFilteringSemiJoinToInnerJoin + .build())); // Perform redirection before CBO rules to ensure stats from destination connector are used // Perform redirection before agg, topN, limit, sample etc. push down into table scan as the destination connector may support a different set of push downs diff --git a/core/trino-main/src/main/java/io/trino/sql/planner/iterative/rule/PushFilterThroughCountAggregation.java b/core/trino-main/src/main/java/io/trino/sql/planner/iterative/rule/PushFilterThroughCountAggregation.java new file mode 100644 index 000000000000..e9f62749b2f1 --- /dev/null +++ b/core/trino-main/src/main/java/io/trino/sql/planner/iterative/rule/PushFilterThroughCountAggregation.java @@ -0,0 +1,270 @@ +/* + * 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 io.trino.sql.planner.iterative.rule; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import io.trino.matching.Capture; +import io.trino.matching.Captures; +import io.trino.matching.Pattern; +import io.trino.metadata.BoundSignature; +import io.trino.spi.predicate.Domain; +import io.trino.spi.predicate.Range; +import io.trino.spi.predicate.TupleDomain; +import io.trino.spi.predicate.ValueSet; +import io.trino.sql.PlannerContext; +import io.trino.sql.planner.DomainTranslator; +import io.trino.sql.planner.Symbol; +import io.trino.sql.planner.iterative.Rule; +import io.trino.sql.planner.iterative.Rule.Context; +import io.trino.sql.planner.iterative.Rule.Result; +import io.trino.sql.planner.plan.AggregationNode; +import io.trino.sql.planner.plan.AggregationNode.Aggregation; +import io.trino.sql.planner.plan.FilterNode; +import io.trino.sql.planner.plan.PlanNode; +import io.trino.sql.planner.plan.ProjectNode; +import io.trino.sql.planner.plan.ValuesNode; +import io.trino.sql.tree.Expression; + +import java.util.Optional; +import java.util.Set; + +import static com.google.common.collect.Iterables.getOnlyElement; +import static io.trino.matching.Capture.newCapture; +import static io.trino.sql.ExpressionUtils.combineConjuncts; +import static io.trino.sql.planner.DomainTranslator.getExtractionResult; +import static io.trino.sql.planner.plan.AggregationNode.Step.SINGLE; +import static io.trino.sql.planner.plan.Patterns.aggregation; +import static io.trino.sql.planner.plan.Patterns.filter; +import static io.trino.sql.planner.plan.Patterns.project; +import static io.trino.sql.planner.plan.Patterns.source; +import static io.trino.sql.tree.BooleanLiteral.TRUE_LITERAL; +import static java.util.Objects.requireNonNull; + +/** + * Push down aggregation's mask based on filter predicate. + *

+ * This rule transforms plans with a FilterNode above an AggregationNode. + * The AggregationNode must be grouped and contain a single aggregation + * assignment with `count()` function and a mask. + *

+ * If the filter predicate is `false` for the aggregation's result value `0`, + * then the aggregation's mask can removed from the aggregation, and + * applied as a filter below the AggregationNode. After such transformation, + * any group such that no rows of that group pass the filter, is removed + * by the pushed down FilterNode, and so it is not processed by the + * AggregationNode. Before the transformation, the group would be processed + * by the AggregationNode, and return `0`, which would then be filtered out + * by the root FilterNode. + *

+ * After the mask pushdown, it is checked whether the root FilterNode is + * still needed, based on the fact that the aggregation never returns `0`. + *

+ * Transforms: + *

 {@code
+ * - filter (count > 0 AND predicate)
+ *     - aggregation
+ *       group by a
+ *       count <- count() mask: m
+ *         - source (a, m)
+ *       }
+ * 
+ * into: + *
 {@code
+ * - filter (predicate)
+ *     - aggregation
+ *       group by a
+ *       count <- count()
+ *         - filter (m)
+ *             - source (a, m)
+ *       }
+ * 
+ */ +public class PushFilterThroughCountAggregation +{ + private final PlannerContext plannerContext; + + public PushFilterThroughCountAggregation(PlannerContext plannerContext) + { + this.plannerContext = requireNonNull(plannerContext, "plannerContext is null"); + } + + public Set> rules() + { + return ImmutableSet.of( + new PushFilterThroughCountAggregationWithoutProject(plannerContext), + new PushFilterThroughCountAggregationWithProject(plannerContext)); + } + + @VisibleForTesting + public static final class PushFilterThroughCountAggregationWithoutProject + implements Rule + { + private static final Capture AGGREGATION = newCapture(); + + private final PlannerContext plannerContext; + private final Pattern pattern; + + public PushFilterThroughCountAggregationWithoutProject(PlannerContext plannerContext) + { + this.plannerContext = requireNonNull(plannerContext, "plannerContext is null"); + this.pattern = filter() + .with(source().matching(aggregation() + .matching(PushFilterThroughCountAggregation::isGroupedCountWithMask) + .capturedAs(AGGREGATION))); + } + + @Override + public Pattern getPattern() + { + return pattern; + } + + @Override + public Result apply(FilterNode node, Captures captures, Context context) + { + return pushFilter(node, captures.get(AGGREGATION), Optional.empty(), plannerContext, context); + } + } + + @VisibleForTesting + public static final class PushFilterThroughCountAggregationWithProject + implements Rule + { + private static final Capture PROJECT = newCapture(); + private static final Capture AGGREGATION = newCapture(); + + private final PlannerContext plannerContext; + private final Pattern pattern; + + public PushFilterThroughCountAggregationWithProject(PlannerContext plannerContext) + { + this.plannerContext = requireNonNull(plannerContext, "plannerContext is null"); + this.pattern = filter() + .with(source().matching(project() + .matching(ProjectNode::isIdentity) + .capturedAs(PROJECT) + .with(source().matching(aggregation() + .matching(PushFilterThroughCountAggregation::isGroupedCountWithMask) + .capturedAs(AGGREGATION))))); + } + + @Override + public Pattern getPattern() + { + return pattern; + } + + @Override + public Result apply(FilterNode node, Captures captures, Context context) + { + return pushFilter(node, captures.get(AGGREGATION), Optional.of(captures.get(PROJECT)), plannerContext, context); + } + } + + private static Result pushFilter(FilterNode filterNode, AggregationNode aggregationNode, Optional projectNode, PlannerContext plannerContext, Context context) + { + Symbol countSymbol = getOnlyElement(aggregationNode.getAggregations().keySet()); + Aggregation aggregation = getOnlyElement(aggregationNode.getAggregations().values()); + + DomainTranslator.ExtractionResult extractionResult = getExtractionResult(plannerContext, context.getSession(), filterNode.getPredicate(), context.getSymbolAllocator().getTypes()); + TupleDomain tupleDomain = extractionResult.getTupleDomain(); + + if (tupleDomain.isNone()) { + // Filter predicate is never satisfied. Replace filter with empty values. + return Result.ofPlanNode(new ValuesNode(filterNode.getId(), filterNode.getOutputSymbols(), ImmutableList.of())); + } + Domain countDomain = tupleDomain.getDomains().get().get(countSymbol); + if (countDomain == null) { + // Filter predicate domain contains all countSymbol values. Cannot filter out `0`. + return Result.empty(); + } + if (countDomain.contains(Domain.singleValue(countDomain.getType(), 0L))) { + // Filter predicate domain contains `0`. Cannot filter out `0`. + return Result.empty(); + } + + // Push down the aggregation's mask. + FilterNode source = new FilterNode( + context.getIdAllocator().getNextId(), + aggregationNode.getSource(), + aggregation.getMask().get().toSymbolReference()); + + // Remove mask from the aggregation. + Aggregation newAggregation = new Aggregation( + aggregation.getResolvedFunction(), + aggregation.getArguments(), + aggregation.isDistinct(), + aggregation.getFilter(), + aggregation.getOrderingScheme(), + Optional.empty()); + + AggregationNode newAggregationNode = new AggregationNode( + aggregationNode.getId(), + source, + ImmutableMap.of(countSymbol, newAggregation), + aggregationNode.getGroupingSets(), + aggregationNode.getPreGroupedSymbols(), + aggregationNode.getStep(), + aggregationNode.getHashSymbol(), + aggregationNode.getGroupIdSymbol()); + + // Restore identity projection if it is present in the original plan. + PlanNode filterSource = projectNode.map(project -> project.replaceChildren(ImmutableList.of(newAggregationNode))).orElse(newAggregationNode); + + // Try to simplify filter above the aggregation. + if (countDomain.getValues().contains(ValueSet.ofRanges(Range.greaterThanOrEqual(countDomain.getType(), 1L)))) { + // After filtering out `0` values, filter predicate's domain contains all remaining countSymbol values. Remove the countSymbol domain. + TupleDomain newTupleDomain = tupleDomain.filter((symbol, domain) -> !symbol.equals(countSymbol)); + Expression newPredicate = combineConjuncts( + plannerContext.getMetadata(), + new DomainTranslator(plannerContext).toPredicate(context.getSession(), newTupleDomain), + extractionResult.getRemainingExpression()); + if (newPredicate.equals(TRUE_LITERAL)) { + return Result.ofPlanNode(filterSource); + } + return Result.ofPlanNode(new FilterNode(filterNode.getId(), filterSource, newPredicate)); + } + + // Filter predicate cannot be simplified. + return Result.ofPlanNode(filterNode.replaceChildren(ImmutableList.of(filterSource))); + } + + private static boolean isGroupedCountWithMask(AggregationNode aggregationNode) + { + if (!isGroupedAggregation(aggregationNode)) { + return false; + } + if (aggregationNode.getAggregations().size() != 1) { + return false; + } + Aggregation aggregation = getOnlyElement(aggregationNode.getAggregations().values()); + + if (aggregation.getMask().isEmpty() || aggregation.getFilter().isPresent()) { + return false; + } + + BoundSignature signature = aggregation.getResolvedFunction().getSignature(); + return signature.getArgumentTypes().isEmpty() && signature.getName().equals("count"); + } + + private static boolean isGroupedAggregation(AggregationNode aggregationNode) + { + return aggregationNode.hasNonEmptyGroupingSet() && + aggregationNode.getGroupingSetCount() == 1 && + aggregationNode.getStep() == SINGLE; + } +} diff --git a/core/trino-main/src/test/java/io/trino/sql/planner/TestLogicalPlanner.java b/core/trino-main/src/test/java/io/trino/sql/planner/TestLogicalPlanner.java index 3dba37642f58..dcceba85cfc0 100644 --- a/core/trino-main/src/test/java/io/trino/sql/planner/TestLogicalPlanner.java +++ b/core/trino-main/src/test/java/io/trino/sql/planner/TestLogicalPlanner.java @@ -151,6 +151,7 @@ import static io.trino.sql.planner.rowpattern.ir.IrQuantifier.oneOrMore; import static io.trino.sql.tree.BooleanLiteral.TRUE_LITERAL; import static io.trino.sql.tree.ComparisonExpression.Operator.EQUAL; +import static io.trino.sql.tree.ComparisonExpression.Operator.GREATER_THAN; import static io.trino.sql.tree.ComparisonExpression.Operator.LESS_THAN; import static io.trino.sql.tree.FrameBound.Type.CURRENT_ROW; import static io.trino.sql.tree.FrameBound.Type.UNBOUNDED_FOLLOWING; @@ -943,23 +944,25 @@ public void testDoubleNestedCorrelatedSubqueries() public void testCorrelatedScalarAggregationRewriteToLeftOuterJoin() { assertPlan( - "SELECT orderkey FROM orders WHERE EXISTS(SELECT 1 WHERE orderkey = 3)", // EXISTS maps to count(*) > 0 - anyTree( - filter("FINAL_COUNT > BIGINT '0'", - project( - aggregation( - singleGroupingSet("ORDERKEY", "UNIQUE"), - ImmutableMap.of(Optional.of("FINAL_COUNT"), functionCall("count", ImmutableList.of())), - ImmutableList.of("ORDERKEY", "UNIQUE"), - ImmutableList.of("NON_NULL"), - Optional.empty(), - SINGLE, - join(LEFT, ImmutableList.of(), Optional.of("BIGINT '3' = ORDERKEY"), - assignUniqueId( - "UNIQUE", - tableScan("orders", ImmutableMap.of("ORDERKEY", "orderkey"))), - project(ImmutableMap.of("NON_NULL", expression("true")), - node(ValuesNode.class)))))))); + "SELECT orderkey, EXISTS(SELECT 1 WHERE orderkey = 3) FROM orders", // EXISTS maps to count(*) > 0 + output( + strictProject( + ImmutableMap.of( + "ORDERKEY", expression("ORDERKEY"), + "exists", expression("FINAL_COUNT > BIGINT '0'")), + aggregation( + singleGroupingSet("ORDERKEY", "UNIQUE"), + ImmutableMap.of(Optional.of("FINAL_COUNT"), functionCall("count", ImmutableList.of())), + ImmutableList.of("ORDERKEY", "UNIQUE"), + ImmutableList.of("NON_NULL"), + Optional.empty(), + SINGLE, + join(LEFT, ImmutableList.of(), Optional.of("BIGINT '3' = ORDERKEY"), + assignUniqueId( + "UNIQUE", + tableScan("orders", ImmutableMap.of("ORDERKEY", "orderkey"))), + project(ImmutableMap.of("NON_NULL", expression("true")), + node(ValuesNode.class))))))); } @Test @@ -1210,32 +1213,38 @@ public void testCorrelatedIn() { assertPlan( "SELECT name FROM region r WHERE regionkey IN (SELECT regionkey FROM nation WHERE name < r.name)", - anyTree( - filter( - "count_matches > BIGINT '0'", - project( - aggregation( - singleGroupingSet("region_regionkey", "region_name", "unique"), - ImmutableMap.of(Optional.of("count_matches"), functionCall("count", ImmutableList.of())), - ImmutableList.of("region_regionkey", "region_name", "unique"), - ImmutableList.of("mask"), - Optional.empty(), - SINGLE, - project( - ImmutableMap.of("mask", expression("((NOT (region_regionkey IS NULL)) AND (NOT (nation_regionkey IS NULL)))")), + output( + project( + ImmutableMap.of("region_name", expression("region_name")), + aggregation( + singleGroupingSet("region_regionkey", "region_name", "unique"), + ImmutableMap.of(), + Optional.empty(), + SINGLE, + project( + ImmutableMap.of( + "region_regionkey", expression("region_regionkey"), + "region_name", expression("region_name"), + "unique", expression("unique")), + filter( + "(region_regionkey IS NULL OR region_regionkey = nation_regionkey OR nation_regionkey IS NULL) AND nation_name < region_name", join( - LEFT, + INNER, ImmutableList.of(), - Optional.of("(region_regionkey IS NULL OR region_regionkey = nation_regionkey OR nation_regionkey IS NULL) AND nation_name < region_name"), + ImmutableList.of(new PlanMatchPattern.DynamicFilterPattern("region_name", GREATER_THAN, "nation_name")), assignUniqueId( "unique", - tableScan("region", ImmutableMap.of( - "region_regionkey", "regionkey", - "region_name", "name"))), + filter( + "NOT (region_regionkey IS NULL)", + tableScan("region", ImmutableMap.of( + "region_regionkey", "regionkey", + "region_name", "name")))), any( - tableScan("nation", ImmutableMap.of( - "nation_name", "name", - "nation_regionkey", "regionkey")))))))))); + filter( + "NOT (nation_regionkey IS NULL)", + tableScan("nation", ImmutableMap.of( + "nation_name", "name", + "nation_regionkey", "regionkey"))))))))))); } @Test @@ -1243,29 +1252,28 @@ public void testCorrelatedExists() { assertPlan( "SELECT regionkey, name FROM region r WHERE EXISTS(SELECT regionkey FROM nation WHERE name < r.name)", - anyTree( - filter( - "count_matches > BIGINT '0'", - project( - aggregation( - singleGroupingSet("region_regionkey", "region_name", "unique"), - ImmutableMap.of(Optional.of("count_matches"), functionCall("count", ImmutableList.of())), - ImmutableList.of("region_regionkey", "region_name", "unique"), - ImmutableList.of("mask"), - Optional.empty(), - SINGLE, - join( - LEFT, - ImmutableList.of(), - Optional.of("nation_name < region_name"), - assignUniqueId( - "unique", - tableScan("region", ImmutableMap.of( - "region_regionkey", "regionkey", - "region_name", "name"))), - any( - project( - ImmutableMap.of("mask", expression("true")), + output( + project( + aggregation( + singleGroupingSet("region_regionkey", "region_name", "unique"), + ImmutableMap.of(), + Optional.empty(), + SINGLE, + project( + filter( + "nation_name < region_name", + join( + INNER, + ImmutableList.of(), + ImmutableList.of(new PlanMatchPattern.DynamicFilterPattern("region_name", GREATER_THAN, "nation_name")), + assignUniqueId( + "unique", + filter( + "true", + tableScan("region", ImmutableMap.of( + "region_regionkey", "regionkey", + "region_name", "name")))), + any( tableScan("nation", ImmutableMap.of("nation_name", "name")))))))))); } diff --git a/core/trino-main/src/test/java/io/trino/sql/planner/iterative/rule/TestPushFilterThroughCountAggregation.java b/core/trino-main/src/test/java/io/trino/sql/planner/iterative/rule/TestPushFilterThroughCountAggregation.java new file mode 100644 index 000000000000..adfc4c83277d --- /dev/null +++ b/core/trino-main/src/test/java/io/trino/sql/planner/iterative/rule/TestPushFilterThroughCountAggregation.java @@ -0,0 +1,365 @@ +/* + * 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 io.trino.sql.planner.iterative.rule; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import io.trino.sql.planner.Symbol; +import io.trino.sql.planner.iterative.rule.PushFilterThroughCountAggregation.PushFilterThroughCountAggregationWithProject; +import io.trino.sql.planner.iterative.rule.PushFilterThroughCountAggregation.PushFilterThroughCountAggregationWithoutProject; +import io.trino.sql.planner.iterative.rule.test.BaseRuleTest; +import io.trino.sql.planner.iterative.rule.test.PlanBuilder; +import io.trino.sql.planner.plan.Assignments; +import org.testng.annotations.Test; + +import static io.trino.spi.type.BigintType.BIGINT; +import static io.trino.sql.planner.assertions.PlanMatchPattern.aggregation; +import static io.trino.sql.planner.assertions.PlanMatchPattern.expression; +import static io.trino.sql.planner.assertions.PlanMatchPattern.filter; +import static io.trino.sql.planner.assertions.PlanMatchPattern.functionCall; +import static io.trino.sql.planner.assertions.PlanMatchPattern.project; +import static io.trino.sql.planner.assertions.PlanMatchPattern.values; + +public class TestPushFilterThroughCountAggregation + extends BaseRuleTest +{ + @Test + public void testDoesNotFireWithNonGroupedAggregation() + { + tester().assertThat(new PushFilterThroughCountAggregationWithoutProject(tester().getPlannerContext())) + .on(p -> { + Symbol g = p.symbol("g"); + Symbol mask = p.symbol("mask"); + Symbol count = p.symbol("count"); + return p.filter( + PlanBuilder.expression("count > 0"), + p.aggregation(builder -> builder + .globalGrouping() + .addAggregation(count, PlanBuilder.expression("count()"), ImmutableList.of(), mask) + .source(p.values(g, mask)))); + }) + .doesNotFire(); + } + + @Test + public void testDoesNotFireWithMultipleAggregations() + { + tester().assertThat(new PushFilterThroughCountAggregationWithoutProject(tester().getPlannerContext())) + .on(p -> { + Symbol g = p.symbol("g"); + Symbol mask = p.symbol("mask"); + Symbol count = p.symbol("count"); + Symbol avg = p.symbol("avg"); + return p.filter( + PlanBuilder.expression("count > 0"), + p.aggregation(builder -> builder + .singleGroupingSet(g) + .addAggregation(count, PlanBuilder.expression("count()"), ImmutableList.of(), mask) + .addAggregation(avg, PlanBuilder.expression("avg(g)"), ImmutableList.of(BIGINT), mask) + .source(p.values(g, mask)))); + }) + .doesNotFire(); + } + + @Test + public void testDoesNotFireWithNoAggregations() + { + tester().assertThat(new PushFilterThroughCountAggregationWithoutProject(tester().getPlannerContext())) + .on(p -> { + Symbol g = p.symbol("g"); + Symbol mask = p.symbol("mask"); + return p.filter( + PlanBuilder.expression("true"), + p.aggregation(builder -> builder + .singleGroupingSet(g) + .source(p.values(g, mask)))); + }) + .doesNotFire(); + } + + @Test + public void testDoesNotFireWithNoMask() + { + tester().assertThat(new PushFilterThroughCountAggregationWithoutProject(tester().getPlannerContext())) + .on(p -> { + Symbol g = p.symbol("g"); + Symbol count = p.symbol("count"); + return p.filter( + PlanBuilder.expression("count > 0"), + p.aggregation(builder -> builder + .singleGroupingSet(g) + .addAggregation(count, PlanBuilder.expression("count()"), ImmutableList.of()) + .source(p.values(g)))); + }) + .doesNotFire(); + } + + @Test + public void testDoesNotFireWithNoCountAggregation() + { + tester().assertThat(new PushFilterThroughCountAggregationWithoutProject(tester().getPlannerContext())) + .on(p -> { + Symbol g = p.symbol("g"); + Symbol mask = p.symbol("mask"); + Symbol count = p.symbol("count"); + return p.filter( + PlanBuilder.expression("count > 0"), + p.aggregation(builder -> builder + .singleGroupingSet(g) + .addAggregation(count, PlanBuilder.expression("count(g)"), ImmutableList.of(BIGINT), mask) + .source(p.values(g, mask)))); + }) + .doesNotFire(); + + tester().assertThat(new PushFilterThroughCountAggregationWithoutProject(tester().getPlannerContext())) + .on(p -> { + Symbol g = p.symbol("g"); + Symbol mask = p.symbol("mask"); + Symbol avg = p.symbol("avg"); + return p.filter( + PlanBuilder.expression("avg > 0"), + p.aggregation(builder -> builder + .singleGroupingSet(g) + .addAggregation(avg, PlanBuilder.expression("avg(g)"), ImmutableList.of(BIGINT), mask) + .source(p.values(g, mask)))); + }) + .doesNotFire(); + } + + @Test + public void testFilterPredicateFalse() + { + tester().assertThat(new PushFilterThroughCountAggregationWithoutProject(tester().getPlannerContext())) + .on(p -> { + Symbol g = p.symbol("g"); + Symbol mask = p.symbol("mask"); + Symbol count = p.symbol("count"); + return p.filter( + PlanBuilder.expression("count < 0 AND count > 0"), + p.aggregation(builder -> builder + .singleGroupingSet(g) + .addAggregation(count, PlanBuilder.expression("count()"), ImmutableList.of(), mask) + .source(p.values(g, mask)))); + }) + .matches( + values("g", "count")); + } + + @Test + public void testDoesNotFireWhenFilterPredicateTrue() + { + tester().assertThat(new PushFilterThroughCountAggregationWithoutProject(tester().getPlannerContext())) + .on(p -> { + Symbol g = p.symbol("g"); + Symbol mask = p.symbol("mask"); + Symbol count = p.symbol("count"); + return p.filter( + PlanBuilder.expression("true"), + p.aggregation(builder -> builder + .singleGroupingSet(g) + .addAggregation(count, PlanBuilder.expression("count()"), ImmutableList.of(), mask) + .source(p.values(g, mask)))); + }) + .doesNotFire(); + } + + @Test + public void testDoesNotFireWhenFilterPredicateSatisfiedByAllCountValues() + { + tester().assertThat(new PushFilterThroughCountAggregationWithoutProject(tester().getPlannerContext())) + .on(p -> { + Symbol g = p.symbol("g"); + Symbol mask = p.symbol("mask"); + Symbol count = p.symbol("count"); + return p.filter( + PlanBuilder.expression("(count < 0 OR count >= 0) AND g = 5"), + p.aggregation(builder -> builder + .singleGroupingSet(g) + .addAggregation(count, PlanBuilder.expression("count()"), ImmutableList.of(), mask) + .source(p.values(g, mask)))); + }) + .doesNotFire(); + } + + @Test + public void testPushDownMaskAndRemoveFilter() + { + tester().assertThat(new PushFilterThroughCountAggregationWithoutProject(tester().getPlannerContext())) + .on(p -> { + Symbol g = p.symbol("g"); + Symbol mask = p.symbol("mask"); + Symbol count = p.symbol("count"); + return p.filter( + PlanBuilder.expression("count > 0"), + p.aggregation(builder -> builder + .singleGroupingSet(g) + .addAggregation(count, PlanBuilder.expression("count()"), ImmutableList.of(), mask) + .source(p.values(g, mask)))); + }) + .matches( + aggregation( + ImmutableMap.of("count", functionCall("count", ImmutableList.of())), + filter( + "mask", + values("g", "mask")))); + } + + @Test + public void testPushDownMaskAndSimplifyFilter() + { + tester().assertThat(new PushFilterThroughCountAggregationWithoutProject(tester().getPlannerContext())) + .on(p -> { + Symbol g = p.symbol("g"); + Symbol mask = p.symbol("mask"); + Symbol count = p.symbol("count"); + return p.filter( + PlanBuilder.expression("count > 0 AND g > 5"), + p.aggregation(builder -> builder + .singleGroupingSet(g) + .addAggregation(count, PlanBuilder.expression("count()"), ImmutableList.of(), mask) + .source(p.values(g, mask)))); + }) + .matches( + filter( + "g > 5", + aggregation( + ImmutableMap.of("count", functionCall("count", ImmutableList.of())), + filter( + "mask", + values("g", "mask"))))); + + tester().assertThat(new PushFilterThroughCountAggregationWithoutProject(tester().getPlannerContext())) + .on(p -> { + Symbol g = p.symbol("g"); + Symbol mask = p.symbol("mask"); + Symbol count = p.symbol("count"); + return p.filter( + PlanBuilder.expression("count > 0 AND count % 2 = 0"), + p.aggregation(builder -> builder + .singleGroupingSet(g) + .addAggregation(count, PlanBuilder.expression("count()"), ImmutableList.of(), mask) + .source(p.values(g, mask)))); + }) + .matches( + filter( + "count % 2 = 0", + aggregation( + ImmutableMap.of("count", functionCall("count", ImmutableList.of())), + filter( + "mask", + values("g", "mask"))))); + } + + @Test + public void testPushDownMaskAndRetainFilter() + { + tester().assertThat(new PushFilterThroughCountAggregationWithoutProject(tester().getPlannerContext())) + .on(p -> { + Symbol g = p.symbol("g"); + Symbol mask = p.symbol("mask"); + Symbol count = p.symbol("count"); + return p.filter( + PlanBuilder.expression("count > 5"), + p.aggregation(builder -> builder + .singleGroupingSet(g) + .addAggregation(count, PlanBuilder.expression("count()"), ImmutableList.of(), mask) + .source(p.values(g, mask)))); + }) + .matches( + filter( + "count > 5", + aggregation( + ImmutableMap.of("count", functionCall("count", ImmutableList.of())), + filter( + "mask", + values("g", "mask"))))); + } + + @Test + public void testWithProject() + { + tester().assertThat(new PushFilterThroughCountAggregationWithProject(tester().getPlannerContext())) + .on(p -> { + Symbol g = p.symbol("g"); + Symbol mask = p.symbol("mask"); + Symbol count = p.symbol("count"); + return p.filter( + PlanBuilder.expression("count > 0"), + p.project( + Assignments.identity(count), + p.aggregation(builder -> builder + .singleGroupingSet(g) + .addAggregation(count, PlanBuilder.expression("count()"), ImmutableList.of(), mask) + .source(p.values(g, mask))))); + }) + .matches( + project( + ImmutableMap.of("count", expression("count")), + aggregation( + ImmutableMap.of("count", functionCall("count", ImmutableList.of())), + filter( + "mask", + values("g", "mask"))))); + + tester().assertThat(new PushFilterThroughCountAggregationWithProject(tester().getPlannerContext())) + .on(p -> { + Symbol g = p.symbol("g"); + Symbol mask = p.symbol("mask"); + Symbol count = p.symbol("count"); + return p.filter( + PlanBuilder.expression("count > 0 AND g > 5"), + p.project( + Assignments.identity(count, g), + p.aggregation(builder -> builder + .singleGroupingSet(g) + .addAggregation(count, PlanBuilder.expression("count()"), ImmutableList.of(), mask) + .source(p.values(g, mask))))); + }) + .matches( + filter( + "g > 5", + project( + ImmutableMap.of("count", expression("count"), "g", expression("g")), + aggregation( + ImmutableMap.of("count", functionCall("count", ImmutableList.of())), + filter( + "mask", + values("g", "mask")))))); + + tester().assertThat(new PushFilterThroughCountAggregationWithProject(tester().getPlannerContext())) + .on(p -> { + Symbol g = p.symbol("g"); + Symbol mask = p.symbol("mask"); + Symbol count = p.symbol("count"); + return p.filter( + PlanBuilder.expression("count > 5"), + p.project( + Assignments.identity(count), + p.aggregation(builder -> builder + .singleGroupingSet(g) + .addAggregation(count, PlanBuilder.expression("count()"), ImmutableList.of(), mask) + .source(p.values(g, mask))))); + }) + .matches( + filter( + "count > 5", + project( + ImmutableMap.of("count", expression("count")), + aggregation( + ImmutableMap.of("count", functionCall("count", ImmutableList.of())), + filter( + "mask", + values("g", "mask")))))); + } +} diff --git a/testing/trino-benchto-benchmarks/src/test/resources/sql/presto/tpcds/partitioned/q16.plan.txt b/testing/trino-benchto-benchmarks/src/test/resources/sql/presto/tpcds/partitioned/q16.plan.txt index f175463ef66c..2583381afcd8 100644 --- a/testing/trino-benchto-benchmarks/src/test/resources/sql/presto/tpcds/partitioned/q16.plan.txt +++ b/testing/trino-benchto-benchmarks/src/test/resources/sql/presto/tpcds/partitioned/q16.plan.txt @@ -2,30 +2,34 @@ final aggregation over () local exchange (GATHER, SINGLE, []) remote exchange (GATHER, SINGLE, []) partial aggregation over () - join (LEFT, PARTITIONED): - final aggregation over (ca_state, cc_county, cs_call_center_sk, cs_ext_ship_cost, cs_net_profit, cs_order_number, cs_ship_addr_sk, cs_ship_date_sk, cs_warehouse_sk, d_date, unique) - local exchange (GATHER, SINGLE, []) - partial aggregation over (ca_state, cc_county, cs_call_center_sk, cs_ext_ship_cost, cs_net_profit, cs_order_number, cs_ship_addr_sk, cs_ship_date_sk, cs_warehouse_sk, d_date, unique) - join (RIGHT, PARTITIONED): - remote exchange (REPARTITION, HASH, ["cs_order_number_25"]) - scan catalog_sales - local exchange (GATHER, SINGLE, []) - remote exchange (REPARTITION, HASH, ["cs_order_number"]) - join (INNER, REPLICATED): - join (INNER, REPLICATED): - join (INNER, REPLICATED): - scan catalog_sales - local exchange (GATHER, SINGLE, []) - remote exchange (REPLICATE, BROADCAST, []) - scan date_dim - local exchange (GATHER, SINGLE, []) - remote exchange (REPLICATE, BROADCAST, []) - scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cs_order_number_25"]) + join (LEFT, REPLICATED): + final aggregation over (ca_state, cc_county, cs_call_center_sk, cs_ext_ship_cost, cs_net_profit, cs_order_number_25, cs_ship_addr_sk, cs_ship_date_sk, cs_warehouse_sk, d_date, unique) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ca_state", "cc_county", "cs_call_center_sk", "cs_ext_ship_cost", "cs_net_profit", "cs_order_number_25", "cs_ship_addr_sk", "cs_ship_date_sk", "cs_warehouse_sk", "d_date", "unique"]) + partial aggregation over (ca_state, cc_county, cs_call_center_sk, cs_ext_ship_cost, cs_net_profit, cs_order_number_25, cs_ship_addr_sk, cs_ship_date_sk, cs_warehouse_sk, d_date, unique) + join (INNER, REPLICATED, can skip output duplicates): + scan catalog_sales local exchange (GATHER, SINGLE, []) remote exchange (REPLICATE, BROADCAST, []) - scan call_center - final aggregation over (cr_order_number) - local exchange (GATHER, SINGLE, []) - remote exchange (REPARTITION, HASH, ["cr_order_number"]) - partial aggregation over (cr_order_number) - scan catalog_returns + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan call_center + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + final aggregation over (cr_order_number) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cr_order_number"]) + partial aggregation over (cr_order_number) + scan catalog_returns diff --git a/testing/trino-benchto-benchmarks/src/test/resources/sql/presto/tpcds/partitioned/q41.plan.txt b/testing/trino-benchto-benchmarks/src/test/resources/sql/presto/tpcds/partitioned/q41.plan.txt index c981cfc5a922..58a96c8d1ec2 100644 --- a/testing/trino-benchto-benchmarks/src/test/resources/sql/presto/tpcds/partitioned/q41.plan.txt +++ b/testing/trino-benchto-benchmarks/src/test/resources/sql/presto/tpcds/partitioned/q41.plan.txt @@ -4,17 +4,9 @@ local exchange (GATHER, SINGLE, []) local exchange (GATHER, SINGLE, []) remote exchange (REPARTITION, HASH, ["i_product_name"]) partial aggregation over (i_product_name) - cross join (can skip output duplicates): - join (LEFT, REPLICATED, can skip output duplicates): + single aggregation over (i_manufact, i_manufact_id, i_product_name, unique) + join (INNER, REPLICATED, can skip output duplicates): scan item local exchange (GATHER, SINGLE, []) remote exchange (REPLICATE, BROADCAST, []) - final aggregation over (i_manufact_14) - local exchange (GATHER, SINGLE, []) - remote exchange (REPARTITION, HASH, ["i_manufact_14"]) - partial aggregation over (i_manufact_14) - scan item - local exchange (GATHER, SINGLE, []) - remote exchange (REPLICATE, BROADCAST, []) - single aggregation over () - values (1 rows) + scan item diff --git a/testing/trino-benchto-benchmarks/src/test/resources/sql/presto/tpcds/partitioned/q94.plan.txt b/testing/trino-benchto-benchmarks/src/test/resources/sql/presto/tpcds/partitioned/q94.plan.txt index 1044d77d30a8..259bf30db48e 100644 --- a/testing/trino-benchto-benchmarks/src/test/resources/sql/presto/tpcds/partitioned/q94.plan.txt +++ b/testing/trino-benchto-benchmarks/src/test/resources/sql/presto/tpcds/partitioned/q94.plan.txt @@ -3,10 +3,10 @@ final aggregation over () remote exchange (GATHER, SINGLE, []) partial aggregation over () join (LEFT, PARTITIONED): - final aggregation over (ca_state, d_date, unique, web_company_name, ws_ext_ship_cost, ws_net_profit, ws_order_number, ws_ship_addr_sk, ws_ship_date_sk, ws_warehouse_sk, ws_web_site_sk) + final aggregation over (ca_state, d_date, unique, web_company_name, ws_ext_ship_cost, ws_net_profit, ws_order_number_25, ws_ship_addr_sk, ws_ship_date_sk, ws_warehouse_sk, ws_web_site_sk) local exchange (GATHER, SINGLE, []) - partial aggregation over (ca_state, d_date, unique, web_company_name, ws_ext_ship_cost, ws_net_profit, ws_order_number, ws_ship_addr_sk, ws_ship_date_sk, ws_warehouse_sk, ws_web_site_sk) - join (RIGHT, PARTITIONED): + partial aggregation over (ca_state, d_date, unique, web_company_name, ws_ext_ship_cost, ws_net_profit, ws_order_number_25, ws_ship_addr_sk, ws_ship_date_sk, ws_warehouse_sk, ws_web_site_sk) + join (INNER, PARTITIONED, can skip output duplicates): remote exchange (REPARTITION, HASH, ["ws_order_number_25"]) scan web_sales local exchange (GATHER, SINGLE, []) diff --git a/testing/trino-benchto-benchmarks/src/test/resources/sql/presto/tpcds/unpartitioned/q16.plan.txt b/testing/trino-benchto-benchmarks/src/test/resources/sql/presto/tpcds/unpartitioned/q16.plan.txt index 390a96298870..ac81595cda39 100644 --- a/testing/trino-benchto-benchmarks/src/test/resources/sql/presto/tpcds/unpartitioned/q16.plan.txt +++ b/testing/trino-benchto-benchmarks/src/test/resources/sql/presto/tpcds/unpartitioned/q16.plan.txt @@ -9,24 +9,26 @@ final aggregation over () remote exchange (REPARTITION, HASH, ["cr_order_number"]) partial aggregation over (cr_order_number) scan catalog_returns - final aggregation over (ca_state, cc_county, cs_call_center_sk, cs_ext_ship_cost, cs_net_profit, cs_order_number, cs_ship_addr_sk, cs_ship_date_sk, cs_warehouse_sk, d_date, unique) - local exchange (GATHER, SINGLE, []) - partial aggregation over (ca_state, cc_county, cs_call_center_sk, cs_ext_ship_cost, cs_net_profit, cs_order_number, cs_ship_addr_sk, cs_ship_date_sk, cs_warehouse_sk, d_date, unique) - join (RIGHT, PARTITIONED): - remote exchange (REPARTITION, HASH, ["cs_order_number_26"]) - scan catalog_sales - local exchange (GATHER, SINGLE, []) - remote exchange (REPARTITION, HASH, ["cs_order_number"]) - join (INNER, REPLICATED): - join (INNER, REPLICATED): - join (INNER, REPLICATED): - scan catalog_sales - local exchange (GATHER, SINGLE, []) - remote exchange (REPLICATE, BROADCAST, []) - scan customer_address - local exchange (GATHER, SINGLE, []) - remote exchange (REPLICATE, BROADCAST, []) - scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cs_order_number_26"]) + final aggregation over (ca_state, cc_county, cs_call_center_sk, cs_ext_ship_cost, cs_net_profit, cs_order_number_26, cs_ship_addr_sk, cs_ship_date_sk, cs_warehouse_sk, d_date, unique) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ca_state", "cc_county", "cs_call_center_sk", "cs_ext_ship_cost", "cs_net_profit", "cs_order_number_26", "cs_ship_addr_sk", "cs_ship_date_sk", "cs_warehouse_sk", "d_date", "unique"]) + partial aggregation over (ca_state, cc_county, cs_call_center_sk, cs_ext_ship_cost, cs_net_profit, cs_order_number_26, cs_ship_addr_sk, cs_ship_date_sk, cs_warehouse_sk, d_date, unique) + join (INNER, REPLICATED, can skip output duplicates): + scan catalog_sales local exchange (GATHER, SINGLE, []) remote exchange (REPLICATE, BROADCAST, []) - scan call_center + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan call_center diff --git a/testing/trino-benchto-benchmarks/src/test/resources/sql/presto/tpcds/unpartitioned/q41.plan.txt b/testing/trino-benchto-benchmarks/src/test/resources/sql/presto/tpcds/unpartitioned/q41.plan.txt index c981cfc5a922..58a96c8d1ec2 100644 --- a/testing/trino-benchto-benchmarks/src/test/resources/sql/presto/tpcds/unpartitioned/q41.plan.txt +++ b/testing/trino-benchto-benchmarks/src/test/resources/sql/presto/tpcds/unpartitioned/q41.plan.txt @@ -4,17 +4,9 @@ local exchange (GATHER, SINGLE, []) local exchange (GATHER, SINGLE, []) remote exchange (REPARTITION, HASH, ["i_product_name"]) partial aggregation over (i_product_name) - cross join (can skip output duplicates): - join (LEFT, REPLICATED, can skip output duplicates): + single aggregation over (i_manufact, i_manufact_id, i_product_name, unique) + join (INNER, REPLICATED, can skip output duplicates): scan item local exchange (GATHER, SINGLE, []) remote exchange (REPLICATE, BROADCAST, []) - final aggregation over (i_manufact_14) - local exchange (GATHER, SINGLE, []) - remote exchange (REPARTITION, HASH, ["i_manufact_14"]) - partial aggregation over (i_manufact_14) - scan item - local exchange (GATHER, SINGLE, []) - remote exchange (REPLICATE, BROADCAST, []) - single aggregation over () - values (1 rows) + scan item diff --git a/testing/trino-benchto-benchmarks/src/test/resources/sql/presto/tpcds/unpartitioned/q94.plan.txt b/testing/trino-benchto-benchmarks/src/test/resources/sql/presto/tpcds/unpartitioned/q94.plan.txt index 23e63a19495d..217dddd8d551 100644 --- a/testing/trino-benchto-benchmarks/src/test/resources/sql/presto/tpcds/unpartitioned/q94.plan.txt +++ b/testing/trino-benchto-benchmarks/src/test/resources/sql/presto/tpcds/unpartitioned/q94.plan.txt @@ -9,24 +9,26 @@ final aggregation over () remote exchange (REPARTITION, HASH, ["wr_order_number"]) partial aggregation over (wr_order_number) scan web_returns - final aggregation over (ca_state, d_date, unique, web_company_name, ws_ext_ship_cost, ws_net_profit, ws_order_number, ws_ship_addr_sk, ws_ship_date_sk, ws_warehouse_sk, ws_web_site_sk) - local exchange (GATHER, SINGLE, []) - partial aggregation over (ca_state, d_date, unique, web_company_name, ws_ext_ship_cost, ws_net_profit, ws_order_number, ws_ship_addr_sk, ws_ship_date_sk, ws_warehouse_sk, ws_web_site_sk) - join (RIGHT, PARTITIONED): - remote exchange (REPARTITION, HASH, ["ws_order_number_26"]) - scan web_sales - local exchange (GATHER, SINGLE, []) - remote exchange (REPARTITION, HASH, ["ws_order_number"]) - join (INNER, REPLICATED): - join (INNER, REPLICATED): - join (INNER, REPLICATED): - scan web_sales - local exchange (GATHER, SINGLE, []) - remote exchange (REPLICATE, BROADCAST, []) - scan customer_address - local exchange (GATHER, SINGLE, []) - remote exchange (REPLICATE, BROADCAST, []) - scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ws_order_number_26"]) + final aggregation over (ca_state, d_date, unique, web_company_name, ws_ext_ship_cost, ws_net_profit, ws_order_number_26, ws_ship_addr_sk, ws_ship_date_sk, ws_warehouse_sk, ws_web_site_sk) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ca_state", "d_date", "unique", "web_company_name", "ws_ext_ship_cost", "ws_net_profit", "ws_order_number_26", "ws_ship_addr_sk", "ws_ship_date_sk", "ws_warehouse_sk", "ws_web_site_sk"]) + partial aggregation over (ca_state, d_date, unique, web_company_name, ws_ext_ship_cost, ws_net_profit, ws_order_number_26, ws_ship_addr_sk, ws_ship_date_sk, ws_warehouse_sk, ws_web_site_sk) + join (INNER, REPLICATED, can skip output duplicates): + scan web_sales local exchange (GATHER, SINGLE, []) remote exchange (REPLICATE, BROADCAST, []) - scan web_site + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan web_site diff --git a/testing/trino-benchto-benchmarks/src/test/resources/sql/presto/tpch/partitioned/q21.plan.txt b/testing/trino-benchto-benchmarks/src/test/resources/sql/presto/tpch/partitioned/q21.plan.txt index 6243637c7b02..1460eaa40b15 100644 --- a/testing/trino-benchto-benchmarks/src/test/resources/sql/presto/tpch/partitioned/q21.plan.txt +++ b/testing/trino-benchto-benchmarks/src/test/resources/sql/presto/tpch/partitioned/q21.plan.txt @@ -9,7 +9,7 @@ local exchange (GATHER, SINGLE, []) final aggregation over (commitdate, name, name_12, nationkey, orderkey, orderstatus, receiptdate, suppkey_0, unique_62) local exchange (GATHER, SINGLE, []) partial aggregation over (commitdate, name, name_12, nationkey, orderkey, orderstatus, receiptdate, suppkey_0, unique_62) - join (LEFT, PARTITIONED): + join (INNER, PARTITIONED): join (INNER, PARTITIONED): remote exchange (REPARTITION, HASH, ["orderkey"]) join (INNER, REPLICATED): diff --git a/testing/trino-benchto-benchmarks/src/test/resources/sql/presto/tpch/unpartitioned/q21.plan.txt b/testing/trino-benchto-benchmarks/src/test/resources/sql/presto/tpch/unpartitioned/q21.plan.txt index c5ffc794f0d1..d62c188bfd61 100644 --- a/testing/trino-benchto-benchmarks/src/test/resources/sql/presto/tpch/unpartitioned/q21.plan.txt +++ b/testing/trino-benchto-benchmarks/src/test/resources/sql/presto/tpch/unpartitioned/q21.plan.txt @@ -4,16 +4,16 @@ local exchange (GATHER, SINGLE, []) local exchange (GATHER, SINGLE, []) remote exchange (REPARTITION, HASH, ["name"]) partial aggregation over (name) - final aggregation over (commitdate, exists, name, name_11, nationkey, orderkey, orderstatus, receiptdate, suppkey_0, unique) + final aggregation over (commitdate, exists, name, name_11, nationkey, orderkey_16, orderstatus, receiptdate, suppkey_0, unique) local exchange (GATHER, SINGLE, []) - partial aggregation over (commitdate, exists, name, name_11, nationkey, orderkey, orderstatus, receiptdate, suppkey_0, unique) + partial aggregation over (commitdate, exists, name, name_11, nationkey, orderkey_16, orderstatus, receiptdate, suppkey_0, unique) join (RIGHT, PARTITIONED): remote exchange (REPARTITION, HASH, ["orderkey_36"]) scan lineitem - final aggregation over (commitdate, name, name_11, nationkey, orderkey, orderstatus, receiptdate, suppkey_0, unique_59) + final aggregation over (commitdate, name, name_11, nationkey, orderkey_16, orderstatus, receiptdate, suppkey_0, unique_59) local exchange (GATHER, SINGLE, []) - partial aggregation over (commitdate, name, name_11, nationkey, orderkey, orderstatus, receiptdate, suppkey_0, unique_59) - join (RIGHT, PARTITIONED): + partial aggregation over (commitdate, name, name_11, nationkey, orderkey_16, orderstatus, receiptdate, suppkey_0, unique_59) + join (INNER, PARTITIONED): remote exchange (REPARTITION, HASH, ["orderkey_16"]) scan lineitem local exchange (GATHER, SINGLE, [])