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();
}
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));
}
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));
}
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));
}
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()));
}
Aggregations