private OuterJoinPushDownResult processLimitedOuterJoin(RowExpression inheritedPredicate, RowExpression outerEffectivePredicate, RowExpression innerEffectivePredicate, RowExpression joinPredicate, Collection<VariableReferenceExpression> outerVariables)
        {
            checkArgument(Iterables.all(VariablesExtractor.extractUnique(outerEffectivePredicate), in(outerVariables)), "outerEffectivePredicate must only contain variables from outerVariables");
            checkArgument(Iterables.all(VariablesExtractor.extractUnique(innerEffectivePredicate), not(in(outerVariables))), "innerEffectivePredicate must not contain variables from outerVariables");
            ImmutableList.Builder<RowExpression> outerPushdownConjuncts = ImmutableList.builder();
            ImmutableList.Builder<RowExpression> innerPushdownConjuncts = ImmutableList.builder();
            ImmutableList.Builder<RowExpression> postJoinConjuncts = ImmutableList.builder();
            ImmutableList.Builder<RowExpression> joinConjuncts = ImmutableList.builder();
            // Strip out non-deterministic conjuncts
            postJoinConjuncts.addAll(filter(extractConjuncts(inheritedPredicate), not(determinismEvaluator::isDeterministic)));
            inheritedPredicate = logicalRowExpressions.filterDeterministicConjuncts(inheritedPredicate);
            outerEffectivePredicate = logicalRowExpressions.filterDeterministicConjuncts(outerEffectivePredicate);
            innerEffectivePredicate = logicalRowExpressions.filterDeterministicConjuncts(innerEffectivePredicate);
            joinConjuncts.addAll(filter(extractConjuncts(joinPredicate), not(determinismEvaluator::isDeterministic)));
            joinPredicate = logicalRowExpressions.filterDeterministicConjuncts(joinPredicate);
            // Generate equality inferences
            EqualityInference inheritedInference = createEqualityInference(inheritedPredicate);
            EqualityInference outerInference = createEqualityInference(inheritedPredicate, outerEffectivePredicate);
            EqualityInference.EqualityPartition equalityPartition = inheritedInference.generateEqualitiesPartitionedBy(in(outerVariables));
            RowExpression outerOnlyInheritedEqualities = logicalRowExpressions.combineConjuncts(equalityPartition.getScopeEqualities());
            EqualityInference potentialNullSymbolInference = createEqualityInference(outerOnlyInheritedEqualities, outerEffectivePredicate, innerEffectivePredicate, joinPredicate);
            // See if we can push inherited predicates down
            for (RowExpression conjunct : nonInferableConjuncts(inheritedPredicate)) {
                RowExpression outerRewritten = outerInference.rewriteExpression(conjunct, in(outerVariables));
                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
                    RowExpression innerRewritten = potentialNullSymbolInference.rewriteExpression(outerRewritten, not(in(outerVariables)));
                    if (innerRewritten != null) {
                        innerPushdownConjuncts.add(innerRewritten);
                    }
                }
                else {
                    postJoinConjuncts.add(conjunct);
                }
            }
            // Add the equalities from the inferences back in
            outerPushdownConjuncts.addAll(equalityPartition.getScopeEqualities());
            postJoinConjuncts.addAll(equalityPartition.getScopeComplementEqualities());
            postJoinConjuncts.addAll(equalityPartition.getScopeStraddlingEqualities());
            // See if we can push down any outer effective predicates to the inner side
            for (RowExpression conjunct : nonInferableConjuncts(outerEffectivePredicate)) {
                RowExpression rewritten = potentialNullSymbolInference.rewriteExpression(conjunct, not(in(outerVariables)));
                if (rewritten != null) {
                    innerPushdownConjuncts.add(rewritten);
                }
            }
            // See if we can push down join predicates to the inner side
            for (RowExpression conjunct : nonInferableConjuncts(joinPredicate)) {
                RowExpression innerRewritten = potentialNullSymbolInference.rewriteExpression(conjunct, not(in(outerVariables)));
                if (innerRewritten != null) {
                    innerPushdownConjuncts.add(innerRewritten);
                }
                else {
                    joinConjuncts.add(conjunct);
                }
            }
            // Push outer and join equalities into the inner side. For example:
            // SELECT * FROM nation LEFT OUTER JOIN region ON nation.regionkey = region.regionkey and nation.name = region.name WHERE nation.name = 'blah'
            EqualityInference potentialNullSymbolInferenceWithoutInnerInferred = createEqualityInference(outerOnlyInheritedEqualities, outerEffectivePredicate, joinPredicate);
            innerPushdownConjuncts.addAll(potentialNullSymbolInferenceWithoutInnerInferred.generateEqualitiesPartitionedBy(not(in(outerVariables))).getScopeEqualities());
            // TODO: we can further improve simplifying the equalities by considering other relationships from the outer side
            EqualityInference.EqualityPartition joinEqualityPartition = createEqualityInference(joinPredicate).generateEqualitiesPartitionedBy(not(in(outerVariables)));
            innerPushdownConjuncts.addAll(joinEqualityPartition.getScopeEqualities());
            joinConjuncts.addAll(joinEqualityPartition.getScopeComplementEqualities())
                    .addAll(joinEqualityPartition.getScopeStraddlingEqualities());
            return new OuterJoinPushDownResult(logicalRowExpressions.combineConjuncts(outerPushdownConjuncts.build()),
                    logicalRowExpressions.combineConjuncts(innerPushdownConjuncts.build()),
                    logicalRowExpressions.combineConjuncts(joinConjuncts.build()),
                    logicalRowExpressions.combineConjuncts(postJoinConjuncts.build()));
        }
