Fix issue849.

Change-Id: I799aba828d93e503093e7443d423ef60de6c6668
Reviewed-on: http://fulliautomatix.ics.uci.edu:8443/216
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: Young-Seok Kim <kisskys@gmail.com>
Reviewed-by: Steven Jacobs <sjaco002@ucr.edu>
diff --git a/algebricks/algebricks-rewriter/src/main/java/edu/uci/ics/hyracks/algebricks/rewriter/rules/AbstractDecorrelationRule.java b/algebricks/algebricks-rewriter/src/main/java/edu/uci/ics/hyracks/algebricks/rewriter/rules/AbstractDecorrelationRule.java
index 387d17c..f897791 100644
--- a/algebricks/algebricks-rewriter/src/main/java/edu/uci/ics/hyracks/algebricks/rewriter/rules/AbstractDecorrelationRule.java
+++ b/algebricks/algebricks-rewriter/src/main/java/edu/uci/ics/hyracks/algebricks/rewriter/rules/AbstractDecorrelationRule.java
@@ -62,6 +62,7 @@
 
     protected boolean isScanOrJoin(LogicalOperatorTag t) {
         if (t == LogicalOperatorTag.DATASOURCESCAN || t == LogicalOperatorTag.INNERJOIN
+                || t == LogicalOperatorTag.UNNEST || t == LogicalOperatorTag.UNNEST_MAP
                 || t == LogicalOperatorTag.LEFTOUTERJOIN) {
             return true;
         }
@@ -97,8 +98,6 @@
                             context);
                 }
             }
-            // g.substituteVarInNestedPlans(ov, newVar);
-            // OperatorManipulationUtil.substituteVarRec(lojoin, ov, newVar);
         }
     }
 
