Search in sources :

Example 6 with UnnestNode

use of io.trino.sql.planner.plan.UnnestNode in project trino by trinodb.

the class UnnestMatcher method detailMatches.

@Override
public MatchResult detailMatches(PlanNode node, StatsProvider stats, Session session, Metadata metadata, SymbolAliases symbolAliases) {
    checkState(shapeMatches(node), "Plan testing framework error: shapeMatches returned false in detailMatches in %s", this.getClass().getName());
    UnnestNode unnestNode = (UnnestNode) node;
    if (unnestNode.getReplicateSymbols().size() != replicateSymbols.size()) {
        return NO_MATCH;
    }
    if (!replicateSymbols.stream().map(symbolAliases::get).map(Symbol::from).collect(toImmutableList()).equals(unnestNode.getReplicateSymbols())) {
        return NO_MATCH;
    }
    if (unnestNode.getMappings().size() != unnestMappings.size()) {
        return NO_MATCH;
    }
    if (!IntStream.range(0, unnestMappings.size()).boxed().allMatch(index -> {
        Mapping nodeMapping = unnestNode.getMappings().get(index);
        PlanMatchPattern.UnnestMapping patternMapping = unnestMappings.get(index);
        return nodeMapping.getInput().toSymbolReference().equals(symbolAliases.get(patternMapping.getInput())) && patternMapping.getOutputs().size() == nodeMapping.getOutputs().size();
    })) {
        return NO_MATCH;
    }
    if (ordinalitySymbol.isPresent() != unnestNode.getOrdinalitySymbol().isPresent()) {
        return NO_MATCH;
    }
    if (!type.equals(unnestNode.getJoinType())) {
        return NO_MATCH;
    }
    if (filter.isPresent() != unnestNode.getFilter().isPresent()) {
        return NO_MATCH;
    }
    if (filter.isEmpty()) {
        return MatchResult.match();
    }
    if (!new ExpressionVerifier(symbolAliases).process(unnestNode.getFilter().get(), filter.get())) {
        return NO_MATCH;
    }
    return MatchResult.match();
}
Also used : IntStream(java.util.stream.IntStream) Symbol(io.trino.sql.planner.Symbol) ImmutableList.toImmutableList(com.google.common.collect.ImmutableList.toImmutableList) StatsProvider(io.trino.cost.StatsProvider) UnnestNode(io.trino.sql.planner.plan.UnnestNode) PlanNode(io.trino.sql.planner.plan.PlanNode) Preconditions.checkState(com.google.common.base.Preconditions.checkState) List(java.util.List) Mapping(io.trino.sql.planner.plan.UnnestNode.Mapping) Objects.requireNonNull(java.util.Objects.requireNonNull) Metadata(io.trino.metadata.Metadata) Optional(java.util.Optional) NO_MATCH(io.trino.sql.planner.assertions.MatchResult.NO_MATCH) Expression(io.trino.sql.tree.Expression) JoinNode(io.trino.sql.planner.plan.JoinNode) Session(io.trino.Session) MoreObjects.toStringHelper(com.google.common.base.MoreObjects.toStringHelper) UnnestNode(io.trino.sql.planner.plan.UnnestNode) Mapping(io.trino.sql.planner.plan.UnnestNode.Mapping)

Example 7 with UnnestNode

use of io.trino.sql.planner.plan.UnnestNode in project trino by trinodb.

the class DecorrelateInnerUnnestWithGlobalAggregation method isSupportedUnnest.

/**
 * This rule supports decorrelation of UnnestNode meeting certain conditions:
 * - the UnnestNode should be based on correlation symbols, that is: either unnest correlation symbols directly,
 * or unnest symbols produced by a projection that uses only correlation symbols.
 * - the UnnestNode should not have any replicate symbols,
 * - the UnnestNode should be of type INNER,
 * - the UnnestNode should not have a filter.
 */
