blob: aac61b7fbad0787d58fbb3c216dd21c64b6a19f7 [file] [log] [blame]
/*
* 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.parse;
import static org.apache.hadoop.hive.ql.parse.BaseSemanticAnalyzer.unescapeIdentifier;
import static org.apache.hadoop.hive.ql.parse.HiveParser.TOK_FUNCTION;
import static org.apache.hadoop.hive.ql.parse.HiveParser.TOK_FUNCTIONDI;
import static org.apache.hadoop.hive.ql.parse.HiveParser.TOK_FUNCTIONSTAR;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.AbstractMap.SimpleEntry;
import org.antlr.runtime.tree.Tree;
import org.apache.hadoop.hive.common.StringInternUtils;
import org.apache.hadoop.hive.ql.exec.FunctionInfo;
import org.apache.hadoop.hive.ql.exec.FunctionRegistry;
import org.apache.hadoop.hive.ql.lib.Node;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.hadoop.hive.ql.parse.BaseSemanticAnalyzer.AnalyzeRewriteContext;
import org.apache.hadoop.hive.ql.parse.BaseSemanticAnalyzer.TableSpec;
/**
* Implementation of the parse information related to a query block.
*
**/
public class QBParseInfo {
private boolean isSubQ;
private String alias;
private ASTNode joinExpr;
private ASTNode hints;
private ASTNode colAliases;
private List<ASTNode> hintList;
private final Map<String, ASTNode> aliasToSrc;
/**
* insclause-0 -> TOK_TAB ASTNode
*/
private final Map<String, ASTNode> nameToDest;
/**
* For 'insert into FOO(x,y) select ...' this stores the
* insclause-0 -> x,y mapping
*/
private final Map<String, List<String>> nameToDestSchema;
private final Map<String, TableSample> nameToSample;
private final Map<ASTNode, String> exprToColumnAlias;
private final Map<String, ASTNode> destToSelExpr;
private final Map<String, ASTNode> destToWhereExpr;
private final Map<String, ASTNode> destToGroupby;
private final Set<String> destRollups;
private final Set<String> destCubes;
private final Set<String> destGroupingSets;
private final Map<String, ASTNode> destToHaving;
private final Map<String, ASTNode> destToQualify;
private final Map<String, Boolean> destToOpType;
// insertIntoTables/insertOverwriteTables map a table's fullName to its ast;
private final Map<String, ASTNode> insertIntoTables;
private final Map<String, ASTNode> insertOverwriteTables;
private ASTNode queryFromExpr;
private boolean isAnalyzeCommand; // used for the analyze command (statistics)
private boolean isNoScanAnalyzeCommand; // used for the analyze command (statistics) (noscan)
private final Map<String, TableSpec> tableSpecs; // used for statistics
private AnalyzeRewriteContext analyzeRewrite;
/**
* ClusterBy is a short name for both DistributeBy and SortBy.
*/
private final Map<String, ASTNode> destToClusterby;
/**
* DistributeBy controls the hashcode of the row, which determines which
* reducer the rows will go to.
*/
private final Map<String, ASTNode> destToDistributeby;
/**
* SortBy controls the reduce keys, which affects the order of rows that the
* reducer receives.
*/
private final Map<String, ASTNode> destToSortby;
/**
* Maping from table/subquery aliases to all the associated lateral view nodes.
*/
private final Map<String, List<ASTNode>> aliasToLateralViews;
private final Map<String, ASTNode> destToLateralView;
/* Order by clause */
private final Map<String, ASTNode> destToOrderby;
// Use SimpleEntry to save the offset and rowcount of limit clause
// KEY of SimpleEntry: offset
// VALUE of SimpleEntry: rowcount
private final Map<String, SimpleEntry<Integer, Integer>> destToLimit;
private int outerQueryLimit;
// used by GroupBy
private final Map<String, Map<String, ASTNode>> destToAggregationExprs;
private final Map<String, List<ASTNode>> destToDistinctFuncExprs;
// used by Windowing
private final Map<String, Map<String, ASTNode>> destToWindowingExprs;
// is the query insert overwrite directory
private boolean isInsertOverwriteDir = false;
@SuppressWarnings("unused")
private static final Logger LOG = LoggerFactory.getLogger(QBParseInfo.class.getName());
public QBParseInfo(String alias, boolean isSubQ) {
aliasToSrc = new HashMap<String, ASTNode>();
nameToDest = new HashMap<String, ASTNode>();
nameToDestSchema = new HashMap<String, List<String>>();
nameToSample = new HashMap<String, TableSample>();
exprToColumnAlias = new HashMap<ASTNode, String>();
destToLateralView = new HashMap<String, ASTNode>();
destToSelExpr = new LinkedHashMap<String, ASTNode>();
destToWhereExpr = new HashMap<String, ASTNode>();
destToGroupby = new HashMap<String, ASTNode>();
destToHaving = new HashMap<String, ASTNode>();
destToQualify = new HashMap<>();
destToClusterby = new HashMap<String, ASTNode>();
destToDistributeby = new HashMap<String, ASTNode>();
destToSortby = new HashMap<String, ASTNode>();
destToOrderby = new HashMap<String, ASTNode>();
destToLimit = new HashMap<String, SimpleEntry<Integer, Integer>>();
destToOpType = new HashMap<>();
insertIntoTables = new HashMap<String, ASTNode>();
insertOverwriteTables = new HashMap<String, ASTNode>();
destRollups = new HashSet<String>();
destCubes = new HashSet<String>();
destGroupingSets = new HashSet<String>();
destToAggregationExprs = new LinkedHashMap<String, Map<String, ASTNode>>();
destToWindowingExprs = new LinkedHashMap<String, Map<String, ASTNode>>();
destToDistinctFuncExprs = new HashMap<String, List<ASTNode>>();
this.alias = StringInternUtils.internIfNotNull(alias);
this.isSubQ = isSubQ;
outerQueryLimit = -1;
aliasToLateralViews = new HashMap<String, List<ASTNode>>();
tableSpecs = new HashMap<String, BaseSemanticAnalyzer.TableSpec>();
}
/*
* If a QB is such that the aggregation expressions need to be handled by
* the Windowing PTF; we invoke this function to clear the AggExprs on the dest.
*/
public void clearAggregationExprsForClause(String clause) {
destToAggregationExprs.get(clause).clear();
}
public void setAggregationExprsForClause(String clause, Map<String, ASTNode> aggregationTrees) {
destToAggregationExprs.put(clause, aggregationTrees);
}
public void addAggregationExprsForClause(String clause, Map<String, ASTNode> aggregationTrees) {
if (destToAggregationExprs.containsKey(clause)) {
destToAggregationExprs.get(clause).putAll(aggregationTrees);
} else {
destToAggregationExprs.put(clause, aggregationTrees);
}
}
public void addInsertIntoTable(String fullName, ASTNode ast) {
insertIntoTables.put(fullName.toLowerCase(), ast);
}
public void setDestToOpType(String clause, boolean value) {
destToOpType.put(clause, value);
}
public boolean isDestToOpTypeInsertOverwrite(String clause) {
if (destToOpType.containsKey(clause)) {
return destToOpType.get(clause);
} else {
return false;
}
}
/**
* See also {@link #getInsertOverwriteTables()}
*/
public boolean isInsertIntoTable(String dbName, String table, String branchName) {
String fullName = dbName + "." + table;
if (branchName != null) {
fullName += "." + branchName;
}
return insertIntoTables.containsKey(fullName.toLowerCase());
}
/**
* Check if a table is in the list to be inserted into
* See also {@link #getInsertOverwriteTables()}
* @param fullTableName table name in dbname.tablename format
* @return
*/
public boolean isInsertIntoTable(String fullTableName) {
return insertIntoTables.containsKey(fullTableName.toLowerCase());
}
public void setInsertOverwriteDirectory(boolean isInsertOverwriteDir) {
this.isInsertOverwriteDir = isInsertOverwriteDir;
}
public Map<String, ASTNode> getAggregationExprsForClause(String clause) {
return destToAggregationExprs.get(clause);
}
public void addWindowingExprToClause(String clause, ASTNode windowingExprNode) {
Map<String, ASTNode> windowingExprs = destToWindowingExprs.get(clause);
if ( windowingExprs == null ) {
windowingExprs = new LinkedHashMap<String, ASTNode>();
destToWindowingExprs.put(clause, windowingExprs);
}
windowingExprs.put(windowingExprNode.toStringTree(), windowingExprNode);
}
public Map<String, ASTNode> getWindowingExprsForClause(String clause) {
return destToWindowingExprs.get(clause);
}
public void clearDistinctFuncExprsForClause(String clause) {
List<ASTNode> l = destToDistinctFuncExprs.get(clause);
if ( l != null ) {
l.clear();
}
}
public void setDistinctFuncExprsForClause(String clause, List<ASTNode> ast) {
destToDistinctFuncExprs.put(clause, ast);
}
public List<ASTNode> getDistinctFuncExprsForClause(String clause) {
return destToDistinctFuncExprs.get(clause);
}
public void setSelExprForClause(String clause, ASTNode ast) {
destToSelExpr.put(clause, ast);
}
public void setQueryFromExpr(ASTNode ast) {
queryFromExpr = ast;
}
public void setWhrExprForClause(String clause, ASTNode ast) {
destToWhereExpr.put(clause, ast);
}
public void setHavingExprForClause(String clause, ASTNode ast) {
destToHaving.put(clause, ast);
}
public void setGroupByExprForClause(String clause, ASTNode ast) {
destToGroupby.put(clause, ast);
}
public void setDestForClause(String clause, ASTNode ast) {
nameToDest.put(clause, ast);
}
List<String> setDestSchemaForClause(String clause, List<String> columnList) {
return nameToDestSchema.put(clause, columnList);
}
List<String> getDestSchemaForClause(String clause) {
return nameToDestSchema.get(clause);
}
/**
* Set the Cluster By AST for the clause.
*
* @param clause
* the name of the clause
* @param ast
* the abstract syntax tree
*/
public void setClusterByExprForClause(String clause, ASTNode ast) {
destToClusterby.put(clause, ast);
}
/**
* Set the Distribute By AST for the clause.
*
* @param clause
* the name of the clause
* @param ast
* the abstract syntax tree
*/
public void setDistributeByExprForClause(String clause, ASTNode ast) {
destToDistributeby.put(clause, ast);
}
/**
* Set the Sort By AST for the clause.
*
* @param clause
* the name of the clause
* @param ast
* the abstract syntax tree
*/
public void setSortByExprForClause(String clause, ASTNode ast) {
destToSortby.put(clause, ast);
}
public void setOrderByExprForClause(String clause, ASTNode ast) {
destToOrderby.put(clause, ast);
}
public void setSrcForAlias(String alias, ASTNode ast) {
aliasToSrc.put(alias.toLowerCase(), ast);
}
public Set<String> getClauseNames() {
return destToSelExpr.keySet();
}
public Set<String> getClauseNamesForDest() {
return nameToDest.keySet();
}
public ASTNode getDestForClause(String clause) {
return nameToDest.get(clause);
}
public ASTNode getWhrForClause(String clause) {
return destToWhereExpr.get(clause);
}
public Map<String, ASTNode> getDestToWhereExpr() {
return destToWhereExpr;
}
public ASTNode getGroupByForClause(String clause) {
return destToGroupby.get(clause);
}
public Set<String> getDestRollups() {
return destRollups;
}
public Set<String> getDestCubes() {
return destCubes;
}
public Set<String> getDestGroupingSets() {
return destGroupingSets;
}
public Map<String, ASTNode> getDestToGroupBy() {
return destToGroupby;
}
public ASTNode getHavingForClause(String clause) {
return destToHaving.get(clause);
}
public Map<String, ASTNode> getDestToHaving() {
return destToHaving;
}
public ASTNode getSelForClause(String clause) {
return destToSelExpr.get(clause);
}
public ASTNode getQueryFrom() {
return queryFromExpr;
}
/**
* Get the Cluster By AST for the clause.
*
* @param clause
* the name of the clause
* @return the abstract syntax tree
*/
public ASTNode getClusterByForClause(String clause) {
return destToClusterby.get(clause);
}
public Map<String, ASTNode> getDestToClusterBy() {
return destToClusterby;
}
/**
* Get the Distribute By AST for the clause.
*
* @param clause
* the name of the clause
* @return the abstract syntax tree
*/
public ASTNode getDistributeByForClause(String clause) {
return destToDistributeby.get(clause);
}
public Map<String, ASTNode> getDestToDistributeBy() {
return destToDistributeby;
}
/**
* Get the Sort By AST for the clause.
*
* @param clause
* the name of the clause
* @return the abstract syntax tree
*/
public ASTNode getSortByForClause(String clause) {
return destToSortby.get(clause);
}
public ASTNode getOrderByForClause(String clause) {
return destToOrderby.get(clause);
}
public Map<String, ASTNode> getDestToSortBy() {
return destToSortby;
}
public Map<String, ASTNode> getDestToOrderBy() {
return destToOrderby;
}
public ASTNode getSrcForAlias(String alias) {
return aliasToSrc.get(alias.toLowerCase());
}
public String getAlias() {
return alias;
}
public void setAlias(String alias) {
this.alias = alias;
}
public boolean getIsSubQ() {
return isSubQ;
}
public void setIsSubQ(boolean isSubQ) {
this.isSubQ = isSubQ;
}
public ASTNode getJoinExpr() {
return joinExpr;
}
public void setJoinExpr(ASTNode joinExpr) {
this.joinExpr = joinExpr;
}
public TableSample getTabSample(String alias) {
return nameToSample.get(alias.toLowerCase());
}
public void setTabSample(String alias, TableSample tableSample) {
nameToSample.put(alias.toLowerCase(), tableSample);
}
public String getExprToColumnAlias(ASTNode expr) {
return exprToColumnAlias.get(expr);
}
public Map<ASTNode, String> getAllExprToColumnAlias() {
return exprToColumnAlias;
}
public boolean hasExprToColumnAlias(ASTNode expr) {
return exprToColumnAlias.containsKey(expr);
}
public void setExprToColumnAlias(ASTNode expr, String alias) {
exprToColumnAlias.put(expr, StringInternUtils.internIfNotNull(alias));
}
public void setDestLimit(String dest, Integer offset, Integer limit) {
destToLimit.put(dest, new SimpleEntry<>(offset, limit));
}
public Integer getDestLimit(String dest) {
return destToLimit.get(dest) == null ? null : destToLimit.get(dest).getValue();
}
public Integer getDestLimitOffset(String dest) {
return destToLimit.get(dest) == null ? 0 : destToLimit.get(dest).getKey();
}
/**
* @return the outerQueryLimit
*/
public int getOuterQueryLimit() {
return outerQueryLimit;
}
/**
* @param outerQueryLimit
* the outerQueryLimit to set
*/
public void setOuterQueryLimit(int outerQueryLimit) {
this.outerQueryLimit = outerQueryLimit;
}
public boolean isTopLevelSimpleSelectStarQuery() {
if (alias != null || destToSelExpr.size() != 1 || !isSimpleSelectQuery()) {
return false;
}
for (ASTNode selExprs : destToSelExpr.values()) {
if (selExprs.getChildCount() != 1) {
return false;
}
Tree sel = selExprs.getChild(0).getChild(0);
if (sel == null || sel.getType() != HiveParser.TOK_ALLCOLREF) {
return false;
}
}
return true;
}
// for fast check of possible existence of RS (will be checked again in SimpleFetchOptimizer)
public boolean isSimpleSelectQuery() {
if (joinExpr != null || !destToOrderby.isEmpty() || !destToSortby.isEmpty()
|| !destToGroupby.isEmpty() || !destToClusterby.isEmpty() || !destToDistributeby.isEmpty()
|| !destRollups.isEmpty() || !destCubes.isEmpty() || !destGroupingSets.isEmpty()
|| !destToHaving.isEmpty()) {
return false;
}
for (Map<String, ASTNode> entry : destToAggregationExprs.values()) {
if (entry != null && !entry.isEmpty()) {
return false;
}
}
for (Map<String, ASTNode> entry : destToWindowingExprs.values()) {
if (entry != null && !entry.isEmpty()) {
return false;
}
}
for (List<ASTNode> ct : destToDistinctFuncExprs.values()) {
if (!ct.isEmpty()) {
return false;
}
}
// exclude insert queries
for (ASTNode v : nameToDest.values()) {
if (!(v.getChild(0).getType() == HiveParser.TOK_TMP_FILE)) {
return false;
}
}
return true;
}
public void setHints(ASTNode hint) {
hints = hint;
}
public void setHintList(List<ASTNode> hintList) {
this.hintList = hintList;
}
public List<ASTNode> getHintList() {
return hintList;
}
public ASTNode getHints() {
return hints;
}
public Map<String, List<ASTNode>> getAliasToLateralViews() {
return aliasToLateralViews;
}
public List<ASTNode> getLateralViewsForAlias(String alias) {
return aliasToLateralViews.get(alias.toLowerCase());
}
public void addLateralViewForAlias(String alias, ASTNode lateralView) {
List<ASTNode> lateralViews = aliasToLateralViews.get(alias);
if (lateralViews == null) {
lateralViews = new ArrayList<ASTNode>();
aliasToLateralViews.put(alias, lateralViews);
}
lateralViews.add(lateralView);
}
public void setIsAnalyzeCommand(boolean isAnalyzeCommand) {
this.isAnalyzeCommand = isAnalyzeCommand;
}
public boolean isAnalyzeCommand() {
return isAnalyzeCommand;
}
public void addTableSpec(String tName, TableSpec tSpec) {
tableSpecs.put(tName, tSpec);
}
public TableSpec getTableSpec(String tName) {
return tableSpecs.get(tName);
}
/**
* This method is used only for the analyze command to get the partition specs
*/
public TableSpec getTableSpec() {
Iterator<String> tName = tableSpecs.keySet().iterator();
return tableSpecs.get(tName.next());
}
public Map<String, SimpleEntry<Integer,Integer>> getDestToLimit() {
return destToLimit;
}
public Map<String, Map<String, ASTNode>> getDestToAggregationExprs() {
return destToAggregationExprs;
}
public Map<String, List<ASTNode>> getDestToDistinctFuncExprs() {
return destToDistinctFuncExprs;
}
public Map<String, TableSample> getNameToSample() {
return nameToSample;
}
public Map<String, ASTNode> getDestToLateralView() {
return destToLateralView;
}
public void setQualifyExprForClause(String dest, ASTNode ast) {
destToQualify.put(dest, ast);
}
public ASTNode getQualifyExprForClause(String dest) {
return destToQualify.get(dest);
}
public boolean hasQualifyClause() {
return !destToQualify.isEmpty();
}
protected static enum ClauseType {
CLUSTER_BY_CLAUSE,
DISTRIBUTE_BY_CLAUSE,
ORDER_BY_CLAUSE,
SORT_BY_CLAUSE
}
public AnalyzeRewriteContext getAnalyzeRewrite() {
return analyzeRewrite;
}
public void setAnalyzeRewrite(AnalyzeRewriteContext analyzeRewrite) {
this.analyzeRewrite = analyzeRewrite;
}
/**
* @return the isNoScanAnalyzeCommand
*/
public boolean isNoScanAnalyzeCommand() {
return isNoScanAnalyzeCommand;
}
/**
* @param isNoScanAnalyzeCommand the isNoScanAnalyzeCommand to set
*/
public void setNoScanAnalyzeCommand(boolean isNoScanAnalyzeCommand) {
this.isNoScanAnalyzeCommand = isNoScanAnalyzeCommand;
}
/**
* See also {@link #isInsertIntoTable(String)}
*/
public Map<String, ASTNode> getInsertOverwriteTables() {
return insertOverwriteTables;
}
public boolean hasInsertTables() {
return this.insertIntoTables.size() > 0 || this.insertOverwriteTables.size() > 0;
}
public boolean isInsertOverwriteDirectory() {
return isInsertOverwriteDir;
}
/**
* Check whether all the expressions in the select clause are aggregate function calls.
* This method starts iterating through the AST nodes representing the expressions in the select clause stored in
* this object. An expression is considered to be an aggregate function call if:
* <ul>
* <li>the AST node type is either TOK_FUNCTION, TOK_FUNCTIONDI or TOK_FUNCTIONSTAR</li>
* <li>the first child of the node is the name of the function</li>
* <li>function is registered in Hive</li>
* <li>the registered function with the specified name is a Generic User Defined Aggregate</li>
* </ul>
* If any of the mentioned criteria fails to match to the current expression this function returns false.
* @return true if all the expressions in the select clause are aggregate function calls.
* @throws SemanticException - thrown when {@link FunctionRegistry#getFunctionInfo} fails.
*/
public boolean isFullyAggregate() throws SemanticException {
for (ASTNode selectClause : destToSelExpr.values()) {
for (Node node : selectClause.getChildren()) {
ASTNode selexpr = (ASTNode) node;
Tree expressionTypeToken = selexpr.getChild(0);
int selectExprType = expressionTypeToken.getType();
if (selectExprType != TOK_FUNCTION && selectExprType != TOK_FUNCTIONDI && selectExprType != TOK_FUNCTIONSTAR) {
return false;
}
if (expressionTypeToken.getChild(0).getType() != HiveParser.Identifier) {
return false;
}
String functionName = unescapeIdentifier(expressionTypeToken.getChild(0).getText());
FunctionInfo functionInfo = FunctionRegistry.getFunctionInfo(functionName);
if (functionInfo == null) {
return false;
}
if (functionInfo.getGenericUDAFResolver() == null) {
return false;
}
}
}
return true;
}
public ASTNode getColAliases() {
return colAliases;
}
public void setColAliases(ASTNode colAliases) {
this.colAliases = colAliases;
}
}