private OuterJoinPushDownResult processLimitedOuterJoin(RowExpression inheritedPredicate, RowExpression outerEffectivePredicate, RowExpression innerEffectivePredicate, RowExpression joinPredicate, Collection<VariableReferenceExpression> outerVariables)
        {
            checkArgument(Iterables.all(VariablesExtractor.extractUnique(outerEffectivePredicate), in(outerVariables)), "outerEffectivePredicate must only contain variables from outerVariables");
            checkArgument(Iterables.all(VariablesExtractor.extractUnique(innerEffectivePredicate), not(in(outerVariables))), "innerEffectivePredicate must not contain variables from outerVariables");
            ImmutableList.Builder<RowExpression> outerPushdownConjuncts = ImmutableList.builder();
            ImmutableList.Builder<RowExpression> innerPushdownConjuncts = ImmutableList.builder();
            ImmutableList.Builder<RowExpression> postJoinConjuncts = ImmutableList.builder();
            ImmutableList.Builder<RowExpression> joinConjuncts = ImmutableList.builder();
           
            postJoinConjuncts.addAll(filter(extractConjuncts(inheritedPredicate), not(determinismEvaluator::isDeterministic)));
            inheritedPredicate = logicalRowExpressions.filterDeterministicConjuncts(inheritedPredicate);
            outerEffectivePredicate = logicalRowExpressions.filterDeterministicConjuncts(outerEffectivePredicate);
            innerEffectivePredicate = logicalRowExpressions.filterDeterministicConjuncts(innerEffectivePredicate);
            joinConjuncts.addAll(filter(extractConjuncts(joinPredicate), not(determinismEvaluator::isDeterministic)));
            joinPredicate = logicalRowExpressions.filterDeterministicConjuncts(joinPredicate);
           
            EqualityInference inheritedInference = createEqualityInference(inheritedPredicate);
            EqualityInference outerInference = createEqualityInference(inheritedPredicate, outerEffectivePredicate);
            EqualityInference.EqualityPartition equalityPartition = inheritedInference.generateEqualitiesPartitionedBy(in(outerVariables));
            RowExpression outerOnlyInheritedEqualities = logicalRowExpressions.combineConjuncts(equalityPartition.getScopeEqualities());
            EqualityInference potentialNullSymbolInference = createEqualityInference(outerOnlyInheritedEqualities, outerEffectivePredicate, innerEffectivePredicate, joinPredicate);
           
            for (RowExpression conjunct : nonInferableConjuncts(inheritedPredicate)) {
                RowExpression outerRewritten = outerInference.rewriteExpression(conjunct, in(outerVariables));
                if (outerRewritten != null) {
                    outerPushdownConjuncts.add(outerRewritten);
                   
                    RowExpression innerRewritten = potentialNullSymbolInference.rewriteExpression(outerRewritten, not(in(outerVariables)));
                    if (innerRewritten != null) {
                        innerPushdownConjuncts.add(innerRewritten);
                    }
                }
                else {
                    postJoinConjuncts.add(conjunct);
                }
            }
           
            outerPushdownConjuncts.addAll(equalityPartition.getScopeEqualities());
            postJoinConjuncts.addAll(equalityPartition.getScopeComplementEqualities());
            postJoinConjuncts.addAll(equalityPartition.getScopeStraddlingEqualities());
           
            for (RowExpression conjunct : nonInferableConjuncts(outerEffectivePredicate)) {
                RowExpression rewritten = potentialNullSymbolInference.rewriteExpression(conjunct, not(in(outerVariables)));
                if (rewritten != null) {
                    innerPushdownConjuncts.add(rewritten);
                }
            }
           
            for (RowExpression conjunct : nonInferableConjuncts(joinPredicate)) {
                RowExpression innerRewritten = potentialNullSymbolInference.rewriteExpression(conjunct, not(in(outerVariables)));
                if (innerRewritten != null) {
                    innerPushdownConjuncts.add(innerRewritten);
                }
                else {
                    joinConjuncts.add(conjunct);
                }
            }
           
           
            EqualityInference potentialNullSymbolInferenceWithoutInnerInferred = createEqualityInference(outerOnlyInheritedEqualities, outerEffectivePredicate, joinPredicate);
            innerPushdownConjuncts.addAll(potentialNullSymbolInferenceWithoutInnerInferred.generateEqualitiesPartitionedBy(not(in(outerVariables))).getScopeEqualities());
           
            EqualityInference.EqualityPartition joinEqualityPartition = createEqualityInference(joinPredicate).generateEqualitiesPartitionedBy(not(in(outerVariables)));
            innerPushdownConjuncts.addAll(joinEqualityPartition.getScopeEqualities());
            joinConjuncts.addAll(joinEqualityPartition.getScopeComplementEqualities())
                    .addAll(joinEqualityPartition.getScopeStraddlingEqualities());
            return new OuterJoinPushDownResult(logicalRowExpressions.combineConjuncts(outerPushdownConjuncts.build()),
                    logicalRowExpressions.combineConjuncts(innerPushdownConjuncts.build()),
                    logicalRowExpressions.combineConjuncts(joinConjuncts.build()),
                    logicalRowExpressions.combineConjuncts(postJoinConjuncts.build()));
        }