private static boolean isSupportedUnnest(PlanNode node, List<Symbol> correlation, Lookup lookup) {
    if (!(node instanceof UnnestNode)) {
        return false;
    }
    UnnestNode unnestNode = (UnnestNode) node;
    List<Symbol> unnestSymbols = unnestNode.getMappings().stream().map(Mapping::getInput).collect(toImmutableList());
    PlanNode unnestSource = lookup.resolve(unnestNode.getSource());
    Set<Symbol> correlationSymbols = ImmutableSet.copyOf(correlation);
    boolean basedOnCorrelation = correlationSymbols.containsAll(unnestSymbols) || unnestSource instanceof ProjectNode && correlationSymbols.containsAll(SymbolsExtractor.extractUnique(((ProjectNode) unnestSource).getAssignments().getExpressions()));
    return isScalar(unnestNode.getSource(), lookup) && unnestNode.getReplicateSymbols().isEmpty() && basedOnCorrelation && unnestNode.getJoinType() == INNER && (unnestNode.getFilter().isEmpty() || unnestNode.getFilter().get().equals(TRUE_LITERAL));
}
Also used : PlanNode(io.trino.sql.planner.plan.PlanNode) UnnestNode(io.trino.sql.planner.plan.UnnestNode) Symbol(io.trino.sql.planner.Symbol) ProjectNode(io.trino.sql.planner.plan.ProjectNode)

Example 8 with UnnestNode

use of io.trino.sql.planner.plan.UnnestNode in project trino by trinodb.

the class DecorrelateLeftUnnestWithGlobalAggregation method apply.

@Override
public Result apply(CorrelatedJoinNode correlatedJoinNode, Captures captures, Context context) {
    // find global aggregation in subquery
    Optional<AggregationNode> globalAggregation = PlanNodeSearcher.searchFrom(correlatedJoinNode.getSubquery(), context.getLookup()).where(DecorrelateLeftUnnestWithGlobalAggregation::isGlobalAggregation).recurseOnlyWhen(node -> node instanceof ProjectNode || isGroupedAggregation(node)).findFirst();
    if (globalAggregation.isEmpty()) {
        return Result.empty();
    }
    // find unnest in subquery
    Optional<UnnestNode> subqueryUnnest = PlanNodeSearcher.searchFrom(correlatedJoinNode.getSubquery(), context.getLookup()).where(node -> isSupportedUnnest(node, correlatedJoinNode.getCorrelation(), context.getLookup())).recurseOnlyWhen(node -> node instanceof ProjectNode || isGlobalAggregation(node) || isGroupedAggregation(node)).findFirst();
    if (subqueryUnnest.isEmpty()) {
        return Result.empty();
    }
    UnnestNode unnestNode = subqueryUnnest.get();
    // assign unique id to input rows to restore semantics of aggregations after rewrite
    PlanNode input = new AssignUniqueId(context.getIdAllocator().getNextId(), correlatedJoinNode.getInput(), context.getSymbolAllocator().newSymbol("unique", BIGINT));
    // pre-project unnest symbols if they were pre-projected in subquery
    // The correlated UnnestNode either unnests correlation symbols directly, or unnests symbols produced by a projection that uses only correlation symbols.
    // Here, any underlying projection that was a source of the correlated UnnestNode, is appended as a source of the rewritten UnnestNode.
    // If the projection is not necessary for UnnestNode (i.e. it does not produce any unnest symbols), it should be pruned afterwards.
    PlanNode unnestSource = context.getLookup().resolve(unnestNode.getSource());
    if (unnestSource instanceof ProjectNode) {
        ProjectNode sourceProjection = (ProjectNode) unnestSource;
        input = new ProjectNode(sourceProjection.getId(), input, Assignments.builder().putIdentities(input.getOutputSymbols()).putAll(sourceProjection.getAssignments()).build());
    }
    // rewrite correlated join to UnnestNode
    UnnestNode rewrittenUnnest = new UnnestNode(context.getIdAllocator().getNextId(), input, input.getOutputSymbols(), unnestNode.getMappings(), unnestNode.getOrdinalitySymbol(), LEFT, Optional.empty());
    // restore all projections, grouped aggregations and global aggregations from the subquery
    PlanNode result = rewriteNodeSequence(context.getLookup().resolve(correlatedJoinNode.getSubquery()), input.getOutputSymbols(), rewrittenUnnest, unnestNode.getId(), context.getLookup());
    // restrict outputs
    return Result.ofPlanNode(restrictOutputs(context.getIdAllocator(), result, ImmutableSet.copyOf(correlatedJoinNode.getOutputSymbols())).orElse(result));
}
Also used : CorrelatedJoin.correlation(io.trino.sql.planner.plan.Patterns.CorrelatedJoin.correlation) QueryCardinalityUtil.isScalar(io.trino.sql.planner.optimizations.QueryCardinalityUtil.isScalar) SINGLE(io.trino.sql.planner.plan.AggregationNode.Step.SINGLE) CorrelatedJoinNode(io.trino.sql.planner.plan.CorrelatedJoinNode) PlanNode(io.trino.sql.planner.plan.PlanNode) CorrelatedJoin.filter(io.trino.sql.planner.plan.Patterns.CorrelatedJoin.filter) LEFT(io.trino.sql.planner.plan.JoinNode.Type.LEFT) ImmutableList(com.google.common.collect.ImmutableList) PlanNodeSearcher(io.trino.sql.planner.optimizations.PlanNodeSearcher) PlanNodeId(io.trino.sql.planner.plan.PlanNodeId) AggregationNode(io.trino.sql.planner.plan.AggregationNode) Rule(io.trino.sql.planner.iterative.Rule) SymbolsExtractor(io.trino.sql.planner.SymbolsExtractor) Pattern.nonEmpty(io.trino.matching.Pattern.nonEmpty) AssignUniqueId(io.trino.sql.planner.plan.AssignUniqueId) ProjectNode(io.trino.sql.planner.plan.ProjectNode) Symbol(io.trino.sql.planner.Symbol) ImmutableSet(com.google.common.collect.ImmutableSet) Lookup(io.trino.sql.planner.iterative.Lookup) ImmutableList.toImmutableList(com.google.common.collect.ImmutableList.toImmutableList) Assignments(io.trino.sql.planner.plan.Assignments) Iterables.getOnlyElement(com.google.common.collect.Iterables.getOnlyElement) TRUE_LITERAL(io.trino.sql.tree.BooleanLiteral.TRUE_LITERAL) Streams(com.google.common.collect.Streams) UnnestNode(io.trino.sql.planner.plan.UnnestNode) AggregationNode.singleGroupingSet(io.trino.sql.planner.plan.AggregationNode.singleGroupingSet) List(java.util.List) Pattern(io.trino.matching.Pattern) BIGINT(io.trino.spi.type.BigintType.BIGINT) Captures(io.trino.matching.Captures) Util.restrictOutputs(io.trino.sql.planner.iterative.rule.Util.restrictOutputs) Optional(java.util.Optional) Patterns.correlatedJoin(io.trino.sql.planner.plan.Patterns.correlatedJoin) PlanNode(io.trino.sql.planner.plan.PlanNode) AssignUniqueId(io.trino.sql.planner.plan.AssignUniqueId) UnnestNode(io.trino.sql.planner.plan.UnnestNode) AggregationNode(io.trino.sql.planner.plan.AggregationNode) ProjectNode(io.trino.sql.planner.plan.ProjectNode)

