org.apache.hadoop.hive.ql.ppd.ExprWalkerInfo Maven / Gradle / Ivy
/*
* 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.hadoop.hive.ql.ppd;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.hadoop.hive.ql.exec.Operator;
import org.apache.hadoop.hive.ql.lib.NodeProcessorCtx;
import org.apache.hadoop.hive.ql.plan.ExprNodeDesc;
import org.apache.hadoop.hive.ql.plan.OperatorDesc;
/**
* Context for Expression Walker for determining predicate pushdown candidates
* It contains a ExprInfo object for each expression that is processed.
*/
public class ExprWalkerInfo implements NodeProcessorCtx {
/** Information maintained for an expr while walking an expr tree. */
protected class ExprInfo {
/**
* true if expr rooted at this node doesn't contain more than one table.
* alias
*/
protected boolean isCandidate = false;
/** alias that this expression refers to. */
protected String alias = null;
/** new expr for this expression. */
protected ExprNodeDesc convertedExpr = null;
}
protected static final Logger LOG = LoggerFactory.getLogger(OpProcFactory.class.getName());
private Operator extends OperatorDesc> op = null;
/**
* Values the expression sub-trees (predicates) that can be pushed down for
* root expression tree. Since there can be more than one alias in an
* expression tree, this is a map from the alias to predicates.
*/
private final Map> pushdownPreds;
/**
* Values the expression sub-trees (predicates) that can not be pushed down for
* root expression tree. Since there can be more than one alias in an
* expression tree, this is a map from the alias to predicates.
*/
private final Map> nonFinalPreds;
/**
* this map contains a expr infos. Each key is a node in the expression tree
* and the information for each node is the value which is used while walking
* the tree by its parent.
*/
private final Map exprInfoMap;
/**
* This is a map from a new pushdown expressions generated by the ExprWalker
* to the old pushdown expression that it originated from. For example, if
* an output column of the current operator is _col0, which comes from an
* input column _col1, this would map the filter "Column[_col1]=2" to
* "Column[_col0]=2" ("Column[_col1]=2" is new because we move from children
* operators to parents in PPD)
*/
private final Map newToOldExprMap;
private boolean isDeterministic = true;
public ExprWalkerInfo() {
pushdownPreds = new HashMap>();
nonFinalPreds = new HashMap>();
exprInfoMap = new IdentityHashMap();
newToOldExprMap = new IdentityHashMap();
}
public ExprWalkerInfo(Operator extends OperatorDesc> op) {
this.op = op;
pushdownPreds = new HashMap>();
exprInfoMap = new IdentityHashMap();
nonFinalPreds = new HashMap>();
newToOldExprMap = new IdentityHashMap();
}
/**
* @return the op of this expression.
*/
public Operator extends OperatorDesc> getOp() {
return op;
}
/**
* @return the new expression to old expression map
*/
public Map getNewToOldExprMap() {
return newToOldExprMap;
}
/**
* Get additional info for a given expression node
*/
public ExprInfo getExprInfo(ExprNodeDesc expr) {
return exprInfoMap.get(expr);
}
/**
* Get additional info for a given expression node if it
* exists, or create a new one and store it if it does not
*/
public ExprInfo addExprInfo(ExprNodeDesc expr) {
ExprInfo exprInfo = new ExprInfo();
exprInfoMap.put(expr, exprInfo);
return exprInfo;
}
/**
* Get additional info for a given expression node if it
* exists, or create a new one and store it if it does not
*/
public ExprInfo addOrGetExprInfo(ExprNodeDesc expr) {
ExprInfo exprInfo = exprInfoMap.get(expr);
if (exprInfo == null) {
exprInfo = new ExprInfo();
exprInfoMap.put(expr, exprInfo);
}
return exprInfo;
}
public void addFinalCandidate(String alias, ExprNodeDesc expr) {
List predicates = getPushdownPreds(alias);
for (ExprNodeDesc curPred: predicates) {
if (curPred.isSame(expr)) {
return;
}
}
predicates.add(expr);
}
/**
* Adds the passed list of pushDowns for the alias.
*
* @param alias
* @param pushDowns
*/
public void addPushDowns(String alias, List pushDowns) {
List predicates = getPushdownPreds(alias);
boolean isNew;
for (ExprNodeDesc newPred: pushDowns) {
isNew = true;
for (ExprNodeDesc curPred: predicates) {
if (curPred.isSame(newPred)) {
isNew = false;
break;
}
}
if (isNew) {
predicates.add(newPred);
}
}
}
/**
* Returns the list of pushdown expressions for each alias that appear in the
* current operator's RowResolver. The exprs in each list can be combined
* using conjunction (AND).
*
* @return the map of alias to a list of pushdown predicates
*/
public Map> getFinalCandidates() {
return pushdownPreds;
}
private List getPushdownPreds(String alias) {
List predicates = pushdownPreds.get(alias);
if (predicates == null) {
pushdownPreds.put(alias, predicates = new ArrayList());
}
return predicates;
}
public boolean hasAnyCandidates() {
if (pushdownPreds == null || pushdownPreds.isEmpty()) {
return false;
}
for (List exprs : pushdownPreds.values()) {
if (!exprs.isEmpty()) {
return true;
}
}
return false;
}
/**
* Adds the specified expr as a non-final candidate
*
* @param expr
*/
public void addNonFinalCandidate(String alias, ExprNodeDesc expr) {
if (nonFinalPreds.get(alias) == null) {
nonFinalPreds.put(alias, new ArrayList());
}
nonFinalPreds.get(alias).add(expr);
}
/**
* Returns list of non-final candidate predicate for each map.
*
* @return list of non-final candidate predicates
*/
public Map> getNonFinalCandidates() {
return nonFinalPreds;
}
public Map> getResidualPredicates(boolean clear) {
Map> oldExprs = new HashMap>();
for (Map.Entry> entry : nonFinalPreds.entrySet()) {
List converted = new ArrayList();
for (ExprNodeDesc newExpr : entry.getValue()) {
converted.add(newToOldExprMap.get(newExpr));
}
oldExprs.put(entry.getKey(), converted);
}
if (clear) {
nonFinalPreds.clear();
}
return oldExprs;
}
/**
* Merges the specified pushdown predicates with the current class.
*
* @param ewi
* ExpressionWalkerInfo
*/
public void merge(ExprWalkerInfo ewi) {
if (ewi == null) {
return;
}
for (Entry> e : ewi.getFinalCandidates()
.entrySet()) {
List predList = pushdownPreds.get(e.getKey());
if (predList != null) {
predList.addAll(e.getValue());
} else {
pushdownPreds.put(e.getKey(), e.getValue());
}
}
for (Entry> e : ewi.getNonFinalCandidates()
.entrySet()) {
List predList = nonFinalPreds.get(e.getKey());
if (predList != null) {
predList.addAll(e.getValue());
} else {
nonFinalPreds.put(e.getKey(), e.getValue());
}
}
newToOldExprMap.putAll(ewi.getNewToOldExprMap());
}
/**
* sets the deterministic flag for this expression.
*
* @param b
* deterministic or not
*/
public void setDeterministic(boolean b) {
isDeterministic = b;
}
/**
* @return whether this expression is deterministic or not.
*/
public boolean isDeterministic() {
return isDeterministic;
}
}