/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.facebook.presto.sql.planner.optimizations; import com.facebook.presto.Session; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.metadata.TableLayoutResult; import com.facebook.presto.spi.ColumnHandle; import com.facebook.presto.spi.Constraint; import com.facebook.presto.spi.predicate.TupleDomain; import com.facebook.presto.spi.type.Type; import com.facebook.presto.sql.planner.DomainTranslator; import com.facebook.presto.sql.planner.PlanNodeIdAllocator; import com.facebook.presto.sql.planner.Symbol; import com.facebook.presto.sql.planner.SymbolAllocator; import com.facebook.presto.sql.planner.plan.FilterNode; import com.facebook.presto.sql.planner.plan.PlanNode; import com.facebook.presto.sql.planner.plan.SimplePlanRewriter; import com.facebook.presto.sql.planner.plan.TableScanNode; import com.facebook.presto.sql.planner.plan.ValuesNode; import com.facebook.presto.sql.tree.BooleanLiteral; import com.facebook.presto.sql.tree.Expression; import com.google.common.collect.ImmutableBiMap; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import java.util.List; import java.util.Map; import java.util.Optional; import static com.facebook.presto.sql.ExpressionUtils.combineConjuncts; import static java.util.Objects.requireNonNull; /** * Pick an arbitrary layout if none has been chosen */ public class PickLayout implements PlanOptimizer { private final Metadata metadata; public PickLayout(Metadata metadata) { requireNonNull(metadata, "metadata is null"); this.metadata = metadata; } @Override public PlanNode optimize(PlanNode plan, Session session, Map<Symbol, Type> types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator) { return SimplePlanRewriter.rewriteWith(new Rewriter(metadata, session, symbolAllocator, idAllocator), plan); } private static class Rewriter extends SimplePlanRewriter<Void> { private final Metadata metadata; private final Session session; private final SymbolAllocator symbolAllocator; private final PlanNodeIdAllocator idAllocator; public Rewriter(Metadata metadata, Session session, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator) { this.metadata = metadata; this.session = session; this.symbolAllocator = symbolAllocator; this.idAllocator = idAllocator; } @Override protected PlanNode visitPlan(PlanNode node, RewriteContext<Void> context) { return context.defaultRewrite(node); } @Override public PlanNode visitFilter(FilterNode node, RewriteContext<Void> context) { if (node.getSource() instanceof TableScanNode && !((TableScanNode) node.getSource()).getLayout().isPresent()) { return planTableScan((TableScanNode) node.getSource(), node.getPredicate()); } return context.defaultRewrite(node); } @Override public PlanNode visitTableScan(TableScanNode node, RewriteContext<Void> context) { if (node.getLayout().isPresent()) { return node; } return planTableScan(node, BooleanLiteral.TRUE_LITERAL); } private PlanNode planTableScan(TableScanNode node, Expression predicate) { DomainTranslator.ExtractionResult decomposedPredicate = DomainTranslator.fromPredicate( metadata, session, predicate, symbolAllocator.getTypes()); TupleDomain<ColumnHandle> simplifiedConstraint = decomposedPredicate.getTupleDomain() .transform(node.getAssignments()::get) .intersect(node.getCurrentConstraint()); List<TableLayoutResult> layouts = metadata.getLayouts( session, node.getTable(), new Constraint<>(simplifiedConstraint, bindings -> true), Optional.of(ImmutableSet.copyOf(node.getAssignments().values()))); if (layouts.isEmpty()) { return new ValuesNode(idAllocator.getNextId(), node.getOutputSymbols(), ImmutableList.of()); } TableLayoutResult layout = layouts.get(0); TableScanNode result = new TableScanNode( node.getId(), node.getTable(), node.getOutputSymbols(), node.getAssignments(), Optional.of(layout.getLayout().getHandle()), simplifiedConstraint.intersect(layout.getLayout().getPredicate()), Optional.ofNullable(node.getOriginalConstraint()).orElse(predicate)); Map<ColumnHandle, Symbol> assignments = ImmutableBiMap.copyOf(node.getAssignments()).inverse(); Expression resultingPredicate = combineConjuncts( decomposedPredicate.getRemainingExpression(), DomainTranslator.toPredicate(layout.getUnenforcedConstraint().transform(assignments::get))); if (!BooleanLiteral.TRUE_LITERAL.equals(resultingPredicate)) { return new FilterNode(idAllocator.getNextId(), result, resultingPredicate); } return result; } } }