Example 9 with UnnestNode

use of io.trino.sql.planner.plan.UnnestNode in project trino by trinodb.

the class DecorrelateUnnest method apply.

@Override
public Result apply(CorrelatedJoinNode correlatedJoinNode, Captures captures, Context context) {
    // determine shape of the subquery
    PlanNode searchRoot = correlatedJoinNode.getSubquery();
    // 1. find EnforceSingleRowNode in the subquery
    Optional<EnforceSingleRowNode> enforceSingleRow = PlanNodeSearcher.searchFrom(searchRoot, context.getLookup()).where(EnforceSingleRowNode.class::isInstance).recurseOnlyWhen(planNode -> false).findFirst();
    if (enforceSingleRow.isPresent()) {
        searchRoot = enforceSingleRow.get().getSource();
    }
    // 2. find correlated UnnestNode in the subquery
    Optional<UnnestNode> subqueryUnnest = PlanNodeSearcher.searchFrom(searchRoot, context.getLookup()).where(node -> isSupportedUnnest(node, correlatedJoinNode.getCorrelation(), context.getLookup())).recurseOnlyWhen(node -> node instanceof ProjectNode || (node instanceof LimitNode && ((LimitNode) node).getCount() > 0) || (node instanceof TopNNode && ((TopNNode) node).getCount() > 0)).findFirst();
    if (subqueryUnnest.isEmpty()) {
        return Result.empty();
    }
    UnnestNode unnestNode = subqueryUnnest.get();
    // assign unique id to input rows
    Symbol uniqueSymbol = context.getSymbolAllocator().newSymbol("unique", BIGINT);
    PlanNode input = new AssignUniqueId(context.getIdAllocator().getNextId(), correlatedJoinNode.getInput(), uniqueSymbol);
    // pre-project unnest symbols if they were pre-projected in subquery
    // The correlated UnnestNode either unnests correlation symbols directly, or unnests symbols produced by a projection that uses only correlation symbols.
    // Here, any underlying projection that was a source of the correlated UnnestNode, is appended as a source of the rewritten UnnestNode.
    // If the projection is not necessary for UnnestNode (i.e. it does not produce any unnest symbols), it should be pruned afterwards.
    PlanNode unnestSource = context.getLookup().resolve(unnestNode.getSource());
    if (unnestSource instanceof ProjectNode) {
        ProjectNode sourceProjection = (ProjectNode) unnestSource;
        input = new ProjectNode(sourceProjection.getId(), input, Assignments.builder().putIdentities(input.getOutputSymbols()).putAll(sourceProjection.getAssignments()).build());
    }
    // determine join type for rewritten UnnestNode
    Type unnestJoinType = LEFT;
    if (enforceSingleRow.isEmpty() && correlatedJoinNode.getType() == CorrelatedJoinNode.Type.INNER && unnestNode.getJoinType() == INNER) {
        unnestJoinType = INNER;
    }
    // make sure that the rewritten node is with ordinality, which might be necessary to restore inner unnest semantics after rewrite.
    Symbol ordinalitySymbol = unnestNode.getOrdinalitySymbol().orElseGet(() -> context.getSymbolAllocator().newSymbol("ordinality", BIGINT));
    // rewrite correlated join to UnnestNode.
    UnnestNode rewrittenUnnest = new UnnestNode(context.getIdAllocator().getNextId(), input, input.getOutputSymbols(), unnestNode.getMappings(), Optional.of(ordinalitySymbol), unnestJoinType, Optional.empty());
    // restore all nodes from the subquery
    PlanNode rewrittenPlan = Rewriter.rewriteNodeSequence(correlatedJoinNode.getSubquery(), input.getOutputSymbols(), ordinalitySymbol, uniqueSymbol, rewrittenUnnest, context.getSession(), metadata, context.getLookup(), context.getIdAllocator(), context.getSymbolAllocator());
    // between unnested rows and synthetic rows added by left unnest.
    if (unnestNode.getJoinType() == INNER && rewrittenUnnest.getJoinType() == LEFT) {
        Assignments.Builder assignments = Assignments.builder().putIdentities(correlatedJoinNode.getInput().getOutputSymbols());
        for (Symbol subquerySymbol : correlatedJoinNode.getSubquery().getOutputSymbols()) {
            assignments.put(subquerySymbol, new IfExpression(new IsNullPredicate(ordinalitySymbol.toSymbolReference()), new Cast(new NullLiteral(), toSqlType(context.getSymbolAllocator().getTypes().get(subquerySymbol))), subquerySymbol.toSymbolReference()));
        }
        rewrittenPlan = new ProjectNode(context.getIdAllocator().getNextId(), rewrittenPlan, assignments.build());
    }
    // restrict outputs
    return Result.ofPlanNode(restrictOutputs(context.getIdAllocator(), rewrittenPlan, ImmutableSet.copyOf(correlatedJoinNode.getOutputSymbols())).orElse(rewrittenPlan));
}
Also used : CorrelatedJoin.correlation(io.trino.sql.planner.plan.Patterns.CorrelatedJoin.correlation) IsNullPredicate(io.trino.sql.tree.IsNullPredicate) SymbolAllocator(io.trino.sql.planner.SymbolAllocator) TypeSignatureProvider.fromTypes(io.trino.sql.analyzer.TypeSignatureProvider.fromTypes) CorrelatedJoinNode(io.trino.sql.planner.plan.CorrelatedJoinNode) FilterNode(io.trino.sql.planner.plan.FilterNode) PlanNode(io.trino.sql.planner.plan.PlanNode) LEFT(io.trino.sql.planner.plan.JoinNode.Type.LEFT) Type(io.trino.sql.planner.plan.JoinNode.Type) PlanNodeSearcher(io.trino.sql.planner.optimizations.PlanNodeSearcher) GREATER_THAN(io.trino.sql.tree.ComparisonExpression.Operator.GREATER_THAN) AssignUniqueId(io.trino.sql.planner.plan.AssignUniqueId) FunctionCall(io.trino.sql.tree.FunctionCall) ImmutableSet(com.google.common.collect.ImmutableSet) ImmutableMap(com.google.common.collect.ImmutableMap) ResolvedFunction(io.trino.metadata.ResolvedFunction) EnforceSingleRowNode(io.trino.sql.planner.plan.EnforceSingleRowNode) ImmutableList.toImmutableList(com.google.common.collect.ImmutableList.toImmutableList) TypeSignatureTranslator.toSqlType(io.trino.sql.analyzer.TypeSignatureTranslator.toSqlType) Assignments(io.trino.sql.planner.plan.Assignments) ComparisonExpression(io.trino.sql.tree.ComparisonExpression) DEFAULT_FRAME(io.trino.sql.planner.plan.WindowNode.Frame.DEFAULT_FRAME) LESS_THAN_OR_EQUAL(io.trino.sql.tree.ComparisonExpression.Operator.LESS_THAN_OR_EQUAL) GenericLiteral(io.trino.sql.tree.GenericLiteral) List(java.util.List) Pattern(io.trino.matching.Pattern) IfExpression(io.trino.sql.tree.IfExpression) BIGINT(io.trino.spi.type.BigintType.BIGINT) Optional(java.util.Optional) Expression(io.trino.sql.tree.Expression) WindowNode(io.trino.sql.planner.plan.WindowNode) Session(io.trino.Session) QueryCardinalityUtil.isScalar(io.trino.sql.planner.optimizations.QueryCardinalityUtil.isScalar) INNER(io.trino.sql.planner.plan.JoinNode.Type.INNER) LimitNode(io.trino.sql.planner.plan.LimitNode) BOOLEAN(io.trino.spi.type.BooleanType.BOOLEAN) Cast(io.trino.sql.tree.Cast) CorrelatedJoin.filter(io.trino.sql.planner.plan.Patterns.CorrelatedJoin.filter) VARCHAR(io.trino.spi.type.VarcharType.VARCHAR) ImmutableList(com.google.common.collect.ImmutableList) RowNumberNode(io.trino.sql.planner.plan.RowNumberNode) Objects.requireNonNull(java.util.Objects.requireNonNull) NullLiteral(io.trino.sql.tree.NullLiteral) Rule(io.trino.sql.planner.iterative.Rule) SymbolsExtractor(io.trino.sql.planner.SymbolsExtractor) Pattern.nonEmpty(io.trino.matching.Pattern.nonEmpty) ProjectNode(io.trino.sql.planner.plan.ProjectNode) Symbol(io.trino.sql.planner.Symbol) StringLiteral(io.trino.sql.tree.StringLiteral) Lookup(io.trino.sql.planner.iterative.Lookup) PlanVisitor(io.trino.sql.planner.plan.PlanVisitor) TopNNode(io.trino.sql.planner.plan.TopNNode) TRUE_LITERAL(io.trino.sql.tree.BooleanLiteral.TRUE_LITERAL) UnnestNode(io.trino.sql.planner.plan.UnnestNode) ImplementLimitWithTies.rewriteLimitWithTiesWithPartitioning(io.trino.sql.planner.iterative.rule.ImplementLimitWithTies.rewriteLimitWithTiesWithPartitioning) QualifiedName(io.trino.sql.tree.QualifiedName) Captures(io.trino.matching.Captures) Specification(io.trino.sql.planner.plan.WindowNode.Specification) Util.restrictOutputs(io.trino.sql.planner.iterative.rule.Util.restrictOutputs) Metadata(io.trino.metadata.Metadata) PlanNodeIdAllocator(io.trino.sql.planner.PlanNodeIdAllocator) Patterns.correlatedJoin(io.trino.sql.planner.plan.Patterns.correlatedJoin) Cast(io.trino.sql.tree.Cast) IfExpression(io.trino.sql.tree.IfExpression) Symbol(io.trino.sql.planner.Symbol) Assignments(io.trino.sql.planner.plan.Assignments) Type(io.trino.sql.planner.plan.JoinNode.Type) TypeSignatureTranslator.toSqlType(io.trino.sql.analyzer.TypeSignatureTranslator.toSqlType) PlanNode(io.trino.sql.planner.plan.PlanNode) AssignUniqueId(io.trino.sql.planner.plan.AssignUniqueId) UnnestNode(io.trino.sql.planner.plan.UnnestNode) LimitNode(io.trino.sql.planner.plan.LimitNode) EnforceSingleRowNode(io.trino.sql.planner.plan.EnforceSingleRowNode) IsNullPredicate(io.trino.sql.tree.IsNullPredicate) ProjectNode(io.trino.sql.planner.plan.ProjectNode) NullLiteral(io.trino.sql.tree.NullLiteral) TopNNode(io.trino.sql.planner.plan.TopNNode)

