You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by jh...@apache.org on 2014/12/27 04:55:13 UTC

[3/3] incubator-calcite git commit: [CALCITE-545] When a projected expression can only have one value, replace with that constant

[CALCITE-545] When a projected expression can only have one value, replace with that constant

Fix conversion of Character to char in generated Java code.


Project: http://git-wip-us.apache.org/repos/asf/incubator-calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-calcite/commit/c9e58edf
Tree: http://git-wip-us.apache.org/repos/asf/incubator-calcite/tree/c9e58edf
Diff: http://git-wip-us.apache.org/repos/asf/incubator-calcite/diff/c9e58edf

Branch: refs/heads/master
Commit: c9e58edf0001af203ecd374d92fb82ed3d152e1d
Parents: 03c2583
Author: Julian Hyde <jh...@apache.org>
Authored: Thu Dec 25 01:08:53 2014 -0800
Committer: Julian Hyde <jh...@apache.org>
Committed: Fri Dec 26 17:54:00 2014 -0800

----------------------------------------------------------------------
 .../adapter/enumerable/RexToLixTranslator.java  |   2 +-
 .../calcite/plan/RelOptPredicateList.java       |  16 ++
 .../rel/rules/ReduceExpressionsRule.java        | 154 ++++++++++++++-----
 .../calcite/rel/rules/ValuesReduceRule.java     |   4 +-
 .../org/apache/calcite/rex/RexExecutorImpl.java |   1 +
 .../org/apache/calcite/rex/RexInputRef.java     |  10 ++
 .../java/org/apache/calcite/rex/RexShuttle.java |  14 ++
 .../java/org/apache/calcite/rex/RexUtil.java    |  28 +++-
 .../apache/calcite/test/RelOptRulesTest.java    |  27 +++-
 .../org/apache/calcite/test/RelOptRulesTest.xml | 108 ++++++++++++-
 10 files changed, 309 insertions(+), 55 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/c9e58edf/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java
index 5b140e5..92bd345 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java
@@ -743,7 +743,7 @@ public class RexToLixTranslator {
         return Expressions.convert_(
             operand, toPrimitive.primitiveClass);
       }
