/*
* 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.spi.type.Type;
import com.facebook.presto.sql.planner.DeterminismEvaluator;
import com.facebook.presto.sql.planner.ExpressionSymbolInliner;
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.Assignments;
import com.facebook.presto.sql.planner.plan.PlanNode;
import com.facebook.presto.sql.planner.plan.ProjectNode;
import com.facebook.presto.sql.planner.plan.SimplePlanRewriter;
import com.facebook.presto.sql.tree.Expression;
import com.facebook.presto.sql.tree.TryExpression;
import com.facebook.presto.sql.util.AstUtils;
import com.google.common.collect.ImmutableList;
import java.util.Map;
import static com.facebook.presto.sql.planner.plan.ChildReplacer.replaceChildren;
import static java.util.Objects.requireNonNull;
/**
* Merges chains of consecutive projections
*/
@Deprecated
public class MergeProjections
implements PlanOptimizer
{
@Override
public PlanNode optimize(PlanNode plan, Session session, Map<Symbol, Type> types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator)
{
requireNonNull(plan, "plan is null");
requireNonNull(session, "session is null");
requireNonNull(types, "types is null");
requireNonNull(symbolAllocator, "symbolAllocator is null");
requireNonNull(idAllocator, "idAllocator is null");
return SimplePlanRewriter.rewriteWith(new Rewriter(), plan);
}
private static class Rewriter
extends SimplePlanRewriter<Void>
{
@Override
public PlanNode visitProject(ProjectNode node, RewriteContext<Void> context)
{
PlanNode source = context.rewrite(node.getSource());
if (source instanceof ProjectNode) {
ProjectNode sourceProject = (ProjectNode) source;
if (isDeterministic(sourceProject) && !containsTry(node)) {
Assignments.Builder projections = Assignments.builder();
for (Map.Entry<Symbol, Expression> projection : node.getAssignments().entrySet()) {
Expression inlined = new ExpressionSymbolInliner(sourceProject.getAssignments().getMap()).rewrite(projection.getValue());
projections.put(projection.getKey(), inlined);
}
return new ProjectNode(node.getId(), sourceProject.getSource(), projections.build());
}
}
return replaceChildren(node, ImmutableList.of(source));
}
private static boolean isDeterministic(ProjectNode node)
{
return node.getAssignments().getExpressions().stream().allMatch(DeterminismEvaluator::isDeterministic);
}
private static boolean containsTry(ProjectNode node)
{
return node.getAssignments()
.getExpressions().stream()
.flatMap(AstUtils::preOrder)
.anyMatch(TryExpression.class::isInstance);
}
}
}