Example 10 with UnnestNode

use of io.trino.sql.planner.plan.UnnestNode in project trino by trinodb.

the class PruneUnnestColumns method pushDownProjectOff.

@Override
protected Optional<PlanNode> pushDownProjectOff(Context context, UnnestNode unnestNode, Set<Symbol> referencedOutputs) {
    ImmutableSet.Builder<Symbol> referencedAndFilterSymbolsBuilder = ImmutableSet.<Symbol>builder().addAll(referencedOutputs);
    unnestNode.getFilter().ifPresent(expression -> referencedAndFilterSymbolsBuilder.addAll(SymbolsExtractor.extractUnique(expression)));
    Set<Symbol> referencedAndFilterSymbols = referencedAndFilterSymbolsBuilder.build();
    List<Symbol> prunedReplicateSymbols = unnestNode.getReplicateSymbols().stream().filter(referencedAndFilterSymbols::contains).collect(toImmutableList());
    Optional<Symbol> prunedOrdinalitySymbol = unnestNode.getOrdinalitySymbol().filter(referencedAndFilterSymbols::contains);
    if (prunedReplicateSymbols.size() == unnestNode.getReplicateSymbols().size() && prunedOrdinalitySymbol.equals(unnestNode.getOrdinalitySymbol())) {
        return Optional.empty();
    }
    return Optional.of(new UnnestNode(unnestNode.getId(), unnestNode.getSource(), prunedReplicateSymbols, unnestNode.getMappings(), prunedOrdinalitySymbol, unnestNode.getJoinType(), unnestNode.getFilter()));
}
Also used : ImmutableSet(com.google.common.collect.ImmutableSet) UnnestNode(io.trino.sql.planner.plan.UnnestNode) Symbol(io.trino.sql.planner.Symbol)

Aggregations

UnnestNode (io.trino.sql.planner.plan.UnnestNode)12 Symbol (io.trino.sql.planner.Symbol)11 PlanNode (io.trino.sql.planner.plan.PlanNode)8 ProjectNode (io.trino.sql.planner.plan.ProjectNode)8 ImmutableList.toImmutableList (com.google.common.collect.ImmutableList.toImmutableList)6 ImmutableList (com.google.common.collect.ImmutableList)5 Assignments (io.trino.sql.planner.plan.Assignments)5 Expression (io.trino.sql.tree.Expression)5 List (java.util.List)5 Optional (java.util.Optional)5 ImmutableSet (com.google.common.collect.ImmutableSet)4 Captures (io.trino.matching.Captures)4 Pattern (io.trino.matching.Pattern)4 Rule (io.trino.sql.planner.iterative.Rule)4 Iterables.getOnlyElement (com.google.common.collect.Iterables.getOnlyElement)3 Session (io.trino.Session)3 Pattern.nonEmpty (io.trino.matching.Pattern.nonEmpty)3 Metadata (io.trino.metadata.Metadata)3 BIGINT (io.trino.spi.type.BigintType.BIGINT)3 SymbolsExtractor (io.trino.sql.planner.SymbolsExtractor)3