use of io.trino.sql.planner.plan.PlanNode in project trino by trinodb.
the class SetOperationMerge method mergeFirstSource.
/**
* Only merge first source node. This method is assumed to be used for EXCEPT, which is a non-associative set operation.
* Provides a correct plan transformation also for associative set operations: UNION and INTERSECT.
*
* @return Merged plan node if applied.
*/
public Optional<SetOperationNode> mergeFirstSource() {
Lookup lookup = context.getLookup();
List<PlanNode> sources = node.getSources().stream().flatMap(lookup::resolveGroup).collect(Collectors.toList());
PlanNode child = sources.get(0);
// Determine if set operations can be merged and whether the resulting set operation is quantified DISTINCT or ALL
Optional<Boolean> mergedQuantifier = mergedQuantifierIsDistinct(node, child);
if (mergedQuantifier.isEmpty()) {
return Optional.empty();
}
ImmutableListMultimap.Builder<Symbol, Symbol> newMappingsBuilder = ImmutableListMultimap.builder();
// Merge all sources of the first source.
addMergedMappings((SetOperationNode) child, 0, newMappingsBuilder);
// Keep remaining as it is
for (int i = 1; i < sources.size(); i++) {
PlanNode source = sources.get(i);
addOriginalMappings(source, i, newMappingsBuilder);
}
if (node instanceof UnionNode) {
return Optional.of(new UnionNode(node.getId(), newSources, newMappingsBuilder.build(), node.getOutputSymbols()));
}
if (node instanceof IntersectNode) {
return Optional.of(new IntersectNode(node.getId(), newSources, newMappingsBuilder.build(), node.getOutputSymbols(), mergedQuantifier.get()));
}
if (node instanceof ExceptNode) {
return Optional.of(new ExceptNode(node.getId(), newSources, newMappingsBuilder.build(), node.getOutputSymbols(), mergedQuantifier.get()));
}
throw new IllegalArgumentException("unexpected node type: " + node.getClass().getSimpleName());
}
use of io.trino.sql.planner.plan.PlanNode in project trino by trinodb.
the class TransformCorrelatedInPredicateToJoin method apply.
private Result apply(ApplyNode apply, InPredicate inPredicate, Symbol inPredicateOutputSymbol, Lookup lookup, PlanNodeIdAllocator idAllocator, SymbolAllocator symbolAllocator, Session session) {
Optional<Decorrelated> decorrelated = new DecorrelatingVisitor(lookup, apply.getCorrelation()).decorrelate(apply.getSubquery());
if (decorrelated.isEmpty()) {
return Result.empty();
}
PlanNode projection = buildInPredicateEquivalent(apply, inPredicate, inPredicateOutputSymbol, decorrelated.get(), idAllocator, symbolAllocator, session);
return Result.ofPlanNode(projection);
}
use of io.trino.sql.planner.plan.PlanNode in project trino by trinodb.
the class TransformCorrelatedScalarSubquery method apply.
@Override
public Result apply(CorrelatedJoinNode correlatedJoinNode, Captures captures, Context context) {
PlanNode subquery = context.getLookup().resolve(correlatedJoinNode.getSubquery());
if (!searchFrom(subquery, context.getLookup()).where(EnforceSingleRowNode.class::isInstance).recurseOnlyWhen(ProjectNode.class::isInstance).matches()) {
return Result.empty();
}
PlanNode rewrittenSubquery = searchFrom(subquery, context.getLookup()).where(EnforceSingleRowNode.class::isInstance).recurseOnlyWhen(ProjectNode.class::isInstance).removeFirst();
Range<Long> subqueryCardinality = extractCardinality(rewrittenSubquery, context.getLookup());
boolean producesAtMostOneRow = Range.closed(0L, 1L).encloses(subqueryCardinality);
if (producesAtMostOneRow) {
boolean producesSingleRow = Range.singleton(1L).encloses(subqueryCardinality);
return Result.ofPlanNode(new CorrelatedJoinNode(context.getIdAllocator().getNextId(), correlatedJoinNode.getInput(), rewrittenSubquery, correlatedJoinNode.getCorrelation(), producesSingleRow ? correlatedJoinNode.getType() : LEFT, correlatedJoinNode.getFilter(), correlatedJoinNode.getOriginSubquery()));
}
Symbol unique = context.getSymbolAllocator().newSymbol("unique", BigintType.BIGINT);
CorrelatedJoinNode rewrittenCorrelatedJoinNode = new CorrelatedJoinNode(context.getIdAllocator().getNextId(), new AssignUniqueId(context.getIdAllocator().getNextId(), correlatedJoinNode.getInput(), unique), rewrittenSubquery, correlatedJoinNode.getCorrelation(), LEFT, correlatedJoinNode.getFilter(), correlatedJoinNode.getOriginSubquery());
Symbol isDistinct = context.getSymbolAllocator().newSymbol("is_distinct", BOOLEAN);
MarkDistinctNode markDistinctNode = new MarkDistinctNode(context.getIdAllocator().getNextId(), rewrittenCorrelatedJoinNode, isDistinct, rewrittenCorrelatedJoinNode.getInput().getOutputSymbols(), Optional.empty());
FilterNode filterNode = new FilterNode(context.getIdAllocator().getNextId(), markDistinctNode, new SimpleCaseExpression(isDistinct.toSymbolReference(), ImmutableList.of(new WhenClause(TRUE_LITERAL, TRUE_LITERAL)), Optional.of(new Cast(FunctionCallBuilder.resolve(context.getSession(), metadata).setName(QualifiedName.of("fail")).addArgument(INTEGER, new LongLiteral(Integer.toString(SUBQUERY_MULTIPLE_ROWS.toErrorCode().getCode()))).addArgument(VARCHAR, new StringLiteral("Scalar sub-query has returned multiple rows")).build(), toSqlType(BOOLEAN)))));
return Result.ofPlanNode(new ProjectNode(context.getIdAllocator().getNextId(), filterNode, Assignments.identity(correlatedJoinNode.getOutputSymbols())));
}
use of io.trino.sql.planner.plan.PlanNode in project trino by trinodb.
the class TransformExistsApplyToCorrelatedJoin method rewriteToNonDefaultAggregation.
private Optional<PlanNode> rewriteToNonDefaultAggregation(ApplyNode applyNode, Context context) {
checkState(applyNode.getSubquery().getOutputSymbols().isEmpty(), "Expected subquery output symbols to be pruned");
Symbol subqueryTrue = context.getSymbolAllocator().newSymbol("subqueryTrue", BOOLEAN);
PlanNode subquery = new ProjectNode(context.getIdAllocator().getNextId(), new LimitNode(context.getIdAllocator().getNextId(), applyNode.getSubquery(), 1L, false), Assignments.of(subqueryTrue, TRUE_LITERAL));
PlanNodeDecorrelator decorrelator = new PlanNodeDecorrelator(plannerContext, context.getSymbolAllocator(), context.getLookup());
if (decorrelator.decorrelateFilters(subquery, applyNode.getCorrelation()).isEmpty()) {
return Optional.empty();
}
Symbol exists = getOnlyElement(applyNode.getSubqueryAssignments().getSymbols());
Assignments.Builder assignments = Assignments.builder().putIdentities(applyNode.getInput().getOutputSymbols()).put(exists, new CoalesceExpression(ImmutableList.of(subqueryTrue.toSymbolReference(), BooleanLiteral.FALSE_LITERAL)));
return Optional.of(new ProjectNode(context.getIdAllocator().getNextId(), new CorrelatedJoinNode(applyNode.getId(), applyNode.getInput(), subquery, applyNode.getCorrelation(), LEFT, TRUE_LITERAL, applyNode.getOriginSubquery()), assignments.build()));
}
use of io.trino.sql.planner.plan.PlanNode in project trino by trinodb.
the class TransformFilteringSemiJoinToInnerJoin method apply.
@Override
public Result apply(FilterNode filterNode, Captures captures, Context context) {
SemiJoinNode semiJoin = captures.get(SEMI_JOIN);
// Do not transform semi-join in context of DELETE
if (PlanNodeSearcher.searchFrom(semiJoin.getSource(), context.getLookup()).where(node -> node instanceof TableScanNode && ((TableScanNode) node).isUpdateTarget()).matches()) {
return Result.empty();
}
Symbol semiJoinSymbol = semiJoin.getSemiJoinOutput();
Predicate<Expression> isSemiJoinSymbol = expression -> expression.equals(semiJoinSymbol.toSymbolReference());
List<Expression> conjuncts = extractConjuncts(filterNode.getPredicate());
if (conjuncts.stream().noneMatch(isSemiJoinSymbol)) {
return Result.empty();
}
Expression filteredPredicate = and(conjuncts.stream().filter(not(isSemiJoinSymbol)).collect(toImmutableList()));
Expression simplifiedPredicate = inlineSymbols(symbol -> {
if (symbol.equals(semiJoinSymbol)) {
return TRUE_LITERAL;
}
return symbol.toSymbolReference();
}, filteredPredicate);
Optional<Expression> joinFilter = simplifiedPredicate.equals(TRUE_LITERAL) ? Optional.empty() : Optional.of(simplifiedPredicate);
PlanNode filteringSourceDistinct = new AggregationNode(context.getIdAllocator().getNextId(), semiJoin.getFilteringSource(), ImmutableMap.of(), singleGroupingSet(ImmutableList.of(semiJoin.getFilteringSourceJoinSymbol())), ImmutableList.of(), SINGLE, Optional.empty(), Optional.empty());
JoinNode innerJoin = new JoinNode(semiJoin.getId(), INNER, semiJoin.getSource(), filteringSourceDistinct, ImmutableList.of(new EquiJoinClause(semiJoin.getSourceJoinSymbol(), semiJoin.getFilteringSourceJoinSymbol())), semiJoin.getSource().getOutputSymbols(), ImmutableList.of(), false, joinFilter, Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), semiJoin.getDynamicFilterId().map(id -> ImmutableMap.of(id, semiJoin.getFilteringSourceJoinSymbol())).orElse(ImmutableMap.of()), Optional.empty());
ProjectNode project = new ProjectNode(context.getIdAllocator().getNextId(), innerJoin, Assignments.builder().putIdentities(innerJoin.getOutputSymbols()).put(semiJoinSymbol, TRUE_LITERAL).build());
return Result.ofPlanNode(project);
}
Aggregations