Impala
Impalaistheopensource,nativeanalyticdatabaseforApacheHadoop.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
Analyzer.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.Arrays;
19 import java.util.Collections;
20 import java.util.HashSet;
21 import java.util.IdentityHashMap;
22 import java.util.Iterator;
23 import java.util.LinkedHashMap;
24 import java.util.LinkedList;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Set;
28 
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31 
55 import com.cloudera.impala.common.Id;
56 import com.cloudera.impala.common.IdGenerator;
59 import com.cloudera.impala.common.Pair;
63 import com.cloudera.impala.thrift.TAccessEvent;
64 import com.cloudera.impala.thrift.TCatalogObjectType;
65 import com.cloudera.impala.thrift.TNetworkAddress;
66 import com.cloudera.impala.thrift.TQueryCtx;
67 import com.cloudera.impala.util.DisjointSet;
69 import com.cloudera.impala.util.ListMap;
71 import com.google.common.base.Joiner;
72 import com.google.common.base.Preconditions;
73 import com.google.common.base.Predicates;
74 import com.google.common.base.Strings;
75 import com.google.common.collect.ImmutableList;
76 import com.google.common.collect.Lists;
77 import com.google.common.collect.Maps;
78 import com.google.common.collect.Sets;
79 
105 public class Analyzer {
106  // Common analysis error messages
107  public final static String DB_DOES_NOT_EXIST_ERROR_MSG = "Database does not exist: ";
108  public final static String DB_ALREADY_EXISTS_ERROR_MSG = "Database already exists: ";
109  public final static String TBL_DOES_NOT_EXIST_ERROR_MSG = "Table does not exist: ";
110  public final static String TBL_ALREADY_EXISTS_ERROR_MSG = "Table already exists: ";
111  public final static String FN_DOES_NOT_EXIST_ERROR_MSG = "Function does not exist: ";
112  public final static String FN_ALREADY_EXISTS_ERROR_MSG = "Function already exists: ";
113  public final static String DATA_SRC_DOES_NOT_EXIST_ERROR_MSG =
114  "Data source does not exist: ";
115  public final static String DATA_SRC_ALREADY_EXISTS_ERROR_MSG =
116  "Data source already exists: ";
117 
118  private final static Logger LOG = LoggerFactory.getLogger(Analyzer.class);
119 
120  private final User user_;
121 
122  // true if the corresponding select block has a limit and/or offset clause
123  private boolean hasLimitOffsetClause_ = false;
124 
125  // Current depth of nested analyze() calls. Used for enforcing a
126  // maximum expr-tree depth. Needs to be manually maintained by the user
127  // of this Analyzer with incrementCallDepth() and decrementCallDepth().
128  private int callDepth_ = 0;
129 
130  // Flag indicating if this analyzer instance belongs to a subquery.
131  private boolean isSubquery_ = false;
132 
133  // If set, when privilege requests are registered they will use this error
134  // error message.
135  private String authErrorMsg_;
136 
137  // If false, privilege requests will not be registered in the analyzer.
138  private boolean enablePrivChecks_ = true;
139 
140  // By default, all registered semi-joined tuples are invisible, i.e., their slots
141  // cannot be referenced. If set, this semi-joined tuple is made visible. Such a tuple
142  // should only be made visible for analyzing the On-clause of its semi-join.
143  // In particular, if there are multiple semi-joins in the same query block, then the
144  // On-clause of any such semi-join is not allowed to reference other semi-joined tuples
145  // except its own. Therefore, only a single semi-joined tuple can be visible at a time.
147 
148  public void setIsSubquery() {
149  isSubquery_ = true;
150  globalState_.containsSubquery = true;
151  }
152  public boolean isSubquery() { return isSubquery_; }
153  public boolean setHasPlanHints() { return globalState_.hasPlanHints = true; }
154  public boolean hasPlanHints() { return globalState_.hasPlanHints; }
155 
156  // state shared between all objects of an Analyzer tree
157  // TODO: Many maps here contain properties about tuples, e.g., whether
158  // a tuple is outer/semi joined, etc. Remove the maps in favor of making
159  // them properties of the tuple descriptor itself.
160  private static class GlobalState {
161  // TODO: Consider adding an "exec-env"-like global singleton that contains the
162  // catalog and authzConfig.
163  public final ImpaladCatalog catalog;
164  public final TQueryCtx queryCtx;
167  public final IdGenerator<ExprId> conjunctIdGenerator = ExprId.createGenerator();
169 
170  // True if we are analyzing an explain request. Should be set before starting
171  // analysis.
172  public boolean isExplain;
173 
174  // Indicates whether the query has plan hints.
175  public boolean hasPlanHints = false;
176 
177  // True if at least one of the analyzers belongs to a subquery.
178  public boolean containsSubquery = false;
179 
180  // whether to use Hive's auto-generated column labels
181  public boolean useHiveColLabels = false;
182 
183  // all registered conjuncts (map from expr id to conjunct)
184  public final Map<ExprId, Expr> conjuncts = Maps.newHashMap();
185 
186  // all registered conjuncts bound by a single tuple id; used in getBoundPredicates()
187  public final ArrayList<ExprId> singleTidConjuncts = Lists.newArrayList();
188 
189  // eqJoinConjuncts[tid] contains all conjuncts of the form
190  // "<lhs> = <rhs>" in which either lhs or rhs is fully bound by tid
191  // and the other side is not bound by tid (ie, predicates that express equi-join
192  // conditions between two tablerefs).
193  // A predicate such as "t1.a = t2.b" has two entries, one for 't1' and
194  // another one for 't2'.
195  public final Map<TupleId, List<ExprId>> eqJoinConjuncts = Maps.newHashMap();
196 
197  // set of conjuncts that have been assigned to some PlanNode
198  public Set<ExprId> assignedConjuncts =
199  Collections.newSetFromMap(new IdentityHashMap<ExprId, Boolean>());
200 
201  // map from outer-joined tuple id, i.e., one that is nullable,
202  // to the last Join clause (represented by its rhs table ref) that outer-joined it
203  public final Map<TupleId, TableRef> outerJoinedTupleIds = Maps.newHashMap();
204 
205  // Map of registered conjunct to the last full outer join (represented by its
206  // rhs table ref) that outer joined it.
207  public final Map<ExprId, TableRef> fullOuterJoinedConjuncts = Maps.newHashMap();
208 
209  // Map of full-outer-joined tuple id to the last full outer join that outer-joined it
210  public final Map<TupleId, TableRef> fullOuterJoinedTupleIds = Maps.newHashMap();
211 
212  // Map from semi-joined tuple id, i.e., one that is invisible outside the join's
213  // On-clause, to its Join clause (represented by its rhs table ref). An anti-join is
214  // a kind of semi-join, so anti-joined tuples are also registered here.
215  public final Map<TupleId, TableRef> semiJoinedTupleIds = Maps.newHashMap();
216 
217  // map from right-hand side table ref of an outer join to the list of
218  // conjuncts in its On clause
219  public final Map<TableRef, List<ExprId>> conjunctsByOjClause = Maps.newHashMap();
220 
221  // map from registered conjunct to its containing outer join On clause (represented
222  // by its right-hand side table ref); this is limited to conjuncts that can only be
223  // correctly evaluated by the originating outer join, including constant conjuncts
224  public final Map<ExprId, TableRef> ojClauseByConjunct = Maps.newHashMap();
225 
226  // map from registered conjunct to its containing semi join On clause (represented
227  // by its right-hand side table ref)
228  public final Map<ExprId, TableRef> sjClauseByConjunct = Maps.newHashMap();
229 
230  // map from slot id to the analyzer/block in which it was registered
231  public final Map<SlotId, Analyzer> blockBySlot = Maps.newHashMap();
232 
233  // Tracks all privilege requests on catalog objects.
234  private final List<PrivilegeRequest> privilegeReqs = Lists.newArrayList();
235 
236  // List of PrivilegeRequest to custom authorization failure error message.
237  // Tracks all privilege requests on catalog objects that need a custom
238  // error message returned to avoid exposing existence of catalog objects.
239  private final List<Pair<PrivilegeRequest, String>> maskedPrivilegeReqs =
240  Lists.newArrayList();
241 
242  // accesses to catalog objects
243  // TODO: This can be inferred from privilegeReqs. They should be coalesced.
244  public Set<TAccessEvent> accessEvents = Sets.newHashSet();
245 
246  // Tracks all warnings (e.g. non-fatal errors) that were generated during analysis.
247  // These are passed to the backend and eventually propagated to the shell. Maps from
248  // warning message to the number of times that warning was logged (in order to avoid
249  // duplicating the same warning over and over).
250  public final LinkedHashMap<String, Integer> warnings =
251  new LinkedHashMap<String, Integer>();
252 
253  public final IdGenerator<EquivalenceClassId> equivClassIdGenerator =
254  EquivalenceClassId.createGenerator();
255 
256  // map from equivalence class id to the list of its member slots
257  private final Map<EquivalenceClassId, ArrayList<SlotId>> equivClassMembers =
258  Maps.newHashMap();
259 
260  // map from slot id to its equivalence class id;
261  // only visible at the root Analyzer
262  private final Map<SlotId, EquivalenceClassId> equivClassBySlotId = Maps.newHashMap();
263 
264  // map for each slot to the canonical slot of its equivalence class
266 
267  // represents the direct and transitive value transfers between slots
269 
270  private final List<Pair<SlotId, SlotId>> registeredValueTransfers =
271  Lists.newArrayList();
272 
273  // Bidirectional map between Integer index and TNetworkAddress.
274  // Decreases the size of the scan range locations.
275  private final ListMap<TNetworkAddress> hostIndex = new ListMap<TNetworkAddress>();
276 
277  // Timeline of important events in the planning process, used for debugging /
278  // profiling
279  private final EventSequence timeline = new EventSequence("Planner Timeline");
280 
283  this.catalog = catalog;
284  this.queryCtx = queryCtx;
285  this.authzConfig = authzConfig;
286  this.lineageGraph = new ColumnLineageGraph();
287  }
288  };
289 
290  private final GlobalState globalState_;
291 
292  public boolean containsSubquery() { return globalState_.containsSubquery; }
293 
294  // An analyzer stores analysis state for a single select block. A select block can be
295  // a top level select statement, or an inline view select block.
296  // ancestors contains the Analyzers of the enclosing select blocks of 'this'
297  // (ancestors[0] contains the immediate parent, etc.).
298  private final ArrayList<Analyzer> ancestors_;
299 
300  // map from lowercase table alias to a view definition in this analyzer's scope
301  private final Map<String, View> localViews_ = Maps.newHashMap();
302 
303  // Map from lowercase table alias to descriptor. Tables without an explicit alias
304  // are assigned two implicit aliases: the unqualified and fully-qualified table name.
305  // Such tables have two entries pointing to the same descriptor. If an alias is
306  // ambiguous, then this map retains the first entry with that alias to simplify error
307  // checking (duplicate vs. ambiguous alias).
308  private final Map<String, TupleDescriptor> aliasMap_ = Maps.newHashMap();
309 
310  // Map from tuple id to its corresponding table ref.
311  private final Map<TupleId, TableRef> tableRefMap_ = Maps.newHashMap();
312 
313  // Set of lowercase ambiguous implicit table aliases.
314  private final Set<String> ambiguousAliases_ = Sets.newHashSet();
315 
316  // map from lowercase explicit or fully-qualified implicit alias to descriptor
317  private final Map<String, SlotDescriptor> slotRefMap_ = Maps.newHashMap();
318 
319  // Tracks the all tables/views found during analysis that were missing metadata.
320  private Set<TableName> missingTbls_ = new HashSet<TableName>();
321 
322  // Indicates whether this analyzer/block is guaranteed to have an empty result set
323  // due to a limit 0 or constant conjunct evaluating to false.
324  private boolean hasEmptyResultSet_ = false;
325 
326  // Indicates whether the select-project-join (spj) portion of this query block
327  // is guaranteed to return an empty result set. Set due to a constant non-Having
328  // conjunct evaluating to false.
329  private boolean hasEmptySpjResultSet_ = false;
330 
331  public Analyzer(ImpaladCatalog catalog, TQueryCtx queryCtx,
332  AuthorizationConfig authzConfig) {
333  ancestors_ = Lists.newArrayList();
334  globalState_ = new GlobalState(catalog, queryCtx, authzConfig);
335  user_ = new User(TSessionStateUtil.getEffectiveUser(queryCtx.session));
336  }
337 
342  public Analyzer(Analyzer parentAnalyzer) {
343  this(parentAnalyzer, parentAnalyzer.globalState_);
344  }
345 
349  private Analyzer(Analyzer parentAnalyzer, GlobalState globalState) {
350  ancestors_ = Lists.newArrayList(parentAnalyzer);
351  ancestors_.addAll(parentAnalyzer.ancestors_);
352  globalState_ = globalState;
353  missingTbls_ = parentAnalyzer.missingTbls_;
354  user_ = parentAnalyzer.getUser();
355  authErrorMsg_ = parentAnalyzer.authErrorMsg_;
356  enablePrivChecks_ = parentAnalyzer.enablePrivChecks_;
357  }
358 
363  public static Analyzer createWithNewGlobalState(Analyzer parentAnalyzer) {
364  GlobalState globalState = new GlobalState(parentAnalyzer.globalState_.catalog,
365  parentAnalyzer.getQueryCtx(), parentAnalyzer.getAuthzConfig());
366  return new Analyzer(parentAnalyzer, globalState);
367  }
368 
374  Preconditions.checkState(tid == null
375  || globalState_.semiJoinedTupleIds.containsKey(tid));
376  Preconditions.checkState(tid == null || visibleSemiJoinedTupleId_ == null);
378  }
379 
380  public Set<TableName> getMissingTbls() { return missingTbls_; }
381  public boolean hasMissingTbls() { return !missingTbls_.isEmpty(); }
382  public boolean hasAncestors() { return !ancestors_.isEmpty(); }
384  return hasAncestors() ? ancestors_.get(0) : null;
385  }
386 
390  public List<String> getWarnings() {
391  List<String> result = new ArrayList<String>();
392  for (Map.Entry<String, Integer> e : globalState_.warnings.entrySet()) {
393  String error = e.getKey();
394  int count = e.getValue();
395  Preconditions.checkState(count > 0);
396  if (count == 1) {
397  result.add(error);
398  } else {
399  result.add(error + " (" + count + " warnings like this)");
400  }
401  }
402  return result;
403  }
404 
409  public void registerLocalView(View view) throws AnalysisException {
410  Preconditions.checkState(view.isLocalView());
411  if (localViews_.put(view.getName().toLowerCase(), view) != null) {
412  throw new AnalysisException(
413  String.format("Duplicate table alias: '%s'", view.getName()));
414  }
415  }
416 
428  String uniqueAlias = ref.getUniqueAlias();
429  if (aliasMap_.containsKey(uniqueAlias)) {
430  throw new AnalysisException("Duplicate table alias: '" + uniqueAlias + "'");
431  }
432 
433  // If ref has no explicit alias, then the unqualified and the fully-qualified table
434  // names are legal implicit aliases. Column references against unqualified implicit
435  // aliases can be ambiguous, therefore, we register such ambiguous aliases here.
436  String unqualifiedAlias = null;
437  String[] aliases = ref.getAliases();
438  if (aliases.length > 1) {
439  unqualifiedAlias = aliases[1];
440  TupleDescriptor tupleDesc = aliasMap_.get(unqualifiedAlias);
441  if (tupleDesc != null) {
442  if (tupleDesc.hasExplicitAlias()) {
443  throw new AnalysisException(
444  "Duplicate table alias: '" + unqualifiedAlias + "'");
445  } else {
446  ambiguousAliases_.add(unqualifiedAlias);
447  }
448  }
449  }
450 
451  // Delegate creation of the tuple descriptor to the concrete table ref.
452  TupleDescriptor result = ref.createTupleDescriptor(this);
453  result.setAliases(aliases, ref.hasExplicitAlias());
454  // Register all legal aliases.
455  for (String alias: aliases) {
456  aliasMap_.put(alias, result);
457  }
458  tableRefMap_.put(result.getId(), ref);
459  return result;
460  }
461 
469  // Return the table if it is already resolved.
470  if (tableRef.isResolved()) return tableRef;
471  // Try to find a matching local view.
472  if (tableRef.getPath().size() == 1) {
473  // Searches the hierarchy of analyzers bottom-up for a registered local view with
474  // a matching alias.
475  String viewAlias = tableRef.getPath().get(0).toLowerCase();
476  Analyzer analyzer = this;
477  do {
478  View localView = analyzer.localViews_.get(viewAlias);
479  if (localView != null) return new InlineViewRef(localView, tableRef);
480  analyzer = (analyzer.ancestors_.isEmpty() ? null : analyzer.ancestors_.get(0));
481  } while (analyzer != null);
482  }
483 
484  // Resolve the table ref's path and determine what resolved table ref
485  // to replace it with.
486  tableRef.analyze(this);
487  Path resolvedPath = tableRef.getResolvedPath();
488  if (resolvedPath.destTable() != null) {
489  Table table = resolvedPath.destTable();
490  Preconditions.checkNotNull(table);
491  if (table instanceof View) return new InlineViewRef((View) table, tableRef);
492  // The table must be a base table.
493  Preconditions.checkState(table instanceof HdfsTable ||
494  table instanceof HBaseTable || table instanceof DataSourceTable);
495  return new BaseTableRef(tableRef, tableRef.getResolvedPath());
496  } else {
497  return new CollectionTableRef(tableRef, tableRef.getResolvedPath());
498  }
499  }
500 
509  Preconditions.checkState(
510  !globalState_.fullOuterJoinedConjuncts.containsKey(e.getId()));
511  List<TupleId> tids = Lists.newArrayList();
512  e.getIds(tids, null);
513  for (TupleId tid: tids) {
514  if (!globalState_.fullOuterJoinedTupleIds.containsKey(tid)) continue;
515  TableRef currentOuterJoin = globalState_.fullOuterJoinedTupleIds.get(tid);
516  globalState_.fullOuterJoinedConjuncts.put(e.getId(), currentOuterJoin);
517  break;
518  }
519  LOG.trace("registerFullOuterJoinedConjunct: " +
520  globalState_.fullOuterJoinedConjuncts.toString());
521  }
522 
527  public void registerFullOuterJoinedTids(List<TupleId> tids, TableRef rhsRef) {
528  for (TupleId tid: tids) {
529  globalState_.fullOuterJoinedTupleIds.put(tid, rhsRef);
530  }
531  LOG.trace("registerFullOuterJoinedTids: " +
532  globalState_.fullOuterJoinedTupleIds.toString());
533  }
534 
538  public void registerOuterJoinedTids(List<TupleId> tids, TableRef rhsRef) {
539  for (TupleId tid: tids) {
540  globalState_.outerJoinedTupleIds.put(tid, rhsRef);
541  }
542  LOG.trace("registerOuterJoinedTids: " + globalState_.outerJoinedTupleIds.toString());
543  }
544 
548  public void registerSemiJoinedTid(TupleId tid, TableRef rhsRef) {
549  globalState_.semiJoinedTupleIds.put(tid, rhsRef);
550  }
551 
557  public TupleDescriptor getDescriptor(String tableAlias) throws AnalysisException {
558  String lookupAlias = tableAlias.toLowerCase();
559  if (ambiguousAliases_.contains(lookupAlias)) {
560  throw new AnalysisException(String.format(
561  "Unqualified table alias is ambiguous: '%s'", tableAlias));
562  }
563  return aliasMap_.get(lookupAlias);
564  }
565 
567  return globalState_.descTbl.getTupleDesc(id);
568  }
569 
570  public TableRef getTableRef(TupleId tid) { return tableRefMap_.get(tid); }
571 
575  public SlotDescriptor getSlotDescriptor(String qualifiedColumnName) {
576  return slotRefMap_.get(qualifiedColumnName);
577  }
578 
583  public boolean isRootAnalyzer() { return ancestors_.isEmpty(); }
584 
590  public boolean hasEmptyResultSet() { return hasEmptyResultSet_; }
591  public void setHasEmptyResultSet() { hasEmptyResultSet_ = true; }
592 
597  public boolean hasEmptySpjResultSet() { return hasEmptySpjResultSet_; }
598 
622  public Path resolvePath(List<String> rawPath, PathType pathType)
624  // We only allow correlated references in predicates of a subquery.
625  boolean resolveInAncestors = (pathType == PathType.SLOT_REF) ? isSubquery_: false;
626  // Convert all path elements to lower case.
627  ArrayList<String> lcRawPath = Lists.newArrayListWithCapacity(rawPath.size());
628  for (String s: rawPath) lcRawPath.add(s.toLowerCase());
629  return resolvePath(lcRawPath, pathType, resolveInAncestors);
630  }
631 
632  private Path resolvePath(List<String> rawPath, PathType pathType,
633  boolean resolveInAncestors) throws AnalysisException, TableLoadingException {
634  // List of all candidate paths with different roots. Paths in this list are initially
635  // unresolved and may be illegal with respect to the pathType.
636  ArrayList<Path> candidates = Lists.newArrayList();
637 
638  // Path rooted at a tuple desc with an explicit or implicit unqualified alias.
639  TupleDescriptor rootDesc = getDescriptor(rawPath.get(0));
640  if (rootDesc != null) {
641  candidates.add(new Path(rootDesc, rawPath.subList(1, rawPath.size())));
642  }
643 
644  // Path rooted at a tuple desc with an implicit qualified alias.
645  if (rawPath.size() > 1) {
646  rootDesc = getDescriptor(rawPath.get(0) + "." + rawPath.get(1));
647  if (rootDesc != null) {
648  candidates.add(new Path(rootDesc, rawPath.subList(2, rawPath.size())));
649  }
650  }
651 
652  LinkedList<String> errors = Lists.newLinkedList();
653  if (pathType == PathType.SLOT_REF || pathType == PathType.STAR) {
654  // Paths rooted at all of the unique registered tuple descriptors.
655  for (TableRef tblRef: tableRefMap_.values()) {
656  candidates.add(new Path(tblRef.getDesc(), rawPath));
657  }
658  } else {
659  // Always prefer table ref paths rooted at a registered tuples descriptor.
660  Preconditions.checkState(pathType == PathType.TABLE_REF);
661  Path result = resolvePaths(rawPath, candidates, pathType, errors);
662  if (result != null) return result;
663  candidates.clear();
664 
665  // Add paths rooted at a table with an unqualified and fully-qualified table name.
666  int end = Math.min(2, rawPath.size());
667  for (int tblNameIdx = 0; tblNameIdx < end; ++tblNameIdx) {
668  String dbName = (tblNameIdx == 0) ? getDefaultDb() : rawPath.get(0);
669  String tblName = rawPath.get(tblNameIdx);
670  Table tbl = null;
671  try {
672  tbl = getTable(dbName, tblName);
673  } catch (AnalysisException e) {
674  if (hasMissingTbls()) throw e;
675  // Ignore other exceptions to allow path resolution to continue.
676  }
677  if (tbl != null) {
678  candidates.add(new Path(tbl, rawPath.subList(tblNameIdx + 1, rawPath.size())));
679  }
680  }
681  }
682 
683  Path result = resolvePaths(rawPath, candidates, pathType, errors);
684  if (result == null && resolveInAncestors && hasAncestors()) {
685  result = getParentAnalyzer().resolvePath(rawPath, pathType, true);
686  }
687  if (result == null) {
688  Preconditions.checkState(!errors.isEmpty());
689  throw new AnalysisException(errors.getFirst());
690  }
691  return result;
692  }
693 
700  private Path resolvePaths(List<String> rawPath, List<Path> paths, PathType pathType,
701  LinkedList<String> errors) {
702  // For generating error messages.
703  String pathTypeStr = null;
704  String pathStr = Joiner.on(".").join(rawPath);
705  if (pathType == PathType.SLOT_REF) {
706  pathTypeStr = "Column/field reference";
707  } else if (pathType == PathType.TABLE_REF) {
708  pathTypeStr = "Table reference";
709  } else {
710  pathTypeStr = "Star expression";
711  pathStr += ".*";
712  }
713 
714  List<Path> legalPaths = Lists.newArrayList();
715  for (Path p: paths) {
716  if (!p.resolve()) continue;
717 
718  // Check legality of the resolved path.
719  if (p.getRootDesc() != null && !isVisible(p.getRootDesc().getId())) {
720  errors.addLast(String.format(
721  "Illegal %s '%s' of semi-/anti-joined table '%s'",
722  pathTypeStr.toLowerCase(), pathStr, p.getRootDesc().getAlias()));
723  continue;
724  }
725  switch (pathType) {
726  // Illegal cases:
727  // 1. Destination type is not a collection.
728  case TABLE_REF: {
729  if (!p.destType().isCollectionType()) {
730  errors.addFirst(String.format(
731  "Illegal table reference to non-collection type: '%s'\n" +
732  "Path resolved to type: %s", pathStr, p.destType().toSql()));
733  continue;
734  }
735  break;
736  }
737  case SLOT_REF: {
738  // Illegal cases:
739  // 1. Path contains an intermediate collection reference.
740  // 2. Destination of the path is a catalog table or a registered alias.
741  if (p.hasNonDestCollection()) {
742  errors.addFirst(String.format(
743  "Illegal column/field reference '%s' with intermediate " +
744  "collection '%s' of type '%s'",
745  pathStr, p.getFirstCollectionName(),
746  p.getFirstCollectionType().toSql()));
747  continue;
748  }
749  // Error should be "Could not resolve...". No need to add it here explicitly.
750  if (p.getMatchedTypes().isEmpty()) continue;
751  break;
752  }
753  // Illegal cases:
754  // 1. Path contains an intermediate collection reference.
755  // 2. Destination type of the path is not a struct.
756  case STAR: {
757  if (p.hasNonDestCollection()) {
758  errors.addFirst(String.format(
759  "Illegal star expression '%s' with intermediate " +
760  "collection '%s' of type '%s'",
761  pathStr, p.getFirstCollectionName(),
762  p.getFirstCollectionType().toSql()));
763  continue;
764  }
765  if (!p.destType().isStructType()) {
766  errors.addFirst(String.format(
767  "Cannot expand star in '%s' because path '%s' resolved to type '%s'." +
768  "\nStar expansion is only valid for paths to a struct type.",
769  pathStr, Joiner.on(".").join(rawPath), p.destType().toSql()));
770  continue;
771  }
772  break;
773  }
774  }
775  legalPaths.add(p);
776  }
777 
778  if (legalPaths.size() > 1) {
779  errors.addFirst(String.format("%s is ambiguous: '%s'",
780  pathTypeStr, pathStr));
781  return null;
782  }
783  if (legalPaths.isEmpty()) {
784  if (errors.isEmpty()) {
785  errors.addFirst(String.format("Could not resolve %s: '%s'",
786  pathTypeStr.toLowerCase(), pathStr));
787  }
788  return null;
789  }
790  return legalPaths.get(0);
791  }
792 
798  // SlotRefs are registered against the tuple's explicit or fully-qualified
799  // implicit alias.
800  TupleDescriptor tupleDesc = slotPath.getRootDesc();
801  String key = slotPath.toString();
802  SlotDescriptor result = slotRefMap_.get(key);
803  if (result != null) return result;
804  result = addSlotDescriptor(tupleDesc);
805  result.setPath(slotPath);
806  slotRefMap_.put(slotPath.toString(), result);
807  return result;
808  }
809 
814  SlotDescriptor result = globalState_.descTbl.addSlotDescriptor(tupleDesc);
815  globalState_.blockBySlot.put(result.getId(), this);
816  return result;
817  }
818 
824  TupleDescriptor tupleDesc) {
825  SlotDescriptor result = globalState_.descTbl.addSlotDescriptor(tupleDesc);
826  globalState_.blockBySlot.put(result.getId(), this);
827  result.setSourceExprs(srcSlotDesc.getSourceExprs());
828  result.setLabel(srcSlotDesc.getLabel());
829  result.setStats(srcSlotDesc.getStats());
830  result.setType(srcSlotDesc.getType());
831  return result;
832  }
833 
837  public void registerConjuncts(List<Expr> l) {
838  for (Expr e: l) {
839  registerConjuncts(e, true);
840  }
841  }
842 
849  public void registerOnClauseConjuncts(Expr e, TableRef rhsRef) {
850  Preconditions.checkNotNull(rhsRef);
851  Preconditions.checkNotNull(e);
852  List<ExprId> ojConjuncts = null;
853  if (rhsRef.getJoinOp().isOuterJoin()) {
854  ojConjuncts = globalState_.conjunctsByOjClause.get(rhsRef);
855  if (ojConjuncts == null) {
856  ojConjuncts = Lists.newArrayList();
857  globalState_.conjunctsByOjClause.put(rhsRef, ojConjuncts);
858  }
859  }
860  for (Expr conjunct: e.getConjuncts()) {
861  registerConjunct(conjunct);
862  if (rhsRef.getJoinOp().isOuterJoin()) {
863  globalState_.ojClauseByConjunct.put(conjunct.getId(), rhsRef);
864  ojConjuncts.add(conjunct.getId());
865  }
866  if (rhsRef.getJoinOp().isSemiJoin()) {
867  globalState_.sjClauseByConjunct.put(conjunct.getId(), rhsRef);
868  }
869  markConstantConjunct(conjunct, false);
870  }
871  }
872 
877  public void registerConjuncts(Expr e, boolean fromHavingClause) {
878  for (Expr conjunct: e.getConjuncts()) {
879  registerConjunct(conjunct);
880  if (!fromHavingClause) conjunct.setIsWhereClauseConjunct();
881  markConstantConjunct(conjunct, fromHavingClause);
882  }
883  }
884 
892  private void markConstantConjunct(Expr conjunct, boolean fromHavingClause) {
893  if (!conjunct.isConstant() || isOjConjunct(conjunct)) return;
894  markConjunctAssigned(conjunct);
895  if ((!fromHavingClause && !hasEmptySpjResultSet_)
896  || (fromHavingClause && !hasEmptyResultSet_)) {
897  try {
898  if (!FeSupport.EvalPredicate(conjunct, globalState_.queryCtx)) {
899  if (fromHavingClause) {
900  hasEmptyResultSet_ = true;
901  } else {
902  hasEmptySpjResultSet_ = true;
903  }
904  }
905  } catch (InternalException ex) {
906  // Should never happen.
907  throw new IllegalStateException(ex);
908  }
909  }
910  }
911 
916  private void registerConjunct(Expr e) {
917  // always generate a new expr id; this might be a cloned conjunct that already
918  // has the id of its origin set
919  e.setId(globalState_.conjunctIdGenerator.getNextId());
920  globalState_.conjuncts.put(e.getId(), e);
921 
922  ArrayList<TupleId> tupleIds = Lists.newArrayList();
923  ArrayList<SlotId> slotIds = Lists.newArrayList();
924  e.getIds(tupleIds, slotIds);
926 
927  // register single tid conjuncts
928  if (tupleIds.size() == 1) globalState_.singleTidConjuncts.add(e.getId());
929 
930  LOG.trace("register tuple/slotConjunct: " + Integer.toString(e.getId().asInt())
931  + " " + e.toSql() + " " + e.debugString());
932 
933  if (!(e instanceof BinaryPredicate)) return;
934  BinaryPredicate binaryPred = (BinaryPredicate) e;
935 
936  // check whether this is an equi-join predicate, ie, something of the
937  // form <expr1> = <expr2> where at least one of the exprs is bound by
938  // exactly one tuple id
939  if (binaryPred.getOp() != BinaryPredicate.Operator.EQ &&
940  binaryPred.getOp() != BinaryPredicate.Operator.NULL_MATCHING_EQ) {
941  return;
942  }
943  // the binary predicate must refer to at least two tuples to be an eqJoinConjunct
944  if (tupleIds.size() < 2) return;
945 
946  // examine children and update eqJoinConjuncts
947  for (int i = 0; i < 2; ++i) {
948  tupleIds = Lists.newArrayList();
949  binaryPred.getChild(i).getIds(tupleIds, null);
950  if (tupleIds.size() == 1) {
951  if (!globalState_.eqJoinConjuncts.containsKey(tupleIds.get(0))) {
952  List<ExprId> conjunctIds = Lists.newArrayList();
953  conjunctIds.add(e.getId());
954  globalState_.eqJoinConjuncts.put(tupleIds.get(0), conjunctIds);
955  } else {
956  globalState_.eqJoinConjuncts.get(tupleIds.get(0)).add(e.getId());
957  }
958  binaryPred.setIsEqJoinConjunct(true);
959  LOG.trace("register eqJoinConjunct: " + Integer.toString(e.getId().asInt()));
960  }
961  }
962  }
963 
969  public void createAuxEquivPredicate(Expr lhs, Expr rhs) {
970  // create an eq predicate between lhs and rhs
971  BinaryPredicate p = new BinaryPredicate(BinaryPredicate.Operator.EQ, lhs, rhs);
972  p.setIsAuxExpr();
973  LOG.trace("register equiv predicate: " + p.toSql() + " " + p.debugString());
974  registerConjunct(p);
975  }
976 
980  public BinaryPredicate createEqPredicate(SlotId lhsSlotId, SlotId rhsSlotId) {
981  BinaryPredicate pred = new BinaryPredicate(BinaryPredicate.Operator.EQ,
982  new SlotRef(globalState_.descTbl.getSlotDesc(lhsSlotId)),
983  new SlotRef(globalState_.descTbl.getSlotDesc(rhsSlotId)));
984  // create casts if needed
985  pred.analyzeNoThrow(this);
986  return pred;
987  }
988 
994  public List<Expr> getUnassignedConjuncts(
995  List<TupleId> tupleIds, boolean inclOjConjuncts) {
996  LOG.trace("getUnassignedConjuncts for " + Id.printIds(tupleIds));
997  List<Expr> result = Lists.newArrayList();
998  for (Expr e: globalState_.conjuncts.values()) {
999  if (e.isBoundByTupleIds(tupleIds)
1000  && !e.isAuxExpr()
1001  && !globalState_.assignedConjuncts.contains(e.getId())
1002  && ((inclOjConjuncts && !e.isConstant())
1003  || !globalState_.ojClauseByConjunct.containsKey(e.getId()))) {
1004  result.add(e);
1005  LOG.trace("getUnassignedConjunct: " + e.toSql());
1006  }
1007  }
1008  return result;
1009  }
1010 
1011  public boolean isOjConjunct(Expr e) {
1012  return globalState_.ojClauseByConjunct.containsKey(e.getId());
1013  }
1014 
1016  return globalState_.fullOuterJoinedConjuncts.get(e.getId());
1017  }
1018 
1019  public boolean isFullOuterJoined(Expr e) {
1020  return globalState_.fullOuterJoinedConjuncts.containsKey(e.getId());
1021  }
1022 
1028  public List<Expr> getUnassignedConjuncts(PlanNode node) {
1029  List<TupleId> tupleIds = node.getTblRefIds();
1030  LOG.trace("getUnassignedConjuncts for node with " + Id.printIds(tupleIds));
1031  List<Expr> result = Lists.newArrayList();
1032  for (Expr e: getUnassignedConjuncts(tupleIds, true)) {
1033  if (canEvalPredicate(node, e)) {
1034  result.add(e);
1035  LOG.trace("getUnassignedConjunct: " + e.toSql());
1036  }
1037  }
1038  return result;
1039  }
1040 
1045  public boolean evalByJoin(Expr e) {
1046  List<TupleId> tids = Lists.newArrayList();
1047  e.getIds(tids, null);
1048  if (tids.isEmpty()) return false;
1049  if (tids.size() > 1 || isOjConjunct(e)
1050  || (isOuterJoined(tids.get(0)) && e.isWhereClauseConjunct())
1052  return true;
1053  }
1054  return false;
1055  }
1056 
1061  public List<Expr> getUnassignedOjConjuncts(TableRef ref) {
1062  Preconditions.checkState(ref.getJoinOp().isOuterJoin());
1063  List<Expr> result = Lists.newArrayList();
1064  List<ExprId> candidates = globalState_.conjunctsByOjClause.get(ref);
1065  if (candidates == null) return result;
1066  for (ExprId conjunctId: candidates) {
1067  if (!globalState_.assignedConjuncts.contains(conjunctId)) {
1068  Expr e = globalState_.conjuncts.get(conjunctId);
1069  Preconditions.checkNotNull(e);
1070  result.add(e);
1071  LOG.trace("getUnassignedOjConjunct: " + e.toSql());
1072  }
1073  }
1074  return result;
1075  }
1076 
1081  return globalState_.outerJoinedTupleIds.get(id);
1082  }
1083 
1089  for (SlotDescriptor slotDesc: tupleDesc.getSlots()) {
1090  if (slotDesc.getColumn() == col) return slotDesc;
1091  }
1092  return null;
1093  }
1094 
1096  return globalState_.descTbl;
1097  }
1098 
1100  if (!globalState_.catalog.isReady()) {
1101  throw new AnalysisException("This Impala daemon is not ready to accept user " +
1102  "requests. Status: Waiting for catalog update from the StateStore.");
1103  }
1104  return globalState_.catalog;
1105  }
1106 
1107  public Set<String> getAliases() {
1108  return aliasMap_.keySet();
1109  }
1110 
1119  public List<Expr> getEqJoinConjuncts(List<TupleId> tids, TableRef joinedTblRef) {
1120  Preconditions.checkNotNull(joinedTblRef);
1121  TupleId id = joinedTblRef.getId();
1122  List<TupleId> nodeTupleIds = Lists.newArrayList(tids);
1123  nodeTupleIds.add(id);
1124  List<ExprId> conjunctIds = globalState_.eqJoinConjuncts.get(id);
1125  if (conjunctIds == null) return null;
1126  List<Expr> result = Lists.newArrayList();
1127  List<ExprId> ojClauseConjuncts = null;
1128  if (joinedTblRef.getJoinOp().isOuterJoin()) {
1129  Preconditions.checkState(joinedTblRef.getOnClause() != null);
1130  ojClauseConjuncts = globalState_.conjunctsByOjClause.get(joinedTblRef);
1131  }
1132  for (ExprId conjunctId: conjunctIds) {
1133  Expr e = globalState_.conjuncts.get(conjunctId);
1134  Preconditions.checkState(e != null);
1135  if (ojClauseConjuncts != null) {
1136  if (ojClauseConjuncts.contains(conjunctId)
1137  && canEvalFullOuterJoinedConjunct(e, nodeTupleIds)
1138  && canEvalAntiJoinedConjunct(e, nodeTupleIds)) {
1139  result.add(e);
1140  }
1141  } else if (canEvalFullOuterJoinedConjunct(e, nodeTupleIds)
1142  && canEvalAntiJoinedConjunct(e, nodeTupleIds)) {
1143  result.add(e);
1144  }
1145  }
1146  return result;
1147  }
1148 
1153  public boolean canEvalFullOuterJoinedConjunct(Expr e, List<TupleId> tids) {
1154  TableRef fullOuterJoin = getFullOuterJoinRef(e);
1155  if (fullOuterJoin == null) return true;
1156  return tids.containsAll(fullOuterJoin.getAllTupleIds());
1157  }
1158 
1169  public boolean canEvalPredicate(List<TupleId> tupleIds, Expr e) {
1170  LOG.trace("canEval: " + e.toSql() + " " + e.debugString() + " "
1171  + Id.printIds(tupleIds));
1172  if (!e.isBoundByTupleIds(tupleIds)) return false;
1173  ArrayList<TupleId> tids = Lists.newArrayList();
1174  e.getIds(tids, null);
1175  if (tids.isEmpty()) return true;
1176 
1177  if (!e.isWhereClauseConjunct()) {
1178  if (tids.size() > 1) {
1179  // If the conjunct is from the ON-clause of an anti join, check if we can
1180  // assign it to this node.
1181  if (isAntiJoinedConjunct(e)) return canEvalAntiJoinedConjunct(e, tupleIds);
1182  // bail if this is from an OJ On clause; the join node will pick
1183  // it up later via getUnassignedOjConjuncts()
1184  if (globalState_.ojClauseByConjunct.containsKey(e.getId())) return false;
1185  // If this is not from an OJ On clause (e.g. where clause or On clause of an
1186  // inner join) and is full-outer joined, we need to make sure it is not
1187  // assigned below the full outer join node that outer-joined it.
1188  return canEvalFullOuterJoinedConjunct(e, tupleIds);
1189  }
1190 
1191  TupleId tid = tids.get(0);
1192  if (globalState_.ojClauseByConjunct.containsKey(e.getId())) {
1193  // OJ On-clause predicate: okay if it's from
1194  // the same On clause that makes tid nullable
1195  // (otherwise e needn't be true when that tuple is set)
1196  if (!globalState_.outerJoinedTupleIds.containsKey(tid)) return false;
1198  != globalState_.outerJoinedTupleIds.get(tid)) {
1199  return false;
1200  }
1201  // Single tuple id conjuncts specified in the FOJ On-clause are not allowed to be
1202  // assigned below that full outer join in the operator tree.
1203  TableRef tblRef = globalState_.ojClauseByConjunct.get(e.getId());
1204  if (tblRef.getJoinOp().isFullOuterJoin()) return false;
1205  } else {
1206  // non-OJ On-clause predicate: not okay if tid is nullable and the
1207  // predicate tests for null
1208  if (globalState_.outerJoinedTupleIds.containsKey(tid)
1209  && isTrueWithNullSlots(e)) {
1210  return false;
1211  }
1212  // If this single tid conjunct is from the On-clause of an anti-join, check if we
1213  // can assign it to this node.
1214  if (isAntiJoinedConjunct(e)) return canEvalAntiJoinedConjunct(e, tupleIds);
1215  }
1216  // Single tid predicate that is not from an OJ On-clause and is outer-joined by a
1217  // full outer join cannot be assigned below that full outer join in the
1218  // operator tree.
1219  return canEvalFullOuterJoinedConjunct(e, tupleIds);
1220  }
1221  if (isAntiJoinedConjunct(e)) return canEvalAntiJoinedConjunct(e, tupleIds);
1222 
1223  for (TupleId tid: tids) {
1224  LOG.trace("canEval: checking tid " + tid.toString());
1225  TableRef rhsRef = getLastOjClause(tid);
1226  // this is not outer-joined; ignore
1227  if (rhsRef == null) continue;
1228  // check whether the last join to outer-join 'tid' is materialized by tupleIds
1229  boolean contains = tupleIds.containsAll(rhsRef.getAllTupleIds());
1230  LOG.trace("canEval: contains=" + (contains ? "true " : "false ")
1231  + Id.printIds(tupleIds) + " " + Id.printIds(rhsRef.getAllTupleIds()));
1232  if (!tupleIds.containsAll(rhsRef.getAllTupleIds())) return false;
1233  }
1234  return true;
1235  }
1236 
1237  private boolean canEvalPredicate(PlanNode node, Expr e) {
1238  return canEvalPredicate(node.getTblRefIds(), e);
1239  }
1240 
1245  public boolean canEvalAntiJoinedConjunct(Expr e, List<TupleId> nodeTupleIds) {
1246  TableRef antiJoinRef = getAntiJoinRef(e);
1247  if (antiJoinRef == null) return true;
1248  List<TupleId> tids = Lists.newArrayList();
1249  e.getIds(tids, null);
1250  if (tids.size() > 1) {
1251  return nodeTupleIds.containsAll(antiJoinRef.getAllTupleIds())
1252  && antiJoinRef.getAllTupleIds().containsAll(nodeTupleIds);
1253  }
1254  // A single tid conjunct that is anti-joined can be safely assigned to a
1255  // node below the anti join that specified it.
1256  return globalState_.semiJoinedTupleIds.containsKey(tids.get(0));
1257  }
1258 
1274  public ArrayList<Expr> getBoundPredicates(TupleId destTid, Set<SlotId> ignoreSlots,
1275  boolean markAssigned) {
1276  ArrayList<Expr> result = Lists.newArrayList();
1277  for (ExprId srcConjunctId: globalState_.singleTidConjuncts) {
1278  Expr srcConjunct = globalState_.conjuncts.get(srcConjunctId);
1279  if (srcConjunct instanceof SlotRef) continue;
1280  Preconditions.checkNotNull(srcConjunct);
1281  List<TupleId> srcTids = Lists.newArrayList();
1282  List<SlotId> srcSids = Lists.newArrayList();
1283  srcConjunct.getIds(srcTids, srcSids);
1284  Preconditions.checkState(srcTids.size() == 1);
1285 
1286  // Generate slot-mappings to bind srcConjunct to destTid.
1287  TupleId srcTid = srcTids.get(0);
1288  List<List<SlotId>> allDestSids =
1289  getEquivDestSlotIds(srcTid, srcSids, destTid, ignoreSlots);
1290  if (allDestSids.isEmpty()) continue;
1291 
1292  // Indicates whether the source slots have equivalent slots that belong
1293  // to an outer-joined tuple.
1294  boolean hasOuterJoinedTuple = false;
1295  for (SlotId srcSid: srcSids) {
1297  hasOuterJoinedTuple = true;
1298  break;
1299  }
1300  }
1301 
1302  // It is incorrect to propagate a Where-clause predicate into a plan subtree that
1303  // is on the nullable side of an outer join if the predicate evaluates to true
1304  // when all its referenced tuples are NULL. The check below is conservative
1305  // because the outer-joined tuple making 'hasOuterJoinedTuple' true could be in a
1306  // parent block of 'srcConjunct', in which case it is safe to propagate
1307  // 'srcConjunct' within child blocks of the outer-joined parent block.
1308  // TODO: Make the check precise by considering the blocks (analyzers) where the
1309  // outer-joined tuples in the dest slot's equivalence classes appear
1310  // relative to 'srcConjunct'.
1311  if (srcConjunct.isWhereClauseConjunct_ && hasOuterJoinedTuple &&
1312  isTrueWithNullSlots(srcConjunct)) {
1313  continue;
1314  }
1315 
1316  // if srcConjunct comes out of an OJ's On clause, we need to make sure it's the
1317  // same as the one that makes destTid nullable
1318  // (otherwise srcConjunct needn't be true when destTid is set)
1319  if (globalState_.ojClauseByConjunct.containsKey(srcConjunct.getId())) {
1320  if (!globalState_.outerJoinedTupleIds.containsKey(destTid)) continue;
1321  if (globalState_.ojClauseByConjunct.get(srcConjunct.getId())
1322  != globalState_.outerJoinedTupleIds.get(destTid)) {
1323  continue;
1324  }
1325  // Do not propagate conjuncts from the on-clause of full-outer or anti-joins.
1326  TableRef tblRef = globalState_.ojClauseByConjunct.get(srcConjunct.getId());
1327  if (tblRef.getJoinOp().isFullOuterJoin()) continue;
1328  }
1329 
1330  // Conjuncts specified in the ON-clause of an anti-join must be evaluated at that
1331  // join node.
1332  if (isAntiJoinedConjunct(srcConjunct)) continue;
1333 
1334  // Generate predicates for all src-to-dest slot mappings.
1335  for (List<SlotId> destSids: allDestSids) {
1336  Preconditions.checkState(destSids.size() == srcSids.size());
1337  Expr p;
1338  if (srcSids.containsAll(destSids)) {
1339  p = srcConjunct;
1340  } else {
1342  for (int i = 0; i < srcSids.size(); ++i) {
1343  smap.put(
1344  new SlotRef(globalState_.descTbl.getSlotDesc(srcSids.get(i))),
1345  new SlotRef(globalState_.descTbl.getSlotDesc(destSids.get(i))));
1346  }
1347  try {
1348  p = srcConjunct.trySubstitute(smap, this, false);
1349  } catch (ImpalaException exc) {
1350  // not an executable predicate; ignore
1351  continue;
1352  }
1353  LOG.trace("new pred: " + p.toSql() + " " + p.debugString());
1354  }
1355 
1356  if (markAssigned) {
1357  // predicate assignment doesn't hold if:
1358  // - the application against slotId doesn't transfer the value back to its
1359  // originating slot
1360  // - the original predicate is on an OJ'd table but doesn't originate from
1361  // that table's OJ clause's ON clause (if it comes from anywhere but that
1362  // ON clause, it needs to be evaluated directly by the join node that
1363  // materializes the OJ'd table)
1364  boolean reverseValueTransfer = true;
1365  for (int i = 0; i < srcSids.size(); ++i) {
1366  if (!hasValueTransfer(destSids.get(i), srcSids.get(i))) {
1367  reverseValueTransfer = false;
1368  break;
1369  }
1370  }
1371 
1372  boolean evalByJoin = evalByJoin(srcConjunct) &&
1373  (globalState_.ojClauseByConjunct.get(srcConjunct.getId())
1374  != globalState_.outerJoinedTupleIds.get(srcTid));
1375 
1376  // mark all bound predicates including duplicate ones
1377  if (reverseValueTransfer && !evalByJoin) markConjunctAssigned(srcConjunct);
1378  }
1379 
1380  // check if we already created this predicate
1381  if (!result.contains(p)) result.add(p);
1382  }
1383  }
1384  return result;
1385  }
1386 
1387  public ArrayList<Expr> getBoundPredicates(TupleId destTid) {
1388  return getBoundPredicates(destTid, new HashSet<SlotId>(), true);
1389  }
1390 
1400  public void invertOuterJoinState(TableRef oldRhsTbl, TableRef newRhsTbl) {
1401  Preconditions.checkState(oldRhsTbl.getJoinOp().isOuterJoin());
1402  // Invert analysis state for an outer join.
1403  List<ExprId> conjunctIds = globalState_.conjunctsByOjClause.remove(oldRhsTbl);
1404  Preconditions.checkNotNull(conjunctIds);
1405  globalState_.conjunctsByOjClause.put(newRhsTbl, conjunctIds);
1406  for (ExprId eid: conjunctIds) {
1407  globalState_.ojClauseByConjunct.put(eid, newRhsTbl);
1408  }
1409  for (Map.Entry<TupleId, TableRef> e: globalState_.outerJoinedTupleIds.entrySet()) {
1410  if (e.getValue() == oldRhsTbl) e.setValue(newRhsTbl);
1411  }
1412  }
1413 
1430  public <T extends Expr> void createEquivConjuncts(List<TupleId> lhsTids, TupleId rhsTid,
1431  List<T> conjuncts) {
1432  Preconditions.checkState(!lhsTids.contains(rhsTid));
1433 
1434  // Equivalence classes only containing slots belonging to lhsTids.
1435  Map<EquivalenceClassId, List<SlotId>> planEquivClasses =
1436  getEquivClasses(lhsTids);
1437 
1438  // Equivalence classes only containing slots belonging to rhsTid.
1439  Map<EquivalenceClassId, List<SlotId>> tidEquivClasses =
1440  getEquivClasses(Lists.newArrayList(rhsTid));
1441 
1442  // Maps from a slot id to its set of equivalent slots. Used to track equivalences
1443  // that have been established by predicates assigned/generated to plan nodes
1444  // materializing lhsTids as well as the given conjuncts.
1445  DisjointSet<SlotId> partialEquivSlots = new DisjointSet<SlotId>();
1446  // Add the partial equivalences to the partialEquivSlots map. The equivalent-slot
1447  // sets of slots from lhsTids are disjoint from those of slots from rhsTid.
1448  // We need to 'connect' the disjoint slot sets by constructing a new predicate
1449  // for each equivalence class (unless there is already one in 'conjuncts').
1450  for (List<SlotId> partialEquivClass: planEquivClasses.values()) {
1451  partialEquivSlots.bulkUnion(partialEquivClass);
1452  }
1453  for (List<SlotId> partialEquivClass: tidEquivClasses.values()) {
1454  partialEquivSlots.bulkUnion(partialEquivClass);
1455  }
1456 
1457  // Set of outer-joined slots referenced by conjuncts.
1458  Set<SlotId> outerJoinedSlots = Sets.newHashSet();
1459 
1460  // Update partialEquivSlots based on equality predicates in 'conjuncts'. Removes
1461  // redundant conjuncts, unless they reference outer-joined slots (see below).
1462  Iterator<T> conjunctIter = conjuncts.iterator();
1463  while (conjunctIter.hasNext()) {
1464  Expr conjunct = conjunctIter.next();
1465  Pair<SlotId, SlotId> eqSlots = BinaryPredicate.getEqSlots(conjunct);
1466  if (eqSlots == null) continue;
1467  EquivalenceClassId firstEqClassId = getEquivClassId(eqSlots.first);
1468  EquivalenceClassId secondEqClassId = getEquivClassId(eqSlots.second);
1469  // slots may not be in the same eq class due to outer joins
1470  if (!firstEqClassId.equals(secondEqClassId)) continue;
1471 
1472  // Retain an otherwise redundant predicate if it references a slot of an
1473  // outer-joined tuple that is not already referenced by another join predicate
1474  // to maintain that the rows must satisfy outer-joined-slot IS NOT NULL
1475  // (otherwise NULL tuples from outer joins could survive).
1476  // TODO: Consider better fixes for outer-joined slots: (1) Create IS NOT NULL
1477  // predicates and place them at the lowest possible plan node. (2) Convert outer
1478  // joins into inner joins (or full outer joins into left/right outer joins).
1479  boolean filtersOuterJoinNulls = false;
1480  if (isOuterJoined(eqSlots.first)
1481  && lhsTids.contains(getTupleId(eqSlots.first))
1482  && !outerJoinedSlots.contains(eqSlots.first)) {
1483  outerJoinedSlots.add(eqSlots.first);
1484  filtersOuterJoinNulls = true;
1485  }
1486  if (isOuterJoined(eqSlots.second)
1487  && lhsTids.contains(getTupleId(eqSlots.second))
1488  && !outerJoinedSlots.contains(eqSlots.second)) {
1489  outerJoinedSlots.add(eqSlots.second);
1490  filtersOuterJoinNulls = true;
1491  }
1492  // retain conjunct if it connects two formerly unconnected equiv classes or
1493  // it is required for outer-join semantics
1494  if (!partialEquivSlots.union(eqSlots.first, eqSlots.second)
1495  && !filtersOuterJoinNulls) {
1496  conjunctIter.remove();
1497  }
1498  }
1499 
1500  // For each equivalence class, construct a new predicate to 'connect' the disjoint
1501  // slot sets.
1502  for (Map.Entry<EquivalenceClassId, List<SlotId>> tidEquivClass:
1503  tidEquivClasses.entrySet()) {
1504  List<SlotId> lhsSlots = planEquivClasses.get(tidEquivClass.getKey());
1505  if (lhsSlots == null) continue;
1506  List<SlotId> rhsSlots = tidEquivClass.getValue();
1507  Preconditions.checkState(!lhsSlots.isEmpty() && !rhsSlots.isEmpty());
1508 
1509  if (!partialEquivSlots.union(lhsSlots.get(0), rhsSlots.get(0))) continue;
1510  // Do not create a new predicate from slots that are full outer joined because that
1511  // predicate may be incorrectly assigned to a node below the associated full outer
1512  // join.
1513  if (isFullOuterJoined(lhsSlots.get(0)) || isFullOuterJoined(rhsSlots.get(0))) {
1514  continue;
1515  }
1516  T newEqPred = (T) createEqPredicate(lhsSlots.get(0), rhsSlots.get(0));
1517  newEqPred.analyzeNoThrow(this);
1518  if (!hasMutualValueTransfer(lhsSlots.get(0), rhsSlots.get(0))) continue;
1519  conjuncts.add(newEqPred);
1520  }
1521  }
1522 
1535  public <T extends Expr> void createEquivConjuncts(TupleId tid, List<T> conjuncts,
1536  Set<SlotId> ignoreSlots) {
1537  // Maps from a slot id to its set of equivalent slots. Used to track equivalences
1538  // that have been established by 'conjuncts' and the 'ignoredsSlots'.
1539  DisjointSet<SlotId> partialEquivSlots = new DisjointSet<SlotId>();
1540 
1541  // Treat ignored slots as already connected. Add the ignored slots at this point
1542  // such that redundant conjuncts are removed.
1543  partialEquivSlots.bulkUnion(ignoreSlots);
1544  partialEquivSlots.checkConsistency();
1545 
1546  // Update partialEquivSlots based on equality predicates in 'conjuncts'. Removes
1547  // redundant conjuncts, unless they reference outer-joined slots (see below).
1548  Iterator<T> conjunctIter = conjuncts.iterator();
1549  while (conjunctIter.hasNext()) {
1550  Expr conjunct = conjunctIter.next();
1551  Pair<SlotId, SlotId> eqSlots = BinaryPredicate.getEqSlots(conjunct);
1552  if (eqSlots == null) continue;
1553  EquivalenceClassId firstEqClassId = getEquivClassId(eqSlots.first);
1554  EquivalenceClassId secondEqClassId = getEquivClassId(eqSlots.second);
1555  // slots may not be in the same eq class due to outer joins
1556  if (!firstEqClassId.equals(secondEqClassId)) continue;
1557  // update equivalences and remove redundant conjuncts
1558  if (!partialEquivSlots.union(eqSlots.first, eqSlots.second)) conjunctIter.remove();
1559  }
1560  // Suppose conjuncts had these predicates belonging to equivalence classes e1 and e2:
1561  // e1: s1 = s2, s3 = s4, s3 = s5
1562  // e2: s10 = s11
1563  // The conjunctsEquivSlots should contain the following entries at this point:
1564  // s1 -> {s1, s2}
1565  // s2 -> {s1, s2}
1566  // s3 -> {s3, s4, s5}
1567  // s4 -> {s3, s4, s5}
1568  // s5 -> {s3, s4, s5}
1569  // s10 -> {s10, s11}
1570  // s11 -> {s10, s11}
1571  // Assuming e1 = {s1, s2, s3, s4, s5} we need to generate one additional equality
1572  // predicate to "connect" {s1, s2} and {s3, s4, s5}.
1573 
1574  // These are the equivalences that need to be established by constructing conjuncts
1575  // to form a minimum spanning tree.
1576  Map<EquivalenceClassId, List<SlotId>> targetEquivClasses =
1577  getEquivClasses(Lists.newArrayList(tid));
1578  for (Map.Entry<EquivalenceClassId, List<SlotId>> targetEquivClass:
1579  targetEquivClasses.entrySet()) {
1580  // Loop over all pairs of equivalent slots and merge their disjoint slots sets,
1581  // creating missing equality predicates as necessary.
1582  List<SlotId> slotIds = targetEquivClass.getValue();
1583  boolean done = false;
1584  for (int i = 1; i < slotIds.size(); ++i) {
1585  SlotId rhs = slotIds.get(i);
1586  for (int j = 0; j < i; ++j) {
1587  SlotId lhs = slotIds.get(j);
1588  if (!partialEquivSlots.union(lhs, rhs)) continue;
1589  T newEqPred = (T) createEqPredicate(lhs, rhs);
1590  newEqPred.analyzeNoThrow(this);
1591  if (!hasMutualValueTransfer(lhs, rhs)) continue;
1592  conjuncts.add(newEqPred);
1593  // Check for early termination.
1594  if (partialEquivSlots.get(lhs).size() == slotIds.size()) {
1595  done = true;
1596  break;
1597  }
1598  }
1599  if (done) break;
1600  }
1601  }
1602  }
1603 
1604  public <T extends Expr> void createEquivConjuncts(TupleId tid, List<T> conjuncts) {
1605  createEquivConjuncts(tid, conjuncts, new HashSet<SlotId>());
1606  }
1607 
1612  private Map<EquivalenceClassId, List<SlotId>> getEquivClasses(List<TupleId> tids) {
1613  Map<EquivalenceClassId, List<SlotId>> result = Maps.newHashMap();
1614  for (TupleId tid: tids) {
1615  for (SlotDescriptor slotDesc: getTupleDesc(tid).getSlots()) {
1616  EquivalenceClassId eqClassId = getEquivClassId(slotDesc.getId());
1617  // Ignore equivalence classes that are empty or only have a single member.
1618  if (globalState_.equivClassMembers.get(eqClassId).size() <= 1) continue;
1619  List<SlotId> slotIds = result.get(eqClassId);
1620  if (slotIds == null) {
1621  slotIds = Lists.newArrayList();
1622  result.put(eqClassId, slotIds);
1623  }
1624  slotIds.add(slotDesc.getId());
1625  }
1626  }
1627  return result;
1628  }
1629 
1637  private List<List<SlotId>> getEquivDestSlotIds(TupleId srcTid, List<SlotId> srcSids,
1638  TupleId destTid, Set<SlotId> ignoreSlots) {
1639  List<List<SlotId>> allDestSids = Lists.newArrayList();
1640  TupleDescriptor destTupleDesc = getTupleDesc(destTid);
1641  if (srcSids.size() == 1) {
1642  // Generate all mappings to propagate predicates of the form <slot> <op> <constant>
1643  // to as many destination slots as possible.
1644  // TODO: If srcTid == destTid we could limit the mapping to partition
1645  // columns because mappings to non-partition columns do not provide
1646  // a performance benefit.
1647  SlotId srcSid = srcSids.get(0);
1648  for (SlotDescriptor destSlot: destTupleDesc.getSlots()) {
1649  if (ignoreSlots.contains(destSlot.getId())) continue;
1650  if (hasValueTransfer(srcSid, destSlot.getId())) {
1651  allDestSids.add(Lists.newArrayList(destSlot.getId()));
1652  }
1653  }
1654  } else if (srcTid.equals(destTid)) {
1655  // Multiple source slot ids and srcTid == destTid. Inter-tuple transfers are
1656  // already expressed by the original conjuncts. Any mapping would be redundant.
1657  // Still add srcSids to the result because we rely on getBoundPredicates() to
1658  // include predicates that can safely be evaluated below an outer join, but must
1659  // also be evaluated by the join itself (evalByJoin() == true).
1660  allDestSids.add(srcSids);
1661  } else {
1662  // Multiple source slot ids and srcTid != destTid. Pick the first mapping
1663  // where each srcSid is mapped to a different destSid to avoid generating
1664  // redundant and/or trivial predicates.
1665  // TODO: This approach is not guaranteed to find the best slot mapping
1666  // (e.g., against partition columns) or all non-redundant mappings.
1667  // The limitations are show in predicate-propagation.test.
1668  List<SlotId> destSids = Lists.newArrayList();
1669  for (SlotId srcSid: srcSids) {
1670  for (SlotDescriptor destSlot: destTupleDesc.getSlots()) {
1671  if (ignoreSlots.contains(destSlot.getId())) continue;
1672  if (hasValueTransfer(srcSid, destSlot.getId())
1673  && !destSids.contains(destSlot.getId())) {
1674  destSids.add(destSlot.getId());
1675  break;
1676  }
1677  }
1678  }
1679  if (destSids.size() == srcSids.size()) allDestSids.add(destSids);
1680  }
1681  return allDestSids;
1682  }
1683 
1688  private boolean hasOuterJoinedTuple(EquivalenceClassId eqClassId) {
1689  ArrayList<SlotId> eqClass = globalState_.equivClassMembers.get(eqClassId);
1690  for (SlotId s: eqClass) {
1691  if (isOuterJoined(getTupleId(s))) return true;
1692  }
1693  return false;
1694  }
1695 
1701  private boolean isTrueWithNullSlots(Expr p) {
1702  // Construct predicate with all SlotRefs substituted by NullLiterals.
1703  List<SlotRef> slotRefs = Lists.newArrayList();
1704  p.collect(Predicates.instanceOf(SlotRef.class), slotRefs);
1705 
1706  Expr nullTuplePred = null;
1707  // Map for substituting SlotRefs with NullLiterals.
1708  ExprSubstitutionMap nullSmap = new ExprSubstitutionMap();
1709  NullLiteral nullLiteral = new NullLiteral();
1710  nullLiteral.analyzeNoThrow(this);
1711  for (SlotRef slotRef: slotRefs) {
1712  nullSmap.put(slotRef.clone(), nullLiteral.clone());
1713  }
1714  nullTuplePred = p.substitute(nullSmap, this, false);
1715  try {
1716  return FeSupport.EvalPredicate(nullTuplePred, getQueryCtx());
1717  } catch (InternalException e) {
1718  Preconditions.checkState(false, "Failed to evaluate generated predicate: "
1719  + nullTuplePred.toSql() + "." + e.getMessage());
1720  }
1721  return true;
1722  }
1723 
1724  public TupleId getTupleId(SlotId slotId) {
1725  return globalState_.descTbl.getSlotDesc(slotId).getParent().getId();
1726  }
1727 
1728  public void registerValueTransfer(SlotId id1, SlotId id2) {
1729  globalState_.registeredValueTransfers.add(new Pair(id1, id2));
1730  }
1731 
1732  public boolean isOuterJoined(TupleId tid) {
1733  return globalState_.outerJoinedTupleIds.containsKey(tid);
1734  }
1735 
1736  public boolean isOuterJoined(SlotId sid) {
1737  return isOuterJoined(getTupleId(sid));
1738  }
1739 
1740  public boolean isSemiJoined(TupleId tid) {
1741  return globalState_.semiJoinedTupleIds.containsKey(tid);
1742  }
1743 
1744  public boolean isAntiJoinedConjunct(Expr e) {
1745  return getAntiJoinRef(e) != null;
1746  }
1747 
1749  TableRef tblRef = globalState_.sjClauseByConjunct.get(e.getId());
1750  if (tblRef == null) return null;
1751  return (tblRef.getJoinOp().isAntiJoin()) ? tblRef : null;
1752  }
1753 
1754  public boolean isFullOuterJoined(TupleId tid) {
1755  return globalState_.fullOuterJoinedTupleIds.containsKey(tid);
1756  }
1757 
1758  public boolean isFullOuterJoined(SlotId sid) {
1759  return isFullOuterJoined(getTupleId(sid));
1760  }
1761 
1762  public boolean isVisible(TupleId tid) {
1763  return tid == visibleSemiJoinedTupleId_ || !isSemiJoined(tid);
1764  }
1765 
1766  public boolean containsOuterJoinedTid(List<TupleId> tids) {
1767  for (TupleId tid: tids) {
1768  if (isOuterJoined(tid)) return true;
1769  }
1770  return false;
1771  }
1772 
1777  public void computeEquivClasses() {
1778  globalState_.valueTransferGraph = new ValueTransferGraph();
1779  globalState_.valueTransferGraph.computeValueTransfers();
1780 
1781  // we start out by assigning each slot to its own equiv class
1782  int numSlots = globalState_.descTbl.getMaxSlotId().asInt() + 1;
1783  for (int i = 0; i < numSlots; ++i) {
1784  EquivalenceClassId id = globalState_.equivClassIdGenerator.getNextId();
1785  globalState_.equivClassMembers.put(id, Lists.newArrayList(new SlotId(i)));
1786  }
1787 
1788  // merge two classes if there is a value transfer between all members of the
1789  // combined class; do this until there's nothing left to merge
1790  boolean merged;
1791  do {
1792  merged = false;
1793  for (Map.Entry<EquivalenceClassId, ArrayList<SlotId>> e1:
1794  globalState_.equivClassMembers.entrySet()) {
1795  for (Map.Entry<EquivalenceClassId, ArrayList<SlotId>> e2:
1796  globalState_.equivClassMembers.entrySet()) {
1797  if (e1.getKey() == e2.getKey()) continue;
1798  List<SlotId> class1Members = e1.getValue();
1799  if (class1Members.isEmpty()) continue;
1800  List<SlotId> class2Members = e2.getValue();
1801  if (class2Members.isEmpty()) continue;
1802 
1803  // check whether we can transfer values between all members
1804  boolean canMerge = true;
1805  for (SlotId class1Slot: class1Members) {
1806  for (SlotId class2Slot: class2Members) {
1807  if (!hasValueTransfer(class1Slot, class2Slot)
1808  && !hasValueTransfer(class2Slot, class1Slot)) {
1809  canMerge = false;
1810  break;
1811  }
1812  }
1813  if (!canMerge) break;
1814  }
1815  if (!canMerge) continue;
1816 
1817  // merge classes 1 and 2 by transfering 2 into 1
1818  class1Members.addAll(class2Members);
1819  class2Members.clear();
1820  merged = true;
1821  }
1822  }
1823  } while (merged);
1824 
1825  // populate equivClassSmap
1827  List<SlotId> members = globalState_.equivClassMembers.get(id);
1828  if (members.isEmpty()) continue;
1829  SlotDescriptor canonicalSlotDesc =
1830  globalState_.descTbl.getSlotDesc(members.get(0));
1831  for (SlotId slotId: globalState_.equivClassMembers.get(id)) {
1832  SlotDescriptor slotDesc = globalState_.descTbl.getSlotDesc(slotId);
1833  globalState_.equivClassSmap.put(
1834  new SlotRef(slotDesc), new SlotRef(canonicalSlotDesc));
1835  }
1836  }
1837 
1838  // populate equivClassBySlotId
1840  for (SlotId slotId: globalState_.equivClassMembers.get(id)) {
1841  globalState_.equivClassBySlotId.put(slotId, id);
1842  }
1843  }
1844  }
1845 
1850  public void getEquivSlots(SlotId slotId, List<TupleId> tupleIds,
1851  List<SlotId> equivSlotIds) {
1852  equivSlotIds.clear();
1853  LOG.trace("getequivslots: slotid=" + Integer.toString(slotId.asInt()));
1854  EquivalenceClassId classId = globalState_.equivClassBySlotId.get(slotId);
1855  for (SlotId memberId: globalState_.equivClassMembers.get(classId)) {
1856  if (tupleIds.contains(
1857  globalState_.descTbl.getSlotDesc(memberId).getParent().getId())) {
1858  equivSlotIds.add(memberId);
1859  }
1860  }
1861  }
1862 
1864  return globalState_.equivClassBySlotId.get(slotId);
1865  }
1866 
1867  public ExprSubstitutionMap getEquivClassSmap() { return globalState_.equivClassSmap; }
1868 
1874  public boolean equivSets(List<Expr> l1, List<Expr> l2) {
1875  List<Expr> substL1 =
1876  Expr.substituteList(l1, globalState_.equivClassSmap, this, false);
1877  Expr.removeDuplicates(substL1);
1878  List<Expr> substL2 =
1879  Expr.substituteList(l2, globalState_.equivClassSmap, this, false);
1880  Expr.removeDuplicates(substL2);
1881  return Expr.equalSets(substL1, substL2);
1882  }
1883 
1887  public boolean equivExprs(Expr e1, Expr e2) {
1888  Expr substE1 = e1.substitute(globalState_.equivClassSmap, this, false);
1889  Expr substE2 = e2.substitute(globalState_.equivClassSmap, this, false);
1890  return substE1.equals(substE2);
1891  }
1892 
1899  public List<Expr> removeRedundantExprs(List<Expr> exprs) {
1900  List<Expr> result = Lists.newArrayList();
1901  List<Expr> normalizedExprs =
1902  Expr.substituteList(exprs, globalState_.equivClassSmap, this, false);
1903  Preconditions.checkState(exprs.size() == normalizedExprs.size());
1904  List<Expr> uniqueExprs = Lists.newArrayList();
1905  for (int i = 0; i < normalizedExprs.size(); ++i) {
1906  if (!uniqueExprs.contains(normalizedExprs.get(i))) {
1907  uniqueExprs.add(normalizedExprs.get(i));
1908  result.add(exprs.get(i).clone());
1909  }
1910  }
1911  return result;
1912  }
1913 
1917  public void markConjunctsAssigned(List<Expr> conjuncts) {
1918  if (conjuncts == null) return;
1919  for (Expr p: conjuncts) {
1920  globalState_.assignedConjuncts.add(p.getId());
1921  LOG.trace("markAssigned " + p.toSql() + " " + p.debugString());
1922  }
1923  }
1924 
1928  public void markConjunctAssigned(Expr conjunct) {
1929  LOG.trace("markAssigned " + conjunct.toSql() + " " + conjunct.debugString());
1930  globalState_.assignedConjuncts.add(conjunct.getId());
1931  }
1932 
1933  public boolean isConjunctAssigned(Expr conjunct) {
1934  return globalState_.assignedConjuncts.contains(conjunct.getId());
1935  }
1936 
1937  public Set<ExprId> getAssignedConjuncts() {
1938  return Sets.newHashSet(globalState_.assignedConjuncts);
1939  }
1940 
1941  public void setAssignedConjuncts(Set<ExprId> assigned) {
1942  globalState_.assignedConjuncts = Sets.newHashSet(assigned);
1943  }
1944 
1948  public boolean hasUnassignedConjuncts() {
1949  for (ExprId id: globalState_.conjuncts.keySet()) {
1950  if (globalState_.assignedConjuncts.contains(id)) continue;
1951  Expr e = globalState_.conjuncts.get(id);
1952  if (e.isAuxExpr()) continue;
1953  LOG.trace("unassigned: " + e.toSql() + " " + e.debugString());
1954  return true;
1955  }
1956  return false;
1957  }
1958 
1962  public void materializeSlots(List<Expr> exprs) {
1963  List<SlotId> slotIds = Lists.newArrayList();
1964  for (Expr e: exprs) {
1965  Preconditions.checkState(e.isAnalyzed_);
1966  e.getIds(null, slotIds);
1967  }
1968  globalState_.descTbl.markSlotsMaterialized(slotIds);
1969  }
1970 
1971  public void materializeSlots(Expr e) {
1972  List<SlotId> slotIds = Lists.newArrayList();
1973  Preconditions.checkState(e.isAnalyzed_);
1974  e.getIds(null, slotIds);
1975  globalState_.descTbl.markSlotsMaterialized(slotIds);
1976  }
1977 
1978 
1989  public Type getCompatibleType(Type lastCompatibleType,
1990  Expr lastCompatibleExpr, Expr expr)
1991  throws AnalysisException {
1992  Type newCompatibleType;
1993  if (lastCompatibleType == null) {
1994  newCompatibleType = expr.getType();
1995  } else {
1996  newCompatibleType = Type.getAssignmentCompatibleType(
1997  lastCompatibleType, expr.getType());
1998  }
1999  if (!newCompatibleType.isValid()) {
2000  throw new AnalysisException(String.format(
2001  "Incompatible return types '%s' and '%s' of exprs '%s' and '%s'.",
2002  lastCompatibleType.toSql(), expr.getType().toSql(),
2003  lastCompatibleExpr.toSql(), expr.toSql()));
2004  }
2005  return newCompatibleType;
2006  }
2007 
2014  public Type castAllToCompatibleType(List<Expr> exprs) throws AnalysisException {
2015  // Determine compatible type of exprs.
2016  Expr lastCompatibleExpr = exprs.get(0);
2017  Type compatibleType = null;
2018  for (int i = 0; i < exprs.size(); ++i) {
2019  exprs.get(i).analyze(this);
2020  compatibleType = getCompatibleType(compatibleType, lastCompatibleExpr,
2021  exprs.get(i));
2022  }
2023  // Add implicit casts if necessary.
2024  for (int i = 0; i < exprs.size(); ++i) {
2025  if (!exprs.get(i).getType().equals(compatibleType)) {
2026  Expr castExpr = exprs.get(i).castTo(compatibleType);
2027  exprs.set(i, castExpr);
2028  }
2029  }
2030  return compatibleType;
2031  }
2032 
2038  public void castToUnionCompatibleTypes(List<List<Expr>> exprLists)
2039  throws AnalysisException {
2040  if (exprLists == null || exprLists.size() < 2) return;
2041 
2042  // Determine compatible types for exprs, position by position.
2043  List<Expr> firstList = exprLists.get(0);
2044  for (int i = 0; i < firstList.size(); ++i) {
2045  // Type compatible with the i-th exprs of all expr lists.
2046  // Initialize with type of i-th expr in first list.
2047  Type compatibleType = firstList.get(i).getType();
2048  // Remember last compatible expr for error reporting.
2049  Expr lastCompatibleExpr = firstList.get(i);
2050  for (int j = 1; j < exprLists.size(); ++j) {
2051  Preconditions.checkState(exprLists.get(j).size() == firstList.size());
2052  compatibleType = getCompatibleType(compatibleType,
2053  lastCompatibleExpr, exprLists.get(j).get(i));
2054  lastCompatibleExpr = exprLists.get(j).get(i);
2055  }
2056  // Now that we've found a compatible type, add implicit casts if necessary.
2057  for (int j = 0; j < exprLists.size(); ++j) {
2058  if (!exprLists.get(j).get(i).getType().equals(compatibleType)) {
2059  Expr castExpr = exprLists.get(j).get(i).castTo(compatibleType);
2060  exprLists.get(j).set(i, castExpr);
2061  }
2062  }
2063  }
2064  }
2065 
2066  public String getDefaultDb() { return globalState_.queryCtx.session.database; }
2067  public User getUser() { return user_; }
2068  public TQueryCtx getQueryCtx() { return globalState_.queryCtx; }
2069  public AuthorizationConfig getAuthzConfig() { return globalState_.authzConfig; }
2070  public ListMap<TNetworkAddress> getHostIndex() { return globalState_.hostIndex; }
2071  public ColumnLineageGraph getColumnLineageGraph() { return globalState_.lineageGraph; }
2072  public String getSerializedLineageGraph() {
2073  Preconditions.checkNotNull(globalState_.lineageGraph);
2074  return globalState_.lineageGraph.toJson();
2075  }
2076 
2077  public ImmutableList<PrivilegeRequest> getPrivilegeReqs() {
2078  return ImmutableList.copyOf(globalState_.privilegeReqs);
2079  }
2080 
2086  public Set<TAccessEvent> getAccessEvents() { return globalState_.accessEvents; }
2087  public void addAccessEvent(TAccessEvent event) {
2088  globalState_.accessEvents.add(event);
2089  }
2090 
2098  public Table getTable(String dbName, String tableName)
2100  Table table = null;
2101  try {
2102  table = getCatalog().getTable(dbName, tableName);
2103  } catch (DatabaseNotFoundException e) {
2104  throw new AnalysisException(DB_DOES_NOT_EXIST_ERROR_MSG + dbName);
2105  } catch (CatalogException e) {
2106  String errMsg = String.format("Failed to load metadata for table: %s", tableName);
2107  // We don't want to log all AnalysisExceptions as ERROR, only failures due to
2108  // TableLoadingExceptions.
2109  LOG.error(String.format("%s\n%s", errMsg, e.getMessage()));
2110  if (e instanceof TableLoadingException) throw (TableLoadingException) e;
2111  throw new TableLoadingException(errMsg, e);
2112  }
2113  if (table == null) {
2114  throw new AnalysisException(
2115  TBL_DOES_NOT_EXIST_ERROR_MSG + dbName + "." + tableName);
2116  }
2117  if (!table.isLoaded()) {
2118  missingTbls_.add(new TableName(table.getDb().getName(), table.getName()));
2119  throw new AnalysisException(
2120  "Table/view is missing metadata: " + table.getFullName());
2121  }
2122  return table;
2123  }
2124 
2134  public Table getTable(TableName tableName, Privilege privilege, boolean addAccessEvent)
2135  throws AnalysisException {
2136  Preconditions.checkNotNull(tableName);
2137  Preconditions.checkNotNull(privilege);
2138  Table table = null;
2139  tableName = new TableName(getTargetDbName(tableName), tableName.getTbl());
2140 
2142  .onTable(tableName.getDb(), tableName.getTbl()).allOf(privilege).toRequest());
2143 
2144  // This may trigger a metadata load, in which case we want to return the errors as
2145  // AnalysisExceptions.
2146  try {
2147  table = getTable(tableName.getDb(), tableName.getTbl());
2148  } catch (TableLoadingException e) {
2149  throw new AnalysisException(e.getMessage(), e);
2150  }
2151  Preconditions.checkNotNull(table);
2152  if (addAccessEvent) {
2153  // Add an audit event for this access
2154  TCatalogObjectType objectType = TCatalogObjectType.TABLE;
2155  if (table instanceof View) objectType = TCatalogObjectType.VIEW;
2156  globalState_.accessEvents.add(new TAccessEvent(
2157  tableName.toString(), objectType, privilege.toString()));
2158  }
2159  return table;
2160  }
2161 
2170  public Table getTable(TableName tableName, Privilege privilege)
2171  throws AnalysisException {
2172  return getTable(tableName, privilege, true);
2173  }
2174 
2185  public Db getDb(String dbName, Privilege privilege) throws AnalysisException {
2186  return getDb(dbName, privilege, true);
2187  }
2188 
2189  public Db getDb(String dbName, Privilege privilege, boolean throwIfDoesNotExist)
2190  throws AnalysisException {
2192  if (privilege == Privilege.ANY) {
2193  registerPrivReq(pb.any().onAnyTable(dbName).toRequest());
2194  } else {
2195  registerPrivReq(pb.allOf(privilege).onDb(dbName).toRequest());
2196  }
2197 
2198  Db db = getCatalog().getDb(dbName);
2199  if (db == null && throwIfDoesNotExist) {
2200  throw new AnalysisException(DB_DOES_NOT_EXIST_ERROR_MSG + dbName);
2201  }
2202  globalState_.accessEvents.add(new TAccessEvent(
2203  dbName, TCatalogObjectType.DATABASE, privilege.toString()));
2204  return db;
2205  }
2206 
2215  public boolean dbContainsTable(String dbName, String tableName, Privilege privilege)
2216  throws AnalysisException {
2217  registerPrivReq(new PrivilegeRequestBuilder().allOf(privilege)
2218  .onTable(dbName, tableName).toRequest());
2219  try {
2220  Db db = getCatalog().getDb(dbName);
2221  if (db == null) {
2222  throw new DatabaseNotFoundException("Database not found: " + dbName);
2223  }
2224  return db.containsTable(tableName);
2225  } catch (DatabaseNotFoundException e) {
2226  throw new AnalysisException(DB_DOES_NOT_EXIST_ERROR_MSG + dbName);
2227  }
2228  }
2229 
2234  public String getTargetDbName(TableName tableName) {
2235  return tableName.isFullyQualified() ? tableName.getDb() : getDefaultDb();
2236  }
2237 
2238  public String getTargetDbName(FunctionName fnName) {
2239  return fnName.isFullyQualified() ? fnName.getDb() : getDefaultDb();
2240  }
2241 
2246  public TableName getFqTableName(TableName tableName) {
2247  if (tableName.isFullyQualified()) return tableName;
2248  return new TableName(getDefaultDb(), tableName.getTbl());
2249  }
2250 
2251  public void setEnablePrivChecks(boolean value) {
2252  enablePrivChecks_ = value;
2253  }
2254  public void setAuthErrMsg(String errMsg) { authErrorMsg_ = errMsg; }
2255  public void setIsExplain() { globalState_.isExplain = true; }
2256  public boolean isExplain() { return globalState_.isExplain; }
2257  public void setUseHiveColLabels(boolean useHiveColLabels) {
2258  globalState_.useHiveColLabels = useHiveColLabels;
2259  }
2260  public boolean useHiveColLabels() { return globalState_.useHiveColLabels; }
2261 
2262  public void setHasLimitOffsetClause(boolean hasLimitOffset) {
2263  this.hasLimitOffsetClause_ = hasLimitOffset;
2264  }
2265 
2266  public List<Expr> getConjuncts() {
2267  return new ArrayList<Expr>(globalState_.conjuncts.values());
2268  }
2269  public Expr getConjunct(ExprId exprId) {
2270  return globalState_.conjuncts.get(exprId);
2271  }
2272 
2273  public int incrementCallDepth() { return ++callDepth_; }
2274  public int decrementCallDepth() { return --callDepth_; }
2275  public int getCallDepth() { return callDepth_; }
2276 
2277  private boolean hasMutualValueTransfer(SlotId slotA, SlotId slotB) {
2278  return hasValueTransfer(slotA, slotB) && hasValueTransfer(slotB, slotA);
2279  }
2280 
2281  public boolean hasValueTransfer(SlotId a, SlotId b) {
2282  return globalState_.valueTransferGraph.hasValueTransfer(a, b);
2283  }
2284 
2285  public EventSequence getTimeline() { return globalState_.timeline; }
2286 
2291  int numSlots = globalState_.descTbl.getMaxSlotId().asInt() + 1;
2292  for (int i = 0; i < numSlots; ++i) {
2293  SlotId slotId = new SlotId(i);
2294  if (globalState_.equivClassBySlotId.get(slotId) == null) {
2295  EquivalenceClassId classId = globalState_.equivClassIdGenerator.getNextId();
2296  globalState_.equivClassMembers.put(classId, Lists.newArrayList(slotId));
2297  globalState_.equivClassBySlotId.put(slotId, classId);
2298  }
2299  }
2300  }
2301 
2302  public Map<String, View> getLocalViews() { return localViews_; }
2303 
2307  public void addWarning(String msg) {
2308  if (msg == null) return;
2309  Integer count = globalState_.warnings.get(msg);
2310  if (count == null) count = 0;
2311  globalState_.warnings.put(msg, count + 1);
2312  }
2313 
2320  private class ValueTransferGraph {
2321  // Represents all bi-directional value transfers. Each disjoint set is a complete
2322  // subgraph of value transfers. Maps from a slot id to its set of slots with mutual
2323  // value transfers (in the original slot domain). Since the value transfer graph is
2324  // a DAG these disjoint sets represent all the strongly connected components.
2325  private final DisjointSet<SlotId> completeSubGraphs_ = new DisjointSet<SlotId>();
2326 
2327  // Maps each slot id in the original slot domain to a slot id in the new slot domain
2328  // created by coalescing complete subgraphs into a single slot, and retaining only
2329  // slots that have value transfers.
2330  // Used for representing a condensed value-transfer graph with dense slot ids.
2331  private int[] coalescedSlots_;
2332 
2333  // Used for generating slot ids in the new slot domain.
2334  private int nextCoalescedSlotId_ = 0;
2335 
2336  // Condensed DAG of value transfers in the new slot domain.
2337  private boolean[][] valueTransfer_;
2338 
2355  public void computeValueTransfers() {
2356  long start = System.currentTimeMillis();
2357 
2358  // Step1: Compute complete subgraphs and get uni-directional value transfers.
2359  List<Pair<SlotId, SlotId>> origValueTransfers = Lists.newArrayList();
2360  partitionValueTransfers(completeSubGraphs_, origValueTransfers);
2361 
2362  // Coalesce complete subgraphs into a single slot and assign new slot ids.
2363  int origNumSlots = globalState_.descTbl.getMaxSlotId().asInt() + 1;
2364  coalescedSlots_ = new int[origNumSlots];
2365  Arrays.fill(coalescedSlots_, -1);
2366  for (Set<SlotId> equivClass: completeSubGraphs_.getSets()) {
2367  int representative = nextCoalescedSlotId_;
2368  for (SlotId slotId: equivClass) {
2369  coalescedSlots_[slotId.asInt()] = representative;
2370  }
2372  }
2373 
2374  // Step 2: Map uni-directional value transfers onto the new slot domain, and
2375  // store the connected components in graphPartitions.
2376  List<Pair<Integer, Integer>> coalescedValueTransfers = Lists.newArrayList();
2377  // A graph partition is a set of slot ids that are connected by uni-directional
2378  // value transfers. The graph corresponding to a graph partition is a DAG.
2379  DisjointSet<Integer> graphPartitions = new DisjointSet<Integer>();
2380  mapSlots(origValueTransfers, coalescedValueTransfers, graphPartitions);
2381  mapSlots(globalState_.registeredValueTransfers, coalescedValueTransfers,
2382  graphPartitions);
2383 
2384  // Step 3: Group the coalesced value transfers by the graph partition they
2385  // belong to. Maps from the graph partition to its list of value transfers.
2386  // TODO: Implement a specialized DisjointSet data structure to avoid this step.
2387  Map<Set<Integer>, List<Pair<Integer, Integer>>> partitionedValueTransfers =
2388  Maps.newHashMap();
2389  for (Pair<Integer, Integer> vt: coalescedValueTransfers) {
2390  Set<Integer> partition = graphPartitions.get(vt.first.intValue());
2391  List<Pair<Integer, Integer>> l = partitionedValueTransfers.get(partition);
2392  if (l == null) {
2393  l = Lists.newArrayList();
2394  partitionedValueTransfers.put(partition, l);
2395  }
2396  l.add(vt);
2397  }
2398 
2399  // Initialize the value transfer graph.
2400  int numCoalescedSlots = nextCoalescedSlotId_ + 1;
2401  valueTransfer_ = new boolean[numCoalescedSlots][numCoalescedSlots];
2402  for (int i = 0; i < numCoalescedSlots; ++i) {
2403  valueTransfer_[i][i] = true;
2404  }
2405 
2406  // Step 4: Compute the transitive closure for each graph partition.
2407  for (Map.Entry<Set<Integer>, List<Pair<Integer, Integer>>> graphPartition:
2408  partitionedValueTransfers.entrySet()) {
2409  // Set value transfers of this partition.
2410  for (Pair<Integer, Integer> vt: graphPartition.getValue()) {
2411  valueTransfer_[vt.first][vt.second] = true;
2412  }
2413  Set<Integer> partitionSlotIds = graphPartition.getKey();
2414  // No transitive value transfers.
2415  if (partitionSlotIds.size() <= 2) continue;
2416 
2417  // Indirection vector into valueTransfer_. Contains one entry for each distinct
2418  // slot id referenced in a value transfer of this partition.
2419  int[] p = new int[partitionSlotIds.size()];
2420  int numPartitionSlots = 0;
2421  for (Integer slotId: partitionSlotIds) {
2422  p[numPartitionSlots++] = slotId;
2423  }
2424  // Compute the transitive closure of this graph partition.
2425  // TODO: Since we are operating on a DAG the performance can be improved if
2426  // necessary (e.g., topological sort + backwards propagation of the transitive
2427  // closure).
2428  boolean changed = false;
2429  do {
2430  changed = false;
2431  for (int i = 0; i < numPartitionSlots; ++i) {
2432  for (int j = 0; j < numPartitionSlots; ++j) {
2433  for (int k = 0; k < numPartitionSlots; ++k) {
2434  if (valueTransfer_[p[i]][p[j]] && valueTransfer_[p[j]][p[k]]
2435  && !valueTransfer_[p[i]][p[k]]) {
2436  valueTransfer_[p[i]][p[k]] = true;
2437  changed = true;
2438  }
2439  }
2440  }
2441  }
2442  } while (changed);
2443  }
2444 
2445  long end = System.currentTimeMillis();
2446  LOG.trace("Time taken in computeValueTransfers(): " + (end - start) + "ms");
2447  }
2448 
2456  public void bulkUpdate(List<Pair<SlotId, SlotId>> mutualValueTransfers) {
2457  // Requires an existing value transfer graph.
2458  Preconditions.checkState(valueTransfer_ != null);
2459  int oldNumSlots = coalescedSlots_.length;
2460  int maxNumSlots = globalState_.descTbl.getMaxSlotId().asInt() + 1;
2461  // Expand the coalesced slots to the new maximum number of slots,
2462  // and initalize the new entries with -1.
2463  coalescedSlots_ = Arrays.copyOf(coalescedSlots_, maxNumSlots);
2464  Arrays.fill(coalescedSlots_, oldNumSlots, maxNumSlots, -1);
2465  for (Pair<SlotId, SlotId> valTrans: mutualValueTransfers) {
2466  SlotId existingSid = valTrans.first;
2467  SlotId newSid = valTrans.second;
2468  // New slot id must not already be registered in the value transfer graph.
2469  Preconditions.checkState(completeSubGraphs_.get(newSid) == null);
2470  Preconditions.checkState(coalescedSlots_[newSid.asInt()] == -1);
2471  completeSubGraphs_.union(existingSid, newSid);
2472  coalescedSlots_[newSid.asInt()] = coalescedSlots_[existingSid.asInt()];
2473  }
2474  }
2475 
2480  public boolean hasValueTransfer(SlotId slotA, SlotId slotB) {
2481  if (slotA.equals(slotB)) return true;
2482  int mappedSrcId = coalescedSlots_[slotA.asInt()];
2483  int mappedDestId = coalescedSlots_[slotB.asInt()];
2484  if (mappedSrcId == -1 || mappedDestId == -1) return false;
2485  if (valueTransfer_[mappedSrcId][mappedDestId]) return true;
2486  Set<SlotId> eqSlots = completeSubGraphs_.get(slotA);
2487  if (eqSlots == null) return false;
2488  return eqSlots.contains(slotB);
2489  }
2490 
2498  private void mapSlots(List<Pair<SlotId, SlotId>> origValueTransfers,
2499  List<Pair<Integer, Integer>> coalescedValueTransfers,
2500  DisjointSet<Integer> graphPartitions) {
2501  for (Pair<SlotId, SlotId> vt: origValueTransfers) {
2502  int src = coalescedSlots_[vt.first.asInt()];
2503  if (src == -1) {
2504  src = nextCoalescedSlotId_;
2505  coalescedSlots_[vt.first.asInt()] = nextCoalescedSlotId_;
2507  }
2508  int dest = coalescedSlots_[vt.second.asInt()];
2509  if (dest == -1) {
2510  dest = nextCoalescedSlotId_;
2511  coalescedSlots_[vt.second.asInt()] = nextCoalescedSlotId_;
2513  }
2514  coalescedValueTransfers.add(
2515  new Pair<Integer, Integer>(Integer.valueOf(src), Integer.valueOf(dest)));
2516  graphPartitions.union(Integer.valueOf(src), Integer.valueOf(dest));
2517  }
2518  }
2519 
2529  private void partitionValueTransfers(DisjointSet<SlotId> completeSubGraphs,
2530  List<Pair<SlotId, SlotId>> valueTransfers) {
2531  // transform equality predicates into a transfer graph
2532  for (ExprId id: globalState_.conjuncts.keySet()) {
2533  Expr e = globalState_.conjuncts.get(id);
2534  Pair<SlotId, SlotId> slotIds = BinaryPredicate.getEqSlots(e);
2535  if (slotIds == null) continue;
2536 
2537  boolean isAntiJoin = false;
2538  TableRef sjTblRef = globalState_.sjClauseByConjunct.get(id);
2539  Preconditions.checkState(sjTblRef == null || sjTblRef.getJoinOp().isSemiJoin());
2540  isAntiJoin = sjTblRef != null && sjTblRef.getJoinOp().isAntiJoin();
2541 
2542  TableRef ojTblRef = globalState_.ojClauseByConjunct.get(id);
2543  Preconditions.checkState(ojTblRef == null || ojTblRef.getJoinOp().isOuterJoin());
2544  if (ojTblRef == null && !isAntiJoin) {
2545  // this eq predicate doesn't involve any outer or anti join, ie, it is true for
2546  // each result row;
2547  // value transfer is not legal if the receiving slot is in an enclosed
2548  // scope of the source slot and the receiving slot's block has a limit
2549  Analyzer firstBlock = globalState_.blockBySlot.get(slotIds.first);
2550  Analyzer secondBlock = globalState_.blockBySlot.get(slotIds.second);
2551  LOG.trace("value transfer: from " + slotIds.first.toString());
2552  Pair<SlotId, SlotId> firstToSecond = null;
2553  Pair<SlotId, SlotId> secondToFirst = null;
2554  if (!(secondBlock.hasLimitOffsetClause_ &&
2555  secondBlock.ancestors_.contains(firstBlock))) {
2556  firstToSecond = new Pair<SlotId, SlotId>(slotIds.first, slotIds.second);
2557  }
2558  if (!(firstBlock.hasLimitOffsetClause_ &&
2559  firstBlock.ancestors_.contains(secondBlock))) {
2560  secondToFirst = new Pair<SlotId, SlotId>(slotIds.second, slotIds.first);
2561  }
2562  // Add bi-directional value transfers to the completeSubGraphs, or
2563  // uni-directional value transfers to valueTransfers.
2564  if (firstToSecond != null && secondToFirst != null
2565  && completeSubGraphs != null) {
2566  completeSubGraphs.union(slotIds.first, slotIds.second);
2567  } else {
2568  if (firstToSecond != null) valueTransfers.add(firstToSecond);
2569  if (secondToFirst != null) valueTransfers.add(secondToFirst);
2570  }
2571  continue;
2572  }
2573  // Outer or semi-joined table ref.
2574  TableRef tblRef = (ojTblRef != null) ? ojTblRef : sjTblRef;
2575  Preconditions.checkNotNull(tblRef);
2576 
2577  if (tblRef.getJoinOp() == JoinOperator.FULL_OUTER_JOIN) {
2578  // full outer joins don't guarantee any value transfer
2579  continue;
2580  }
2581 
2582  // this is some form of outer or anti join
2583  SlotId outerSlot, innerSlot;
2584  if (tblRef.getId() == getTupleId(slotIds.first)) {
2585  innerSlot = slotIds.first;
2586  outerSlot = slotIds.second;
2587  } else if (tblRef.getId() == getTupleId(slotIds.second)) {
2588  innerSlot = slotIds.second;
2589  outerSlot = slotIds.first;
2590  } else {
2591  // this eq predicate is part of an OJ/AJ clause but doesn't reference
2592  // the joined table -> ignore this, we can't reason about when it'll
2593  // actually be true
2594  continue;
2595  }
2596 
2597  // value transfer is always legal because the outer and inner slot must come from
2598  // the same block; transitive value transfers into inline views with a limit are
2599  // prevented because the inline view's aux predicates won't transfer values into
2600  // the inline view's block (handled in the 'tableRef == null' case above)
2601  // TODO: We could propagate predicates into anti-joined plan subtrees by
2602  // inverting the condition (paying special attention to NULLs).
2603  if (tblRef.getJoinOp() == JoinOperator.LEFT_OUTER_JOIN
2604  || tblRef.getJoinOp() == JoinOperator.LEFT_ANTI_JOIN
2606  valueTransfers.add(new Pair<SlotId, SlotId>(outerSlot, innerSlot));
2607  } else if (tblRef.getJoinOp() == JoinOperator.RIGHT_OUTER_JOIN
2608  || tblRef.getJoinOp() == JoinOperator.RIGHT_ANTI_JOIN) {
2609  valueTransfers.add(new Pair<SlotId, SlotId>(innerSlot, outerSlot));
2610  }
2611  }
2612  }
2613 
2622  public boolean validate(StringBuilder expected, StringBuilder actual) {
2623  Preconditions.checkState(expected.length() == 0 && actual.length() == 0);
2624  int numSlots = globalState_.descTbl.getMaxSlotId().asInt() + 1;
2625  boolean[][] expectedValueTransfer = new boolean[numSlots][numSlots];
2626  for (int i = 0; i < numSlots; ++i) {
2627  expectedValueTransfer[i][i] = true;
2628  }
2629 
2630  // Initialize expectedValueTransfer with the value transfers from conjuncts_.
2631  List<Pair<SlotId, SlotId>> valueTransfers = Lists.newArrayList();
2632  partitionValueTransfers(null, valueTransfers);
2633  for (Pair<SlotId, SlotId> vt: valueTransfers) {
2634  expectedValueTransfer[vt.first.asInt()][vt.second.asInt()] = true;
2635  }
2636  // Set registered value tranfers in expectedValueTransfer.
2637  for (Pair<SlotId, SlotId> vt: globalState_.registeredValueTransfers) {
2638  expectedValueTransfer[vt.first.asInt()][vt.second.asInt()] = true;
2639  }
2640 
2641  // Compute the expected transitive closure.
2642  boolean changed = false;
2643  do {
2644  changed = false;
2645  for (int i = 0; i < numSlots; ++i) {
2646  for (int j = 0; j < numSlots; ++j) {
2647  for (int k = 0; k < numSlots; ++k) {
2648  if (expectedValueTransfer[i][j] && expectedValueTransfer[j][k]
2649  && !expectedValueTransfer[i][k]) {
2650  expectedValueTransfer[i][k] = true;
2651  changed = true;
2652  }
2653  }
2654  }
2655  }
2656  } while (changed);
2657 
2658  // Populate actual value transfer graph.
2659  boolean[][] actualValueTransfer = new boolean[numSlots][numSlots];
2660  for (int i = 0; i < numSlots; ++i) {
2661  for (int j = 0; j < numSlots; ++j) {
2662  actualValueTransfer[i][j] = hasValueTransfer(new SlotId(i), new SlotId(j));
2663  }
2664  }
2665 
2666  // Print matrices and string-compare them.
2667  PrintUtils.printMatrix(expectedValueTransfer, 3, expected);
2668  PrintUtils.printMatrix(actualValueTransfer, 3, actual);
2669  String expectedStr = expected.toString();
2670  String actualStr = actual.toString();
2671  return expectedStr.equals(actualStr);
2672  }
2673  }
2674 
2685  public void registerPrivReq(PrivilegeRequest privReq) {
2686  if (!enablePrivChecks_) return;
2687 
2688  if (Strings.isNullOrEmpty(authErrorMsg_)) {
2689  globalState_.privilegeReqs.add(privReq);
2690  } else {
2691  globalState_.maskedPrivilegeReqs.add(Pair.create(privReq, authErrorMsg_));
2692  }
2693  }
2694 
2700  public void authorize(AuthorizationChecker authzChecker) throws AuthorizationException {
2702  // If this is a system database, some actions should always be allowed
2703  // or disabled, regardless of what is in the auth policy.
2704  String dbName = null;
2705  if (privReq.getAuthorizeable() instanceof AuthorizeableDb) {
2706  dbName = privReq.getName();
2707  } else if (privReq.getAuthorizeable() instanceof AuthorizeableTable) {
2709  dbName = tbl.getDbName();
2710  }
2711  if (dbName != null && checkSystemDbAccess(dbName, privReq.getPrivilege())) {
2712  continue;
2713  }
2714 
2715  // Perform the authorization check.
2716  authzChecker.checkAccess(getUser(), privReq);
2717  }
2718 
2719  for (Pair<PrivilegeRequest, String> maskedReq: globalState_.maskedPrivilegeReqs) {
2720  // Perform the authorization check.
2721  if (!authzChecker.hasAccess(getUser(), maskedReq.first)) {
2722  throw new AuthorizationException(maskedReq.second);
2723  }
2724  }
2725  }
2726 
2732  private boolean checkSystemDbAccess(String dbName, Privilege privilege)
2733  throws AuthorizationException {
2734  Db db = globalState_.catalog.getDb(dbName);
2735  if (db != null && db.isSystemDb()) {
2736  switch (privilege) {
2737  case VIEW_METADATA:
2738  case ANY:
2739  return true;
2740  default:
2741  throw new AuthorizationException("Cannot modify system database.");
2742  }
2743  }
2744  return false;
2745  }
2746 }
void markConstantConjunct(Expr conjunct, boolean fromHavingClause)
Definition: Analyzer.java:892
boolean hasMutualValueTransfer(SlotId slotA, SlotId slotB)
Definition: Analyzer.java:2277
String getTargetDbName(FunctionName fnName)
Definition: Analyzer.java:2238
Type castAllToCompatibleType(List< Expr > exprs)
Definition: Analyzer.java:2014
SlotDescriptor registerSlotRef(Path slotPath)
Definition: Analyzer.java:797
void invertOuterJoinState(TableRef oldRhsTbl, TableRef newRhsTbl)
Definition: Analyzer.java:1400
boolean isFullOuterJoined(SlotId sid)
Definition: Analyzer.java:1758
public< T extends Expr > void createEquivConjuncts(TupleId tid, List< T > conjuncts, Set< SlotId > ignoreSlots)
Definition: Analyzer.java:1535
TableRef getTableRef(TupleId tid)
Definition: Analyzer.java:570
Path resolvePath(List< String > rawPath, PathType pathType)
Definition: Analyzer.java:622
final Map< TupleId, List< ExprId > > eqJoinConjuncts
Definition: Analyzer.java:195
void createAuxEquivPredicate(Expr lhs, Expr rhs)
Definition: Analyzer.java:969
boolean hasValueTransfer(SlotId a, SlotId b)
Definition: Analyzer.java:2281
void markConjunctAssigned(Expr conjunct)
Definition: Analyzer.java:1928
Analyzer(Analyzer parentAnalyzer)
Definition: Analyzer.java:342
final Map< TupleId, TableRef > semiJoinedTupleIds
Definition: Analyzer.java:215
final Map< EquivalenceClassId, ArrayList< SlotId > > equivClassMembers
Definition: Analyzer.java:257
List< Expr > getEqJoinConjuncts(List< TupleId > tids, TableRef joinedTblRef)
Definition: Analyzer.java:1119
void registerConjuncts(Expr e, boolean fromHavingClause)
Definition: Analyzer.java:877
boolean isFullOuterJoined(TupleId tid)
Definition: Analyzer.java:1754
TupleDescriptor getDescriptor(String tableAlias)
Definition: Analyzer.java:557
static Analyzer createWithNewGlobalState(Analyzer parentAnalyzer)
Definition: Analyzer.java:363
ArrayList< Expr > getBoundPredicates(TupleId destTid)
Definition: Analyzer.java:1387
SlotDescriptor addSlotDescriptor(TupleDescriptor tupleDesc)
Definition: Analyzer.java:813
final Map< String, TupleDescriptor > aliasMap_
Definition: Analyzer.java:308
Set< TAccessEvent > getAccessEvents()
Definition: Analyzer.java:2086
TableRef resolveTableRef(TableRef tableRef)
Definition: Analyzer.java:468
boolean canEvalPredicate(List< TupleId > tupleIds, Expr e)
Definition: Analyzer.java:1169
ArrayList< TupleId > getTblRefIds()
Definition: PlanNode.java:201
void registerConjuncts(List< Expr > l)
Definition: Analyzer.java:837
void castToUnionCompatibleTypes(List< List< Expr >> exprLists)
Definition: Analyzer.java:2038
TupleId getTupleId(SlotId slotId)
Definition: Analyzer.java:1724
final Map< ExprId, TableRef > fullOuterJoinedConjuncts
Definition: Analyzer.java:207
BinaryPredicate createEqPredicate(SlotId lhsSlotId, SlotId rhsSlotId)
Definition: Analyzer.java:980
final List< Pair< SlotId, SlotId > > registeredValueTransfers
Definition: Analyzer.java:270
final Set< String > ambiguousAliases_
Definition: Analyzer.java:314
final Map< ExprId, TableRef > sjClauseByConjunct
Definition: Analyzer.java:228
void setHasLimitOffsetClause(boolean hasLimitOffset)
Definition: Analyzer.java:2262
void registerOuterJoinedTids(List< TupleId > tids, TableRef rhsRef)
Definition: Analyzer.java:538
void setAssignedConjuncts(Set< ExprId > assigned)
Definition: Analyzer.java:1941
final Map< String, View > localViews_
Definition: Analyzer.java:301
TableRef getLastOjClause(TupleId id)
Definition: Analyzer.java:1080
Operator(String description, String name, TComparisonOp thriftOp)
Path resolvePath(List< String > rawPath, PathType pathType, boolean resolveInAncestors)
Definition: Analyzer.java:632
Analyzer(Analyzer parentAnalyzer, GlobalState globalState)
Definition: Analyzer.java:349
void markConjunctsAssigned(List< Expr > conjuncts)
Definition: Analyzer.java:1917
Type getCompatibleType(Type lastCompatibleType, Expr lastCompatibleExpr, Expr expr)
Definition: Analyzer.java:1989
void registerValueTransfer(SlotId id1, SlotId id2)
Definition: Analyzer.java:1728
boolean canEvalAntiJoinedConjunct(Expr e, List< TupleId > nodeTupleIds)
Definition: Analyzer.java:1245
final Map< TupleId, TableRef > fullOuterJoinedTupleIds
Definition: Analyzer.java:210
void registerOnClauseConjuncts(Expr e, TableRef rhsRef)
Definition: Analyzer.java:849
void bulkUpdate(List< Pair< SlotId, SlotId >> mutualValueTransfers)
Definition: Analyzer.java:2456
Db getDb(String dbName, Privilege privilege, boolean throwIfDoesNotExist)
Definition: Analyzer.java:2189
List< Expr > getUnassignedOjConjuncts(TableRef ref)
Definition: Analyzer.java:1061
Path resolvePaths(List< String > rawPath, List< Path > paths, PathType pathType, LinkedList< String > errors)
Definition: Analyzer.java:700
public< T extends Expr > void createEquivConjuncts(List< TupleId > lhsTids, TupleId rhsTid, List< T > conjuncts)
Definition: Analyzer.java:1430
final List< PrivilegeRequest > privilegeReqs
Definition: Analyzer.java:234
List< List< SlotId > > getEquivDestSlotIds(TupleId srcTid, List< SlotId > srcSids, TupleId destTid, Set< SlotId > ignoreSlots)
Definition: Analyzer.java:1637
final Map< TupleId, TableRef > outerJoinedTupleIds
Definition: Analyzer.java:203
void authorize(AuthorizationChecker authzChecker)
Definition: Analyzer.java:2700
SlotDescriptor copySlotDescriptor(SlotDescriptor srcSlotDesc, TupleDescriptor tupleDesc)
Definition: Analyzer.java:823
void setEnablePrivChecks(boolean value)
Definition: Analyzer.java:2251
final Map< ExprId, TableRef > ojClauseByConjunct
Definition: Analyzer.java:224
final List< Pair< PrivilegeRequest, String > > maskedPrivilegeReqs
Definition: Analyzer.java:239
void registerPrivReq(PrivilegeRequest privReq)
Definition: Analyzer.java:2685
final Map< SlotId, EquivalenceClassId > equivClassBySlotId
Definition: Analyzer.java:262
void registerSemiJoinedTid(TupleId tid, TableRef rhsRef)
Definition: Analyzer.java:548
Analyzer(ImpaladCatalog catalog, TQueryCtx queryCtx, AuthorizationConfig authzConfig)
Definition: Analyzer.java:331
Db getDb(String dbName, Privilege privilege)
Definition: Analyzer.java:2185
List< Expr > removeRedundantExprs(List< Expr > exprs)
Definition: Analyzer.java:1899
SlotDescriptor getSlotDescriptor(String qualifiedColumnName)
Definition: Analyzer.java:575
GlobalState(ImpaladCatalog catalog, TQueryCtx queryCtx, AuthorizationConfig authzConfig)
Definition: Analyzer.java:281
int SlotId
Definition: global-types.h:24
static boolean EvalPredicate(Expr pred, TQueryCtx queryCtx)
Definition: FeSupport.java:179
static final String TBL_ALREADY_EXISTS_ERROR_MSG
Definition: Analyzer.java:110
AuthorizationConfig getAuthzConfig()
Definition: Analyzer.java:2069
TupleDescriptor getTupleDesc(TupleId id)
Definition: Analyzer.java:566
boolean equivExprs(Expr e1, Expr e2)
Definition: Analyzer.java:1887
Map< String, View > getLocalViews()
Definition: Analyzer.java:2302
final Map< TableRef, List< ExprId > > conjunctsByOjClause
Definition: Analyzer.java:219
TupleDescriptor registerTableRef(TableRef ref)
Definition: Analyzer.java:427
TableName getFqTableName(TableName tableName)
Definition: Analyzer.java:2246
final Map< TupleId, TableRef > tableRefMap_
Definition: Analyzer.java:311
void addAccessEvent(TAccessEvent event)
Definition: Analyzer.java:2087
final Map< SlotId, Analyzer > blockBySlot
Definition: Analyzer.java:231
boolean isOuterJoined(TupleId tid)
Definition: Analyzer.java:1732
boolean isConjunctAssigned(Expr conjunct)
Definition: Analyzer.java:1933
ArrayList< Expr > getBoundPredicates(TupleId destTid, Set< SlotId > ignoreSlots, boolean markAssigned)
Definition: Analyzer.java:1274
void materializeSlots(List< Expr > exprs)
Definition: Analyzer.java:1962
void setUseHiveColLabels(boolean useHiveColLabels)
Definition: Analyzer.java:2257
String getTargetDbName(TableName tableName)
Definition: Analyzer.java:2234
uint64_t count
SlotDescriptor getColumnSlot(TupleDescriptor tupleDesc, Column col)
Definition: Analyzer.java:1088
ImmutableList< PrivilegeRequest > getPrivilegeReqs()
Definition: Analyzer.java:2077
public< T extends Expr > void createEquivConjuncts(TupleId tid, List< T > conjuncts)
Definition: Analyzer.java:1604
static final String DB_DOES_NOT_EXIST_ERROR_MSG
Definition: Analyzer.java:107
void mapSlots(List< Pair< SlotId, SlotId >> origValueTransfers, List< Pair< Integer, Integer >> coalescedValueTransfers, DisjointSet< Integer > graphPartitions)
Definition: Analyzer.java:2498
List< Expr > getUnassignedConjuncts(PlanNode node)
Definition: Analyzer.java:1028
List< Expr > getUnassignedConjuncts(List< TupleId > tupleIds, boolean inclOjConjuncts)
Definition: Analyzer.java:994
final IdGenerator< ExprId > conjunctIdGenerator
Definition: Analyzer.java:167
ColumnLineageGraph getColumnLineageGraph()
Definition: Analyzer.java:2071
ExprSubstitutionMap getEquivClassSmap()
Definition: Analyzer.java:1867
boolean containsOuterJoinedTid(List< TupleId > tids)
Definition: Analyzer.java:1766
Table getTable(String dbName, String tableName)
Definition: Analyzer.java:2098
static final String DATA_SRC_ALREADY_EXISTS_ERROR_MSG
Definition: Analyzer.java:115
static final String DB_ALREADY_EXISTS_ERROR_MSG
Definition: Analyzer.java:108
Map< EquivalenceClassId, List< SlotId > > getEquivClasses(List< TupleId > tids)
Definition: Analyzer.java:1612
boolean equivSets(List< Expr > l1, List< Expr > l2)
Definition: Analyzer.java:1874
final ListMap< TNetworkAddress > hostIndex
Definition: Analyzer.java:275
void partitionValueTransfers(DisjointSet< SlotId > completeSubGraphs, List< Pair< SlotId, SlotId >> valueTransfers)
Definition: Analyzer.java:2529
static final String FN_DOES_NOT_EXIST_ERROR_MSG
Definition: Analyzer.java:111
static final String DATA_SRC_DOES_NOT_EXIST_ERROR_MSG
Definition: Analyzer.java:113
boolean canEvalFullOuterJoinedConjunct(Expr e, List< TupleId > tids)
Definition: Analyzer.java:1153
final IdGenerator< EquivalenceClassId > equivClassIdGenerator
Definition: Analyzer.java:253
final Map< String, SlotDescriptor > slotRefMap_
Definition: Analyzer.java:317
boolean hasValueTransfer(SlotId slotA, SlotId slotB)
Definition: Analyzer.java:2480
EquivalenceClassId getEquivClassId(SlotId slotId)
Definition: Analyzer.java:1863
boolean checkSystemDbAccess(String dbName, Privilege privilege)
Definition: Analyzer.java:2732
final LinkedHashMap< String, Integer > warnings
Definition: Analyzer.java:250
boolean isBoundByTupleIds(List< TupleId > tids)
Definition: Expr.java:852
void getEquivSlots(SlotId slotId, List< TupleId > tupleIds, List< SlotId > equivSlotIds)
Definition: Analyzer.java:1850
static final String TBL_DOES_NOT_EXIST_ERROR_MSG
Definition: Analyzer.java:109
Table getTable(TableName tableName, Privilege privilege)
Definition: Analyzer.java:2170
final ArrayList< Analyzer > ancestors_
Definition: Analyzer.java:298
boolean canEvalPredicate(PlanNode node, Expr e)
Definition: Analyzer.java:1237
void registerFullOuterJoinedTids(List< TupleId > tids, TableRef rhsRef)
Definition: Analyzer.java:527
Table getTable(TableName tableName, Privilege privilege, boolean addAccessEvent)
Definition: Analyzer.java:2134
ListMap< TNetworkAddress > getHostIndex()
Definition: Analyzer.java:2070
void setVisibleSemiJoinedTuple(TupleId tid)
Definition: Analyzer.java:373
static String getEffectiveUser(TSessionState session)
static final String FN_ALREADY_EXISTS_ERROR_MSG
Definition: Analyzer.java:112
boolean hasOuterJoinedTuple(EquivalenceClassId eqClassId)
Definition: Analyzer.java:1688
boolean dbContainsTable(String dbName, String tableName, Privilege privilege)
Definition: Analyzer.java:2215
boolean validate(StringBuilder expected, StringBuilder actual)
Definition: Analyzer.java:2622