First version of variable inlining rewrite rule.

git-svn-id: https://asterixdb.googlecode.com/svn/branches/asterix_inline_vars@772 eaa15691-b419-025a-1212-ee371bd00084
diff --git a/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/base/RuleCollections.java b/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/base/RuleCollections.java
index 0f28239..1662b7e 100644
--- a/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/base/RuleCollections.java
+++ b/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/base/RuleCollections.java
@@ -19,6 +19,7 @@
 import java.util.List;
 
 import edu.uci.ics.asterix.optimizer.rules.AsterixInlineVariablesRule;
+import edu.uci.ics.asterix.optimizer.rules.AsterixProperInlineVariablesRule;
 import edu.uci.ics.asterix.optimizer.rules.ByNameToByIndexFieldAccessRule;
 import edu.uci.ics.asterix.optimizer.rules.ConstantFoldingRule;
 import edu.uci.ics.asterix.optimizer.rules.CountVarToCountOneRule;
@@ -116,7 +117,7 @@
 
     public final static List<IAlgebraicRewriteRule> buildCondPushDownAndJoinInferenceRuleCollection() {
         List<IAlgebraicRewriteRule> condPushDownAndJoinInference = new LinkedList<IAlgebraicRewriteRule>();
-
+                
         condPushDownAndJoinInference.add(new PushSelectDownRule());
         condPushDownAndJoinInference.add(new PushDieUpRule());
         condPushDownAndJoinInference.add(new RemoveRedundantListifyRule());
@@ -131,6 +132,7 @@
         condPushDownAndJoinInference.add(new SubplanOutOfGroupRule());
         condPushDownAndJoinInference.add(new InsertOuterJoinRule());
         condPushDownAndJoinInference.add(new AsterixInlineVariablesRule());
+        condPushDownAndJoinInference.add(new AsterixProperInlineVariablesRule());
         condPushDownAndJoinInference.add(new RemoveUnusedAssignAndAggregateRule());
         condPushDownAndJoinInference.add(new FactorRedundantGroupAndDecorVarsRule());
         condPushDownAndJoinInference.add(new PushAggregateIntoGroupbyRule());
diff --git a/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/rules/AsterixProperInlineVariablesRule.java b/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/rules/AsterixProperInlineVariablesRule.java
new file mode 100644
index 0000000..579752a
--- /dev/null
+++ b/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/rules/AsterixProperInlineVariablesRule.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright 2009-2010 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.asterix.optimizer.rules;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.lang3.mutable.Mutable;
+
+import edu.uci.ics.asterix.om.functions.AsterixBuiltinFunctions;
+import edu.uci.ics.hyracks.algebricks.common.exceptions.AlgebricksException;
+import edu.uci.ics.hyracks.algebricks.common.utils.Pair;
+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.AbstractFunctionCallExpression;
+import edu.uci.ics.hyracks.algebricks.core.algebra.expressions.AbstractLogicalExpression;
+import edu.uci.ics.hyracks.algebricks.core.algebra.expressions.IVariableTypeEnvironment;
+import edu.uci.ics.hyracks.algebricks.core.algebra.expressions.VariableReferenceExpression;
+import edu.uci.ics.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+import edu.uci.ics.hyracks.algebricks.core.algebra.operators.logical.AbstractLogicalOperator;
+import edu.uci.ics.hyracks.algebricks.core.algebra.operators.logical.AssignOperator;
+import edu.uci.ics.hyracks.algebricks.core.algebra.operators.logical.ProjectOperator;
+import edu.uci.ics.hyracks.algebricks.core.algebra.operators.logical.visitors.VariableUtilities;
+import edu.uci.ics.hyracks.algebricks.core.algebra.visitors.ILogicalExpressionReferenceTransform;
+import edu.uci.ics.hyracks.algebricks.core.rewriter.base.IAlgebraicRewriteRule;
+
+public class AsterixProperInlineVariablesRule implements IAlgebraicRewriteRule {
+
+    // Map of variables that could be replaced by their producing expression.
+    // Populated during the top-down sweep of the plan
+    private Map<LogicalVariable, ILogicalExpression> varAssignRhs = new HashMap<LogicalVariable, ILogicalExpression>();
+    
+    // Maps from variable to parents of project operators that project that var.
+    private Map<LogicalVariable, List<ILogicalOperator>> affectedProjects = new HashMap<LogicalVariable, List<ILogicalOperator>>();
+    
+    private InlineVariablesVisitor inlineVisitor = new InlineVariablesVisitor(varAssignRhs);
+    
+    // TODO: For now we must exclude these functions to avoid breaking our type casting rules
+    // IntroduceStaticTypeCastRule and IntroduceDynamicTypeCastRule.
+    private static Set<FunctionIdentifier> doNotInlineFuncs = new HashSet<FunctionIdentifier>();
+    static {
+        doNotInlineFuncs.add(AsterixBuiltinFunctions.CLOSED_RECORD_CONSTRUCTOR);
+        doNotInlineFuncs.add(AsterixBuiltinFunctions.OPEN_RECORD_CONSTRUCTOR);
+    }
+    
+    @Override
+    public boolean rewritePost(Mutable<ILogicalOperator> opRef, IOptimizationContext context) {
+        return false;
+    }
+
+    @Override
+    /**
+     * 
+     * Does one big DFS sweep over the plan.
+     * 
+     */
+    public boolean rewritePre(Mutable<ILogicalOperator> opRef, IOptimizationContext context) throws AlgebricksException {
+        varAssignRhs.clear();
+        affectedProjects.clear();
+        inlineVisitor.setContext(context);
+        boolean modified = inlineVariables(opRef, context);
+        if (modified) {
+            context.addToDontApplySet(this, opRef.getValue());
+        }
+        return modified;
+    }
+
+    private boolean inlineVariables(Mutable<ILogicalOperator> opRef, IOptimizationContext context)
+            throws AlgebricksException {
+        AbstractLogicalOperator op = (AbstractLogicalOperator) opRef.getValue();
+        if (context.checkIfInDontApplySet(this, op)) {
+            return false;
+        }
+
+        // Update mapping from variables to expressions during descent.
+        if (op.getOperatorTag() == LogicalOperatorTag.ASSIGN) {
+            AssignOperator assignOp = (AssignOperator) op;
+            List<LogicalVariable> vars = assignOp.getVariables();
+            List<Mutable<ILogicalExpression>> exprs = assignOp.getExpressions();            
+            for (int i = 0; i < vars.size(); i++) {
+                ILogicalExpression expr = exprs.get(i).getValue();
+                // Ignore functions that are in the doNotInline set.
+                if (expr.getExpressionTag() == LogicalExpressionTag.FUNCTION_CALL) {
+                    AbstractFunctionCallExpression funcExpr = (AbstractFunctionCallExpression) expr;
+                    if (doNotInlineFuncs.contains(funcExpr.getFunctionIdentifier())) {
+                        continue;
+                    }
+                }
+                varAssignRhs.put(vars.get(i), exprs.get(i).getValue());
+            }
+        }
+
+        boolean modified = false;
+        for (Mutable<ILogicalOperator> inputOpRef : op.getInputs()) {
+            AbstractLogicalOperator inputOp = (AbstractLogicalOperator) inputOpRef.getValue();
+            if (inputOp.getOperatorTag() == LogicalOperatorTag.PROJECT) {
+                ProjectOperator projectOp = (ProjectOperator) inputOp;
+                List<LogicalVariable> projectVars = projectOp.getVariables();
+                for (LogicalVariable var : projectVars) {
+                    List<ILogicalOperator> projectParents = affectedProjects.get(var);
+                    if (projectParents == null) {
+                        projectParents = new ArrayList<ILogicalOperator>();
+                        affectedProjects.put(var, projectParents);
+                    }
+                    projectParents.add(op);
+                }
+            }
+            
+            if (inlineVariables(inputOpRef, context)) {
+                modified = true;
+            }
+        }
+
+        switch (op.getOperatorTag()) {
+            case WRITE: 
+            case WRITE_RESULT:
+            case INSERT_DELETE:
+            case INDEX_INSERT_DELETE:
+            case PROJECT:
+            // We can currently only order/group by a variable reference expression.
+            case ORDER:
+            case GROUP:
+            case DISTINCT:
+            case AGGREGATE:
+            case INNERJOIN:
+            case LEFTOUTERJOIN: {
+                break;
+            }
+            default: {
+                if (op.acceptExpressionTransform(inlineVisitor)) {
+                    modified = true;
+                }
+            }
+        }
+        
+        if (modified) {
+            context.computeAndSetTypeEnvironmentForOperator(op);
+            context.addToDontApplySet(this, op);
+        }
+
+        return modified;
+    }
+
+    private class InlineVariablesVisitor implements ILogicalExpressionReferenceTransform {
+        private IOptimizationContext context;
+        private final Map<LogicalVariable, ILogicalExpression> varAssignRhs;
+        
+        public InlineVariablesVisitor(Map<LogicalVariable, ILogicalExpression> varAssignRhs) {
+            this.varAssignRhs = varAssignRhs;
+        }
+        
+        public void setContext(IOptimizationContext context) {
+            this.context = context;
+        }
+
+        @Override
+        public boolean transform(Mutable<ILogicalExpression> exprRef) {
+            ILogicalExpression e = exprRef.getValue();
+            switch (((AbstractLogicalExpression) e).getExpressionTag()) {
+                case VARIABLE: {
+                    // look for a required substitution
+                    LogicalVariable var = ((VariableReferenceExpression) e).getVariableReference();
+                    if (context.shouldNotBeInlined(var)) {
+                        return false;
+                    }
+                    ILogicalExpression rhs = varAssignRhs.get(var);
+                    if (rhs == null) {
+                        // Variable was not produced by an assign.
+                        return false;
+                    }
+                    // Replace variable reference with rhs expr.
+                    exprRef.setValue(rhs);
+                    
+                    // Remove affected projects.
+                    List<ILogicalOperator> projectParents = affectedProjects.get(var);
+                    if (projectParents != null) {
+                        for (ILogicalOperator parentOp : projectParents) {
+                            int numInputs = parentOp.getInputs().size();
+                            for (int i = 0; i < numInputs; i++) {
+                                Mutable<ILogicalOperator> inputOpRef = parentOp.getInputs().get(i);
+                                AbstractLogicalOperator inputOp = (AbstractLogicalOperator) inputOpRef.getValue();
+                                if (inputOp.getOperatorTag() != LogicalOperatorTag.PROJECT) {
+                                    continue;
+                                }
+                                ProjectOperator projectOp = (ProjectOperator) inputOp;
+                                if (projectOp.getVariables().contains(var)) {
+                                    // Remove project op.
+                                    parentOp.getInputs().set(i, inputOp.getInputs().get(0));
+                                }
+                            }
+                        }
+                    }
+                    return true;
+                }
+                case FUNCTION_CALL: {
+                    AbstractFunctionCallExpression fce = (AbstractFunctionCallExpression) e;
+                    boolean modified = false;
+                    for (Mutable<ILogicalExpression> arg : fce.getArguments()) {
+                        if (transform(arg)) {
+                            modified = true;
+                        }
+                    }
+                    return modified;
+                }
+                default: {
+                    return false;
+                }
+            }
+        }
+    }
+}