private outerjoinpushdownresult processlimitedouterjoin(rowexpression inheritedpredicate, rowexpression outereffectivepredicate, rowexpression innereffectivepredicate, rowexpression joinpredicate, collection<variablereferenceexpression> outervariables) { checkargument(iterables.all(variablesextractor.extractunique(outereffectivepredicate), in(outervariables)), "outereffectivepredicate must only contain variables from outervariables"); checkargument(iterables.all(variablesextractor.extractunique(innereffectivepredicate), not(in(outervariables))), "innereffectivepredicate must not contain variables from outervariables"); immutablelist.builder<rowexpression> outerpushdownconjuncts = immutablelist.builder(); immutablelist.builder<rowexpression> innerpushdownconjuncts = immutablelist.builder(); immutablelist.builder<rowexpression> postjoinconjuncts = immutablelist.builder(); immutablelist.builder<rowexpression> joinconjuncts = immutablelist.builder(); postjoinconjuncts.addall(filter(extractconjuncts(inheritedpredicate), not(determinismevaluator::isdeterministic))); inheritedpredicate = logicalrowexpressions.filterdeterministicconjuncts(inheritedpredicate); outereffectivepredicate = logicalrowexpressions.filterdeterministicconjuncts(outereffectivepredicate); innereffectivepredicate = logicalrowexpressions.filterdeterministicconjuncts(innereffectivepredicate); joinconjuncts.addall(filter(extractconjuncts(joinpredicate), not(determinismevaluator::isdeterministic))); joinpredicate = logicalrowexpressions.filterdeterministicconjuncts(joinpredicate); equalityinference inheritedinference = createequalityinference(inheritedpredicate); equalityinference outerinference = createequalityinference(inheritedpredicate, outereffectivepredicate); equalityinference.equalitypartition equalitypartition = inheritedinference.generateequalitiespartitionedby(in(outervariables)); rowexpression outeronlyinheritedequalities = logicalrowexpressions.combineconjuncts(equalitypartition.getscopeequalities()); equalityinference potentialnullsymbolinference = createequalityinference(outeronlyinheritedequalities, outereffectivepredicate, innereffectivepredicate, joinpredicate); for (rowexpression conjunct : noninferableconjuncts(inheritedpredicate)) { rowexpression outerrewritten = outerinference.rewriteexpression(conjunct, in(outervariables)); if (outerrewritten != null) { outerpushdownconjuncts.add(outerrewritten); rowexpression innerrewritten = potentialnullsymbolinference.rewriteexpression(outerrewritten, not(in(outervariables))); if (innerrewritten != null) { innerpushdownconjuncts.add(innerrewritten); } } else { postjoinconjuncts.add(conjunct); } } outerpushdownconjuncts.addall(equalitypartition.getscopeequalities()); postjoinconjuncts.addall(equalitypartition.getscopecomplementequalities()); postjoinconjuncts.addall(equalitypartition.getscopestraddlingequalities()); for (rowexpression conjunct : noninferableconjuncts(outereffectivepredicate)) { rowexpression rewritten = potentialnullsymbolinference.rewriteexpression(conjunct, not(in(outervariables))); if (rewritten != null) { innerpushdownconjuncts.add(rewritten); } } for (rowexpression conjunct : noninferableconjuncts(joinpredicate)) { rowexpression innerrewritten = potentialnullsymbolinference.rewriteexpression(conjunct, not(in(outervariables))); if (innerrewritten != null) { innerpushdownconjuncts.add(innerrewritten); } else { joinconjuncts.add(conjunct); } } equalityinference potentialnullsymbolinferencewithoutinnerinferred = createequalityinference(outeronlyinheritedequalities, outereffectivepredicate, joinpredicate); innerpushdownconjuncts.addall(potentialnullsymbolinferencewithoutinnerinferred.generateequalitiespartitionedby(not(in(outervariables))).getscopeequalities()); equalityinference.equalitypartition joinequalitypartition = createequalityinference(joinpredicate).generateequalitiespartitionedby(not(in(outervariables))); innerpushdownconjuncts.addall(joinequalitypartition.getscopeequalities()); joinconjuncts.addall(joinequalitypartition.getscopecomplementequalities()) .addall(joinequalitypartition.getscopestraddlingequalities()); return new outerjoinpushdownresult(logicalrowexpressions.combineconjuncts(outerpushdownconjuncts.build()), logicalrowexpressions.combineconjuncts(innerpushdownconjuncts.build()), logicalrowexpressions.combineconjuncts(joinconjuncts.build()), logicalrowexpressions.combineconjuncts(postjoinconjuncts.build())); }