-      if (fromNumber) {
+      if (fromNumber || fromBox == Primitive.CHAR) {
         // Generate "x.shortValue()".
         return Expressions.unbox(operand, toPrimitive);
       } else {

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/c9e58edf/core/src/main/java/org/apache/calcite/plan/RelOptPredicateList.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptPredicateList.java b/core/src/main/java/org/apache/calcite/plan/RelOptPredicateList.java
index 2cfdf13..aad89b4 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptPredicateList.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptPredicateList.java
@@ -17,8 +17,10 @@
 package org.apache.calcite.plan;
 
 import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexUtil;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
 
 /**
  * Predicates that are known to hold in the output of a particular relational
@@ -69,6 +71,20 @@ public class RelOptPredicateList {
     return new RelOptPredicateList(pulledUpPredicatesList,
         leftInferredPredicateList, rightInferredPredicatesList);
   }
+
+  public RelOptPredicateList union(RelOptPredicateList list) {
+    return RelOptPredicateList.of(
+        Iterables.concat(pulledUpPredicates, list.pulledUpPredicates),
+        Iterables.concat(leftInferredPredicates, list.leftInferredPredicates),
+        Iterables.concat(rightInferredPredicates,
+            list.rightInferredPredicates));
+  }
+
+  public RelOptPredicateList shift(int offset) {
+    return RelOptPredicateList.of(RexUtil.shift(pulledUpPredicates, offset),
+        RexUtil.shift(leftInferredPredicates, offset),
+        RexUtil.shift(rightInferredPredicates, offset));
+  }
 }
 
 // End RelOptPredicateList.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/c9e58edf/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java b/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java
index f5a0ef7..862e629 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java
@@ -17,6 +17,7 @@
 package org.apache.calcite.rel.rules;
 
 import org.apache.calcite.plan.RelOptPlanner;
+import org.apache.calcite.plan.RelOptPredicateList;
 import org.apache.calcite.plan.RelOptRule;
 import org.apache.calcite.plan.RelOptRuleCall;
 import org.apache.calcite.plan.RelOptUtil;
@@ -26,6 +27,7 @@ import org.apache.calcite.rel.logical.LogicalCalc;
 import org.apache.calcite.rel.logical.LogicalFilter;
 import org.apache.calcite.rel.logical.LogicalProject;
 import org.apache.calcite.rel.logical.LogicalValues;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.rex.RexBuilder;
@@ -48,11 +50,16 @@ import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.SqlOperator;
 import org.apache.calcite.sql.fun.SqlRowOperator;
 import org.apache.calcite.sql.fun.SqlStdOperatorTable;
+import org.apache.calcite.util.Pair;
 import org.apache.calcite.util.Stacks;
 import org.apache.calcite.util.Util;
 
-import java.util.ArrayList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+
+import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.regex.Pattern;
 
 /**
@@ -87,12 +94,14 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
       new ReduceExpressionsRule(LogicalFilter.class,
           "ReduceExpressionsRule(Filter)") {
         public void onMatch(RelOptRuleCall call) {
-          LogicalFilter filter = call.rel(0);
-          List<RexNode> expList = new ArrayList<RexNode>(1);
-          expList.add(filter.getCondition());
+          final LogicalFilter filter = call.rel(0);
+          final List<RexNode> expList =
+              Lists.newArrayList(filter.getCondition());
           RexNode newConditionExp;
           boolean reduced;
-          if (reduceExpressions(filter, expList)) {
+          final RelOptPredicateList predicates =
+              RelMetadataQuery.getPulledUpPredicates(filter.getInput());
+          if (reduceExpressions(filter, expList, predicates)) {
             assert expList.size() == 1;
             newConditionExp = expList.get(0);
             reduced = true;
@@ -178,9 +187,11 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
           "ReduceExpressionsRule(Project)") {
         public void onMatch(RelOptRuleCall call) {
           LogicalProject project = call.rel(0);
-          List<RexNode> expList =
-              new ArrayList<RexNode>(project.getProjects());
-          if (reduceExpressions(project, expList)) {
+          final RelOptPredicateList predicates =
+              RelMetadataQuery.getPulledUpPredicates(project.getInput());
+          final List<RexNode> expList =
+              Lists.newArrayList(project.getProjects());
+          if (reduceExpressions(project, expList, predicates)) {
             call.transformTo(
                 new LogicalProject(
                     project.getCluster(),
@@ -201,9 +212,15 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
           "ReduceExpressionsRule(Join)") {
         public void onMatch(RelOptRuleCall call) {
           final Join join = call.rel(0);
-          List<RexNode> expList = new ArrayList<RexNode>(1);
-          expList.add(join.getCondition());
-          if (reduceExpressions(join, expList)) {
+          final List<RexNode> expList = Lists.newArrayList(join.getCondition());
+          final int fieldCount = join.getLeft().getRowType().getFieldCount();
+          final RelOptPredicateList leftPredicates =
+              RelMetadataQuery.getPulledUpPredicates(join.getLeft());
+          final RelOptPredicateList rightPredicates =
+              RelMetadataQuery.getPulledUpPredicates(join.getRight());
+          final RelOptPredicateList predicates =
+              leftPredicates.union(rightPredicates.shift(fieldCount));
+          if (reduceExpressions(join, expList, predicates)) {
             call.transformTo(
                 join.copy(
                     join.getTraitSet(),
@@ -228,8 +245,7 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
           final List<RexNode> exprList = program.getExprList();
 
           // Form a list of expressions with sub-expressions fully expanded.
-          final List<RexNode> expandedExprList =
-              new ArrayList<RexNode>(exprList.size());
+          final List<RexNode> expandedExprList = Lists.newArrayList();
           final RexShuttle shuttle =
               new RexShuttle() {
                 public RexNode visitLocalRef(RexLocalRef localRef) {
@@ -239,12 +255,13 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
           for (RexNode expr : exprList) {
             expandedExprList.add(expr.accept(shuttle));
           }
-          if (reduceExpressions(calc, expandedExprList)) {
+          final RelOptPredicateList predicates = RelOptPredicateList.EMPTY;
+          if (reduceExpressions(calc, expandedExprList, predicates)) {
             final RexProgramBuilder builder =
                 new RexProgramBuilder(
                     calc.getInput().getRowType(),
                     calc.getCluster().getRexBuilder());
-            List<RexLocalRef> list = new ArrayList<RexLocalRef>();
+            final List<RexLocalRef> list = Lists.newArrayList();
             for (RexNode expr : expandedExprList) {
               list.add(builder.registerInput(expr));
             }
@@ -306,16 +323,20 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
    *
    * @param rel     Relational expression
    * @param expList List of expressions, modified in place
+   * @param predicates Constraints known to hold on input expressions
    * @return whether reduction found something to change, and succeeded
    */
-  static boolean reduceExpressions(RelNode rel, List<RexNode> expList) {
+  static boolean reduceExpressions(RelNode rel, List<RexNode> expList,
+      RelOptPredicateList predicates) {
     RexBuilder rexBuilder = rel.getCluster().getRexBuilder();
 
     // Find reducible expressions.
-    List<RexNode> constExps = new ArrayList<RexNode>();
-    List<Boolean> addCasts = new ArrayList<Boolean>();
-    List<RexNode> removableCasts = new ArrayList<RexNode>();
-    findReducibleExps(rel.getCluster().getTypeFactory(), expList,
+    final List<RexNode> constExps = Lists.newArrayList();
+    List<Boolean> addCasts = Lists.newArrayList();
+    final List<RexNode> removableCasts = Lists.newArrayList();
+    final ImmutableMap<RexNode, RexLiteral> constants =
+        predicateConstants(predicates);
+    findReducibleExps(rel.getCluster().getTypeFactory(), expList, constants,
         constExps, addCasts, removableCasts);
     if (constExps.isEmpty() && removableCasts.isEmpty()) {
       return false;
@@ -326,19 +347,17 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
     // reducing that argument to a constant first will result in not being
     // able to locate the original cast expression.
     if (!removableCasts.isEmpty()) {
-      List<RexNode> reducedExprs = new ArrayList<RexNode>();
-      List<Boolean> noCasts = new ArrayList<Boolean>();
+      final List<RexNode> reducedExprs = Lists.newArrayList();
       for (RexNode exp : removableCasts) {
         RexCall call = (RexCall) exp;
         reducedExprs.add(call.getOperands().get(0));
-        noCasts.add(false);
       }
       RexReplacer replacer =
           new RexReplacer(
               rexBuilder,
               removableCasts,
               reducedExprs,
-              noCasts);
+              Collections.nCopies(removableCasts.size(), false));
       replacer.mutate(expList);
     }
 
@@ -346,11 +365,26 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
       return true;
     }
 
+    final List<RexNode> constExps2 = Lists.newArrayList(constExps);
+    if (!constants.isEmpty()) {
+      //noinspection unchecked
+      final List<Map.Entry<RexNode, RexNode>> pairs =
+          (List<Map.Entry<RexNode, RexNode>>) (List)
+              Lists.newArrayList(constants.entrySet());
+      RexReplacer replacer =
+          new RexReplacer(
+              rexBuilder,
+              Pair.left(pairs),
+              Pair.right(pairs),
+              Collections.nCopies(pairs.size(), false));
+      replacer.mutate(constExps2);
+    }
+
     // Compute the values they reduce to.
     RelOptPlanner.Executor executor =
         rel.getCluster().getPlanner().getExecutor();
-    List<RexNode> reducedValues = new ArrayList<RexNode>();
-    executor.reduce(rexBuilder, constExps, reducedValues);
+    final List<RexNode> reducedValues = Lists.newArrayList();
+    executor.reduce(rexBuilder, constExps2, reducedValues);
 
     // For Project, we have to be sure to preserve the result
     // types, so always cast regardless of the expression type.
@@ -360,9 +394,7 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
     // like when the expression is a UDR argument, that need to be
     // handled as special cases.
     if (rel instanceof LogicalProject) {
-      for (int i = 0; i < reducedValues.size(); i++) {
-        addCasts.set(i, true);
-      }
+      addCasts = Collections.nCopies(reducedValues.size(), true);
     }
 
     RexReplacer replacer =
@@ -382,6 +414,7 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
    * @param typeFactory    Type factory
    * @param exps           list of candidate expressions to be examined for
    *                       reduction
+   * @param constants      List of expressions known to be constant
    * @param constExps      returns the list of expressions that can be constant
    *                       reduced
    * @param addCasts       indicator for each expression that can be constant
@@ -389,21 +422,35 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
    *                       expression is potentially necessary
    * @param removableCasts returns the list of cast expressions where the cast
    */
-  private static void findReducibleExps(
-      RelDataTypeFactory typeFactory,
-      List<RexNode> exps,
-      List<RexNode> constExps,
-      List<Boolean> addCasts,
+  private static void findReducibleExps(RelDataTypeFactory typeFactory,
+      List<RexNode> exps, ImmutableMap<RexNode, RexLiteral> constants,
+      List<RexNode> constExps, List<Boolean> addCasts,
       List<RexNode> removableCasts) {
     ReducibleExprLocator gardener =
-        new ReducibleExprLocator(typeFactory, constExps, addCasts,
-            removableCasts);
+        new ReducibleExprLocator(typeFactory, constants, constExps,
+            addCasts, removableCasts);
     for (RexNode exp : exps) {
       gardener.analyze(exp);
     }
     assert constExps.size() == addCasts.size();
   }
 
+  private static ImmutableMap<RexNode, RexLiteral> predicateConstants(
+      RelOptPredicateList predicates) {
+    final ImmutableMap.Builder<RexNode, RexLiteral> builder =
+        ImmutableMap.builder();
+    for (RexNode predicate : predicates.pulledUpPredicates) {
+      switch (predicate.getKind()) {
+      case EQUALS:
+        final List<RexNode> operands = ((RexCall) predicate).getOperands();
+        if (operands.get(1) instanceof RexLiteral) {
+          builder.put(operands.get(0), (RexLiteral) operands.get(1));
+        }
+      }
+    }
+    return builder.build();
+  }
+
   //~ Inner Classes ----------------------------------------------------------
 
   /**
@@ -427,10 +474,26 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
       this.addCasts = addCasts;
     }
 
-    @Override public RexNode visitCall(final RexCall call) {
+    @Override public RexNode visitInputRef(RexInputRef inputRef) {
+      RexNode node = visit(inputRef);
+      if (node == null) {
+        return super.visitInputRef(inputRef);
+      }
+      return node;
+    }
+
+    @Override public RexNode visitCall(RexCall call) {
+      RexNode node = visit(call);
+      if (node == null) {
+        return super.visitCall(call);
+      }
+      return node;
+    }
+
+    private RexNode visit(final RexNode call) {
       int i = reducibleExps.indexOf(call);
       if (i == -1) {
-        return super.visitCall(call);
+        return null;
       }
       RexNode replacement = reducedValues.get(i);
       if (addCasts.get(i)
@@ -467,6 +530,8 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
 
     private final List<Constancy> stack;
 
+    private final ImmutableMap<RexNode, RexLiteral> constants;
+
     private final List<RexNode> constExprs;
 
     private final List<Boolean> addCasts;
@@ -476,16 +541,17 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
     private final List<SqlOperator> parentCallTypeStack;
 
     ReducibleExprLocator(RelDataTypeFactory typeFactory,
-        List<RexNode> constExprs, List<Boolean> addCasts,
-        List<RexNode> removableCasts) {
+        ImmutableMap<RexNode, RexLiteral> constants, List<RexNode> constExprs,
+        List<Boolean> addCasts, List<RexNode> removableCasts) {
       // go deep
       super(true);
       this.typeFactory = typeFactory;
+      this.constants = constants;
       this.constExprs = constExprs;
       this.addCasts = addCasts;
       this.removableCasts = removableCasts;
-      this.stack = new ArrayList<Constancy>();
-      this.parentCallTypeStack = new ArrayList<SqlOperator>();
+      this.stack = Lists.newArrayList();
+      this.parentCallTypeStack = Lists.newArrayList();
     }
 
     public void analyze(RexNode exp) {
@@ -541,6 +607,10 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
     }
 
     public Void visitInputRef(RexInputRef inputRef) {
+      if (constants.containsKey(inputRef)) {
+        stack.add(Constancy.REDUCIBLE_CONSTANT);
+        return null;
+      }
       return pushVariable();
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/c9e58edf/core/src/main/java/org/apache/calcite/rel/rules/ValuesReduceRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/ValuesReduceRule.java b/core/src/main/java/org/apache/calcite/rel/rules/ValuesReduceRule.java
index 0f77897..18e0f92 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/ValuesReduceRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/ValuesReduceRule.java
@@ -16,6 +16,7 @@
  */
 package org.apache.calcite.rel.rules;
 
+import org.apache.calcite.plan.RelOptPredicateList;
 import org.apache.calcite.plan.RelOptRule;
 import org.apache.calcite.plan.RelOptRuleCall;
 import org.apache.calcite.plan.RelOptRuleOperand;
@@ -172,7 +173,8 @@ public abstract class ValuesReduceRule extends RelOptRule {
     assert reducibleExps.size() == (values.getTuples().size() * fieldsPerRow);
 
     // Compute the values they reduce to.
-    ReduceExpressionsRule.reduceExpressions(values, reducibleExps);
+    final RelOptPredicateList predicates = RelOptPredicateList.EMPTY;
+    ReduceExpressionsRule.reduceExpressions(values, reducibleExps, predicates);
 
     int changeCount = 0;
     final ImmutableList.Builder<ImmutableList<RexLiteral>> tuplesBuilder =

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/c9e58edf/core/src/main/java/org/apache/calcite/rex/RexExecutorImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexExecutorImpl.java b/core/src/main/java/org/apache/calcite/rex/RexExecutorImpl.java
index 9825367..51e0beb 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexExecutorImpl.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexExecutorImpl.java
@@ -57,6 +57,7 @@ public class RexExecutorImpl implements RelOptPlanner.Executor {
     final RelDataType emptyRowType = typeFactory.builder().build();
     return compile(rexBuilder, constExps, getter, emptyRowType);
   }
+
   private String compile(RexBuilder rexBuilder, List<RexNode> constExps,
       RexToLixTranslator.InputGetter getter, RelDataType rowType) {
     final RexProgramBuilder programBuilder =

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/c9e58edf/core/src/main/java/org/apache/calcite/rex/RexInputRef.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexInputRef.java b/core/src/main/java/org/apache/calcite/rex/RexInputRef.java
index edc38da..0c475db 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexInputRef.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexInputRef.java
@@ -68,6 +68,16 @@ public class RexInputRef extends RexSlot {
 
   //~ Methods ----------------------------------------------------------------
 
+  @Override public boolean equals(Object obj) {
+    return this == obj
+        || obj instanceof RexInputRef
+        && index == ((RexInputRef) obj).index;
+  }
+
+  @Override public int hashCode() {
+    return index;
+  }
+
   /**
    * Creates a reference to a given field in a row type.
    */

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/c9e58edf/core/src/main/java/org/apache/calcite/rex/RexShuttle.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexShuttle.java b/core/src/main/java/org/apache/calcite/rex/RexShuttle.java
index 9c6e46d..e3c0302 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexShuttle.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexShuttle.java
@@ -16,10 +16,13 @@
  */
 package org.apache.calcite.rex;
 
+import com.google.common.base.Function;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
 
 import java.util.ArrayList;
 import java.util.List;
+import javax.annotation.Nullable;
 
 /**
  * Passes over a row-expression, calling a handler method for each node,
@@ -244,6 +247,17 @@ public class RexShuttle implements RexVisitor<RexNode> {
   }
 
   /**
+   * Applies this shuttle to each expression in an iterable.
+   */
+  public final Iterable<RexNode> apply(Iterable<? extends RexNode> iterable) {
+    return Iterables.transform(iterable, new Function<RexNode, RexNode>() {
+      public RexNode apply(@Nullable RexNode t) {
+        return t == null ? null : t.accept(RexShuttle.this);
+      }
+    });
+  }
+
+  /**
    * Applies this shuttle to an expression, or returns null if the expression
    * is null.
    */

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/c9e58edf/core/src/main/java/org/apache/calcite/rex/RexUtil.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexUtil.java b/core/src/main/java/org/apache/calcite/rex/RexUtil.java
index 286f8fb..dd06cb2 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexUtil.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexUtil.java
@@ -1007,12 +1007,14 @@ public class RexUtil {
    * Shifts every {@link RexInputRef} in an expression by {@code offset}.
    */
   public static RexNode shift(RexNode node, final int offset) {
-    return node.accept(
-        new RexShuttle() {
-          @Override public RexNode visitInputRef(RexInputRef input) {
-            return new RexInputRef(input.getIndex() + offset, input.getType());
-          }
-        });
+    return node.accept(new RexShiftShuttle(offset));
+  }
+
+  /**
+   * Shifts every {@link RexInputRef} in an expression by {@code offset}.
+   */
+  public static Iterable<RexNode> shift(Iterable<RexNode> nodes, int offset) {
+    return new RexShiftShuttle(offset).apply(nodes);
   }
 
   /**
@@ -1354,6 +1356,20 @@ public class RexUtil {
       return composeDisjunction(rexBuilder, nodes, false);
     }
   }
+
+  /** Shuttle that adds {@code offset} to each {@link RexInputRef} in an
+   * expression. */
+  private static class RexShiftShuttle extends RexShuttle {
+    private final int offset;
+
+    public RexShiftShuttle(int offset) {
+      this.offset = offset;
+    }
+
+    @Override public RexNode visitInputRef(RexInputRef input) {
+      return new RexInputRef(input.getIndex() + offset, input.getType());
+    }
+  }
 }
 
 // End RexUtil.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/c9e58edf/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
index cf78de9..a9c6c49 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
@@ -16,6 +16,7 @@
  */
 package org.apache.calcite.test;
 
+import org.apache.calcite.plan.RelOptRule;
 import org.apache.calcite.plan.RelOptUtil;
 import org.apache.calcite.plan.hep.HepMatchOrder;
 import org.apache.calcite.plan.hep.HepPlanner;
@@ -76,6 +77,7 @@ import com.google.common.collect.Lists;
 import org.junit.Ignore;
 import org.junit.Test;
 
+import java.util.Arrays;
 import java.util.List;
 
 import static org.junit.Assert.assertTrue;
@@ -1123,7 +1125,7 @@ public class RelOptRulesTest extends RelOptTestBase {
             + "group by rollup(x, y)");
   }
 
-  public void transitiveInference() throws Exception {
+  public void transitiveInference(RelOptRule... extraRules) throws Exception {
     final DiffRepository diffRepos = getDiffRepos();
     String sql = diffRepos.expand(null, "${sql}");
 
@@ -1161,6 +1163,7 @@ public class RelOptRulesTest extends RelOptTestBase {
         .addRuleInstance(FilterProjectTransposeRule.INSTANCE)
         .addRuleInstance(FilterSetOpTransposeRule.INSTANCE)
         .addRuleInstance(JoinPushTransitivePredicatesRule.INSTANCE)
+        .addRuleCollection(Arrays.asList(extraRules))
         .build();
     HepPlanner planner2 = new HepPlanner(program2);
     planner.registerMetadataProviders(list);
@@ -1207,12 +1210,12 @@ public class RelOptRulesTest extends RelOptTestBase {
     transitiveInference();
   }
 
-  @Test public void testTransitiveInferencePreventProjectPullup()
+  @Test public void testTransitiveInferencePreventProjectPullUp()
       throws Exception {
     transitiveInference();
   }
 
-  @Test public void testTransitiveInferencePullupThruAlias() throws Exception {
+  @Test public void testTransitiveInferencePullUpThruAlias() throws Exception {
     transitiveInference();
   }
 
@@ -1249,6 +1252,24 @@ public class RelOptRulesTest extends RelOptTestBase {
     transitiveInference();
   }
 
+  @Test public void testPullConstantIntoProject() throws Exception {
+    transitiveInference(ReduceExpressionsRule.PROJECT_INSTANCE);
+  }
+
+  @Test public void testPullConstantIntoFilter() throws Exception {
+    transitiveInference(ReduceExpressionsRule.FILTER_INSTANCE);
+  }
+
+  @Test public void testPullConstantIntoJoin() throws Exception {
+    transitiveInference(ReduceExpressionsRule.JOIN_INSTANCE);
+  }
+
+  @Test public void testPullConstantIntoJoin2() throws Exception {
+    transitiveInference(ReduceExpressionsRule.JOIN_INSTANCE,
+        ReduceExpressionsRule.PROJECT_INSTANCE,
+        FilterProjectTransposeRule.INSTANCE);
+  }
+
   @Test public void testPushFilterWithRank() throws Exception {
     HepProgram program = new HepProgramBuilder().addRuleInstance(
         FilterProjectTransposeRule.INSTANCE).build();

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/c9e58edf/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
----------------------------------------------------------------------
diff --git a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
index 4ddefe1..23322b5 100644
--- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
@@ -2379,7 +2379,7 @@ LogicalProject(EXPR$0=[1])
 ]]>
         </Resource>
     </TestCase>
-    <TestCase name="testTransitiveInferencePreventProjectPullup">
+    <TestCase name="testTransitiveInferencePreventProjectPullUp">
         <Resource name="sql">
             <![CDATA[select 1 from (select comm as deptno from sales.emp where deptno > 7) d inner join sales.emp e on d.deptno = e.deptno ]]>
         </Resource>
@@ -2404,7 +2404,7 @@ LogicalProject(EXPR$0=[1])
 ]]>
         </Resource>
     </TestCase>
-    <TestCase name="testTransitiveInferencePullupThruAlias">
+    <TestCase name="testTransitiveInferencePullUpThruAlias">
         <Resource name="sql">
             <![CDATA[select 1 from (select comm as deptno from sales.emp where comm > 7) d inner join sales.emp e on d.deptno = e.deptno ]]>
         </Resource>
@@ -2639,6 +2639,110 @@ LogicalProject(EXPR$0=[1])
 ]]>
         </Resource>
     </TestCase>
+    <TestCase name="testPullConstantIntoProject">
+        <Resource name="sql">
+            <![CDATA[select deptno, deptno + 1, empno + deptno from sales.emp where deptno = 10]]>
+        </Resource>
+        <Resource name="planBefore">
+            <![CDATA[
+LogicalProject(DEPTNO=[$7], EXPR$1=[+($7, 1)], EXPR$2=[+($0, $7)])
+  LogicalFilter(condition=[=($7, 10)])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+        <Resource name="planAfter">
+            <![CDATA[
+LogicalProject(DEPTNO=[10], EXPR$1=[11], EXPR$2=[+($0, 10)])
+  LogicalFilter(condition=[=($7, 10)])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+    </TestCase>
+    <TestCase name="testPullConstantIntoFilter">
+        <Resource name="sql">
+            <![CDATA[select * from (select * from sales.emp where deptno = 10) where deptno + 5 > empno]]>
+        </Resource>
+        <Resource name="planBefore">
+            <![CDATA[
+LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
+  LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
+    LogicalFilter(condition=[>(+($7, 5), $0)])
+      LogicalFilter(condition=[=($7, 10)])
+        LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+        <Resource name="planAfter">
+            <![CDATA[
+LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
+  LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
+    LogicalFilter(condition=[>(15, $0)])
+      LogicalFilter(condition=[=($7, 10)])
+        LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+    </TestCase>
+    <TestCase name="testPullConstantIntoJoin">
+        <Resource name="sql">
+            <![CDATA[select *
+    from (select * from sales.emp where empno = 10) as e
+    left join sales.dept as d on e.empno = d.deptno]]>
+        </Resource>
+        <Resource name="planBefore">
+            <![CDATA[
+LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], DEPTNO0=[$9], NAME=[$10])
+  LogicalJoin(condition=[=($0, $9)], joinType=[left])
+    LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
+      LogicalFilter(condition=[=($0, 10)])
+        LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+    LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+]]>
+        </Resource>
+        <Resource name="planAfter">
+            <![CDATA[
+LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], DEPTNO0=[$9], NAME=[$10])
+  LogicalJoin(condition=[true], joinType=[left])
+    LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
+      LogicalFilter(condition=[=($0, 10)])
+        LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+    LogicalFilter(condition=[=($0, 10)])
+      LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+]]>
+        </Resource>
+    </TestCase>
+    <TestCase name="testPullConstantIntoJoin2">
+        <Resource name="sql">
+            <![CDATA[select *
+    from (select * from sales.emp where empno = 10) as e
+    join sales.dept as d on e.empno = d.deptno and e.deptno + e.empno = d.deptno + 5]]>
+        </Resource>
+        <Resource name="planBefore">
+            <![CDATA[
+LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], DEPTNO0=[$9], NAME=[$10])
+  LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], DEPTNO0=[$10], NAME=[$11])
+    LogicalJoin(condition=[AND(=($0, $10), =($9, $12))], joinType=[inner])
+      LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], $f9=[+($7, $0)])
+        LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
+          LogicalFilter(condition=[=($0, 10)])
+            LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+      LogicalProject(DEPTNO=[$0], NAME=[$1], $f2=[+($0, 5)])
+        LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+]]>
+        </Resource>
+        <Resource name="planAfter">
+            <![CDATA[
+LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], DEPTNO0=[$9], NAME=[$10])
+  LogicalProject(EMPNO=[10], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], DEPTNO0=[$10], NAME=[$11])
+    LogicalJoin(condition=[AND(=(10, $10), =($9, $12))], joinType=[inner])
+      LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], $f9=[+($7, $0)])
+        LogicalProject(EMPNO=[10], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
+          LogicalFilter(condition=[=($0, 10)])
+            LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+      LogicalProject(DEPTNO=[10], NAME=[$1], $f2=[15])
+        LogicalFilter(condition=[=($0, 10)])
+          LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+]]>
+        </Resource>
+    </TestCase>
     <TestCase name="testStrengthenJoinType">
         <Resource name="sql">
             <![CDATA[select * from dept where exists (