use of io.trino.sql.tree.NullLiteral 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.tree.NullLiteral in project trino by trinodb.
the class TestReplaceRedundantJoinWithProject method testReplaceFULLJoin.
@Test
public void testReplaceFULLJoin() {
tester().assertThat(new ReplaceRedundantJoinWithProject()).on(p -> p.join(FULL, p.values(10, p.symbol("a")), p.values(0, p.symbol("b")))).matches(project(ImmutableMap.of("a", expression("a"), "b", expression("CAST(null AS bigint)")), values(ImmutableList.of("a"), nCopies(10, ImmutableList.of(new NullLiteral())))));
tester().assertThat(new ReplaceRedundantJoinWithProject()).on(p -> p.join(FULL, p.values(0, p.symbol("a")), p.values(10, p.symbol("b")))).matches(project(ImmutableMap.of("a", expression("CAST(null AS bigint)"), "b", expression("b")), values(ImmutableList.of("b"), nCopies(10, ImmutableList.of(new NullLiteral())))));
}
use of io.trino.sql.tree.NullLiteral in project trino by trinodb.
the class TestReplaceRedundantJoinWithSource method testReplaceInnerJoinWithFilter.
@Test
public void testReplaceInnerJoinWithFilter() {
tester().assertThat(new ReplaceRedundantJoinWithSource()).on(p -> p.join(INNER, p.values(10, p.symbol("a")), p.values(1), expression("a > 0"))).matches(filter("a > 0", values(ImmutableList.of("a"), nCopies(10, ImmutableList.of(new NullLiteral())))));
tester().assertThat(new ReplaceRedundantJoinWithSource()).on(p -> p.join(INNER, p.values(1), p.values(10, p.symbol("b")), expression("b > 0"))).matches(filter("b > 0", values(ImmutableList.of("b"), nCopies(10, ImmutableList.of(new NullLiteral())))));
}
use of io.trino.sql.tree.NullLiteral in project trino by trinodb.
the class PushAggregationThroughOuterJoin method createAggregationOverNull.
private MappedAggregationInfo createAggregationOverNull(AggregationNode referenceAggregation, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator) {
// Create a values node that consists of a single row of nulls.
// Map the output symbols from the referenceAggregation's source
// to symbol references for the new values node.
ImmutableList.Builder<Symbol> nullSymbols = ImmutableList.builder();
ImmutableList.Builder<Expression> nullLiterals = ImmutableList.builder();
ImmutableMap.Builder<Symbol, Symbol> sourcesSymbolMappingBuilder = ImmutableMap.builder();
for (Symbol sourceSymbol : referenceAggregation.getSource().getOutputSymbols()) {
Type type = symbolAllocator.getTypes().get(sourceSymbol);
nullLiterals.add(new Cast(new NullLiteral(), toSqlType(type)));
Symbol nullSymbol = symbolAllocator.newSymbol("null", type);
nullSymbols.add(nullSymbol);
sourcesSymbolMappingBuilder.put(sourceSymbol, nullSymbol);
}
ValuesNode nullRow = new ValuesNode(idAllocator.getNextId(), nullSymbols.build(), ImmutableList.of(new Row(nullLiterals.build())));
// For each aggregation function in the reference node, create a corresponding aggregation function
// that points to the nullRow. Map the symbols from the aggregations in referenceAggregation to the
// symbols in these new aggregations.
ImmutableMap.Builder<Symbol, Symbol> aggregationsSymbolMappingBuilder = ImmutableMap.builder();
ImmutableMap.Builder<Symbol, AggregationNode.Aggregation> aggregationsOverNullBuilder = ImmutableMap.builder();
SymbolMapper mapper = symbolMapper(sourcesSymbolMappingBuilder.buildOrThrow());
for (Map.Entry<Symbol, AggregationNode.Aggregation> entry : referenceAggregation.getAggregations().entrySet()) {
Symbol aggregationSymbol = entry.getKey();
Aggregation overNullAggregation = mapper.map(entry.getValue());
Symbol overNullSymbol = symbolAllocator.newSymbol(overNullAggregation.getResolvedFunction().getSignature().getName(), symbolAllocator.getTypes().get(aggregationSymbol));
aggregationsOverNullBuilder.put(overNullSymbol, overNullAggregation);
aggregationsSymbolMappingBuilder.put(aggregationSymbol, overNullSymbol);
}
Map<Symbol, Symbol> aggregationsSymbolMapping = aggregationsSymbolMappingBuilder.buildOrThrow();
// create an aggregation node whose source is the null row.
AggregationNode aggregationOverNullRow = new AggregationNode(idAllocator.getNextId(), nullRow, aggregationsOverNullBuilder.buildOrThrow(), globalAggregation(), ImmutableList.of(), AggregationNode.Step.SINGLE, Optional.empty(), Optional.empty());
return new MappedAggregationInfo(aggregationOverNullRow, aggregationsSymbolMapping);
}
use of io.trino.sql.tree.NullLiteral in project trino by trinodb.
the class ReplaceRedundantJoinWithProject method appendNulls.
private static ProjectNode appendNulls(PlanNode source, List<Symbol> sourceOutputs, List<Symbol> nullSymbols, PlanNodeIdAllocator idAllocator, SymbolAllocator symbolAllocator) {
Assignments.Builder assignments = Assignments.builder().putIdentities(sourceOutputs);
nullSymbols.stream().forEach(symbol -> assignments.put(symbol, new Cast(new NullLiteral(), toSqlType(symbolAllocator.getTypes().get(symbol)))));
return new ProjectNode(idAllocator.getNextId(), source, assignments.build());
}
Aggregations