Impala
Impalaistheopensource,nativeanalyticdatabaseforApacheHadoop.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
SingleNodePlanner.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.planner;
16 
17 import java.util.ArrayList;
18 import java.util.Collections;
19 import java.util.Comparator;
20 import java.util.Iterator;
21 import java.util.List;
22 import java.util.ListIterator;
23 import java.util.Set;
24 
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
27 
58 import com.cloudera.impala.common.Pair;
59 import com.google.common.base.Preconditions;
60 import com.google.common.base.Predicate;
61 import com.google.common.collect.Iterables;
62 import com.google.common.collect.Lists;
63 import com.google.common.collect.Sets;
64 
71 public class SingleNodePlanner {
72  private final static Logger LOG = LoggerFactory.getLogger(SingleNodePlanner.class);
73 
74  private final PlannerContext ctx_;
75 
77  ctx_ = ctx;
78  }
79 
97  QueryStmt queryStmt = ctx_.getQueryStmt();
98  // Use the stmt's analyzer which is not necessarily the root analyzer
99  // to detect empty result sets.
100  Analyzer analyzer = queryStmt.getAnalyzer();
101  analyzer.computeEquivClasses();
102  analyzer.getTimeline().markEvent("Equivalence classes computed");
103 
104  // Mark slots referenced by output exprs as materialized, prior to generating the
105  // plan tree.
106  // We need to mark the result exprs of the topmost select block as materialized, so
107  // that PlanNode.init() can compute the final mem layout of materialized tuples
108  // (the byte size of tuples is needed for cost computations).
109  // TODO: instead of materializing everything produced by the plan root, derive
110  // referenced slots from destination fragment and add a materialization node
111  // if not all output is needed by destination fragment
112  // TODO 2: should the materialization decision be cost-based?
113  if (queryStmt.getBaseTblResultExprs() != null) {
114  analyzer.materializeSlots(queryStmt.getBaseTblResultExprs());
115  }
116 
117  LOG.trace("desctbl: " + analyzer.getDescTbl().debugString());
118  PlanNode singleNodePlan = createQueryPlan(queryStmt, analyzer,
119  ctx_.getQueryOptions().isDisable_outermost_topn());
120  Preconditions.checkNotNull(singleNodePlan);
121  return singleNodePlan;
122  }
123 
127  private PlanNode createEmptyNode(QueryStmt stmt, Analyzer analyzer)
128  throws InternalException {
129  ArrayList<TupleId> tupleIds = Lists.newArrayList();
130  stmt.getMaterializedTupleIds(tupleIds);
131  EmptySetNode node = new EmptySetNode(ctx_.getNextNodeId(), tupleIds);
132  node.init(analyzer);
133  // Set the output smap to resolve exprs referencing inline views within stmt.
134  // Not needed for a UnionStmt because it materializes its input operands.
135  if (stmt instanceof SelectStmt) {
136  node.setOutputSmap(((SelectStmt) stmt).getBaseTblSmap());
137  }
138  return node;
139  }
140 
145  private PlanNode createQueryPlan(QueryStmt stmt, Analyzer analyzer, boolean disableTopN)
146  throws ImpalaException {
147  if (analyzer.hasEmptyResultSet()) return createEmptyNode(stmt, analyzer);
148 
149  PlanNode root;
150  if (stmt instanceof SelectStmt) {
151  SelectStmt selectStmt = (SelectStmt) stmt;
152  root = createSelectPlan(selectStmt, analyzer);
153 
154  // insert possible AnalyticEvalNode before SortNode
155  if (((SelectStmt) stmt).getAnalyticInfo() != null) {
156  AnalyticInfo analyticInfo = selectStmt.getAnalyticInfo();
157  ArrayList<TupleId> stmtTupleIds = Lists.newArrayList();
158  stmt.getMaterializedTupleIds(stmtTupleIds);
159  AnalyticPlanner analyticPlanner =
160  new AnalyticPlanner(stmtTupleIds, analyticInfo, analyzer, ctx_);
161  List<Expr> inputPartitionExprs = Lists.newArrayList();
162  AggregateInfo aggInfo = selectStmt.getAggInfo();
163  root = analyticPlanner.createSingleNodePlan(root,
164  aggInfo != null ? aggInfo.getGroupingExprs() : null, inputPartitionExprs);
165  if (aggInfo != null && !inputPartitionExprs.isEmpty()) {
166  // analytic computation will benefit from a partition on inputPartitionExprs
167  aggInfo.setPartitionExprs(inputPartitionExprs);
168  }
169  }
170  } else {
171  Preconditions.checkState(stmt instanceof UnionStmt);
172  root = createUnionPlan((UnionStmt) stmt, analyzer);
173  }
174 
175  // Avoid adding a sort node if the sort tuple has no materialized slots.
176  boolean sortHasMaterializedSlots = false;
177  if (stmt.evaluateOrderBy()) {
178  for (SlotDescriptor sortSlotDesc:
179  stmt.getSortInfo().getSortTupleDescriptor().getSlots()) {
180  if (sortSlotDesc.isMaterialized()) {
181  sortHasMaterializedSlots = true;
182  break;
183  }
184  }
185  }
186 
187  if (stmt.evaluateOrderBy() && sortHasMaterializedSlots) {
188  long limit = stmt.getLimit();
189  // TODO: External sort could be used for very large limits
190  // not just unlimited order-by
191  boolean useTopN = stmt.hasLimit() && !disableTopN;
192  root = new SortNode(ctx_.getNextNodeId(), root, stmt.getSortInfo(),
193  useTopN, stmt.getOffset());
194  Preconditions.checkState(root.hasValidStats());
195  root.setLimit(limit);
196  root.init(analyzer);
197  } else {
198  root.setLimit(stmt.getLimit());
199  root.computeStats(analyzer);
200  }
201 
202  return root;
203  }
204 
213  Analyzer analyzer, List<TupleId> tupleIds, PlanNode root)
214  throws InternalException {
215  // No point in adding SelectNode on top of an EmptyNode.
216  if (root instanceof EmptySetNode) return root;
217  Preconditions.checkNotNull(root);
218  // Gather unassigned conjuncts and generate predicates to enfore
219  // slot equivalences for each tuple id.
220  List<Expr> conjuncts = analyzer.getUnassignedConjuncts(root);
221  for (TupleId tid: tupleIds) {
222  analyzer.createEquivConjuncts(tid, conjuncts);
223  }
224  if (conjuncts.isEmpty()) return root;
225  // evaluate conjuncts in SelectNode
226  SelectNode selectNode = new SelectNode(ctx_.getNextNodeId(), root, conjuncts);
227  // init() marks conjuncts as assigned
228  selectNode.init(analyzer);
229  Preconditions.checkState(selectNode.hasValidStats());
230  return selectNode;
231  }
232 
247  Analyzer analyzer, List<Pair<TableRef, PlanNode>> refPlans)
248  throws ImpalaException {
249  LOG.trace("createCheapestJoinPlan");
250  if (refPlans.size() == 1) return refPlans.get(0).second;
251 
252  // collect eligible candidates for the leftmost input; list contains
253  // (plan, materialized size)
254  ArrayList<Pair<TableRef, Long>> candidates = Lists.newArrayList();
255  for (Pair<TableRef, PlanNode> entry: refPlans) {
256  TableRef ref = entry.first;
257  JoinOperator joinOp = ref.getJoinOp();
258 
259  // The rhs table of an outer/semi join can appear as the left-most input if we
260  // invert the lhs/rhs and the join op. However, we may only consider this inversion
261  // for the very first join in refPlans, otherwise we could reorder tables/joins
262  // across outer/semi joins which is generally incorrect. The null-aware
263  // left anti-join operator is never considered for inversion because we can't
264  // execute the null-aware right anti-join efficiently.
265  // TODO: Allow the rhs of any cross join as the leftmost table. This needs careful
266  // consideration of the joinOps that result from such a re-ordering (IMPALA-1281).
267  if (((joinOp.isOuterJoin() || joinOp.isSemiJoin() || joinOp.isCrossJoin()) &&
268  ref != refPlans.get(1).first) || joinOp.isNullAwareLeftAntiJoin()) {
269  // ref cannot appear as the leftmost input
270  continue;
271  }
272 
273  PlanNode plan = entry.second;
274  if (plan.getCardinality() == -1) {
275  // use 0 for the size to avoid it becoming the leftmost input
276  // TODO: Consider raw size of scanned partitions in the absence of stats.
277  candidates.add(new Pair(ref, new Long(0)));
278  LOG.trace("candidate " + ref.getUniqueAlias() + ": 0");
279  continue;
280  }
281  Preconditions.checkNotNull(ref.getDesc());
282  long materializedSize =
283  (long) Math.ceil(plan.getAvgRowSize() * (double) plan.getCardinality());
284  candidates.add(new Pair(ref, new Long(materializedSize)));
285  LOG.trace("candidate " + ref.getUniqueAlias() + ": " + Long.toString(materializedSize));
286  }
287  if (candidates.isEmpty()) return null;
288 
289  // order candidates by descending materialized size; we want to minimize the memory
290  // consumption of the materialized hash tables required for the join sequence
291  Collections.sort(candidates,
292  new Comparator<Pair<TableRef, Long>>() {
293  public int compare(Pair<TableRef, Long> a, Pair<TableRef, Long> b) {
294  long diff = b.second - a.second;
295  return (diff < 0 ? -1 : (diff > 0 ? 1 : 0));
296  }
297  });
298 
299  for (Pair<TableRef, Long> candidate: candidates) {
300  PlanNode result = createJoinPlan(analyzer, candidate.first, refPlans);
301  if (result != null) return result;
302  }
303  return null;
304  }
305 
312  Analyzer analyzer, TableRef leftmostRef, List<Pair<TableRef, PlanNode>> refPlans)
313  throws ImpalaException {
314 
315  LOG.trace("createJoinPlan: " + leftmostRef.getUniqueAlias());
316  // the refs that have yet to be joined
317  List<Pair<TableRef, PlanNode>> remainingRefs = Lists.newArrayList();
318  PlanNode root = null; // root of accumulated join plan
319  for (Pair<TableRef, PlanNode> entry: refPlans) {
320  if (entry.first == leftmostRef) {
321  root = entry.second;
322  } else {
323  remainingRefs.add(entry);
324  }
325  }
326  Preconditions.checkNotNull(root);
327  // refs that have been joined. The union of joinedRefs and the refs in remainingRefs
328  // are the set of all table refs.
329  Set<TableRef> joinedRefs = Sets.newHashSet();
330  joinedRefs.add(leftmostRef);
331 
332  // If the leftmostTblRef is an outer/semi/cross join, we must invert it.
333  boolean planHasInvertedJoin = false;
334  if (leftmostRef.getJoinOp().isOuterJoin()
335  || leftmostRef.getJoinOp().isSemiJoin()
336  || leftmostRef.getJoinOp().isCrossJoin()) {
337  // TODO: Revisit the interaction of join inversion here and the analysis state
338  // that is changed in analyzer.invertOuterJoin(). Changing the analysis state
339  // should not be necessary because the semantics of an inverted outer join do
340  // not change.
341  leftmostRef.invertJoin(refPlans, analyzer);
342  planHasInvertedJoin = true;
343  }
344 
345  long numOps = 0;
346  int i = 0;
347  while (!remainingRefs.isEmpty()) {
348  // we minimize the resulting cardinality at each step in the join chain,
349  // which minimizes the total number of hash table lookups
350  PlanNode newRoot = null;
351  Pair<TableRef, PlanNode> minEntry = null;
352  for (Pair<TableRef, PlanNode> entry: remainingRefs) {
353  TableRef ref = entry.first;
354  LOG.trace(Integer.toString(i) + " considering ref " + ref.getUniqueAlias());
355 
356  // Determine whether we can or must consider this join at this point in the plan.
357  // Place outer/semi joins at a fixed position in the plan tree (IMPALA-860),
358  // s.t. all the tables appearing to the left/right of an outer/semi join in
359  // the original query still remain to the left/right after join ordering. This
360  // prevents join re-ordering across outer/semi joins which is generally wrong.
361  // The checks below relies on remainingRefs being in the order as they originally
362  // appeared in the query.
363  JoinOperator joinOp = ref.getJoinOp();
364  if (joinOp.isOuterJoin() || joinOp.isSemiJoin()) {
365  List<TupleId> currentTids = Lists.newArrayList(root.getTblRefIds());
366  currentTids.add(ref.getId());
367  // Place outer/semi joins at a fixed position in the plan tree. We know that
368  // the join resulting from 'ref' must become the new root if the current
369  // root materializes exactly those tuple ids corresponding to TableRefs
370  // appearing to the left of 'ref' in the original query.
371  List<TupleId> tableRefTupleIds = ref.getAllTupleIds();
372  if (!currentTids.containsAll(tableRefTupleIds) ||
373  !tableRefTupleIds.containsAll(currentTids)) {
374  // Do not consider the remaining table refs to prevent incorrect re-ordering
375  // of tables across outer/semi/anti joins.
376  break;
377  }
378  } else if (ref.getJoinOp().isCrossJoin()) {
379  if (!joinedRefs.contains(ref.getLeftTblRef())) continue;
380  }
381 
382  PlanNode rhsPlan = entry.second;
383  analyzer.setAssignedConjuncts(root.getAssignedConjuncts());
384 
385  boolean invertJoin = false;
386  if (joinOp.isOuterJoin() || joinOp.isSemiJoin() || joinOp.isCrossJoin()) {
387  // Invert the join if doing so reduces the size of build-side hash table
388  // (may also reduce network costs depending on the join strategy).
389  // Only consider this optimization if both the lhs/rhs cardinalities are known.
390  // The null-aware left anti-join operator is never considered for inversion
391  // because we can't execute the null-aware right anti-join efficiently.
392  long lhsCard = root.getCardinality();
393  long rhsCard = rhsPlan.getCardinality();
394  if (lhsCard != -1 && rhsCard != -1 &&
395  lhsCard * root.getAvgRowSize() < rhsCard * rhsPlan.getAvgRowSize() &&
396  !joinOp.isNullAwareLeftAntiJoin()) {
397  invertJoin = true;
398  }
399  }
400  PlanNode candidate = null;
401  if (invertJoin) {
402  ref.setJoinOp(ref.getJoinOp().invert());
403  candidate = createJoinNode(analyzer, rhsPlan, root, ref, null);
404  planHasInvertedJoin = true;
405  } else {
406  candidate = createJoinNode(analyzer, root, rhsPlan, null, ref);
407  }
408  if (candidate == null) continue;
409  LOG.trace("cardinality=" + Long.toString(candidate.getCardinality()));
410 
411  // Use 'candidate' as the new root; don't consider any other table refs at this
412  // position in the plan.
413  if (joinOp.isOuterJoin() || joinOp.isSemiJoin()) {
414  newRoot = candidate;
415  minEntry = entry;
416  break;
417  }
418 
419  // Always prefer Hash Join over Cross Join due to limited costing infrastructure
420  if (newRoot == null
421  || (candidate.getClass().equals(newRoot.getClass())
422  && candidate.getCardinality() < newRoot.getCardinality())
423  || (candidate instanceof HashJoinNode && newRoot instanceof CrossJoinNode)) {
424  newRoot = candidate;
425  minEntry = entry;
426  }
427  }
428  if (newRoot == null) {
429  // Currently, it should not be possible to invert a join for a plan that turns
430  // out to be non-executable because (1) the joins we consider for inversion are
431  // barriers in the join order, and (2) the caller of this function only considers
432  // other leftmost table refs if a plan turns out to be non-executable.
433  // TODO: This preconditions check will need to be changed to undo the in-place
434  // modifications made to table refs for join inversion, if the caller decides to
435  // explore more leftmost table refs.
436  Preconditions.checkState(!planHasInvertedJoin);
437  return null;
438  }
439 
440  // we need to insert every rhs row into the hash table and then look up
441  // every lhs row
442  long lhsCardinality = root.getCardinality();
443  long rhsCardinality = minEntry.second.getCardinality();
444  numOps += lhsCardinality + rhsCardinality;
445  LOG.debug(Integer.toString(i) + " chose " + minEntry.first.getUniqueAlias()
446  + " #lhs=" + Long.toString(lhsCardinality)
447  + " #rhs=" + Long.toString(rhsCardinality)
448  + " #ops=" + Long.toString(numOps));
449  remainingRefs.remove(minEntry);
450  joinedRefs.add(minEntry.first);
451  root = newRoot;
452  // assign id_ after running through the possible choices in order to end up
453  // with a dense sequence of node ids
454  root.setId(ctx_.getNextNodeId());
455  analyzer.setAssignedConjuncts(root.getAssignedConjuncts());
456  ++i;
457  }
458 
459  return root;
460  }
461 
466  Analyzer analyzer, List<Pair<TableRef, PlanNode>> refPlans)
467  throws ImpalaException {
468  // create left-deep sequence of binary hash joins; assign node ids as we go along
469  Preconditions.checkState(!refPlans.isEmpty());
470  PlanNode root = refPlans.get(0).second;
471  for (int i = 1; i < refPlans.size(); ++i) {
472  TableRef innerRef = refPlans.get(i).first;
473  PlanNode innerPlan = refPlans.get(i).second;
474  root = createJoinNode(analyzer, root, innerPlan, null, innerRef);
475  root.setId(ctx_.getNextNodeId());
476  }
477  return root;
478  }
479 
484  private PlanNode createSelectPlan(SelectStmt selectStmt, Analyzer analyzer)
485  throws ImpalaException {
486  // no from clause -> materialize the select's exprs with a UnionNode
487  if (selectStmt.getTableRefs().isEmpty()) {
488  return createConstantSelectPlan(selectStmt, analyzer);
489  }
490 
491  // Slot materialization:
492  // We need to mark all slots as materialized that are needed during the execution
493  // of selectStmt, and we need to do that prior to creating plans for the TableRefs
494  // (because createTableRefNode() might end up calling computeMemLayout() on one or
495  // more TupleDescriptors, at which point all referenced slots need to be marked).
496  //
497  // For non-join predicates, slots are marked as follows:
498  // - for base table scan predicates, this is done directly by ScanNode.init(), which
499  // can do a better job because it doesn't need to materialize slots that are only
500  // referenced for partition pruning, for instance
501  // - for inline views, non-join predicates are pushed down, at which point the
502  // process repeats itself.
503  selectStmt.materializeRequiredSlots(analyzer);
504 
505  ArrayList<TupleId> rowTuples = Lists.newArrayList();
506  // collect output tuples of subtrees
507  for (TableRef tblRef: selectStmt.getTableRefs()) {
508  rowTuples.addAll(tblRef.getMaterializedTupleIds());
509  }
510 
511  // If the selectStmt's select-project-join portion returns an empty result set
512  // create a plan that feeds the aggregation of selectStmt with an empty set.
513  // Make sure the slots of the aggregation exprs and the tuples that they reference
514  // are materialized (see IMPALA-1960).
515  if (analyzer.hasEmptySpjResultSet()) {
516  PlanNode emptySetNode = new EmptySetNode(ctx_.getNextNodeId(), rowTuples);
517  emptySetNode.init(analyzer);
518  emptySetNode.setOutputSmap(selectStmt.getBaseTblSmap());
519  return createAggregationPlan(selectStmt, analyzer, emptySetNode);
520  }
521 
522  // create plans for our table refs; use a list here instead of a map to
523  // maintain a deterministic order of traversing the TableRefs during join
524  // plan generation (helps with tests)
525  List<Pair<TableRef, PlanNode>> refPlans = Lists.newArrayList();
526  for (TableRef ref: selectStmt.getTableRefs()) {
527  PlanNode plan = createTableRefNode(analyzer, ref);
528  Preconditions.checkState(plan != null);
529  refPlans.add(new Pair(ref, plan));
530  }
531  // save state of conjunct assignment; needed for join plan generation
532  for (Pair<TableRef, PlanNode> entry: refPlans) {
533  entry.second.setAssignedConjuncts(analyzer.getAssignedConjuncts());
534  }
535 
536  PlanNode root = null;
537  if (!selectStmt.getSelectList().isStraightJoin()) {
538  Set<ExprId> assignedConjuncts = analyzer.getAssignedConjuncts();
539  root = createCheapestJoinPlan(analyzer, refPlans);
540  if (root == null) analyzer.setAssignedConjuncts(assignedConjuncts);
541  }
542  if (selectStmt.getSelectList().isStraightJoin() || root == null) {
543  // we didn't have enough stats to do a cost-based join plan, or the STRAIGHT_JOIN
544  // keyword was in the select list: use the FROM clause order instead
545  root = createFromClauseJoinPlan(analyzer, refPlans);
546  Preconditions.checkNotNull(root);
547  }
548 
549  // add aggregation, if any
550  if (selectStmt.getAggInfo() != null) {
551  root = createAggregationPlan(selectStmt, analyzer, root);
552  }
553 
554  // All the conjuncts_ should be assigned at this point.
555  // TODO: Re-enable this check here and/or elswehere.
556  //Preconditions.checkState(!analyzer.hasUnassignedConjuncts());
557  return root;
558  }
559 
564  private PlanNode createAggregationPlan(SelectStmt selectStmt, Analyzer analyzer,
565  PlanNode root) throws InternalException {
566  Preconditions.checkState(selectStmt.getAggInfo() != null);
567  // add aggregation, if required
568  AggregateInfo aggInfo = selectStmt.getAggInfo();
569  root = new AggregationNode(ctx_.getNextNodeId(), root, aggInfo);
570  root.init(analyzer);
571  Preconditions.checkState(root.hasValidStats());
572  // if we're computing DISTINCT agg fns, the analyzer already created the
573  // 2nd phase agginfo
574  if (aggInfo.isDistinctAgg()) {
575  ((AggregationNode)root).unsetNeedsFinalize();
576  // The output of the 1st phase agg is the 1st phase intermediate.
577  ((AggregationNode)root).setIntermediateTuple();
578  root = new AggregationNode(ctx_.getNextNodeId(), root,
579  aggInfo.getSecondPhaseDistinctAggInfo());
580  root.init(analyzer);
581  Preconditions.checkState(root.hasValidStats());
582  }
583  // add Having clause
584  root.assignConjuncts(analyzer);
585  return root;
586  }
587 
592  private PlanNode createConstantSelectPlan(SelectStmt selectStmt, Analyzer analyzer)
593  throws InternalException {
594  Preconditions.checkState(selectStmt.getTableRefs().isEmpty());
595  ArrayList<Expr> resultExprs = selectStmt.getResultExprs();
596  ArrayList<String> colLabels = selectStmt.getColLabels();
597  // Create tuple descriptor for materialized tuple.
598  TupleDescriptor tupleDesc = analyzer.getDescTbl().createTupleDescriptor("union");
599  tupleDesc.setIsMaterialized(true);
600  UnionNode unionNode = new UnionNode(ctx_.getNextNodeId(), tupleDesc.getId());
601 
602  // Analysis guarantees that selects without a FROM clause only have constant exprs.
603  unionNode.addConstExprList(Lists.newArrayList(resultExprs));
604 
605  // Replace the select stmt's resultExprs with SlotRefs into tupleDesc.
606  for (int i = 0; i < resultExprs.size(); ++i) {
607  SlotDescriptor slotDesc = analyzer.addSlotDescriptor(tupleDesc);
608  slotDesc.setLabel(colLabels.get(i));
609  slotDesc.setSourceExpr(resultExprs.get(i));
610  slotDesc.setType(resultExprs.get(i).getType());
611  slotDesc.setStats(ColumnStats.fromExpr(resultExprs.get(i)));
612  slotDesc.setIsMaterialized(true);
613  SlotRef slotRef = new SlotRef(slotDesc);
614  resultExprs.set(i, slotRef);
615  }
616  tupleDesc.computeMemLayout();
617  // UnionNode.init() needs tupleDesc to have been initialized
618  unionNode.init(analyzer);
619  return unionNode;
620  }
621 
632  private ValueRange createHBaseValueRange(SlotDescriptor d, List<Expr> conjuncts) {
633  ListIterator<Expr> i = conjuncts.listIterator();
634  ValueRange result = null;
635  while (i.hasNext()) {
636  Expr e = i.next();
637  if (!(e instanceof BinaryPredicate)) continue;
638  BinaryPredicate comp = (BinaryPredicate) e;
639  if (comp.getOp() == BinaryPredicate.Operator.NE) continue;
640  Expr slotBinding = comp.getSlotBinding(d.getId());
641  if (slotBinding == null || !slotBinding.isConstant() ||
642  !slotBinding.getType().equals(Type.STRING)) {
643  continue;
644  }
645 
646  if (comp.getOp() == BinaryPredicate.Operator.EQ) {
647  i.remove();
648  return ValueRange.createEqRange(slotBinding);
649  }
650 
651  if (result == null) result = new ValueRange();
652 
653  // TODO: do we need copies here?
654  if (comp.getOp() == BinaryPredicate.Operator.GT
655  || comp.getOp() == BinaryPredicate.Operator.GE) {
656  if (result.getLowerBound() == null) {
657  result.setLowerBound(slotBinding);
658  result.setLowerBoundInclusive(comp.getOp() == BinaryPredicate.Operator.GE);
659  i.remove();
660  }
661  } else {
662  if (result.getUpperBound() == null) {
663  result.setUpperBound(slotBinding);
664  result.setUpperBoundInclusive(comp.getOp() == BinaryPredicate.Operator.LE);
665  i.remove();
666  }
667  }
668  }
669  return result;
670  }
671 
684  private PlanNode createInlineViewPlan(Analyzer analyzer, InlineViewRef inlineViewRef)
685  throws ImpalaException {
686  // If possible, "push down" view predicates; this is needed in order to ensure
687  // that predicates such as "x + y = 10" are evaluated in the view's plan tree
688  // rather than a SelectNode grafted on top of that plan tree.
689  // This doesn't prevent predicate propagation, because predicates like
690  // "x = 10" that get pushed down are still connected to equivalent slots
691  // via the equality predicates created for the view's select list.
692  // Include outer join conjuncts here as well because predicates from the
693  // On-clause of an outer join may be pushed into the inline view as well.
694  migrateConjunctsToInlineView(analyzer, inlineViewRef);
695 
696  // Turn a constant select into a UnionNode that materializes the exprs.
697  // TODO: unify this with createConstantSelectPlan(), this is basically the
698  // same thing
699  QueryStmt viewStmt = inlineViewRef.getViewStmt();
700  if (viewStmt instanceof SelectStmt) {
701  SelectStmt selectStmt = (SelectStmt) viewStmt;
702  if (selectStmt.getTableRefs().isEmpty()) {
703  if (inlineViewRef.getAnalyzer().hasEmptyResultSet()) {
704  return createEmptyNode(viewStmt, inlineViewRef.getAnalyzer());
705  }
706  // Analysis should have generated a tuple id_ into which to materialize the exprs.
707  Preconditions.checkState(inlineViewRef.getMaterializedTupleIds().size() == 1);
708  // we need to materialize all slots of our inline view tuple
709  analyzer.getTupleDesc(inlineViewRef.getId()).materializeSlots();
710  UnionNode unionNode = new UnionNode(ctx_.getNextNodeId(),
711  inlineViewRef.getMaterializedTupleIds().get(0));
712  if (analyzer.hasEmptyResultSet()) return unionNode;
713  unionNode.setTblRefIds(Lists.newArrayList(inlineViewRef.getId()));
714  unionNode.addConstExprList(selectStmt.getBaseTblResultExprs());
715  unionNode.init(analyzer);
716  return unionNode;
717  }
718  }
719 
720  PlanNode rootNode =
721  createQueryPlan(inlineViewRef.getViewStmt(), inlineViewRef.getAnalyzer(), false);
722  // TODO: we should compute the "physical layout" of the view's descriptor, so that
723  // the avg row size is availble during optimization; however, that means we need to
724  // select references to its resultExprs from the enclosing scope(s)
725  rootNode.setTblRefIds(Lists.newArrayList(inlineViewRef.getId()));
726 
727  ExprSubstitutionMap inlineViewSmap = inlineViewRef.getSmap();
728  if (analyzer.isOuterJoined(inlineViewRef.getId())) {
729  // Exprs against non-matched rows of an outer join should always return NULL.
730  // Make the rhs exprs of the inline view's smap nullable, if necessary.
731  List<Expr> nullableRhs = TupleIsNullPredicate.wrapExprs(
732  inlineViewSmap.getRhs(), rootNode.getTupleIds(), analyzer);
733  inlineViewSmap = new ExprSubstitutionMap(inlineViewSmap.getLhs(), nullableRhs);
734  }
735  // Set output smap of rootNode *before* creating a SelectNode for proper resolution.
736  // The output smap is the composition of the inline view's smap and the output smap
737  // of the inline view's plan root. This ensures that all downstream exprs referencing
738  // the inline view are replaced with exprs referencing the physical output of
739  // the inline view's plan.
740  ExprSubstitutionMap composedSmap = ExprSubstitutionMap.compose(inlineViewSmap,
741  rootNode.getOutputSmap(), analyzer);
742  rootNode.setOutputSmap(composedSmap);
743 
744  // If the inline view has a LIMIT/OFFSET or unassigned conjuncts due to analytic
745  // functions, we may have conjuncts that need to be assigned to a SELECT node on
746  // top of the current plan root node.
747  //
748  // TODO: This check is also repeated in migrateConjunctsToInlineView() because we
749  // need to make sure that equivalences are not enforced multiple times. Consolidate
750  // the assignment of conjuncts and the enforcement of equivalences into a single
751  // place.
752  if (!canMigrateConjuncts(inlineViewRef)) {
753  rootNode = addUnassignedConjuncts(
754  analyzer, inlineViewRef.getDesc().getId().asList(), rootNode);
755  }
756  return rootNode;
757  }
758 
770  InlineViewRef inlineViewRef) {
771  List<Expr> unassignedConjuncts =
772  analyzer.getUnassignedConjuncts(inlineViewRef.getId().asList(), true);
773  if (!canMigrateConjuncts(inlineViewRef)) {
774  // mark (fully resolve) slots referenced by unassigned conjuncts as
775  // materialized
776  List<Expr> substUnassigned = Expr.substituteList(unassignedConjuncts,
777  inlineViewRef.getBaseTblSmap(), analyzer, false);
778  analyzer.materializeSlots(substUnassigned);
779  return;
780  }
781 
782  List<Expr> preds = Lists.newArrayList();
783  for (Expr e: unassignedConjuncts) {
784  if (analyzer.canEvalPredicate(inlineViewRef.getId().asList(), e)) {
785  preds.add(e);
786  }
787  }
788  unassignedConjuncts.removeAll(preds);
789  // Generate predicates to enforce equivalences among slots of the inline view
790  // tuple. These predicates are also migrated into the inline view.
791  analyzer.createEquivConjuncts(inlineViewRef.getId(), preds);
792 
793  // create new predicates against the inline view's unresolved result exprs, not
794  // the resolved result exprs, in order to avoid skipping scopes (and ignoring
795  // limit clauses on the way)
796  List<Expr> viewPredicates =
797  Expr.substituteList(preds, inlineViewRef.getSmap(), analyzer, false);
798 
799  // Remove unregistered predicates that reference the same slot on
800  // both sides (e.g. a = a). Such predicates have been generated from slot
801  // equivalences and may incorrectly reject rows with nulls (IMPALA-1412).
802  Predicate<Expr> isIdentityPredicate = new Predicate<Expr>() {
803  @Override
804  public boolean apply(Expr expr) {
805  if (!(expr instanceof BinaryPredicate)
806  || ((BinaryPredicate) expr).getOp() != BinaryPredicate.Operator.EQ) {
807  return false;
808  }
809  if (!expr.isRegisteredPredicate()
810  && expr.getChild(0) instanceof SlotRef
811  && expr.getChild(1) instanceof SlotRef
812  && (((SlotRef) expr.getChild(0)).getSlotId() ==
813  ((SlotRef) expr.getChild(1)).getSlotId())) {
814  return true;
815  }
816  return false;
817  }
818  };
819  Iterables.removeIf(viewPredicates, isIdentityPredicate);
820 
821  // "migrate" conjuncts_ by marking them as assigned and re-registering them with
822  // new ids.
823  // Mark pre-substitution conjuncts as assigned, since the ids of the new exprs may
824  // have changed.
825  analyzer.markConjunctsAssigned(preds);
826  inlineViewRef.getAnalyzer().registerConjuncts(viewPredicates);
827 
828  // mark (fully resolve) slots referenced by remaining unassigned conjuncts as
829  // materialized
830  List<Expr> substUnassigned = Expr.substituteList(unassignedConjuncts,
831  inlineViewRef.getBaseTblSmap(), analyzer, false);
832  analyzer.materializeSlots(substUnassigned);
833  }
834 
838  private boolean canMigrateConjuncts(InlineViewRef inlineViewRef) {
839  return !inlineViewRef.getViewStmt().hasLimit()
840  && !inlineViewRef.getViewStmt().hasOffset()
841  && (!(inlineViewRef.getViewStmt() instanceof SelectStmt)
842  || !((SelectStmt) inlineViewRef.getViewStmt()).hasAnalyticInfo());
843  }
844 
848  private PlanNode createScanNode(Analyzer analyzer, TableRef tblRef)
849  throws InternalException {
850  ScanNode scanNode = null;
851  if (tblRef.getTable() instanceof HdfsTable) {
852  scanNode = new HdfsScanNode(ctx_.getNextNodeId(), tblRef.getDesc(),
853  (HdfsTable)tblRef.getTable());
854  scanNode.init(analyzer);
855  return scanNode;
856  } else if (tblRef.getTable() instanceof DataSourceTable) {
857  scanNode = new DataSourceScanNode(ctx_.getNextNodeId(), tblRef.getDesc());
858  scanNode.init(analyzer);
859  return scanNode;
860  } else if (tblRef.getTable() instanceof HBaseTable) {
861  // HBase table
862  scanNode = new HBaseScanNode(ctx_.getNextNodeId(), tblRef.getDesc());
863  } else {
864  throw new InternalException("Invalid table ref class: " + tblRef.getClass());
865  }
866  // TODO: move this to HBaseScanNode.init();
867  Preconditions.checkState(scanNode instanceof HBaseScanNode);
868 
869  List<Expr> conjuncts = analyzer.getUnassignedConjuncts(scanNode);
870  // mark conjuncts_ assigned here; they will either end up inside a
871  // ValueRange or will be evaluated directly by the node
872  analyzer.markConjunctsAssigned(conjuncts);
873  List<ValueRange> keyRanges = Lists.newArrayList();
874  // determine scan predicates for clustering cols
875  for (int i = 0; i < tblRef.getTable().getNumClusteringCols(); ++i) {
876  SlotDescriptor slotDesc = analyzer.getColumnSlot(
877  tblRef.getDesc(), tblRef.getTable().getColumns().get(i));
878  if (slotDesc == null || !slotDesc.getType().isStringType()) {
879  // the hbase row key is mapped to a non-string type
880  // (since it's stored in ascii it will be lexicographically ordered,
881  // and non-string comparisons won't work)
882  keyRanges.add(null);
883  } else {
884  // create ValueRange from conjuncts_ for slot; also removes conjuncts_ that were
885  // used as input for filter
886  keyRanges.add(createHBaseValueRange(slotDesc, conjuncts));
887  }
888  }
889 
890  ((HBaseScanNode)scanNode).setKeyRanges(keyRanges);
891  scanNode.addConjuncts(conjuncts);
892  scanNode.init(analyzer);
893 
894  return scanNode;
895  }
896 
913  Analyzer analyzer, List<TupleId> planIds, TableRef joinedTblRef,
914  List<BinaryPredicate> joinConjuncts, List<Expr> joinPredicates) {
915  joinConjuncts.clear();
916  joinPredicates.clear();
917  TupleId tblRefId = joinedTblRef.getId();
918  List<TupleId> tblRefIds = tblRefId.asList();
919  List<Expr> candidates = analyzer.getEqJoinConjuncts(planIds, joinedTblRef);
920  if (candidates == null) return;
921 
922  List<TupleId> joinTupleIds = Lists.newArrayList();
923  joinTupleIds.addAll(planIds);
924  joinTupleIds.add(tblRefId);
925  for (Expr e: candidates) {
926  // Ignore predicate if one of its children is a constant.
927  if (e.getChild(0).isConstant() || e.getChild(1).isConstant()) continue;
928 
929  Expr rhsExpr = null;
930  if (e.getChild(0).isBoundByTupleIds(tblRefIds)) {
931  rhsExpr = e.getChild(0);
932  } else {
933  Preconditions.checkState(e.getChild(1).isBoundByTupleIds(tblRefIds));
934  rhsExpr = e.getChild(1);
935  }
936 
937  Expr lhsExpr = null;
938  if (e.getChild(1).isBoundByTupleIds(planIds)) {
939  lhsExpr = e.getChild(1);
940  } else if (e.getChild(0).isBoundByTupleIds(planIds)) {
941  lhsExpr = e.getChild(0);
942  } else {
943  // not an equi-join condition between lhsIds and rhsId
944  continue;
945  }
946 
947  Preconditions.checkState(lhsExpr != rhsExpr);
948  joinPredicates.add(e);
949  BinaryPredicate joinConjunct =
950  new BinaryPredicate(((BinaryPredicate)e).getOp(), lhsExpr, rhsExpr);
951  joinConjunct.analyzeNoThrow(analyzer);
952  joinConjuncts.add(joinConjunct);
953  }
954  if (!joinPredicates.isEmpty()) return;
955  Preconditions.checkState(joinConjuncts.isEmpty());
956 
957  // construct joinConjunct entries derived from equivalence class membership
958  List<SlotId> lhsSlotIds = Lists.newArrayList();
959  for (SlotDescriptor slotDesc: joinedTblRef.getDesc().getSlots()) {
960  analyzer.getEquivSlots(slotDesc.getId(), planIds, lhsSlotIds);
961  if (!lhsSlotIds.isEmpty()) {
962  // construct a BinaryPredicates in order to get correct casting;
963  // we only do this for one of the equivalent slots, all the other implied
964  // equalities are redundant
965  BinaryPredicate pred =
966  analyzer.createEqPredicate(lhsSlotIds.get(0), slotDesc.getId());
967  joinConjuncts.add(pred);
968  }
969  }
970  }
971 
978  Analyzer analyzer, PlanNode outer, PlanNode inner, TableRef outerRef,
979  TableRef innerRef) throws ImpalaException {
980  Preconditions.checkState(innerRef != null ^ outerRef != null);
981  TableRef tblRef = (innerRef != null) ? innerRef : outerRef;
982 
983  List<BinaryPredicate> eqJoinConjuncts = Lists.newArrayList();
984  List<Expr> eqJoinPredicates = Lists.newArrayList();
985  // get eq join predicates for the TableRefs' ids (not the PlanNodes' ids, which
986  // are materialized)
987  if (innerRef != null) {
989  analyzer, outer.getTblRefIds(), innerRef, eqJoinConjuncts, eqJoinPredicates);
990  // Outer joins should only use On-clause predicates as eqJoinConjuncts.
991  if (!innerRef.getJoinOp().isOuterJoin()) {
992  analyzer.createEquivConjuncts(outer.getTblRefIds(), innerRef.getId(),
993  eqJoinConjuncts);
994  }
995  } else {
997  analyzer, inner.getTblRefIds(), outerRef, eqJoinConjuncts, eqJoinPredicates);
998  // Outer joins should only use On-clause predicates as eqJoinConjuncts.
999  if (!outerRef.getJoinOp().isOuterJoin()) {
1000  analyzer.createEquivConjuncts(inner.getTblRefIds(), outerRef.getId(),
1001  eqJoinConjuncts);
1002  }
1003  // Reverse the lhs/rhs of the join conjuncts.
1004  for (BinaryPredicate eqJoinConjunct: eqJoinConjuncts) {
1005  Expr swapTmp = eqJoinConjunct.getChild(0);
1006  eqJoinConjunct.setChild(0, eqJoinConjunct.getChild(1));
1007  eqJoinConjunct.setChild(1, swapTmp);
1008  }
1009  }
1010 
1011  // Handle implicit cross joins
1012  if (eqJoinConjuncts.isEmpty()) {
1013  // Since our only implementation of semi and outer joins is hash-based, and we do
1014  // not re-order semi and outer joins, we must have eqJoinConjuncts here to execute
1015  // this query.
1016  // TODO Revisit when we add more semi/join implementations.
1017  if (tblRef.getJoinOp().isOuterJoin() ||
1018  tblRef.getJoinOp().isSemiJoin()) {
1019  throw new NotImplementedException(
1020  String.format("%s join with '%s' without equi-join " +
1021  "conjuncts is not supported.",
1022  tblRef.getJoinOp().isOuterJoin() ? "Outer" : "Semi",
1023  innerRef.getUniqueAlias()));
1024  }
1025  CrossJoinNode result = new CrossJoinNode(outer, inner);
1026  result.init(analyzer);
1027  return result;
1028  }
1029 
1030  // Handle explicit cross joins with equi join conditions
1031  if (tblRef.getJoinOp() == JoinOperator.CROSS_JOIN) {
1032  tblRef.setJoinOp(JoinOperator.INNER_JOIN);
1033  }
1034 
1035  analyzer.markConjunctsAssigned(eqJoinPredicates);
1036 
1037  List<Expr> otherJoinConjuncts = Lists.newArrayList();
1038  if (tblRef.getJoinOp().isOuterJoin()) {
1039  // Also assign conjuncts from On clause. All remaining unassigned conjuncts
1040  // that can be evaluated by this join are assigned in createSelectPlan().
1041  otherJoinConjuncts = analyzer.getUnassignedOjConjuncts(tblRef);
1042  } else if (tblRef.getJoinOp().isSemiJoin()) {
1043  // Unassigned conjuncts bound by the invisible tuple id of a semi join must have
1044  // come from the join's On-clause, and therefore, must be added to the other join
1045  // conjuncts to produce correct results.
1046  otherJoinConjuncts =
1047  analyzer.getUnassignedConjuncts(tblRef.getAllTupleIds(), false);
1048  if (tblRef.getJoinOp().isNullAwareLeftAntiJoin()) {
1049  boolean hasNullMatchingEqOperator = false;
1050  // Keep only the null-matching eq conjunct in the eqJoinConjuncts and move
1051  // all the others in otherJoinConjuncts. The BE relies on this
1052  // separation for correct execution of the null-aware left anti join.
1053  Iterator<BinaryPredicate> it = eqJoinConjuncts.iterator();
1054  while (it.hasNext()) {
1055  BinaryPredicate conjunct = it.next();
1056  if (!conjunct.isNullMatchingEq()) {
1057  otherJoinConjuncts.add(conjunct);
1058  it.remove();
1059  } else {
1060  // Only one null-matching eq conjunct is allowed
1061  Preconditions.checkState(!hasNullMatchingEqOperator);
1062  hasNullMatchingEqOperator = true;
1063  }
1064  }
1065  Preconditions.checkState(hasNullMatchingEqOperator);
1066  }
1067  }
1068  analyzer.markConjunctsAssigned(otherJoinConjuncts);
1069 
1070  HashJoinNode result =
1071  new HashJoinNode(outer, inner, tblRef, eqJoinConjuncts, otherJoinConjuncts);
1072  result.init(analyzer);
1073  return result;
1074  }
1075 
1080  private PlanNode createTableRefNode(Analyzer analyzer, TableRef tblRef)
1081  throws ImpalaException {
1082  if (tblRef instanceof BaseTableRef || tblRef instanceof CollectionTableRef) {
1083  return createScanNode(analyzer, tblRef);
1084  } else if (tblRef instanceof InlineViewRef) {
1085  return createInlineViewPlan(analyzer, (InlineViewRef) tblRef);
1086  }
1087  throw new InternalException(
1088  "Unknown TableRef node: " + tblRef.getClass().getSimpleName());
1089  }
1090 
1099  Analyzer analyzer, UnionStmt unionStmt, List<UnionOperand> unionOperands,
1100  PlanNode unionDistinctPlan)
1101  throws ImpalaException {
1102  UnionNode unionNode =
1103  new UnionNode(ctx_.getNextNodeId(), unionStmt.getTupleId());
1104  for (UnionOperand op: unionOperands) {
1105  QueryStmt queryStmt = op.getQueryStmt();
1106  if (op.isDropped()) continue;
1107  if (queryStmt instanceof SelectStmt) {
1108  SelectStmt selectStmt = (SelectStmt) queryStmt;
1109  if (selectStmt.getTableRefs().isEmpty()) {
1110  unionNode.addConstExprList(selectStmt.getBaseTblResultExprs());
1111  continue;
1112  }
1113  }
1114  PlanNode opPlan = createQueryPlan(queryStmt, analyzer, false);
1115  if (opPlan instanceof EmptySetNode) continue;
1116  unionNode.addChild(opPlan, op.getQueryStmt().getBaseTblResultExprs());
1117  }
1118  if (unionDistinctPlan != null) {
1119  Preconditions.checkState(unionStmt.hasDistinctOps());
1120  Preconditions.checkState(unionDistinctPlan instanceof AggregationNode);
1121  unionNode.addChild(unionDistinctPlan,
1122  unionStmt.getDistinctAggInfo().getGroupingExprs());
1123  }
1124  unionNode.init(analyzer);
1125  return unionNode;
1126  }
1127 
1141  private PlanNode createUnionPlan(UnionStmt unionStmt, Analyzer analyzer)
1142  throws ImpalaException {
1143  List<Expr> conjuncts =
1144  analyzer.getUnassignedConjuncts(unionStmt.getTupleId().asList(), false);
1145  if (!unionStmt.hasAnalyticExprs()) {
1146  // Turn unassigned predicates for unionStmt's tupleId_ into predicates for
1147  // the individual operands.
1148  // Do this prior to creating the operands' plan trees so they get a chance to
1149  // pick up propagated predicates.
1150  for (UnionOperand op: unionStmt.getOperands()) {
1151  List<Expr> opConjuncts =
1152  Expr.substituteList(conjuncts, op.getSmap(), analyzer, false);
1153  op.getAnalyzer().registerConjuncts(opConjuncts);
1154  // Some of the opConjuncts have become constant and eval'd to false, or an
1155  // ancestor block is already guaranteed to return empty results.
1156  if (op.getAnalyzer().hasEmptyResultSet()) op.drop();
1157  }
1158  analyzer.markConjunctsAssigned(conjuncts);
1159  } else {
1160  // mark slots referenced by the yet-unassigned conjuncts
1161  analyzer.materializeSlots(conjuncts);
1162  }
1163  // mark slots after predicate propagation but prior to plan tree generation
1164  unionStmt.materializeRequiredSlots(analyzer);
1165 
1166  PlanNode result = null;
1167  // create DISTINCT tree
1168  if (unionStmt.hasDistinctOps()) {
1169  result = createUnionPlan(
1170  analyzer, unionStmt, unionStmt.getDistinctOperands(), null);
1171  result = new AggregationNode(
1172  ctx_.getNextNodeId(), result, unionStmt.getDistinctAggInfo());
1173  result.init(analyzer);
1174  }
1175  // create ALL tree
1176  if (unionStmt.hasAllOps()) {
1177  result = createUnionPlan(analyzer, unionStmt, unionStmt.getAllOperands(), result);
1178  }
1179 
1180  if (unionStmt.hasAnalyticExprs()) {
1181  result = addUnassignedConjuncts(
1182  analyzer, unionStmt.getTupleId().asList(), result);
1183  }
1184  return result;
1185  }
1186 }
PlanNode createJoinPlan(Analyzer analyzer, TableRef leftmostRef, List< Pair< TableRef, PlanNode >> refPlans)
PlanNode createUnionPlan(UnionStmt unionStmt, Analyzer analyzer)
PlanNode createConstantSelectPlan(SelectStmt selectStmt, Analyzer analyzer)
static final ScalarType STRING
Definition: Type.java:53
boolean canEvalPredicate(List< TupleId > tupleIds, Expr e)
Definition: Analyzer.java:1169
void getHashLookupJoinConjuncts(Analyzer analyzer, List< TupleId > planIds, TableRef joinedTblRef, List< BinaryPredicate > joinConjuncts, List< Expr > joinPredicates)
PlanNode createScanNode(Analyzer analyzer, TableRef tblRef)
PlanNode createQueryPlan(QueryStmt stmt, Analyzer analyzer, boolean disableTopN)
Operator(String description, String name, TComparisonOp thriftOp)
int TupleId
Definition: global-types.h:23
PlanNode createEmptyNode(QueryStmt stmt, Analyzer analyzer)
boolean canMigrateConjuncts(InlineViewRef inlineViewRef)
ArrayList< TupleId > getTupleIds()
Definition: PlanNode.java:196
ValueRange createHBaseValueRange(SlotDescriptor d, List< Expr > conjuncts)
PlanNode createAggregationPlan(SelectStmt selectStmt, Analyzer analyzer, PlanNode root)
int SlotId
Definition: global-types.h:24
PlanNode createSelectPlan(SelectStmt selectStmt, Analyzer analyzer)
PlanNode createFromClauseJoinPlan(Analyzer analyzer, List< Pair< TableRef, PlanNode >> refPlans)
UnionNode createUnionPlan(Analyzer analyzer, UnionStmt unionStmt, List< UnionOperand > unionOperands, PlanNode unionDistinctPlan)
ArrayList< Expr > getBaseTblResultExprs()
Definition: QueryStmt.java:280
PlanNode createInlineViewPlan(Analyzer analyzer, InlineViewRef inlineViewRef)
PlanNode createTableRefNode(Analyzer analyzer, TableRef tblRef)
PlanNode addUnassignedConjuncts(Analyzer analyzer, List< TupleId > tupleIds, PlanNode root)
PlanNode createJoinNode(Analyzer analyzer, PlanNode outer, PlanNode inner, TableRef outerRef, TableRef innerRef)
void migrateConjunctsToInlineView(Analyzer analyzer, InlineViewRef inlineViewRef)
PlanNode createCheapestJoinPlan(Analyzer analyzer, List< Pair< TableRef, PlanNode >> refPlans)