Impala
Impalaistheopensource,nativeanalyticdatabaseforApacheHadoop.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
QueryStmt.java
Go to the documentation of this file.
1 // Copyright 2012 Cloudera Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 package com.cloudera.impala.analysis;
16 
17 import java.util.ArrayList;
18 import java.util.List;
19 import java.util.ListIterator;
20 import java.util.Set;
21 
25 import com.cloudera.impala.common.TreeNode;
26 import com.google.common.base.Preconditions;
27 import com.google.common.base.Predicates;
28 import com.google.common.collect.Lists;
29 import com.google.common.collect.Sets;
30 
41 public abstract class QueryStmt extends StatementBase {
43 
44  protected ArrayList<OrderByElement> orderByElements_;
46 
47  // For a select statment:
48  // original list of exprs in select clause (star-expanded, ordinals and
49  // aliases substituted, agg output substituted)
50  // For a union statement:
51  // list of slotrefs into the tuple materialized by the union.
52  protected ArrayList<Expr> resultExprs_ = Lists.newArrayList();
53 
54  // For a select statment: select list exprs resolved to base tbl refs
55  // For a union statement: same as resultExprs
56  protected ArrayList<Expr> baseTblResultExprs_ = Lists.newArrayList();
57 
63 
70  protected final ArrayList<Expr> ambiguousAliasList_ = Lists.newArrayList();
71 
72  protected SortInfo sortInfo_;
73 
74  // evaluateOrderBy_ is true if there is an order by clause that must be evaluated.
75  // False for nested query stmts with an order-by clause without offset/limit.
76  // sortInfo_ is still generated and used in analysis to ensure that the order-by clause
77  // is well-formed.
78  protected boolean evaluateOrderBy_;
79 
80  // Analyzer that was used to analyze this query statement.
81  protected Analyzer analyzer_;
82 
83  public Analyzer getAnalyzer() { return analyzer_; }
84 
85  QueryStmt(ArrayList<OrderByElement> orderByElements, LimitElement limitElement) {
86  orderByElements_ = orderByElements;
87  sortInfo_ = null;
88  limitElement_ = limitElement == null ? new LimitElement(null, null) : limitElement;
89  }
90 
91  @Override
92  public void analyze(Analyzer analyzer) throws AnalysisException {
93  super.analyze(analyzer);
94  this.analyzer_ = analyzer;
95  analyzeLimit(analyzer);
96  if (hasWithClause()) withClause_.analyze(analyzer);
97  }
98 
99  private void analyzeLimit(Analyzer analyzer) throws AnalysisException {
100  if (limitElement_.getOffsetExpr() != null && !hasOrderByClause()) {
101  throw new AnalysisException("OFFSET requires an ORDER BY clause: " +
102  limitElement_.toSql().trim());
103  }
104  limitElement_.analyze(analyzer);
105  }
106 
115  protected void createSortInfo(Analyzer analyzer) throws AnalysisException {
116  // not computing order by
117  if (orderByElements_ == null) {
118  evaluateOrderBy_ = false;
119  return;
120  }
121 
122  ArrayList<Expr> orderingExprs = Lists.newArrayList();
123  ArrayList<Boolean> isAscOrder = Lists.newArrayList();
124  ArrayList<Boolean> nullsFirstParams = Lists.newArrayList();
125 
126  // extract exprs
127  for (OrderByElement orderByElement: orderByElements_) {
128  if (orderByElement.getExpr().contains(Predicates.instanceOf(Subquery.class))) {
129  throw new AnalysisException(
130  "Subqueries are not supported in the ORDER BY clause.");
131  }
132  // create copies, we don't want to modify the original parse node, in case
133  // we need to print it
134  orderingExprs.add(orderByElement.getExpr().clone());
135  isAscOrder.add(Boolean.valueOf(orderByElement.isAsc()));
136  nullsFirstParams.add(orderByElement.getNullsFirstParam());
137  }
138  substituteOrdinals(orderingExprs, "ORDER BY", analyzer);
139  Expr ambiguousAlias = getFirstAmbiguousAlias(orderingExprs);
140  if (ambiguousAlias != null) {
141  throw new AnalysisException("Column '" + ambiguousAlias.toSql() +
142  "' in ORDER BY clause is ambiguous");
143  }
144  orderingExprs = Expr.trySubstituteList(orderingExprs, aliasSmap_, analyzer, false);
145 
146  if (!analyzer.isRootAnalyzer() && hasOffset() && !hasLimit()) {
147  throw new AnalysisException("Order-by with offset without limit not supported" +
148  " in nested queries.");
149  }
150 
151  sortInfo_ = new SortInfo(orderingExprs, isAscOrder, nullsFirstParams);
152  // order by w/o limit and offset in inline views, union operands and insert statements
153  // are ignored.
154  if (!hasLimit() && !hasOffset() && !analyzer.isRootAnalyzer()) {
155  evaluateOrderBy_ = false;
156  // Return a warning that the order by was ignored.
157  StringBuilder strBuilder = new StringBuilder();
158  strBuilder.append("Ignoring ORDER BY clause without LIMIT or OFFSET: ");
159  strBuilder.append("ORDER BY ");
160  strBuilder.append(orderByElements_.get(0).toSql());
161  for (int i = 1; i < orderByElements_.size(); ++i) {
162  strBuilder.append(", ").append(orderByElements_.get(i).toSql());
163  }
164  strBuilder.append(".\nAn ORDER BY appearing in a view, subquery, union operand, ");
165  strBuilder.append("or an insert/ctas statement has no effect on the query result ");
166  strBuilder.append("unless a LIMIT and/or OFFSET is used in conjunction ");
167  strBuilder.append("with the ORDER BY.");
168  analyzer.addWarning(strBuilder.toString());
169  } else {
170  evaluateOrderBy_ = true;
171  }
172  }
173 
185  protected void createSortTupleInfo(Analyzer analyzer) {
186  Preconditions.checkState(evaluateOrderBy_);
187 
188  // sourceSlots contains the slots from the input row to materialize.
189  Set<SlotRef> sourceSlots = Sets.newHashSet();
190  TreeNode.collect(resultExprs_, Predicates.instanceOf(SlotRef.class), sourceSlots);
191  TreeNode.collect(sortInfo_.getOrderingExprs(), Predicates.instanceOf(SlotRef.class),
192  sourceSlots);
193 
194  TupleDescriptor sortTupleDesc = analyzer.getDescTbl().createTupleDescriptor("sort");
195  List<Expr> sortTupleExprs = Lists.newArrayList();
196  sortTupleDesc.setIsMaterialized(true);
197  // substOrderBy is the mapping from slot refs in the input row to slot refs in the
198  // materialized sort tuple.
199  ExprSubstitutionMap substOrderBy = new ExprSubstitutionMap();
200  for (SlotRef origSlotRef: sourceSlots) {
201  SlotDescriptor origSlotDesc = origSlotRef.getDesc();
202  SlotDescriptor materializedDesc =
203  analyzer.copySlotDescriptor(origSlotDesc, sortTupleDesc);
204  SlotRef cloneRef = new SlotRef(materializedDesc);
205  substOrderBy.put(origSlotRef, cloneRef);
206  analyzer.createAuxEquivPredicate(cloneRef, origSlotRef);
207  sortTupleExprs.add(origSlotRef);
208  }
209 
210  resultExprs_ = Expr.substituteList(resultExprs_, substOrderBy, analyzer, false);
211  sortInfo_.substituteOrderingExprs(substOrderBy, analyzer);
212  sortInfo_.setMaterializedTupleInfo(sortTupleDesc, sortTupleExprs);
213  }
214 
219  protected Expr getFirstAmbiguousAlias(List<Expr> exprs) {
220  for (Expr exp: exprs) {
221  if (ambiguousAliasList_.contains(exp)) return exp;
222  }
223  return null;
224  }
225 
230  protected void substituteOrdinals(List<Expr> exprs, String errorPrefix,
231  Analyzer analyzer) throws AnalysisException {
232  // Substitute ordinals.
233  ListIterator<Expr> i = exprs.listIterator();
234  while (i.hasNext()) {
235  Expr expr = i.next();
236  if (!(expr instanceof NumericLiteral)) continue;
237  expr.analyze(analyzer);
238  if (!expr.getType().isIntegerType()) continue;
239  long pos = ((NumericLiteral) expr).getLongValue();
240  if (pos < 1) {
241  throw new AnalysisException(
242  errorPrefix + ": ordinal must be >= 1: " + expr.toSql());
243  }
244  if (pos > resultExprs_.size()) {
245  throw new AnalysisException(
246  errorPrefix + ": ordinal exceeds number of items in select list: "
247  + expr.toSql());
248  }
249  // Create copy to protect against accidentally shared state.
250  i.set(resultExprs_.get((int) pos - 1).clone());
251  }
252  }
253 
257  public abstract ArrayList<String> getColLabels();
258 
267  public abstract void getMaterializedTupleIds(ArrayList<TupleId> tupleIdList);
268 
269  public void setWithClause(WithClause withClause) { this.withClause_ = withClause; }
270  public boolean hasWithClause() { return withClause_ != null; }
271  public WithClause getWithClause() { return withClause_; }
272  public boolean hasOrderByClause() { return orderByElements_ != null; }
273  public boolean hasLimit() { return limitElement_.getLimitExpr() != null; }
274  public long getLimit() { return limitElement_.getLimit(); }
275  public boolean hasOffset() { return limitElement_.getOffsetExpr() != null; }
276  public long getOffset() { return limitElement_.getOffset(); }
277  public SortInfo getSortInfo() { return sortInfo_; }
278  public boolean evaluateOrderBy() { return evaluateOrderBy_; }
279  public ArrayList<Expr> getResultExprs() { return resultExprs_; }
280  public ArrayList<Expr> getBaseTblResultExprs() { return baseTblResultExprs_; }
281  public void setLimit(long limit) throws AnalysisException {
282  Preconditions.checkState(limit >= 0);
283  long newLimit = hasLimit() ? Math.min(limit, getLimit()) : limit;
284  limitElement_ = new LimitElement(new NumericLiteral(Long.toString(newLimit),
285  Type.BIGINT), null);
286  }
287 
297  public abstract void materializeRequiredSlots(Analyzer analyzer)
298  throws InternalException;
299 
303  protected void materializeSlots(Analyzer analyzer, List<Expr> exprs) {
304  List<SlotId> slotIds = Lists.newArrayList();
305  for (Expr e: exprs) {
306  e.getIds(null, slotIds);
307  }
308  analyzer.getDescTbl().markSlotsMaterialized(slotIds);
309  }
310 
311  public ArrayList<OrderByElement> cloneOrderByElements() {
312  return orderByElements_ != null ? Lists.newArrayList(orderByElements_) : null;
313  }
314 
316  return withClause_ != null ? withClause_.clone() : null;
317  }
318 
319  @Override
320  public abstract QueryStmt clone();
321 }
void substituteOrdinals(List< Expr > exprs, String errorPrefix, Analyzer analyzer)
Definition: QueryStmt.java:230
static final ScalarType BIGINT
Definition: Type.java:50
ArrayList< OrderByElement > cloneOrderByElements()
Definition: QueryStmt.java:311
abstract void materializeRequiredSlots(Analyzer analyzer)
void createSortTupleInfo(Analyzer analyzer)
Definition: QueryStmt.java:185
final ExprSubstitutionMap aliasSmap_
Definition: QueryStmt.java:62
Expr getFirstAmbiguousAlias(List< Expr > exprs)
Definition: QueryStmt.java:219
void materializeSlots(Analyzer analyzer, List< Expr > exprs)
Definition: QueryStmt.java:303
void analyze(Analyzer analyzer)
Definition: QueryStmt.java:92
ArrayList< Expr > getBaseTblResultExprs()
Definition: QueryStmt.java:280
ArrayList< OrderByElement > orderByElements_
Definition: QueryStmt.java:44
abstract void getMaterializedTupleIds(ArrayList< TupleId > tupleIdList)
void setWithClause(WithClause withClause)
Definition: QueryStmt.java:269
void createSortInfo(Analyzer analyzer)
Definition: QueryStmt.java:115
void analyzeLimit(Analyzer analyzer)
Definition: QueryStmt.java:99
abstract ArrayList< String > getColLabels()
final ArrayList< Expr > ambiguousAliasList_
Definition: QueryStmt.java:70
QueryStmt(ArrayList< OrderByElement > orderByElements, LimitElement limitElement)
Definition: QueryStmt.java:85