Search in sources :

Example 6 with ProjectNode

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

the class InlineProjectIntoFilter method apply.

@Override
public Result apply(FilterNode node, Captures captures, Context context) {
    ProjectNode projectNode = captures.get(PROJECTION);
    List<Expression> filterConjuncts = extractConjuncts(node.getPredicate());
    Map<Boolean, List<Expression>> conjuncts = filterConjuncts.stream().collect(partitioningBy(SymbolReference.class::isInstance));
    List<Expression> simpleConjuncts = conjuncts.get(true);
    List<Expression> complexConjuncts = conjuncts.get(false);
    // Do not inline expression if the symbol is used multiple times in simple conjuncts.
    Set<Expression> simpleUniqueConjuncts = simpleConjuncts.stream().collect(Collectors.groupingBy(identity(), counting())).entrySet().stream().filter(entry -> entry.getValue() == 1).map(Map.Entry::getKey).collect(toImmutableSet());
    Set<Expression> complexConjunctSymbols = SymbolsExtractor.extractUnique(complexConjuncts).stream().map(Symbol::toSymbolReference).collect(toImmutableSet());
    // Do not inline expression if the symbol is used in complex conjuncts.
    Set<Expression> simpleConjunctsToInline = Sets.difference(simpleUniqueConjuncts, complexConjunctSymbols);
    if (simpleConjunctsToInline.isEmpty()) {
        return Result.empty();
    }
    ImmutableList.Builder<Expression> newConjuncts = ImmutableList.builder();
    Assignments.Builder newAssignments = Assignments.builder();
    Assignments.Builder postFilterAssignmentsBuilder = Assignments.builder();
    for (Expression conjunct : filterConjuncts) {
        if (simpleConjunctsToInline.contains(conjunct)) {
            Expression expression = projectNode.getAssignments().get(Symbol.from(conjunct));
            if (expression == null || expression instanceof SymbolReference) {
                // expression == null -> The symbol is not produced by the underlying projection (i.e. it is a correlation symbol).
                // expression instanceof SymbolReference -> Do not inline trivial projections.
                newConjuncts.add(conjunct);
            } else {
                newConjuncts.add(expression);
                newAssignments.putIdentities(SymbolsExtractor.extractUnique(expression));
                postFilterAssignmentsBuilder.put(Symbol.from(conjunct), TRUE_LITERAL);
            }
        } else {
            newConjuncts.add(conjunct);
        }
    }
    Assignments postFilterAssignments = postFilterAssignmentsBuilder.build();
    if (postFilterAssignments.isEmpty()) {
        return Result.empty();
    }
    Set<Symbol> postFilterSymbols = postFilterAssignments.getSymbols();
    // Remove inlined expressions from the underlying projection.
    newAssignments.putAll(projectNode.getAssignments().filter(symbol -> !postFilterSymbols.contains(symbol)));
    Map<Symbol, Expression> outputAssignments = new HashMap<>();
    outputAssignments.putAll(Assignments.identity(node.getOutputSymbols()).getMap());
    // Restore inlined symbols.
    outputAssignments.putAll(postFilterAssignments.getMap());
    return Result.ofPlanNode(new ProjectNode(context.getIdAllocator().getNextId(), new FilterNode(node.getId(), new ProjectNode(projectNode.getId(), projectNode.getSource(), newAssignments.build()), combineConjuncts(metadata, newConjuncts.build())), Assignments.builder().putAll(outputAssignments).build()));
}
Also used : Collectors.partitioningBy(java.util.stream.Collectors.partitioningBy) Patterns.filter(io.trino.sql.planner.plan.Patterns.filter) Collectors.counting(java.util.stream.Collectors.counting) HashMap(java.util.HashMap) Capture.newCapture(io.trino.matching.Capture.newCapture) FilterNode(io.trino.sql.planner.plan.FilterNode) ImmutableList(com.google.common.collect.ImmutableList) Map(java.util.Map) ImmutableSet.toImmutableSet(com.google.common.collect.ImmutableSet.toImmutableSet) Rule(io.trino.sql.planner.iterative.Rule) SymbolsExtractor(io.trino.sql.planner.SymbolsExtractor) ProjectNode(io.trino.sql.planner.plan.ProjectNode) Symbol(io.trino.sql.planner.Symbol) Assignments(io.trino.sql.planner.plan.Assignments) Set(java.util.Set) TRUE_LITERAL(io.trino.sql.tree.BooleanLiteral.TRUE_LITERAL) Collectors(java.util.stream.Collectors) Sets(com.google.common.collect.Sets) Capture(io.trino.matching.Capture) List(java.util.List) Pattern(io.trino.matching.Pattern) Patterns.source(io.trino.sql.planner.plan.Patterns.source) SymbolReference(io.trino.sql.tree.SymbolReference) Captures(io.trino.matching.Captures) Function.identity(java.util.function.Function.identity) Metadata(io.trino.metadata.Metadata) Expression(io.trino.sql.tree.Expression) ExpressionUtils.extractConjuncts(io.trino.sql.ExpressionUtils.extractConjuncts) Patterns.project(io.trino.sql.planner.plan.Patterns.project) ExpressionUtils.combineConjuncts(io.trino.sql.ExpressionUtils.combineConjuncts) HashMap(java.util.HashMap) ImmutableList(com.google.common.collect.ImmutableList) SymbolReference(io.trino.sql.tree.SymbolReference) Symbol(io.trino.sql.planner.Symbol) FilterNode(io.trino.sql.planner.plan.FilterNode) Assignments(io.trino.sql.planner.plan.Assignments) Expression(io.trino.sql.tree.Expression) ProjectNode(io.trino.sql.planner.plan.ProjectNode) ImmutableList(com.google.common.collect.ImmutableList) List(java.util.List) HashMap(java.util.HashMap) Map(java.util.Map)

