Hyracks fix for issue 838. This commit adds a rule that takes assigns out of subplans when the assigns only involve free variables

Change-Id: Id0b3bc1116a568b7b7920ffd448bd791ba249dc9
Reviewed-on: http://fulliautomatix.ics.uci.edu:8443/199
Reviewed-by: Yingyi Bu <buyingyi@gmail.com>
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.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 fb3e96a..387d17c 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
@@ -47,8 +47,7 @@
 
     protected boolean descOrSelfIsScanOrJoin(AbstractLogicalOperator op2) {
         LogicalOperatorTag t = op2.getOperatorTag();
-        if (t == LogicalOperatorTag.DATASOURCESCAN || t == LogicalOperatorTag.INNERJOIN
-                || t == LogicalOperatorTag.LEFTOUTERJOIN) {
+        if (isScanOrJoin(t)) {
             return true;
         }
         if (op2.getInputs().size() != 1) {
@@ -61,6 +60,14 @@
         return false;
     }
 
+    protected boolean isScanOrJoin(LogicalOperatorTag t) {
+        if (t == LogicalOperatorTag.DATASOURCESCAN || t == LogicalOperatorTag.INNERJOIN
+                || t == LogicalOperatorTag.LEFTOUTERJOIN) {
+            return true;
+        }
+        return false;
+    }
+
     protected Set<LogicalVariable> computeGbyVarsUsingPksOnly(Set<LogicalVariable> varSet, AbstractLogicalOperator op,
             IOptimizationContext context) throws AlgebricksException {
         PhysicalOptimizationsUtil.computeFDsAndEquivalenceClasses(op, context);
diff --git a/algebricks/algebricks-rewriter/src/main/java/edu/uci/ics/hyracks/algebricks/rewriter/rules/MoveFreeVariableOperatorOutOfSubplanRule.java b/algebricks/algebricks-rewriter/src/main/java/edu/uci/ics/hyracks/algebricks/rewriter/rules/MoveFreeVariableOperatorOutOfSubplanRule.java
new file mode 100644
index 0000000..b01f320
--- /dev/null
+++ b/algebricks/algebricks-rewriter/src/main/java/edu/uci/ics/hyracks/algebricks/rewriter/rules/MoveFreeVariableOperatorOutOfSubplanRule.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2009-2014 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 java.util.HashSet;
+import java.util.ListIterator;
+import java.util.Set;
+
+import org.apache.commons.lang3.mutable.Mutable;
+
+import edu.uci.ics.hyracks.algebricks.common.exceptions.AlgebricksException;
+import edu.uci.ics.hyracks.algebricks.core.algebra.base.ILogicalOperator;
+import edu.uci.ics.hyracks.algebricks.core.algebra.base.ILogicalPlan;
+import edu.uci.ics.hyracks.algebricks.core.algebra.base.IOptimizationContext;
+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.operators.logical.AbstractLogicalOperator;
+import edu.uci.ics.hyracks.algebricks.core.algebra.operators.logical.SubplanOperator;
+import edu.uci.ics.hyracks.algebricks.core.algebra.operators.logical.visitors.VariableUtilities;
+import edu.uci.ics.hyracks.algebricks.core.algebra.util.OperatorPropertiesUtil;
+
+public class MoveFreeVariableOperatorOutOfSubplanRule extends AbstractDecorrelationRule {
+
+    @Override
+    public boolean rewritePost(Mutable<ILogicalOperator> opRef, IOptimizationContext context)
+            throws AlgebricksException {
+        /*
+         * This rule looks for an assign within a subplan that uses only 
+         * variables from outside of the subplan
+         * 
+         * It moves this assign outside of the subplan
+         * 
+         */
+        AbstractLogicalOperator op0 = (AbstractLogicalOperator) opRef.getValue();
+        if (op0.getOperatorTag() != LogicalOperatorTag.SUBPLAN) {
+            return false;
+        }
+        SubplanOperator subplan = (SubplanOperator) op0;
+
+        Mutable<ILogicalOperator> leftRef = subplan.getInputs().get(0);
+        if (((AbstractLogicalOperator) leftRef.getValue()).getOperatorTag() == LogicalOperatorTag.EMPTYTUPLESOURCE) {
+            return false;
+        }
+
+        ListIterator<ILogicalPlan> plansIter = subplan.getNestedPlans().listIterator();
+        ILogicalPlan p = null;
+        while (plansIter.hasNext()) {
+            p = plansIter.next();
+        }
+        if (p == null) {
+            return false;
+        }
+        if (p.getRoots().size() != 1) {
+            return false;
+        }
+        Mutable<ILogicalOperator> opRef1 = p.getRoots().get(0);
+
+        //The root operator will not be movable. Start with the second op
+        AbstractLogicalOperator op1 = (AbstractLogicalOperator) opRef1.getValue();
+        if (op1.getInputs().size() != 1) {
+            return false;
+        }
+        Mutable<ILogicalOperator> op2Ref = op1.getInputs().get(0);
+
+        //Get all variables that come from outside of the loop
+        Set<LogicalVariable> free = new HashSet<LogicalVariable>();
+        OperatorPropertiesUtil.getFreeVariablesInSelfOrDesc(op1, free);
+
+        while (op2Ref != null) {
+            //Get the operator that we want to look at
+            AbstractLogicalOperator op2 = (AbstractLogicalOperator) op2Ref.getValue();
+
+            //Make sure we are looking at subplan with a scan/join
+            if (op2.getInputs().size() != 1 || !descOrSelfIsScanOrJoin(op2)) {
+                return false;
+            }
+            boolean notApplicable = false;
+
+            //Get its used variables
+            Set<LogicalVariable> used = new HashSet<LogicalVariable>();
+            VariableUtilities.getUsedVariables(op2, used);
+
+            //not movable if the operator is not an assign
+            //Might be helpful in the future for other operations in the future
+            if (op2.getOperatorTag() != LogicalOperatorTag.ASSIGN) {
+                notApplicable = true;
+            }
+
+            //Make sure that all of its used variables come from outside
+            for (LogicalVariable var : used) {
+                if (!free.contains(var)) {
+                    notApplicable = true;
+                }
+            }
+
+            if (notApplicable) {
+                op2Ref = op2.getInputs().get(0);
+            } else {
+                //Make the input of op2 be the input of op1
+                op2Ref.setValue(op2.getInputs().get(0).getValue());
+
+                //Make the outside of the subplan the input of op2
+                Mutable<ILogicalOperator> outsideRef = op2.getInputs().get(0);
+                outsideRef.setValue(op0.getInputs().get(0).getValue());
+
+                //Make op2 the input of the subplan
+                Mutable<ILogicalOperator> op2OutsideRef = op0.getInputs().get(0);
+                op2OutsideRef.setValue(op2);
+
+                return true;
+            }
+
+        }
+        return false;
+    }
+
+}