@Override public PlanNode rewriteAggregation( AggregationNode node, Expression inheritedPredicate, PlanRewriter<Expression> planRewriter) { EqualityInference equalityInference = createEqualityInference(inheritedPredicate); List<Expression> pushdownConjuncts = new ArrayList<>(); List<Expression> postAggregationConjuncts = new ArrayList<>(); // Strip out non-deterministic conjuncts postAggregationConjuncts.addAll( ImmutableList.copyOf(filter(extractConjuncts(inheritedPredicate), not(deterministic())))); inheritedPredicate = stripNonDeterministicConjuncts(inheritedPredicate); // Sort non-equality predicates by those that can be pushed down and those that cannot for (Expression conjunct : EqualityInference.nonInferrableConjuncts(inheritedPredicate)) { Expression rewrittenConjunct = equalityInference.rewriteExpression(conjunct, in(node.getGroupBy())); if (rewrittenConjunct != null) { pushdownConjuncts.add(rewrittenConjunct); } else { postAggregationConjuncts.add(conjunct); } } // Add the equality predicates back in EqualityInference.EqualityPartition equalityPartition = equalityInference.generateEqualitiesPartitionedBy(in(node.getGroupBy())); pushdownConjuncts.addAll(equalityPartition.getScopeEqualities()); postAggregationConjuncts.addAll(equalityPartition.getScopeComplementEqualities()); postAggregationConjuncts.addAll(equalityPartition.getScopeStraddlingEqualities()); PlanNode rewrittenSource = planRewriter.rewrite(node.getSource(), combineConjuncts(pushdownConjuncts)); PlanNode output = node; if (rewrittenSource != node.getSource()) { output = new AggregationNode( node.getId(), rewrittenSource, node.getGroupBy(), node.getAggregations(), node.getFunctions(), node.getMasks(), node.getStep(), node.getSampleWeight(), node.getConfidence()); } if (!postAggregationConjuncts.isEmpty()) { output = new FilterNode( idAllocator.getNextId(), output, combineConjuncts(postAggregationConjuncts)); } return output; }
@Override public PlanNode rewriteSemiJoin( SemiJoinNode node, Expression inheritedPredicate, PlanRewriter<Expression> planRewriter) { Expression sourceEffectivePredicate = EffectivePredicateExtractor.extract(node.getSource()); List<Expression> sourceConjuncts = new ArrayList<>(); List<Expression> filteringSourceConjuncts = new ArrayList<>(); List<Expression> postJoinConjuncts = new ArrayList<>(); // TODO: see if there are predicates that can be inferred from the semi join output // Push inherited and source predicates to filtering source via a contrived join predicate // (but needs to avoid touching NULL values in the filtering source) Expression joinPredicate = equalsExpression(node.getSourceJoinSymbol(), node.getFilteringSourceJoinSymbol()); EqualityInference joinInference = createEqualityInference(inheritedPredicate, sourceEffectivePredicate, joinPredicate); for (Expression conjunct : Iterables.concat( EqualityInference.nonInferrableConjuncts(inheritedPredicate), EqualityInference.nonInferrableConjuncts(sourceEffectivePredicate))) { Expression rewrittenConjunct = joinInference.rewriteExpression(conjunct, equalTo(node.getFilteringSourceJoinSymbol())); if (rewrittenConjunct != null && DeterminismEvaluator.isDeterministic(rewrittenConjunct)) { // Alter conjunct to include an OR filteringSourceJoinSymbol IS NULL disjunct Expression rewrittenConjunctOrNull = expressionOrNullSymbols(equalTo(node.getFilteringSourceJoinSymbol())) .apply(rewrittenConjunct); filteringSourceConjuncts.add(rewrittenConjunctOrNull); } } EqualityInference.EqualityPartition joinInferenceEqualityPartition = joinInference.generateEqualitiesPartitionedBy( equalTo(node.getFilteringSourceJoinSymbol())); filteringSourceConjuncts.addAll( ImmutableList.copyOf( transform( joinInferenceEqualityPartition.getScopeEqualities(), expressionOrNullSymbols(equalTo(node.getFilteringSourceJoinSymbol()))))); // Push inheritedPredicates down to the source if they don't involve the semi join output EqualityInference inheritedInference = createEqualityInference(inheritedPredicate); for (Expression conjunct : EqualityInference.nonInferrableConjuncts(inheritedPredicate)) { Expression rewrittenConjunct = inheritedInference.rewriteExpression(conjunct, in(node.getSource().getOutputSymbols())); // Since each source row is reflected exactly once in the output, ok to push // non-deterministic predicates down if (rewrittenConjunct != null) { sourceConjuncts.add(rewrittenConjunct); } else { postJoinConjuncts.add(conjunct); } } // Add the inherited equality predicates back in EqualityInference.EqualityPartition equalityPartition = inheritedInference.generateEqualitiesPartitionedBy( in(node.getSource().getOutputSymbols())); sourceConjuncts.addAll(equalityPartition.getScopeEqualities()); postJoinConjuncts.addAll(equalityPartition.getScopeComplementEqualities()); postJoinConjuncts.addAll(equalityPartition.getScopeStraddlingEqualities()); PlanNode rewrittenSource = planRewriter.rewrite(node.getSource(), combineConjuncts(sourceConjuncts)); PlanNode rewrittenFilteringSource = planRewriter.rewrite( node.getFilteringSource(), combineConjuncts(filteringSourceConjuncts)); PlanNode output = node; if (rewrittenSource != node.getSource() || rewrittenFilteringSource != node.getFilteringSource()) { output = new SemiJoinNode( node.getId(), rewrittenSource, rewrittenFilteringSource, node.getSourceJoinSymbol(), node.getFilteringSourceJoinSymbol(), node.getSemiJoinOutput()); } if (!postJoinConjuncts.isEmpty()) { output = new FilterNode(idAllocator.getNextId(), output, combineConjuncts(postJoinConjuncts)); } return output; }
private OuterJoinPushDownResult processOuterJoin( Expression inheritedPredicate, Expression outerEffectivePredicate, Expression innerEffectivePredicate, Expression joinPredicate, Collection<Symbol> outerSymbols) { checkArgument( Iterables.all( DependencyExtractor.extractUnique(outerEffectivePredicate), in(outerSymbols)), "outerEffectivePredicate must only contain symbols from outerSymbols"); checkArgument( Iterables.all( DependencyExtractor.extractUnique(innerEffectivePredicate), not(in(outerSymbols))), "innerEffectivePredicate must not contain symbols from outerSymbols"); ImmutableList.Builder<Expression> outerPushdownConjuncts = ImmutableList.builder(); ImmutableList.Builder<Expression> innerPushdownConjuncts = ImmutableList.builder(); ImmutableList.Builder<Expression> postJoinConjuncts = ImmutableList.builder(); // Strip out non-deterministic conjuncts postJoinConjuncts.addAll(filter(extractConjuncts(inheritedPredicate), not(deterministic()))); inheritedPredicate = stripNonDeterministicConjuncts(inheritedPredicate); outerEffectivePredicate = stripNonDeterministicConjuncts(outerEffectivePredicate); innerEffectivePredicate = stripNonDeterministicConjuncts(innerEffectivePredicate); joinPredicate = stripNonDeterministicConjuncts(joinPredicate); // Generate equality inferences EqualityInference inheritedInference = createEqualityInference(inheritedPredicate); EqualityInference outerInference = createEqualityInference(inheritedPredicate, outerEffectivePredicate); EqualityInference.EqualityPartition equalityPartition = inheritedInference.generateEqualitiesPartitionedBy(in(outerSymbols)); Expression outerOnlyInheritedEqualities = combineConjuncts(equalityPartition.getScopeEqualities()); EqualityInference potentialNullSymbolInference = createEqualityInference( outerOnlyInheritedEqualities, outerEffectivePredicate, innerEffectivePredicate, joinPredicate); EqualityInference potentialNullSymbolInferenceWithoutInnerInferred = createEqualityInference( outerOnlyInheritedEqualities, outerEffectivePredicate, joinPredicate); // Sort through conjuncts in inheritedPredicate that were not used for inference for (Expression conjunct : EqualityInference.nonInferrableConjuncts(inheritedPredicate)) { Expression outerRewritten = outerInference.rewriteExpression(conjunct, in(outerSymbols)); if (outerRewritten != null) { outerPushdownConjuncts.add(outerRewritten); // A conjunct can only be pushed down into an inner side if it can be rewritten in terms // of the outer side Expression innerRewritten = potentialNullSymbolInference.rewriteExpression(outerRewritten, not(in(outerSymbols))); if (innerRewritten != null) { innerPushdownConjuncts.add(innerRewritten); } } else { postJoinConjuncts.add(conjunct); } } // See if we can push down any outer or join predicates to the inner side for (Expression conjunct : EqualityInference.nonInferrableConjuncts(and(outerEffectivePredicate, joinPredicate))) { Expression rewritten = potentialNullSymbolInference.rewriteExpression(conjunct, not(in(outerSymbols))); if (rewritten != null) { innerPushdownConjuncts.add(rewritten); } } // TODO: consider adding join predicate optimizations to outer joins // Add the equalities from the inferences back in outerPushdownConjuncts.addAll(equalityPartition.getScopeEqualities()); postJoinConjuncts.addAll(equalityPartition.getScopeComplementEqualities()); postJoinConjuncts.addAll(equalityPartition.getScopeStraddlingEqualities()); innerPushdownConjuncts.addAll( potentialNullSymbolInferenceWithoutInnerInferred .generateEqualitiesPartitionedBy(not(in(outerSymbols))) .getScopeEqualities()); return new OuterJoinPushDownResult( combineConjuncts(outerPushdownConjuncts.build()), combineConjuncts(innerPushdownConjuncts.build()), combineConjuncts(postJoinConjuncts.build())); }