Impala
Impalaistheopensource,nativeanalyticdatabaseforApacheHadoop.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
AggregateInfoBase.java
Go to the documentation of this file.
1 package com.cloudera.impala.analysis;
2 
3 import java.util.ArrayList;
4 import java.util.List;
5 
6 import org.slf4j.Logger;
7 import org.slf4j.LoggerFactory;
8 
12 import com.google.common.base.Objects;
13 import com.google.common.base.Preconditions;
14 import com.google.common.collect.Lists;
15 
20 public abstract class AggregateInfoBase {
21  private final static Logger LOG =
22  LoggerFactory.getLogger(AggregateInfoBase.class);
23 
24  // For aggregations: All unique grouping expressions from a select block.
25  // For analytics: Empty.
26  protected ArrayList<Expr> groupingExprs_;
27 
28  // For aggregations: All unique aggregate expressions from a select block.
29  // For analytics: The results of AnalyticExpr.getFnCall() for the unique
30  // AnalyticExprs of a select block.
31  protected ArrayList<FunctionCallExpr> aggregateExprs_;
32 
33  // The tuple into which the intermediate output of an aggregation is materialized.
34  // Contains groupingExprs.size() + aggregateExprs.size() slots, the first of which
35  // contain the values of the grouping exprs, followed by slots into which the
36  // aggregateExprs' update()/merge() symbols materialize their output, i.e., slots
37  // of the aggregate functions' intermediate types.
38  // Identical to outputTupleDesc_ if no aggregateExpr has an output type that is
39  // different from its intermediate type.
41 
42  // The tuple into which the final output of the aggregation is materialized.
43  // Contains groupingExprs.size() + aggregateExprs.size() slots, the first of which
44  // contain the values of the grouping exprs, followed by slots into which the
45  // aggregateExprs' finalize() symbol write its result, i.e., slots of the aggregate
46  // functions' output types.
48 
49  // For aggregation: indices into aggregate exprs for that need to be materialized
50  // For analytics: indices into the analytic exprs and their corresponding aggregate
51  // exprs that need to be materialized.
52  // Populated in materializeRequiredSlots() which must be implemented by subclasses.
53  protected ArrayList<Integer> materializedSlots_ = Lists.newArrayList();
54 
55  protected AggregateInfoBase(ArrayList<Expr> groupingExprs,
56  ArrayList<FunctionCallExpr> aggExprs) {
57  Preconditions.checkState(groupingExprs != null || aggExprs != null);
59  groupingExprs != null ? Expr.cloneList(groupingExprs) : new ArrayList<Expr>();
60  Preconditions.checkState(aggExprs != null || !(this instanceof AnalyticInfo));
62  aggExprs != null ? Expr.cloneList(aggExprs) : new ArrayList<FunctionCallExpr>();
63  }
64 
70  protected void createTupleDescs(Analyzer analyzer) {
71  // Create the intermediate tuple desc first, so that the tuple ids are increasing
72  // from bottom to top in the plan tree.
73  intermediateTupleDesc_ = createTupleDesc(analyzer, false);
75  outputTupleDesc_ = createTupleDesc(analyzer, true);
76  } else {
78  }
79  }
80 
87  private TupleDescriptor createTupleDesc(Analyzer analyzer, boolean isOutputTuple) {
88  TupleDescriptor result =
89  analyzer.getDescTbl().createTupleDescriptor(
90  tupleDebugName() + (isOutputTuple ? "-out" : "-intermed"));
91  List<Expr> exprs = Lists.newArrayListWithCapacity(
92  groupingExprs_.size() + aggregateExprs_.size());
93  exprs.addAll(groupingExprs_);
94  exprs.addAll(aggregateExprs_);
95 
96  int aggregateExprStartIndex = groupingExprs_.size();
97  for (int i = 0; i < exprs.size(); ++i) {
98  Expr expr = exprs.get(i);
99  SlotDescriptor slotDesc = analyzer.addSlotDescriptor(result);
100  slotDesc.setLabel(expr.toSql());
101  slotDesc.setSourceExpr(expr);
102  slotDesc.setStats(ColumnStats.fromExpr(expr));
103  Preconditions.checkState(expr.getType().isValid());
104  slotDesc.setType(expr.getType());
105  if (i < aggregateExprStartIndex) {
106  // register equivalence between grouping slot and grouping expr;
107  // do this only when the grouping expr isn't a constant, otherwise
108  // it'll simply show up as a gratuitous HAVING predicate
109  // (which would actually be incorrect if the constant happens to be NULL)
110  if (!expr.isConstant()) {
111  analyzer.createAuxEquivPredicate(new SlotRef(slotDesc), expr.clone());
112  }
113  } else {
114  Preconditions.checkArgument(expr instanceof FunctionCallExpr);
115  FunctionCallExpr aggExpr = (FunctionCallExpr)expr;
116  if (aggExpr.isMergeAggFn()) {
117  slotDesc.setLabel(aggExpr.getChild(0).toSql());
118  slotDesc.setSourceExpr(aggExpr.getChild(0));
119  } else {
120  slotDesc.setLabel(aggExpr.toSql());
121  slotDesc.setSourceExpr(aggExpr);
122  }
123 
124  // count(*) is non-nullable.
125  if (aggExpr.getFnName().getFunction().equals("count")) {
126  // TODO: Consider making nullability a property of types or of builtin agg fns.
127  // row_number, rank, and dense_rank are non-nullable as well.
128  slotDesc.setIsNullable(false);
129  }
130  if (!isOutputTuple) {
131  Type intermediateType = ((AggregateFunction)aggExpr.fn_).getIntermediateType();
132  if (intermediateType != null) {
133  // Use the output type as intermediate if the function has a wildcard decimal.
134  if (!intermediateType.isWildcardDecimal()) {
135  slotDesc.setType(intermediateType);
136  } else {
137  Preconditions.checkState(expr.getType().isDecimal());
138  }
139  }
140  }
141  }
142  }
143  String prefix = (isOutputTuple ? "result " : "intermediate ");
144  LOG.trace(prefix + " tuple=" + result.debugString());
145  return result;
146  }
147 
153  public abstract void materializeRequiredSlots(Analyzer analyzer,
154  ExprSubstitutionMap smap);
155 
156  public ArrayList<Expr> getGroupingExprs() { return groupingExprs_; }
157  public ArrayList<FunctionCallExpr> getAggregateExprs() { return aggregateExprs_; }
160  public TupleId getIntermediateTupleId() { return intermediateTupleDesc_.getId(); }
161  public TupleId getOutputTupleId() { return outputTupleDesc_.getId(); }
162  public boolean requiresIntermediateTuple() {
163  Preconditions.checkNotNull(intermediateTupleDesc_);
164  Preconditions.checkNotNull(outputTupleDesc_);
166  }
167 
173  public static <T extends Expr> boolean requiresIntermediateTuple(List<T> aggExprs) {
174  for (Expr aggExpr: aggExprs) {
175  Type intermediateType = ((AggregateFunction) aggExpr.fn_).getIntermediateType();
176  if (intermediateType != null) return true;
177  }
178  return false;
179  }
180 
181  public String debugString() {
182  StringBuilder out = new StringBuilder();
183  out.append(Objects.toStringHelper(this)
184  .add("grouping_exprs", Expr.debugString(groupingExprs_))
185  .add("aggregate_exprs", Expr.debugString(aggregateExprs_))
186  .add("intermediate_tuple", (intermediateTupleDesc_ == null)
187  ? "null" : intermediateTupleDesc_.debugString())
188  .add("output_tuple", (outputTupleDesc_ == null)
189  ? "null" : outputTupleDesc_.debugString())
190  .toString());
191  return out.toString();
192  }
193 
194  protected abstract String tupleDebugName();
195 }
static< TextendsExpr > boolean requiresIntermediateTuple(List< T > aggExprs)
abstract void materializeRequiredSlots(Analyzer analyzer, ExprSubstitutionMap smap)
ArrayList< FunctionCallExpr > getAggregateExprs()
TupleDescriptor createTupleDesc(Analyzer analyzer, boolean isOutputTuple)
AggregateInfoBase(ArrayList< Expr > groupingExprs, ArrayList< FunctionCallExpr > aggExprs)