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;
+ }
+ }
+ }
+ }
+}