Impala
Impalaistheopensource,nativeanalyticdatabaseforApacheHadoop.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
TableRef.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.Set;
20 
28 import com.cloudera.impala.common.Pair;
30 import com.cloudera.impala.thrift.TAccessEvent;
31 import com.cloudera.impala.thrift.TCatalogObjectType;
32 import com.google.common.base.Joiner;
33 import com.google.common.base.Preconditions;
34 import com.google.common.collect.Lists;
35 import com.google.common.collect.Sets;
36 
45 public class TableRef implements ParseNode {
46  // Path to a collection type. Not set for inline views.
47  protected List<String> rawPath_;
48 
49  // Resolution of rawPath_ if applicable. Result of analysis.
50  protected Path resolvedPath_;
51 
52  // Legal aliases of this table ref. Contains the explicit alias as its sole element if
53  // there is one. Otherwise, contains the two implicit aliases. Implicit aliases are set
54  // in the c'tor of the corresponding resolved table ref (subclasses of TableRef) during
55  // analysis. By convention, for table refs with multiple implicit aliases, aliases_[0]
56  // contains the fully-qualified implicit alias to ensure that aliases_[0] always
57  // uniquely identifies this table ref regardless of whether it has an explicit alias.
58  protected String[] aliases_;
59 
60  // Indicates whether this table ref is given an explicit alias,
61  protected boolean hasExplicitAlias_;
62 
63  protected JoinOperator joinOp_;
64  protected ArrayList<String> joinHints_;
65  protected Expr onClause_;
66  protected List<String> usingColNames_;
67 
68  // set after analyzeJoinHints(); true if explicitly set via hints
69  private boolean isBroadcastJoin_;
70  private boolean isPartitionedJoin_;
71 
72  // the ref to the left of us, if we're part of a JOIN clause
73  protected TableRef leftTblRef_;
74 
75  // true if this TableRef has been analyzed; implementing subclass should set it to true
76  // at the end of analyze() call.
77  protected boolean isAnalyzed_;
78 
79  // all (logical) TupleIds referenced in the On clause
80  protected List<TupleId> onClauseTupleIds_ = Lists.newArrayList();
81 
82  // analysis output
84 
85  public TableRef(List<String> path, String alias) {
86  super();
87  rawPath_ = path;
88  if (alias != null) {
89  aliases_ = new String[] { alias.toLowerCase() };
90  hasExplicitAlias_ = true;
91  } else {
92  hasExplicitAlias_ = false;
93  }
94  isAnalyzed_ = false;
95  }
96 
100  protected TableRef(TableRef other) {
101  super();
102  Preconditions.checkNotNull(other);
103  this.rawPath_ = other.rawPath_;
104  this.resolvedPath_ = other.resolvedPath_;
105  this.aliases_ = other.aliases_;
106  this.hasExplicitAlias_ = other.hasExplicitAlias_;
107  this.joinOp_ = other.joinOp_;
108  this.joinHints_ =
109  (other.joinHints_ != null) ? Lists.newArrayList(other.joinHints_) : null;
110  this.usingColNames_ =
111  (other.usingColNames_ != null) ? Lists.newArrayList(other.usingColNames_) : null;
112  this.onClause_ = (other.onClause_ != null) ? other.onClause_.clone().reset() : null;
113  this.isAnalyzed_ = false;
114  }
115 
116  @Override
117  public void analyze(Analyzer analyzer) throws AnalysisException {
118  try {
119  resolvedPath_ = analyzer.resolvePath(rawPath_, PathType.TABLE_REF);
120  } catch (AnalysisException e) {
121  if (!analyzer.hasMissingTbls()) {
122  // Register privilege requests to prefer reporting an authorization error over
123  // an analysis error. We should not accidentally reveal the non-existence of a
124  // table/database if the user is not authorized.
125  if (rawPath_.size() > 1) {
126  analyzer.registerPrivReq(new PrivilegeRequestBuilder()
127  .onTable(rawPath_.get(0), rawPath_.get(1))
128  .allOf(getPrivilegeRequirement()).toRequest());
129  }
130  analyzer.registerPrivReq(new PrivilegeRequestBuilder()
131  .onTable(analyzer.getDefaultDb(), rawPath_.get(0))
132  .allOf(getPrivilegeRequirement()).toRequest());
133  }
134  throw e;
135  } catch (TableLoadingException e) {
136  throw new AnalysisException(String.format(
137  "Failed to load metadata for table: '%s'", Joiner.on(".").join(rawPath_)), e);
138  }
139 
140  if (resolvedPath_.getRootTable() != null) {
141  // Add access event for auditing.
142  Table table = resolvedPath_.getRootTable();
143  if (table instanceof View) {
144  View view = (View) table;
145  if (!view.isLocalView()) {
146  analyzer.addAccessEvent(new TAccessEvent(
147  table.getFullName(), TCatalogObjectType.VIEW,
148  getPrivilegeRequirement().toString()));
149  }
150  } else {
151  analyzer.addAccessEvent(new TAccessEvent(
152  table.getFullName(), TCatalogObjectType.TABLE,
153  getPrivilegeRequirement().toString()));
154  }
155 
156  // Add privilege requests for authorization.
157  TableName tableName = table.getTableName();
158  analyzer.registerPrivReq(new PrivilegeRequestBuilder()
159  .onTable(tableName.getDb(), tableName.getTbl())
160  .allOf(getPrivilegeRequirement()).toRequest());
161  }
162  }
163 
170  throws AnalysisException {
171  TupleDescriptor result = analyzer.getDescTbl().createTupleDescriptor(
172  getClass().getSimpleName() + " " + getUniqueAlias());
173  result.setPath(resolvedPath_);
174  return result;
175  }
176 
181  protected void setJoinAttrs(TableRef other) {
182  this.joinOp_ = other.joinOp_;
183  this.joinHints_ = other.joinHints_;
184  this.onClause_ = other.onClause_;
185  this.usingColNames_ = other.usingColNames_;
186  }
187 
189  // if it's not explicitly set, we're doing an inner join
190  return (joinOp_ == null ? JoinOperator.INNER_JOIN : joinOp_);
191  }
192 
197  public boolean isChildRef() { return false; }
198 
199  public List<String> getPath() { return rawPath_; }
200  public Path getResolvedPath() { return resolvedPath_; }
201 
205  public String[] getAliases() { return aliases_; }
206 
212  public String getUniqueAlias() { return aliases_[0]; }
213 
219  public boolean hasExplicitAlias() { return hasExplicitAlias_; }
220 
224  public String getExplicitAlias() {
225  if (hasExplicitAlias()) return getUniqueAlias();
226  return null;
227  }
228 
229  public Table getTable() {
230  Preconditions.checkNotNull(resolvedPath_);
231  return resolvedPath_.getRootTable();
232  }
233  public ArrayList<String> getJoinHints() { return joinHints_; }
234  public Expr getOnClause() { return onClause_; }
235  public List<String> getUsingClause() { return usingColNames_; }
236  public void setJoinOp(JoinOperator op) { this.joinOp_ = op; }
237  public void setOnClause(Expr e) { this.onClause_ = e; }
238  public void setUsingClause(List<String> colNames) { this.usingColNames_ = colNames; }
239  public TableRef getLeftTblRef() { return leftTblRef_; }
240  public void setLeftTblRef(TableRef leftTblRef) { this.leftTblRef_ = leftTblRef; }
241  public void setJoinHints(ArrayList<String> hints) { this.joinHints_ = hints; }
242  public boolean isBroadcastJoin() { return isBroadcastJoin_; }
243  public boolean isPartitionedJoin() { return isPartitionedJoin_; }
244  public List<TupleId> getOnClauseTupleIds() { return onClauseTupleIds_; }
245  public boolean isResolved() { return !getClass().equals(TableRef.class); }
246 
251  Preconditions.checkState(isAnalyzed_);
252  // after analyze(), desc should be set.
253  Preconditions.checkState(desc_ != null);
254  return desc_;
255  }
256 
260  public TupleId getId() {
261  Preconditions.checkState(isAnalyzed_);
262  // after analyze(), desc should be set.
263  Preconditions.checkState(desc_ != null);
264  return desc_.getId();
265  }
266 
267  public List<TupleId> getMaterializedTupleIds() {
268  // This function should only be called after analyze().
269  Preconditions.checkState(isAnalyzed_);
270  Preconditions.checkNotNull(desc_);
271  return desc_.getId().asList();
272  }
273 
278  public List<TupleId> getAllMaterializedTupleIds() {
279  if (leftTblRef_ != null) {
280  List<TupleId> result =
281  Lists.newArrayList(leftTblRef_.getAllMaterializedTupleIds());
282  result.addAll(getMaterializedTupleIds());
283  return result;
284  } else {
285  return getMaterializedTupleIds();
286  }
287  }
288 
292  public List<TupleId> getAllTupleIds() {
293  Preconditions.checkState(isAnalyzed_);
294  if (leftTblRef_ != null) {
295  List<TupleId> result = leftTblRef_.getAllTupleIds();
296  result.add(desc_.getId());
297  return result;
298  } else {
299  return Lists.newArrayList(desc_.getId());
300  }
301  }
302 
303  private void analyzeJoinHints(Analyzer analyzer) throws AnalysisException {
304  if (joinHints_ == null) return;
305  for (String hint: joinHints_) {
306  if (hint.equalsIgnoreCase("BROADCAST")) {
311  throw new AnalysisException(
312  joinOp_.toString() + " does not support BROADCAST.");
313  }
314  if (isPartitionedJoin_) {
315  throw new AnalysisException("Conflicting JOIN hint: " + hint);
316  }
317  isBroadcastJoin_ = true;
318  analyzer.setHasPlanHints();
319  } else if (hint.equalsIgnoreCase("SHUFFLE")) {
321  throw new AnalysisException("CROSS JOIN does not support SHUFFLE.");
322  }
323  if (isBroadcastJoin_) {
324  throw new AnalysisException("Conflicting JOIN hint: " + hint);
325  }
326  isPartitionedJoin_ = true;
327  analyzer.setHasPlanHints();
328  } else {
329  analyzer.addWarning("JOIN hint not recognized: " + hint);
330  }
331  }
332  }
333 
339  public void analyzeJoin(Analyzer analyzer) throws AnalysisException {
340  Preconditions.checkState(desc_ != null);
341  analyzeJoinHints(analyzer);
343  // A CROSS JOIN is always a broadcast join, regardless of the join hints
344  isBroadcastJoin_ = true;
345  }
346 
347  if (usingColNames_ != null) {
348  Preconditions.checkState(joinOp_ != JoinOperator.CROSS_JOIN);
349  // Turn USING clause into equivalent ON clause.
350  onClause_ = null;
351  for (String colName: usingColNames_) {
352  // check whether colName exists both for our table and the one
353  // to the left of us
354  Path leftColPath = new Path(leftTblRef_.getDesc(),
355  Lists.newArrayList(colName.toLowerCase()));
356  if (!leftColPath.resolve()) {
357  throw new AnalysisException(
358  "unknown column " + colName + " for alias "
359  + leftTblRef_.getUniqueAlias() + " (in \"" + this.toSql() + "\")");
360  }
361  Path rightColPath = new Path(desc_,
362  Lists.newArrayList(colName.toLowerCase()));
363  if (!rightColPath.resolve()) {
364  throw new AnalysisException(
365  "unknown column " + colName + " for alias "
366  + getUniqueAlias() + " (in \"" + this.toSql() + "\")");
367  }
368 
369  // create predicate "<left>.colName = <right>.colName"
370  BinaryPredicate eqPred =
371  new BinaryPredicate(BinaryPredicate.Operator.EQ,
373  new SlotRef(Path.createRawPath(getUniqueAlias(), colName)));
374  onClause_ = CompoundPredicate.createConjunction(eqPred, onClause_);
375  }
376  }
377 
378  // at this point, both 'this' and leftTblRef have been analyzed and registered;
379  // register the tuple ids of the TableRefs on the nullable side of an outer join
382  analyzer.registerOuterJoinedTids(getId().asList(), this);
383  }
386  analyzer.registerOuterJoinedTids(leftTblRef_.getAllTupleIds(), this);
387  }
388  // register the tuple ids of a full outer join
390  analyzer.registerFullOuterJoinedTids(leftTblRef_.getAllTupleIds(), this);
391  analyzer.registerFullOuterJoinedTids(getId().asList(), this);
392  }
393  // register the tuple id of the rhs of a left semi join
394  TupleId semiJoinedTupleId = null;
398  analyzer.registerSemiJoinedTid(getId(), this);
399  semiJoinedTupleId = getId();
400  }
401  // register the tuple id of the lhs of a right semi join
404  analyzer.registerSemiJoinedTid(leftTblRef_.getId(), this);
405  semiJoinedTupleId = leftTblRef_.getId();
406  }
407 
408  if (onClause_ != null) {
409  Preconditions.checkState(joinOp_ != JoinOperator.CROSS_JOIN);
410  analyzer.setVisibleSemiJoinedTuple(semiJoinedTupleId);
411  onClause_.analyze(analyzer);
412  analyzer.setVisibleSemiJoinedTuple(null);
413  onClause_.checkReturnsBool("ON clause", true);
414  if (onClause_.contains(Expr.isAggregatePredicate())) {
415  throw new AnalysisException(
416  "aggregate function not allowed in ON clause: " + toSql());
417  }
418  if (onClause_.contains(AnalyticExpr.class)) {
419  throw new AnalysisException(
420  "analytic expression not allowed in ON clause: " + toSql());
421  }
422  Set<TupleId> onClauseTupleIds = Sets.newHashSet();
423  for (Expr e: onClause_.getConjuncts()) {
424  // Outer join clause conjuncts are registered for this particular table ref
425  // (ie, can only be evaluated by the plan node that implements this join).
426  // The exception are conjuncts that only pertain to the nullable side
427  // of the outer join; those can be evaluated directly when materializing tuples
428  // without violating outer join semantics.
429  analyzer.registerOnClauseConjuncts(e, this);
430  List<TupleId> tupleIds = Lists.newArrayList();
431  e.getIds(tupleIds, null);
432  onClauseTupleIds.addAll(tupleIds);
433  }
434  onClauseTupleIds_.addAll(onClauseTupleIds);
435  } else if (!isChildRef()
436  && (getJoinOp().isOuterJoin() || getJoinOp().isSemiJoin())) {
437  throw new AnalysisException(joinOp_.toString() + " requires an ON or USING clause.");
438  }
439  }
440 
447  public void invertJoin(List<Pair<TableRef, PlanNode>> refPlans, Analyzer analyzer) {
448  // Assert that this is the first join in a series of joins.
449  Preconditions.checkState(leftTblRef_.leftTblRef_ == null);
450  // Find a table ref that references 'this' as its left table (if any) and change
451  // it to reference 'this.leftTblRef_ 'instead, because 'this.leftTblRef_' will
452  // become the new rhs of the inverted join.
453  for (Pair<TableRef, PlanNode> refPlan: refPlans) {
454  if (refPlan.first.leftTblRef_ == this) {
455  refPlan.first.setLeftTblRef(leftTblRef_);
456  break;
457  }
458  }
459  if (joinOp_.isOuterJoin()) analyzer.invertOuterJoinState(this, leftTblRef_);
460  leftTblRef_.setJoinOp(getJoinOp().invert());
461  leftTblRef_.setLeftTblRef(this);
462  leftTblRef_.setOnClause(onClause_);
463  joinOp_ = null;
464  leftTblRef_ = null;
465  onClause_ = null;
466  }
467 
468  protected String tableRefToSql() {
469  String aliasSql = null;
470  String alias = getExplicitAlias();
471  if (alias != null) aliasSql = ToSqlUtils.getIdentSql(alias);
472  List<String> path = rawPath_;
473  if (resolvedPath_ != null) path = resolvedPath_.getFullyQualifiedRawPath();
474  return ToSqlUtils.getPathSql(path) + ((aliasSql != null) ? " " + aliasSql : "");
475  }
476 
477  @Override
478  public String toSql() {
479  if (joinOp_ == null) {
480  // prepend "," if we're part of a sequence of table refs w/o an
481  // explicit JOIN clause
482  return (leftTblRef_ != null ? ", " : "") + tableRefToSql();
483  }
484 
485  StringBuilder output = new StringBuilder(" " + joinOp_.toString() + " ");
486  if(joinHints_ != null) output.append(ToSqlUtils.getPlanHintsSql(joinHints_) + " ");
487  output.append(tableRefToSql());
488  if (usingColNames_ != null) {
489  output.append(" USING (").append(Joiner.on(", ").join(usingColNames_)).append(")");
490  } else if (onClause_ != null) {
491  output.append(" ON ").append(onClause_.toSql());
492  }
493  return output.toString();
494  }
495 
500 
501  @Override
502  public TableRef clone() { return new TableRef(this); }
503 }
void invertOuterJoinState(TableRef oldRhsTbl, TableRef newRhsTbl)
Definition: Analyzer.java:1400
string path("/usr/lib/sasl2:/usr/lib64/sasl2:/usr/local/lib/sasl2:/usr/lib/x86_64-linux-gnu/sasl2")
static com.google.common.base.Predicate< Expr > isAggregatePredicate()
Definition: Expr.java:523
void setJoinHints(ArrayList< String > hints)
Definition: TableRef.java:241
void analyze(Analyzer analyzer)
Definition: TableRef.java:117
ArrayList< String > getJoinHints()
Definition: TableRef.java:233
void analyzeJoinHints(Analyzer analyzer)
Definition: TableRef.java:303
void setJoinAttrs(TableRef other)
Definition: TableRef.java:181
List< TupleId > getMaterializedTupleIds()
Definition: TableRef.java:267
TableRef(List< String > path, String alias)
Definition: TableRef.java:85
void setUsingClause(List< String > colNames)
Definition: TableRef.java:238
void setJoinOp(JoinOperator op)
Definition: TableRef.java:236
void invertJoin(List< Pair< TableRef, PlanNode >> refPlans, Analyzer analyzer)
Definition: TableRef.java:447
static ArrayList< String > createRawPath(String rootAlias, String fieldName)
Definition: Path.java:350
void setLeftTblRef(TableRef leftTblRef)
Definition: TableRef.java:240
TupleDescriptor createTupleDescriptor(Analyzer analyzer)
Definition: TableRef.java:169
void analyzeJoin(Analyzer analyzer)
Definition: TableRef.java:339
List< TupleId > getAllMaterializedTupleIds()
Definition: TableRef.java:278