use of io.trino.sql.tree.BooleanLiteral.TRUE_LITERAL 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()));
}
use of io.trino.sql.tree.BooleanLiteral.TRUE_LITERAL in project trino by trinodb.
the class DomainTranslator method toPredicate.
private Expression toPredicate(Session session, Domain domain, SymbolReference reference) {
if (domain.getValues().isNone()) {
return domain.isNullAllowed() ? new IsNullPredicate(reference) : FALSE_LITERAL;
}
if (domain.getValues().isAll()) {
return domain.isNullAllowed() ? TRUE_LITERAL : new NotExpression(new IsNullPredicate(reference));
}
List<Expression> disjuncts = new ArrayList<>();
disjuncts.addAll(domain.getValues().getValuesProcessor().transform(ranges -> extractDisjuncts(session, domain.getType(), ranges, reference), discreteValues -> extractDisjuncts(session, domain.getType(), discreteValues, reference), allOrNone -> {
throw new IllegalStateException("Case should not be reachable");
}));
// Add nullability disjuncts
if (domain.isNullAllowed()) {
disjuncts.add(new IsNullPredicate(reference));
}
return combineDisjunctsWithDefault(plannerContext.getMetadata(), disjuncts, TRUE_LITERAL);
}
use of io.trino.sql.tree.BooleanLiteral.TRUE_LITERAL 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.tree.BooleanLiteral.TRUE_LITERAL in project trino by trinodb.
the class SimplifyFilterPredicate method simplifyFilterExpression.
private Optional<Expression> simplifyFilterExpression(Expression expression) {
if (expression instanceof IfExpression) {
IfExpression ifExpression = (IfExpression) expression;
Expression condition = ifExpression.getCondition();
Expression trueValue = ifExpression.getTrueValue();
Optional<Expression> falseValue = ifExpression.getFalseValue();
if (trueValue.equals(TRUE_LITERAL) && (falseValue.isEmpty() || isNotTrue(falseValue.get()))) {
return Optional.of(condition);
}
if (isNotTrue(trueValue) && falseValue.isPresent() && falseValue.get().equals(TRUE_LITERAL)) {
return Optional.of(isFalseOrNullPredicate(condition));
}
if (falseValue.isPresent() && falseValue.get().equals(trueValue) && isDeterministic(trueValue, metadata)) {
return Optional.of(trueValue);
}
if (isNotTrue(trueValue) && (falseValue.isEmpty() || isNotTrue(falseValue.get()))) {
return Optional.of(FALSE_LITERAL);
}
if (condition.equals(TRUE_LITERAL)) {
return Optional.of(trueValue);
}
if (isNotTrue(condition)) {
return Optional.of(falseValue.orElse(FALSE_LITERAL));
}
return Optional.empty();
}
if (expression instanceof NullIfExpression) {
NullIfExpression nullIfExpression = (NullIfExpression) expression;
return Optional.of(LogicalExpression.and(nullIfExpression.getFirst(), isFalseOrNullPredicate(nullIfExpression.getSecond())));
}
if (expression instanceof SearchedCaseExpression) {
SearchedCaseExpression caseExpression = (SearchedCaseExpression) expression;
Optional<Expression> defaultValue = caseExpression.getDefaultValue();
List<Expression> operands = caseExpression.getWhenClauses().stream().map(WhenClause::getOperand).collect(toImmutableList());
List<Expression> results = caseExpression.getWhenClauses().stream().map(WhenClause::getResult).collect(toImmutableList());
long trueResultsCount = results.stream().filter(result -> result.equals(TRUE_LITERAL)).count();
long notTrueResultsCount = results.stream().filter(SimplifyFilterPredicate::isNotTrue).count();
// all results true
if (trueResultsCount == results.size() && defaultValue.isPresent() && defaultValue.get().equals(TRUE_LITERAL)) {
return Optional.of(TRUE_LITERAL);
}
// all results not true
if (notTrueResultsCount == results.size() && (defaultValue.isEmpty() || isNotTrue(defaultValue.get()))) {
return Optional.of(FALSE_LITERAL);
}
// one result true, and remaining results not true
if (trueResultsCount == 1 && notTrueResultsCount == results.size() - 1 && (defaultValue.isEmpty() || isNotTrue(defaultValue.get()))) {
ImmutableList.Builder<Expression> builder = ImmutableList.builder();
for (WhenClause whenClause : caseExpression.getWhenClauses()) {
Expression operand = whenClause.getOperand();
Expression result = whenClause.getResult();
if (isNotTrue(result)) {
builder.add(isFalseOrNullPredicate(operand));
} else {
builder.add(operand);
return Optional.of(combineConjuncts(metadata, builder.build()));
}
}
}
// all results not true, and default true
if (notTrueResultsCount == results.size() && defaultValue.isPresent() && defaultValue.get().equals(TRUE_LITERAL)) {
ImmutableList.Builder<Expression> builder = ImmutableList.builder();
operands.stream().forEach(operand -> builder.add(isFalseOrNullPredicate(operand)));
return Optional.of(combineConjuncts(metadata, builder.build()));
}
// skip clauses with not true conditions
List<WhenClause> whenClauses = new ArrayList<>();
for (WhenClause whenClause : caseExpression.getWhenClauses()) {
Expression operand = whenClause.getOperand();
if (operand.equals(TRUE_LITERAL)) {
if (whenClauses.isEmpty()) {
return Optional.of(whenClause.getResult());
}
return Optional.of(new SearchedCaseExpression(whenClauses, Optional.of(whenClause.getResult())));
}
if (!isNotTrue(operand)) {
whenClauses.add(whenClause);
}
}
if (whenClauses.isEmpty()) {
return Optional.of(defaultValue.orElse(FALSE_LITERAL));
}
if (whenClauses.size() < caseExpression.getWhenClauses().size()) {
return Optional.of(new SearchedCaseExpression(whenClauses, defaultValue));
}
return Optional.empty();
}
if (expression instanceof SimpleCaseExpression) {
SimpleCaseExpression caseExpression = (SimpleCaseExpression) expression;
Optional<Expression> defaultValue = caseExpression.getDefaultValue();
if (caseExpression.getOperand() instanceof NullLiteral) {
return Optional.of(defaultValue.orElse(FALSE_LITERAL));
}
List<Expression> results = caseExpression.getWhenClauses().stream().map(WhenClause::getResult).collect(toImmutableList());
if (results.stream().allMatch(result -> result.equals(TRUE_LITERAL)) && defaultValue.isPresent() && defaultValue.get().equals(TRUE_LITERAL)) {
return Optional.of(TRUE_LITERAL);
}
if (results.stream().allMatch(SimplifyFilterPredicate::isNotTrue) && (defaultValue.isEmpty() || isNotTrue(defaultValue.get()))) {
return Optional.of(FALSE_LITERAL);
}
return Optional.empty();
}
return Optional.empty();
}
use of io.trino.sql.tree.BooleanLiteral.TRUE_LITERAL 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