diff --git a/algebricks/algebricks-rewriter/src/main/java/edu/uci/ics/hyracks/algebricks/rewriter/rules/LeftOuterJoinToInnerJoinRule.java b/algebricks/algebricks-rewriter/src/main/java/edu/uci/ics/hyracks/algebricks/rewriter/rules/LeftOuterJoinToInnerJoinRule.java
deleted file mode 100644
index 247c10d..0000000
--- a/algebricks/algebricks-rewriter/src/main/java/edu/uci/ics/hyracks/algebricks/rewriter/rules/LeftOuterJoinToInnerJoinRule.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright 2009-2013 by The Regents of the University of California
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * you may obtain a copy of the License from
- * 
- *     http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package edu.uci.ics.hyracks.algebricks.rewriter.rules;
-
-import org.apache.commons.lang3.mutable.Mutable;
-
-import edu.uci.ics.hyracks.algebricks.common.exceptions.AlgebricksException;
-import edu.uci.ics.hyracks.algebricks.common.utils.ListSet;
-import edu.uci.ics.hyracks.algebricks.core.algebra.base.ILogicalExpression;
-import edu.uci.ics.hyracks.algebricks.core.algebra.base.ILogicalOperator;
-import edu.uci.ics.hyracks.algebricks.core.algebra.base.IOptimizationContext;
-import edu.uci.ics.hyracks.algebricks.core.algebra.base.LogicalExpressionTag;
-import edu.uci.ics.hyracks.algebricks.core.algebra.base.LogicalOperatorTag;
-import edu.uci.ics.hyracks.algebricks.core.algebra.base.LogicalVariable;
-import edu.uci.ics.hyracks.algebricks.core.algebra.expressions.ScalarFunctionCallExpression;
-import edu.uci.ics.hyracks.algebricks.core.algebra.expressions.VariableReferenceExpression;
-import edu.uci.ics.hyracks.algebricks.core.algebra.functions.AlgebricksBuiltinFunctions;
-import edu.uci.ics.hyracks.algebricks.core.algebra.operators.logical.AbstractLogicalOperator;
-import edu.uci.ics.hyracks.algebricks.core.algebra.operators.logical.InnerJoinOperator;
-import edu.uci.ics.hyracks.algebricks.core.algebra.operators.logical.LeftOuterJoinOperator;
-import edu.uci.ics.hyracks.algebricks.core.algebra.operators.logical.SelectOperator;
-import edu.uci.ics.hyracks.algebricks.core.algebra.operators.logical.visitors.VariableUtilities;
-import edu.uci.ics.hyracks.algebricks.core.rewriter.base.IAlgebraicRewriteRule;
-
-/**
- * This rule is to convert an outer join into an inner join when possible.
- * 
- * The specific pattern this rule will invoke for is:
- * select not(is-null($v)) // $v is from the right branch of the left outer join below
- *   left-outer-join
- * 
- * The pattern will be rewritten to:
- * inner-join
- * 
- * @author yingyib
- */
-public class LeftOuterJoinToInnerJoinRule implements IAlgebraicRewriteRule {
-
-    @Override
-    public boolean rewritePre(Mutable<ILogicalOperator> opRef, IOptimizationContext context) throws AlgebricksException {
-        return false;
-    }
-
-    @Override
-    public boolean rewritePost(Mutable<ILogicalOperator> opRef, IOptimizationContext context)
-            throws AlgebricksException {
-        AbstractLogicalOperator op = (AbstractLogicalOperator) opRef.getValue();
-        if (op.getOperatorTag() != LogicalOperatorTag.SELECT) {
-            return false;
-        }
-        Mutable<ILogicalOperator> op2Ref = op.getInputs().get(0);
-        AbstractLogicalOperator op2 = (AbstractLogicalOperator) op2Ref.getValue();
-        if (op2.getOperatorTag() != LogicalOperatorTag.LEFTOUTERJOIN) {
-            return false;
-        }
-        SelectOperator selectOp = (SelectOperator) op;
-        LeftOuterJoinOperator joinOp = (LeftOuterJoinOperator) op2;
-        ILogicalExpression condition = selectOp.getCondition().getValue();
-        if (condition.getExpressionTag() != LogicalExpressionTag.FUNCTION_CALL) {
-            return false;
-        }
-        ScalarFunctionCallExpression func = (ScalarFunctionCallExpression) condition;
-        /** check if the filter condition on top of the LOJ is not(is-null($v)), where $v is from the right child of LOJ */
-        if (!convertable(func, joinOp)) {
-            return false;
-        }
-        ILogicalOperator newJoin = new InnerJoinOperator(joinOp.getCondition(), joinOp.getInputs().get(0), joinOp
-                .getInputs().get(1));
-        opRef.setValue(newJoin);
-        context.computeAndSetTypeEnvironmentForOperator(newJoin);
-        return true;
-    }
-
-    /**
-     * check if the condition is not(is-null(var)) and var is from the right branch of the join
-     */
-    private boolean convertable(ScalarFunctionCallExpression func, LeftOuterJoinOperator join)
-            throws AlgebricksException {
-        if (func.getFunctionIdentifier() != AlgebricksBuiltinFunctions.NOT) {
-            return false;
-        }
-        ILogicalExpression arg = func.getArguments().get(0).getValue();
-        if (arg.getExpressionTag() != LogicalExpressionTag.FUNCTION_CALL) {
-            return false;
-        }
-        ScalarFunctionCallExpression func2 = (ScalarFunctionCallExpression) arg;
-        if (func2.getFunctionIdentifier() != AlgebricksBuiltinFunctions.IS_NULL) {
-            return false;
-        }
-        ILogicalExpression arg2 = func2.getArguments().get(0).getValue();
-        if (arg2.getExpressionTag() != LogicalExpressionTag.VARIABLE) {
-            return false;
-        }
-        VariableReferenceExpression varExpr = (VariableReferenceExpression) arg2;
-        LogicalVariable var = varExpr.getVariableReference();
-        ListSet<LogicalVariable> leftVars = new ListSet<LogicalVariable>();
-        ListSet<LogicalVariable> rightVars = new ListSet<LogicalVariable>();
-        VariableUtilities.getLiveVariables(join.getInputs().get(0).getValue(), leftVars);
-        VariableUtilities.getLiveVariables(join.getInputs().get(1).getValue(), rightVars);
-        if (!rightVars.contains(var)) {
-            return false;
-        }
-        return true;
-    }
-
-}
diff --git a/algebricks/algebricks-rewriter/src/main/java/edu/uci/ics/hyracks/algebricks/rewriter/rules/PushSelectIntoJoinRule.java b/algebricks/algebricks-rewriter/src/main/java/edu/uci/ics/hyracks/algebricks/rewriter/rules/PushSelectIntoJoinRule.java
index 76706a9..e29299c 100644
--- a/algebricks/algebricks-rewriter/src/main/java/edu/uci/ics/hyracks/algebricks/rewriter/rules/PushSelectIntoJoinRule.java
+++ b/algebricks/algebricks-rewriter/src/main/java/edu/uci/ics/hyracks/algebricks/rewriter/rules/PushSelectIntoJoinRule.java
@@ -37,6 +37,7 @@
 import edu.uci.ics.hyracks.algebricks.core.algebra.functions.AlgebricksBuiltinFunctions;
 import edu.uci.ics.hyracks.algebricks.core.algebra.operators.logical.AbstractBinaryJoinOperator;
 import edu.uci.ics.hyracks.algebricks.core.algebra.operators.logical.AbstractLogicalOperator;
