| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you 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 org.apache.impala.planner; |
| |
| import java.util.ArrayList; |
| |
| import org.apache.impala.analysis.Analyzer; |
| import org.apache.impala.analysis.Expr; |
| import org.apache.impala.common.InternalException; |
| import org.apache.impala.thrift.TExecNodePhase; |
| import org.apache.impala.thrift.TExplainLevel; |
| import org.apache.impala.thrift.TPlanNode; |
| import org.apache.impala.thrift.TPlanNodeType; |
| import org.apache.impala.thrift.TQueryOptions; |
| |
| import com.google.common.base.Preconditions; |
| |
| /** |
| * A SubplanNode evaluates its right child plan tree for every row from its left child, |
| * and returns those rows produced by the right child. The right child is called the |
| * 'subplan tree' and the left child the 'input'. A SubplanNode is similar to a join, |
| * but different in the following respects. First, a SubplanNode does not do any real |
| * work itself. It only returns rows produced by the right child plan tree, which |
| * typically has a dependency on the current input row (see SingularRowSrcNode and |
| * UnnestNode). Second, no join predicates are required. A SubplanNode does not |
| * evaluate any conjuncts. |
| */ |
| public class SubplanNode extends PlanNode { |
| private PlanNode subplan_; |
| |
| public SubplanNode(PlanNode input) { |
| super("SUBPLAN"); |
| children_.add(input); |
| } |
| |
| /** |
| * Sets the subplan of this SubplanNode. Dependent plan nodes such as UnnestNodes |
| * and SingularRowSrcNodes need to know their SubplanNode parent, therefore, setting |
| * the subplan in this SubplanNode is deferred until the subplan tree has been |
| * constructed (which requires the parent SubplanNode to have been constructed). |
| */ |
| public void setSubplan(PlanNode subplan) { |
| Preconditions.checkState(children_.size() == 1); |
| subplan_ = subplan; |
| children_.add(subplan); |
| computeTupleIds(); |
| } |
| |
| @Override |
| public void computeTupleIds() { |
| Preconditions.checkNotNull(subplan_); |
| clearTupleIds(); |
| tblRefIds_.addAll(subplan_.getTblRefIds()); |
| tupleIds_.addAll(subplan_.getTupleIds()); |
| nullableTupleIds_.addAll(subplan_.getNullableTupleIds()); |
| } |
| |
| @Override |
| public void init(Analyzer analyzer) throws InternalException { |
| // Subplan root must have been set. |
| Preconditions.checkState(children_.size() == 2); |
| // Check that there are no unassigned conjuncts that can be evaluated by this node. |
| // All such conjuncts should have already been assigned in the right child. |
| assignConjuncts(analyzer); |
| Preconditions.checkState(conjuncts_.isEmpty()); |
| computeStats(analyzer); |
| outputSmap_ = getChild(1).getOutputSmap(); |
| // Save state of assigned conjuncts for join-ordering attempts (see member comment). |
| assignedConjuncts_ = analyzer.getAssignedConjuncts(); |
| } |
| |
| @Override |
| protected void computeStats(Analyzer analyzer) { |
| super.computeStats(analyzer); |
| if (getChild(0).cardinality_ != -1 && getChild(1).cardinality_ != -1) { |
| cardinality_ = |
| checkedMultiply(getChild(0).cardinality_, getChild(1).cardinality_); |
| } else { |
| cardinality_ = -1; |
| } |
| cardinality_ = capCardinalityAtLimit(cardinality_); |
| } |
| |
| @Override |
| public void computeNodeResourceProfile(TQueryOptions queryOptions) { |
| // TODO: add an estimate |
| nodeResourceProfile_ = ResourceProfile.noReservation(0); |
| } |
| |
| @Override |
| public ExecPhaseResourceProfiles computeTreeResourceProfiles( |
| TQueryOptions queryOptions) { |
| // All nodes in a subplan remain open at the same time across iterations of a subplan, |
| // therefore the peak resource consumption is simply the sum of all node resources. |
| ResourceProfile subplanProfile = subplanComputePeakResources(this); |
| return new ExecPhaseResourceProfiles(subplanProfile, subplanProfile); |
| } |
| |
| private static ResourceProfile subplanComputePeakResources(PlanNode node) { |
| ResourceProfile result = node.nodeResourceProfile_; |
| for (PlanNode child: node.getChildren()) { |
| result = result.sum(subplanComputePeakResources(child)); |
| } |
| return result; |
| } |
| |
| @Override |
| public void computePipelineMembership() { |
| children_.get(0).computePipelineMembership(); |
| pipelines_ = new ArrayList<>(); |
| for (PipelineMembership leftPipeline : children_.get(0).getPipelines()) { |
| if (leftPipeline.getPhase() == TExecNodePhase.GETNEXT) { |
| pipelines_.add(new PipelineMembership( |
| leftPipeline.getId(), leftPipeline.getHeight() + 1, TExecNodePhase.GETNEXT)); |
| } |
| } |
| children_.get(1).setPipelinesRecursive(pipelines_); |
| } |
| |
| @Override |
| protected String getNodeExplainString(String prefix, String detailPrefix, |
| TExplainLevel detailLevel) { |
| StringBuilder output = new StringBuilder(); |
| output.append(String.format("%s%s\n", prefix, getDisplayLabel())); |
| if (detailLevel.ordinal() >= TExplainLevel.STANDARD.ordinal()) { |
| if (!conjuncts_.isEmpty()) { |
| output.append(detailPrefix |
| + "predicates: " + Expr.getExplainString(conjuncts_, detailLevel) + "\n"); |
| } |
| } |
| return output.toString(); |
| } |
| |
| @Override |
| protected void toThrift(TPlanNode msg) { msg.node_type = TPlanNodeType.SUBPLAN_NODE; } |
| } |