Example 7 with ProjectNode

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

the class InlineProjections method extractInliningTargets.

private static Set<Symbol> extractInliningTargets(PlannerContext plannerContext, ProjectNode parent, ProjectNode child, Session session, TypeAnalyzer typeAnalyzer, TypeProvider types) {
    // candidates for inlining are
    // 1. references to simple constants or symbol references
    // 2. references to complex expressions that
    // a. are not inputs to try() expressions
    // b. appear only once across all expressions
    // c. are not identity projections
    // which come from the child, as opposed to an enclosing scope.
    Set<Symbol> childOutputSet = ImmutableSet.copyOf(child.getOutputSymbols());
    Map<Symbol, Long> dependencies = parent.getAssignments().getExpressions().stream().flatMap(expression -> SymbolsExtractor.extractAll(expression).stream()).filter(childOutputSet::contains).collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
    // find references to simple constants or symbol references
    Set<Symbol> basicReferences = dependencies.keySet().stream().filter(input -> isEffectivelyLiteral(plannerContext, session, child.getAssignments().get(input)) || child.getAssignments().get(input) instanceof SymbolReference).filter(// skip identities, otherwise, this rule will keep firing forever
    input -> !child.getAssignments().isIdentity(input)).collect(toSet());
    // exclude any complex inputs to TRY expressions. Inlining them would potentially
    // change the semantics of those expressions
    Set<Symbol> tryArguments = parent.getAssignments().getExpressions().stream().flatMap(expression -> extractTryArguments(expression).stream()).collect(toSet());
    Set<Symbol> singletons = dependencies.entrySet().stream().filter(// reference appears just once across all expressions in parent project node
    entry -> entry.getValue() == 1).filter(// they are not inputs to TRY. Otherwise, inlining might change semantics
    entry -> !tryArguments.contains(entry.getKey())).filter(// skip identities, otherwise, this rule will keep firing forever
    entry -> !child.getAssignments().isIdentity(entry.getKey())).filter(entry -> {
        // skip dereferences, otherwise, inlining can cause conflicts with PushdownDereferences
        Expression assignment = child.getAssignments().get(entry.getKey());
        if (assignment instanceof SubscriptExpression) {
            if (typeAnalyzer.getType(session, types, ((SubscriptExpression) assignment).getBase()) instanceof RowType) {
                return false;
            }
        }
        return true;
    }).map(Map.Entry::getKey).collect(toSet());
    return Sets.union(singletons, basicReferences);
}
Also used : Function(java.util.function.Function) Capture.newCapture(io.trino.matching.Capture.newCapture) PlanNode(io.trino.sql.planner.plan.PlanNode) SubscriptExpression(io.trino.sql.tree.SubscriptExpression) ExpressionSymbolInliner.inlineSymbols(io.trino.sql.planner.ExpressionSymbolInliner.inlineSymbols) ImmutableList(com.google.common.collect.ImmutableList) Map(java.util.Map) Objects.requireNonNull(java.util.Objects.requireNonNull) Rule(io.trino.sql.planner.iterative.Rule) SymbolsExtractor(io.trino.sql.planner.SymbolsExtractor) TryExpression(io.trino.sql.tree.TryExpression) ProjectNode(io.trino.sql.planner.plan.ProjectNode) Collectors.toSet(java.util.stream.Collectors.toSet) Symbol(io.trino.sql.planner.Symbol) RowType(io.trino.spi.type.RowType) ImmutableSet(com.google.common.collect.ImmutableSet) Assignments(io.trino.sql.planner.plan.Assignments) Set(java.util.Set) Collectors(java.util.stream.Collectors) Sets(com.google.common.collect.Sets) Capture(io.trino.matching.Capture) ExpressionUtils.isEffectivelyLiteral(io.trino.sql.ExpressionUtils.isEffectivelyLiteral) Pattern(io.trino.matching.Pattern) TypeAnalyzer(io.trino.sql.planner.TypeAnalyzer) AstUtils(io.trino.sql.util.AstUtils) Patterns.source(io.trino.sql.planner.plan.Patterns.source) SymbolReference(io.trino.sql.tree.SymbolReference) Captures(io.trino.matching.Captures) TypeProvider(io.trino.sql.planner.TypeProvider) Optional(java.util.Optional) Expression(io.trino.sql.tree.Expression) Patterns.project(io.trino.sql.planner.plan.Patterns.project) Session(io.trino.Session) PlannerContext(io.trino.sql.PlannerContext) SubscriptExpression(io.trino.sql.tree.SubscriptExpression) TryExpression(io.trino.sql.tree.TryExpression) Expression(io.trino.sql.tree.Expression) Symbol(io.trino.sql.planner.Symbol) SymbolReference(io.trino.sql.tree.SymbolReference) SubscriptExpression(io.trino.sql.tree.SubscriptExpression) RowType(io.trino.spi.type.RowType) Map(java.util.Map)