+import edu.uci.ics.hyracks.algebricks.core.algebra.operators.logical.InnerJoinOperator;
 import edu.uci.ics.hyracks.algebricks.core.algebra.operators.logical.SelectOperator;
 import edu.uci.ics.hyracks.algebricks.core.algebra.operators.logical.visitors.VariableUtilities;
 import edu.uci.ics.hyracks.algebricks.core.algebra.util.OperatorPropertiesUtil;
@@ -151,20 +152,36 @@
             addCondToJoin(select, join, context);
         } else { // push down
             Iterator<Mutable<ILogicalOperator>> branchIter = join.getInputs().iterator();
-
+            ILogicalExpression selectCondition = select.getCondition().getValue();
+            boolean lojToInner = false;
             for (int j = 0; j < intersectsBranch.length; j++) {
                 Mutable<ILogicalOperator> branch = branchIter.next();
                 boolean inter = intersectsBranch[j];
                 if (inter) {
-                    copySelectToBranch(select, branch, context);
+                    if (j > 0 && isLoj) {
+                        // if a left outer join, if the select condition is not-null filtering,
+                        // we rewrite left outer join
+                        // to inner join for this case.
+                        if (containsNotNullFiltering(selectCondition)) {
+                            lojToInner = true;
+                        }
+                    }
+                    if ((j > 0 && isLoj) && containsNullFiltering(selectCondition)) {
+                        // Select is-null($$var) cannot be pushed in the right branch of a LOJ;
+                        notPushedStack.addFirst(select);
+                    } else {
+                        // Conditions for the left branch can always be pushed.
+                        // Other conditions can be pushed to the right branch of a LOJ.
+                        copySelectToBranch(select, branch, context);
+                    }
                 }
-
-                // if a left outer join, we can only push conditions into the
-                // outer branch.
-                if (j == 0 && isLoj) {
-                    // stop at this branch
-                    break;
-                }
+            }
+            if (lojToInner) {
+                // Rewrites left outer join  to inner join.
+                InnerJoinOperator innerJoin = new InnerJoinOperator(join.getCondition());
+                innerJoin.getInputs().addAll(join.getInputs());
+                join = innerJoin;
+                context.computeAndSetTypeEnvironmentForOperator(join);
             }
         }
         ILogicalOperator top = join;
@@ -231,4 +248,62 @@
         branch.setValue(newSelect);
         context.computeAndSetTypeEnvironmentForOperator(newSelect);
     }
+
+    /**
+     * Whether the expression contains a not-null filtering
+     * 
+     * @param expr
+     * @return true if the expression contains a not-null filtering function call; false otherwise.
+     */
+    private boolean containsNotNullFiltering(ILogicalExpression expr) {
+        if (expr.getExpressionTag() != LogicalExpressionTag.FUNCTION_CALL) {
+            return false;
+        }
+        ScalarFunctionCallExpression func = (ScalarFunctionCallExpression) expr;
+        if (func.getFunctionIdentifier() == AlgebricksBuiltinFunctions.AND) {
+            for (Mutable<ILogicalExpression> argumentRef : func.getArguments()) {
+                if (containsNotNullFiltering(argumentRef.getValue())) {
+                    return true;
+                }
+            }
+            return false;
+        }
+        if (func.getFunctionIdentifier() != AlgebricksBuiltinFunctions.NOT) {
+            return false;
+        }
+        ILogicalExpression arg = func.getArguments().get(0).getValue();
+        if (arg.getExpressionTag() != LogicalExpressionTag.FUNCTION_CALL) {
+            return false;
+        }
+        ScalarFunctionCallExpression func2 = (ScalarFunctionCallExpression) arg;
+        if (func2.getFunctionIdentifier() != AlgebricksBuiltinFunctions.IS_NULL) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Whether the expression contains a null filtering
+     * 
+     * @param expr
+     * @return true if the expression contains a null filtering function call; false otherwise.
+     */
+    private boolean containsNullFiltering(ILogicalExpression expr) {
+        if (expr.getExpressionTag() != LogicalExpressionTag.FUNCTION_CALL) {
+            return false;
+        }
+        ScalarFunctionCallExpression func = (ScalarFunctionCallExpression) expr;
+        if (func.getFunctionIdentifier() == AlgebricksBuiltinFunctions.AND) {
+            for (Mutable<ILogicalExpression> argumentRef : func.getArguments()) {
+                if (containsNullFiltering(argumentRef.getValue())) {
+                    return true;
+                }
+            }
+            return false;
+        }
+        if (func.getFunctionIdentifier() != AlgebricksBuiltinFunctions.IS_NULL) {
+            return false;
+        }
+        return true;
+    }
 }
\ No newline at end of file