use of io.trino.sql.planner.plan.ValuesNode in project trino by trinodb.
the class PushPredicateIntoTableScan method pushFilterIntoTableScan.
public static Optional<PlanNode> pushFilterIntoTableScan(FilterNode filterNode, TableScanNode node, boolean pruneWithPredicateExpression, Session session, SymbolAllocator symbolAllocator, PlannerContext plannerContext, TypeAnalyzer typeAnalyzer, StatsProvider statsProvider, DomainTranslator domainTranslator) {
if (!isAllowPushdownIntoConnectors(session)) {
return Optional.empty();
}
SplitExpression splitExpression = splitExpression(plannerContext, filterNode.getPredicate());
DomainTranslator.ExtractionResult decomposedPredicate = DomainTranslator.getExtractionResult(plannerContext, session, splitExpression.getDeterministicPredicate(), symbolAllocator.getTypes());
TupleDomain<ColumnHandle> newDomain = decomposedPredicate.getTupleDomain().transformKeys(node.getAssignments()::get).intersect(node.getEnforcedConstraint());
Map<NodeRef<Expression>, Type> remainingExpressionTypes = typeAnalyzer.getTypes(session, symbolAllocator.getTypes(), decomposedPredicate.getRemainingExpression());
Optional<ConnectorExpression> connectorExpression = new ConnectorExpressionTranslator.SqlToConnectorExpressionTranslator(session, remainingExpressionTypes, plannerContext).process(decomposedPredicate.getRemainingExpression());
Map<String, ColumnHandle> connectorExpressionAssignments = connectorExpression.map(ignored -> node.getAssignments().entrySet().stream().collect(toImmutableMap(entry -> entry.getKey().getName(), Map.Entry::getValue))).orElse(ImmutableMap.of());
Map<ColumnHandle, Symbol> assignments = ImmutableBiMap.copyOf(node.getAssignments()).inverse();
Constraint constraint;
// use evaluator only when there is some predicate which could not be translated into tuple domain
if (pruneWithPredicateExpression && !TRUE_LITERAL.equals(decomposedPredicate.getRemainingExpression())) {
LayoutConstraintEvaluator evaluator = new LayoutConstraintEvaluator(plannerContext, typeAnalyzer, session, symbolAllocator.getTypes(), node.getAssignments(), combineConjuncts(plannerContext.getMetadata(), splitExpression.getDeterministicPredicate(), // which would be expensive to evaluate in the call to isCandidate below.
domainTranslator.toPredicate(session, newDomain.simplify().transformKeys(assignments::get))));
constraint = new Constraint(newDomain, connectorExpression.orElse(TRUE), connectorExpressionAssignments, evaluator::isCandidate, evaluator.getArguments());
} else {
// Currently, invoking the expression interpreter is very expensive.
// TODO invoke the interpreter unconditionally when the interpreter becomes cheap enough.
constraint = new Constraint(newDomain, connectorExpression.orElse(TRUE), connectorExpressionAssignments);
}
// check if new domain is wider than domain already provided by table scan
if (constraint.predicate().isEmpty() && // TODO do we need to track enforced ConnectorExpression in TableScanNode?
TRUE.equals(connectorExpression.orElse(TRUE)) && newDomain.contains(node.getEnforcedConstraint())) {
Expression resultingPredicate = createResultingPredicate(plannerContext, session, symbolAllocator, typeAnalyzer, splitExpression.getDynamicFilter(), TRUE_LITERAL, splitExpression.getNonDeterministicPredicate(), decomposedPredicate.getRemainingExpression());
if (!TRUE_LITERAL.equals(resultingPredicate)) {
return Optional.of(new FilterNode(filterNode.getId(), node, resultingPredicate));
}
return Optional.of(node);
}
if (newDomain.isNone()) {
// to turn the subtree into a Values node
return Optional.of(new ValuesNode(node.getId(), node.getOutputSymbols(), ImmutableList.of()));
}
Optional<ConstraintApplicationResult<TableHandle>> result = plannerContext.getMetadata().applyFilter(session, node.getTable(), constraint);
if (result.isEmpty()) {
return Optional.empty();
}
TableHandle newTable = result.get().getHandle();
TableProperties newTableProperties = plannerContext.getMetadata().getTableProperties(session, newTable);
Optional<TablePartitioning> newTablePartitioning = newTableProperties.getTablePartitioning();
if (newTableProperties.getPredicate().isNone()) {
return Optional.of(new ValuesNode(node.getId(), node.getOutputSymbols(), ImmutableList.of()));
}
TupleDomain<ColumnHandle> remainingFilter = result.get().getRemainingFilter();
Optional<ConnectorExpression> remainingConnectorExpression = result.get().getRemainingExpression();
boolean precalculateStatistics = result.get().isPrecalculateStatistics();
verifyTablePartitioning(session, plannerContext.getMetadata(), node, newTablePartitioning);
TableScanNode tableScan = new TableScanNode(node.getId(), newTable, node.getOutputSymbols(), node.getAssignments(), computeEnforced(newDomain, remainingFilter), // TODO (https://github.com/trinodb/trino/issues/8144) distinguish between predicate pushed down and remaining
deriveTableStatisticsForPushdown(statsProvider, session, precalculateStatistics, filterNode), node.isUpdateTarget(), node.getUseConnectorNodePartitioning());
Expression remainingDecomposedPredicate;
if (remainingConnectorExpression.isEmpty() || remainingConnectorExpression.equals(connectorExpression)) {
remainingDecomposedPredicate = decomposedPredicate.getRemainingExpression();
} else {
Map<String, Symbol> variableMappings = assignments.values().stream().collect(toImmutableMap(Symbol::getName, Function.identity()));
Expression translatedExpression = ConnectorExpressionTranslator.translate(session, remainingConnectorExpression.get(), plannerContext, variableMappings, new LiteralEncoder(plannerContext));
if (connectorExpression.isEmpty()) {
remainingDecomposedPredicate = ExpressionUtils.combineConjuncts(plannerContext.getMetadata(), translatedExpression, decomposedPredicate.getRemainingExpression());
} else {
remainingDecomposedPredicate = translatedExpression;
}
}
Expression resultingPredicate = createResultingPredicate(plannerContext, session, symbolAllocator, typeAnalyzer, splitExpression.getDynamicFilter(), domainTranslator.toPredicate(session, remainingFilter.transformKeys(assignments::get)), splitExpression.getNonDeterministicPredicate(), remainingDecomposedPredicate);
if (!TRUE_LITERAL.equals(resultingPredicate)) {
return Optional.of(new FilterNode(filterNode.getId(), tableScan, resultingPredicate));
}
return Optional.of(tableScan);
}
use of io.trino.sql.planner.plan.ValuesNode in project trino by trinodb.
the class PruneCountAggregationOverScalar method apply.
@Override
public Result apply(AggregationNode parent, Captures captures, Context context) {
if (!parent.hasDefaultOutput() || parent.getOutputSymbols().size() != 1) {
return Result.empty();
}
FunctionId countFunctionId = metadata.resolveFunction(context.getSession(), QualifiedName.of("count"), ImmutableList.of()).getFunctionId();
Map<Symbol, AggregationNode.Aggregation> assignments = parent.getAggregations();
for (Map.Entry<Symbol, AggregationNode.Aggregation> entry : assignments.entrySet()) {
AggregationNode.Aggregation aggregation = entry.getValue();
requireNonNull(aggregation, "aggregation is null");
ResolvedFunction resolvedFunction = aggregation.getResolvedFunction();
if (!countFunctionId.equals(resolvedFunction.getFunctionId())) {
return Result.empty();
}
}
if (!assignments.isEmpty() && isScalar(parent.getSource(), context.getLookup())) {
return Result.ofPlanNode(new ValuesNode(parent.getId(), parent.getOutputSymbols(), ImmutableList.of(new Row(ImmutableList.of(new GenericLiteral("BIGINT", "1"))))));
}
return Result.empty();
}
use of io.trino.sql.planner.plan.ValuesNode in project trino by trinodb.
the class TransformCorrelatedSingleRowSubqueryToProject method apply.
@Override
public Result apply(CorrelatedJoinNode parent, Captures captures, Context context) {
List<ValuesNode> values = searchFrom(parent.getSubquery(), context.getLookup()).recurseOnlyWhen(ProjectNode.class::isInstance).where(ValuesNode.class::isInstance).findAll();
if (values.size() != 1 || !isSingleRowValuesWithNoColumns(values.get(0))) {
return Result.empty();
}
List<ProjectNode> subqueryProjections = searchFrom(parent.getSubquery(), context.getLookup()).where(node -> node instanceof ProjectNode && !node.getOutputSymbols().equals(parent.getCorrelation())).findAll();
if (subqueryProjections.size() == 0) {
return Result.ofPlanNode(parent.getInput());
}
if (subqueryProjections.size() == 1) {
Assignments assignments = Assignments.builder().putIdentities(parent.getInput().getOutputSymbols()).putAll(subqueryProjections.get(0).getAssignments()).build();
return Result.ofPlanNode(projectNode(parent.getInput(), assignments, context));
}
return Result.empty();
}
use of io.trino.sql.planner.plan.ValuesNode in project trino by trinodb.
the class PushdownFilterIntoWindow method apply.
@Override
public Result apply(FilterNode node, Captures captures, Context context) {
Session session = context.getSession();
TypeProvider types = context.getSymbolAllocator().getTypes();
WindowNode windowNode = captures.get(childCapture);
DomainTranslator.ExtractionResult extractionResult = DomainTranslator.getExtractionResult(plannerContext, session, node.getPredicate(), types);
TupleDomain<Symbol> tupleDomain = extractionResult.getTupleDomain();
Optional<RankingType> rankingType = toTopNRankingType(windowNode);
Symbol rankingSymbol = getOnlyElement(windowNode.getWindowFunctions().keySet());
OptionalInt upperBound = extractUpperBound(tupleDomain, rankingSymbol);
if (upperBound.isEmpty()) {
return Result.empty();
}
if (upperBound.getAsInt() <= 0) {
return Result.ofPlanNode(new ValuesNode(node.getId(), node.getOutputSymbols(), ImmutableList.of()));
}
TopNRankingNode newSource = new TopNRankingNode(windowNode.getId(), windowNode.getSource(), windowNode.getSpecification(), rankingType.get(), rankingSymbol, upperBound.getAsInt(), false, Optional.empty());
if (!allRowNumberValuesInDomain(tupleDomain, rankingSymbol, upperBound.getAsInt())) {
return Result.ofPlanNode(new FilterNode(node.getId(), newSource, node.getPredicate()));
}
// Remove the row number domain because it is absorbed into the node
TupleDomain<Symbol> newTupleDomain = tupleDomain.filter((symbol, domain) -> !symbol.equals(rankingSymbol));
Expression newPredicate = ExpressionUtils.combineConjuncts(plannerContext.getMetadata(), extractionResult.getRemainingExpression(), new DomainTranslator(plannerContext).toPredicate(session, newTupleDomain));
if (newPredicate.equals(BooleanLiteral.TRUE_LITERAL)) {
return Result.ofPlanNode(newSource);
}
return Result.ofPlanNode(new FilterNode(node.getId(), newSource, newPredicate));
}
use of io.trino.sql.planner.plan.ValuesNode in project trino by trinodb.
the class RelationPlanner method visitValues.
@Override
protected RelationPlan visitValues(Values node, Void context) {
Scope scope = analysis.getScope(node);
ImmutableList.Builder<Symbol> outputSymbolsBuilder = ImmutableList.builder();
for (Field field : scope.getRelationType().getVisibleFields()) {
Symbol symbol = symbolAllocator.newSymbol(field);
outputSymbolsBuilder.add(symbol);
}
List<Symbol> outputSymbols = outputSymbolsBuilder.build();
TranslationMap translationMap = new TranslationMap(outerContext, analysis.getScope(node), analysis, lambdaDeclarationToSymbolMap, outputSymbols);
ImmutableList.Builder<Expression> rows = ImmutableList.builder();
for (Expression row : node.getRows()) {
if (row instanceof Row) {
rows.add(new Row(((Row) row).getItems().stream().map(item -> coerceIfNecessary(analysis, item, translationMap.rewrite(item))).collect(toImmutableList())));
} else if (analysis.getType(row) instanceof RowType) {
rows.add(coerceIfNecessary(analysis, row, translationMap.rewrite(row)));
} else {
rows.add(new Row(ImmutableList.of(coerceIfNecessary(analysis, row, translationMap.rewrite(row)))));
}
}
ValuesNode valuesNode = new ValuesNode(idAllocator.getNextId(), outputSymbols, rows.build());
return new RelationPlan(valuesNode, scope, outputSymbols, outerContext);
}
Aggregations