Example 8 with ProjectNode

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

the class DecorrelateInnerUnnestWithGlobalAggregation method apply.

@Override
public Result apply(CorrelatedJoinNode correlatedJoinNode, Captures captures, Context context) {
    // find global aggregation in subquery
    List<PlanNode> globalAggregations = PlanNodeSearcher.searchFrom(correlatedJoinNode.getSubquery(), context.getLookup()).where(DecorrelateInnerUnnestWithGlobalAggregation::isGlobalAggregation).recurseOnlyWhen(node -> node instanceof ProjectNode || isGlobalAggregation(node)).findAll();
    if (globalAggregations.isEmpty()) {
        return Result.empty();
    }
    // if there are multiple global aggregations, the one that is closest to the source is the "reducing" aggregation, because it reduces multiple input rows to single output row
    AggregationNode reducingAggregation = (AggregationNode) globalAggregations.get(globalAggregations.size() - 1);
    // find unnest in subquery
    Optional<UnnestNode> subqueryUnnest = PlanNodeSearcher.searchFrom(reducingAggregation.getSource(), context.getLookup()).where(node -> isSupportedUnnest(node, correlatedJoinNode.getCorrelation(), context.getLookup())).recurseOnlyWhen(node -> node instanceof ProjectNode || 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
    Symbol ordinalitySymbol = unnestNode.getOrdinalitySymbol().orElseGet(() -> context.getSymbolAllocator().newSymbol("ordinality", BIGINT));
    UnnestNode rewrittenUnnest = new UnnestNode(context.getIdAllocator().getNextId(), input, input.getOutputSymbols(), unnestNode.getMappings(), Optional.of(ordinalitySymbol), LEFT, Optional.empty());
    // append mask symbol based on ordinality to distinguish between the unnested rows and synthetic null rows
    Symbol mask = context.getSymbolAllocator().newSymbol("mask", BOOLEAN);
    ProjectNode sourceWithMask = new ProjectNode(context.getIdAllocator().getNextId(), rewrittenUnnest, Assignments.builder().putIdentities(rewrittenUnnest.getOutputSymbols()).put(mask, new IsNotNullPredicate(ordinalitySymbol.toSymbolReference())).build());
    // restore all projections, grouped aggregations and global aggregations from the subquery
    PlanNode result = rewriteNodeSequence(context.getLookup().resolve(correlatedJoinNode.getSubquery()), input.getOutputSymbols(), mask, sourceWithMask, reducingAggregation.getId(), unnestNode.getId(), context.getSymbolAllocator(), context.getIdAllocator(), 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) INNER(io.trino.sql.planner.plan.JoinNode.Type.INNER) SymbolAllocator(io.trino.sql.planner.SymbolAllocator) BOOLEAN(io.trino.spi.type.BooleanType.BOOLEAN) SINGLE(io.trino.sql.planner.plan.AggregationNode.Step.SINGLE) CorrelatedJoinNode(io.trino.sql.planner.plan.CorrelatedJoinNode) PlanNode(io.trino.sql.planner.plan.PlanNode) AggregationDecorrelation.rewriteWithMasks(io.trino.sql.planner.iterative.rule.AggregationDecorrelation.rewriteWithMasks) 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) IsNotNullPredicate(io.trino.sql.tree.IsNotNullPredicate) Map(java.util.Map) 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) ImmutableMap(com.google.common.collect.ImmutableMap) Lookup(io.trino.sql.planner.iterative.Lookup) ImmutableList.toImmutableList(com.google.common.collect.ImmutableList.toImmutableList) Assignments(io.trino.sql.planner.plan.Assignments) Set(java.util.Set) 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) ExpressionUtils.and(io.trino.sql.ExpressionUtils.and) Captures(io.trino.matching.Captures) Util.restrictOutputs(io.trino.sql.planner.iterative.rule.Util.restrictOutputs) Mapping(io.trino.sql.planner.plan.UnnestNode.Mapping) Optional(java.util.Optional) Expression(io.trino.sql.tree.Expression) PlanNodeIdAllocator(io.trino.sql.planner.PlanNodeIdAllocator) 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) Symbol(io.trino.sql.planner.Symbol) ProjectNode(io.trino.sql.planner.plan.ProjectNode) AggregationNode(io.trino.sql.planner.plan.AggregationNode) IsNotNullPredicate(io.trino.sql.tree.IsNotNullPredicate)

Example 9 with ProjectNode

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

the class DecorrelateLeftUnnestWithGlobalAggregation 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 LEFT,
 * - 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(UnnestNode.Mapping::getInput).collect(toImmutableList());
    PlanNode unnestSource = lookup.resolve(unnestNode.getSource());
    boolean basedOnCorrelation = ImmutableSet.copyOf(correlation).containsAll(unnestSymbols) || unnestSource instanceof ProjectNode && ImmutableSet.copyOf(correlation).containsAll(SymbolsExtractor.extractUnique(((ProjectNode) unnestSource).getAssignments().getExpressions()));
    return isScalar(unnestNode.getSource(), lookup) && unnestNode.getReplicateSymbols().isEmpty() && basedOnCorrelation && unnestNode.getJoinType() == LEFT && (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 10 with ProjectNode

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

the class DecorrelateUnnest 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 or LEFT,
 * - 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(UnnestNode.Mapping::getInput).collect(toImmutableList());
    PlanNode unnestSource = lookup.resolve(unnestNode.getSource());
    boolean basedOnCorrelation = ImmutableSet.copyOf(correlation).containsAll(unnestSymbols) || unnestSource instanceof ProjectNode && ImmutableSet.copyOf(correlation).containsAll(SymbolsExtractor.extractUnique(((ProjectNode) unnestSource).getAssignments().getExpressions()));
    return isScalar(unnestNode.getSource(), lookup) && unnestNode.getReplicateSymbols().isEmpty() && basedOnCorrelation && (unnestNode.getJoinType() == INNER || unnestNode.getJoinType() == LEFT) && (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)

Aggregations

ProjectNode (io.trino.sql.planner.plan.ProjectNode)101 Assignments (io.trino.sql.planner.plan.Assignments)64 Expression (io.trino.sql.tree.Expression)58 Symbol (io.trino.sql.planner.Symbol)57 PlanNode (io.trino.sql.planner.plan.PlanNode)50 ImmutableList (com.google.common.collect.ImmutableList)41 Map (java.util.Map)35 Captures (io.trino.matching.Captures)29 Pattern (io.trino.matching.Pattern)29 Rule (io.trino.sql.planner.iterative.Rule)29 Set (java.util.Set)25 Capture (io.trino.matching.Capture)23 Capture.newCapture (io.trino.matching.Capture.newCapture)23 SymbolReference (io.trino.sql.tree.SymbolReference)23 Objects.requireNonNull (java.util.Objects.requireNonNull)23 Patterns.source (io.trino.sql.planner.plan.Patterns.source)22 TypeAnalyzer (io.trino.sql.planner.TypeAnalyzer)20 AggregationNode (io.trino.sql.planner.plan.AggregationNode)20 FilterNode (io.trino.sql.planner.plan.FilterNode)20 ImmutableMap.toImmutableMap (com.google.common.collect.ImmutableMap.toImmutableMap)19