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 2016/10/24 22:01:27 UTC

[2/2] calcite git commit: [CALCITE-1465] Store constants as a derived field in RelOptPredicateList

[CALCITE-1465] Store constants as a derived field in RelOptPredicateList


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

Branch: refs/heads/master
Commit: 0f36aec7fd2c8de1acfe381af7bb4f0690511377
Parents: 2ee0e35
Author: Julian Hyde <jh...@apache.org>
Authored: Mon Oct 24 00:25:29 2016 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Mon Oct 24 15:01:13 2016 -0700

----------------------------------------------------------------------
 .../calcite/plan/RelOptPredicateList.java       |  50 ++++--
 .../calcite/rel/metadata/RelMdPredicates.java   |  22 ++-
 .../AggregateProjectPullUpConstantsRule.java    |   9 +-
 .../rel/rules/ReduceExpressionsRule.java        | 167 ++-----------------
 .../rel/rules/UnionPullUpConstantsRule.java     |  25 ++-
 .../java/org/apache/calcite/rex/RexUtil.java    | 159 ++++++++++++++++++
 .../org/apache/calcite/test/RexProgramTest.java |  61 +++++++
 7 files changed, 293 insertions(+), 200 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/0f36aec7/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 1dce56d..1acc86f 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptPredicateList.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptPredicateList.java
@@ -16,10 +16,13 @@
  */
 package org.apache.calcite.plan;
 
+import org.apache.calcite.rex.RexBuilder;
 import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.rex.RexUtil;
 
+import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 
 /**
  * Predicates that are known to hold in the output of a particular relational
@@ -63,7 +66,8 @@ import com.google.common.collect.ImmutableList;
 public class RelOptPredicateList {
   private static final ImmutableList<RexNode> EMPTY_LIST = ImmutableList.of();
   public static final RelOptPredicateList EMPTY =
-      new RelOptPredicateList(EMPTY_LIST, EMPTY_LIST, EMPTY_LIST);
+      new RelOptPredicateList(EMPTY_LIST, EMPTY_LIST, EMPTY_LIST,
+          ImmutableMap.<RexNode, RexNode>of());
 
   /** Predicates that can be pulled up from the relational expression and its
    * inputs. */
@@ -77,13 +81,20 @@ public class RelOptPredicateList {
    * Empty if the relational expression is not a join. */
   public final ImmutableList<RexNode> rightInferredPredicates;
 
-  private RelOptPredicateList(Iterable<RexNode> pulledUpPredicates,
-      Iterable<RexNode> leftInferredPredicates,
-      Iterable<RexNode> rightInferredPredicates) {
-    this.pulledUpPredicates = ImmutableList.copyOf(pulledUpPredicates);
-    this.leftInferredPredicates = ImmutableList.copyOf(leftInferredPredicates);
+  /** A map of each (e, constant) pair that occurs within
+   * {@link #pulledUpPredicates}. */
+  public final ImmutableMap<RexNode, RexNode> constantMap;
+
+  private RelOptPredicateList(ImmutableList<RexNode> pulledUpPredicates,
+      ImmutableList<RexNode> leftInferredPredicates,
+      ImmutableList<RexNode> rightInferredPredicates,
+      ImmutableMap<RexNode, RexNode> constantMap) {
+    this.pulledUpPredicates = Preconditions.checkNotNull(pulledUpPredicates);
+    this.leftInferredPredicates =
+        Preconditions.checkNotNull(leftInferredPredicates);
     this.rightInferredPredicates =
-        ImmutableList.copyOf(rightInferredPredicates);
+        Preconditions.checkNotNull(rightInferredPredicates);
+    this.constantMap = Preconditions.checkNotNull(constantMap);
   }
 
   /** Creates a RelOptPredicateList with only pulled-up predicates, no inferred
@@ -94,18 +105,19 @@ public class RelOptPredicateList {
    * @param pulledUpPredicates Predicates that apply to the rows returned by the
    * relational expression
    */
-  public static RelOptPredicateList of(Iterable<RexNode> pulledUpPredicates) {
+  public static RelOptPredicateList of(RexBuilder rexBuilder,
+      Iterable<RexNode> pulledUpPredicates) {
     ImmutableList<RexNode> pulledUpPredicatesList =
         ImmutableList.copyOf(pulledUpPredicates);
     if (pulledUpPredicatesList.isEmpty()) {
       return EMPTY;
     }
-    return new RelOptPredicateList(pulledUpPredicatesList, EMPTY_LIST,
-        EMPTY_LIST);
+    return of(rexBuilder, pulledUpPredicatesList, EMPTY_LIST, EMPTY_LIST);
   }
 
   /** Creates a RelOptPredicateList for a join.
    *
+   * @param rexBuilder Rex builder
    * @param pulledUpPredicates Predicates that apply to the rows returned by the
    * relational expression
    * @param leftInferredPredicates Predicates that were inferred from the right
@@ -113,7 +125,8 @@ public class RelOptPredicateList {
    * @param rightInferredPredicates Predicates that were inferred from the left
    *                                input
    */
-  public static RelOptPredicateList of(Iterable<RexNode> pulledUpPredicates,
+  public static RelOptPredicateList of(RexBuilder rexBuilder,
+      Iterable<RexNode> pulledUpPredicates,
       Iterable<RexNode> leftInferredPredicates,
       Iterable<RexNode> rightInferredPredicates) {
     final ImmutableList<RexNode> pulledUpPredicatesList =
@@ -127,12 +140,16 @@ public class RelOptPredicateList {
         && rightInferredPredicatesList.isEmpty()) {
       return EMPTY;
     }
+    final ImmutableMap<RexNode, RexNode> constantMap =
+        RexUtil.predicateConstants(RexNode.class, rexBuilder,
+            pulledUpPredicatesList);
     return new RelOptPredicateList(pulledUpPredicatesList,
-        leftInferredPredicateList, rightInferredPredicatesList);
+        leftInferredPredicateList, rightInferredPredicatesList, constantMap);
   }
 
-  public RelOptPredicateList union(RelOptPredicateList list) {
-    return RelOptPredicateList.of(
+  public RelOptPredicateList union(RexBuilder rexBuilder,
+      RelOptPredicateList list) {
+    return RelOptPredicateList.of(rexBuilder,
         concat(pulledUpPredicates, list.pulledUpPredicates),
         concat(leftInferredPredicates, list.leftInferredPredicates),
         concat(rightInferredPredicates, list.rightInferredPredicates));
@@ -150,8 +167,9 @@ public class RelOptPredicateList {
     }
   }
 
-  public RelOptPredicateList shift(int offset) {
-    return RelOptPredicateList.of(RexUtil.shift(pulledUpPredicates, offset),
+  public RelOptPredicateList shift(RexBuilder rexBuilder, int offset) {
+    return RelOptPredicateList.of(rexBuilder,
+        RexUtil.shift(pulledUpPredicates, offset),
         RexUtil.shift(leftInferredPredicates, offset),
         RexUtil.shift(rightInferredPredicates, offset));
   }

http://git-wip-us.apache.org/repos/asf/calcite/blob/0f36aec7/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java
index e8f3790..ba0d6d6 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java
@@ -217,7 +217,7 @@ public class RelMdPredicates
         projectPullUpPredicates.add(rexBuilder.makeCall(op, args));
       }
     }
-    return RelOptPredicateList.of(projectPullUpPredicates);
+    return RelOptPredicateList.of(rexBuilder, projectPullUpPredicates);
   }
 
   /**
@@ -225,11 +225,12 @@ public class RelMdPredicates
    */
   public RelOptPredicateList getPredicates(Filter filter, RelMetadataQuery mq) {
     final RelNode input = filter.getInput();
+    final RexBuilder rexBuilder = filter.getCluster().getRexBuilder();
     final RelOptPredicateList inputInfo = mq.getPulledUpPredicates(input);
 
     return Util.first(inputInfo, RelOptPredicateList.EMPTY)
-        .union(
-            RelOptPredicateList.of(
+        .union(filter.getCluster().getRexBuilder(),
+            RelOptPredicateList.of(rexBuilder,
                 RelOptUtil.conjunctions(filter.getCondition())));
   }
 
@@ -283,6 +284,7 @@ public class RelMdPredicates
    */
   public RelOptPredicateList getPredicates(Aggregate agg, RelMetadataQuery mq) {
     final RelNode input = agg.getInput();
+    final RexBuilder rexBuilder = agg.getCluster().getRexBuilder();
     final RelOptPredicateList inputInfo = mq.getPulledUpPredicates(input);
     final List<RexNode> aggPullUpPredicates = new ArrayList<>();
 
@@ -309,7 +311,7 @@ public class RelMdPredicates
         aggPullUpPredicates.add(r);
       }
     }
-    return RelOptPredicateList.of(aggPullUpPredicates);
+    return RelOptPredicateList.of(rexBuilder, aggPullUpPredicates);
   }
 
   /**
@@ -362,7 +364,7 @@ public class RelMdPredicates
     if (!disjPred.isAlwaysTrue()) {
       preds.add(disjPred);
     }
-    return RelOptPredicateList.of(preds);
+    return RelOptPredicateList.of(rB, preds);
   }
 
   /**
@@ -388,11 +390,12 @@ public class RelMdPredicates
     if (!Bug.CALCITE_1048_FIXED) {
       return RelOptPredicateList.EMPTY;
     }
+    final RexBuilder rexBuilder = r.getCluster().getRexBuilder();
     RelOptPredicateList list = null;
     for (RelNode r2 : r.getRels()) {
       RelOptPredicateList list2 = mq.getPulledUpPredicates(r2);
       if (list2 != null) {
-        list = list == null ? list2 : list.union(list2);
+        list = list == null ? list2 : list.union(rexBuilder, list2);
       }
     }
     return Util.first(list, RelOptPredicateList.EMPTY);
@@ -572,6 +575,7 @@ public class RelMdPredicates
         }
       }
 
+      final RexBuilder rexBuilder = joinRel.getCluster().getRexBuilder();
       switch (joinType) {
       case INNER:
         Iterable<RexNode> pulledUpPredicates;
@@ -586,14 +590,14 @@ public class RelMdPredicates
                 RelOptUtil.conjunctions(joinRel.getCondition()),
                 inferredPredicates);
         }
-        return RelOptPredicateList.of(pulledUpPredicates,
+        return RelOptPredicateList.of(rexBuilder, pulledUpPredicates,
           leftInferredPredicates, rightInferredPredicates);
       case LEFT:
-        return RelOptPredicateList.of(
+        return RelOptPredicateList.of(rexBuilder,
             RelOptUtil.conjunctions(leftChildPredicates),
             leftInferredPredicates, rightInferredPredicates);
       case RIGHT:
-        return RelOptPredicateList.of(
+        return RelOptPredicateList.of(rexBuilder,
             RelOptUtil.conjunctions(rightChildPredicates),
             inferredPredicates, EMPTY_LIST);
       default:

http://git-wip-us.apache.org/repos/asf/calcite/blob/0f36aec7/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectPullUpConstantsRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectPullUpConstantsRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectPullUpConstantsRule.java
index 3d05e89..f39cdf9 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectPullUpConstantsRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectPullUpConstantsRule.java
@@ -35,8 +35,6 @@ import org.apache.calcite.tools.RelBuilderFactory;
 import org.apache.calcite.util.ImmutableBitSet;
 import org.apache.calcite.util.Pair;
 
-import com.google.common.collect.ImmutableMap;
-
 import java.util.ArrayList;
 import java.util.List;
 import java.util.NavigableMap;
@@ -114,15 +112,12 @@ public class AggregateProjectPullUpConstantsRule extends RelOptRule {
     if (predicates == null) {
       return;
     }
-    final ImmutableMap<RexNode, RexNode> constants =
-        ReduceExpressionsRule.predicateConstants(RexNode.class, rexBuilder,
-            predicates);
     final NavigableMap<Integer, RexNode> map = new TreeMap<>();
     for (int key : aggregate.getGroupSet()) {
       final RexInputRef ref =
           rexBuilder.makeInputRef(aggregate.getInput(), key);
-      if (constants.containsKey(ref)) {
-        map.put(key, constants.get(ref));
+      if (predicates.constantMap.containsKey(ref)) {
+        map.put(key, predicates.constantMap.get(ref));
       }
     }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/0f36aec7/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 53b4d42..80a1c57 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
@@ -68,11 +68,8 @@ import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Deque;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import java.util.regex.Pattern;
 
 /**
@@ -299,8 +296,10 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
           mq.getPulledUpPredicates(join.getLeft());
       final RelOptPredicateList rightPredicates =
           mq.getPulledUpPredicates(join.getRight());
+      final RexBuilder rexBuilder = join.getCluster().getRexBuilder();
       final RelOptPredicateList predicates =
-          leftPredicates.union(rightPredicates.shift(fieldCount));
+          leftPredicates.union(rexBuilder,
+              rightPredicates.shift(rexBuilder, fieldCount));
       if (!reduceExpressions(join, expList, predicates, true)) {
         return;
       }
@@ -487,10 +486,8 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
     final List<RexNode> constExps = Lists.newArrayList();
     List<Boolean> addCasts = Lists.newArrayList();
     final List<RexNode> removableCasts = Lists.newArrayList();
-    final ImmutableMap<RexNode, RexNode> constants =
-        predicateConstants(RexNode.class, rexBuilder, predicates);
-    findReducibleExps(rel.getCluster().getTypeFactory(), expList, constants,
-        constExps, addCasts, removableCasts);
+    findReducibleExps(rel.getCluster().getTypeFactory(), expList,
+        predicates.constantMap, constExps, addCasts, removableCasts);
     if (constExps.isEmpty() && removableCasts.isEmpty()) {
       return false;
     }
@@ -519,11 +516,11 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
     }
 
     final List<RexNode> constExps2 = Lists.newArrayList(constExps);
-    if (!constants.isEmpty()) {
+    if (!predicates.constantMap.isEmpty()) {
       //noinspection unchecked
       final List<Map.Entry<RexNode, RexNode>> pairs =
           (List<Map.Entry<RexNode, RexNode>>) (List)
-              Lists.newArrayList(constants.entrySet());
+              Lists.newArrayList(predicates.constantMap.entrySet());
       RexReplacer replacer =
           new RexReplacer(
               rexBuilder,
@@ -615,152 +612,14 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
    *           definition of constant, or {@link RexNode} to use
    *           {@link RexUtil#isConstant(RexNode)}
    * @return Map from values to constants
-   */
-  public static <C extends RexNode> ImmutableMap<RexNode, C> predicateConstants(
-          Class<C> clazz, RexBuilder rexBuilder, RelOptPredicateList predicates) {
-    // We cannot use an ImmutableMap.Builder here. If there are multiple entries
-    // with the same key (e.g. "WHERE deptno = 1 AND deptno = 2"), it doesn't
-    // matter which we take, so the latter will replace the former.
-    // The basic idea is to find all the pairs of RexNode = RexLiteral
-    // (1) If 'predicates' contain a non-EQUALS, we bail out.
-    // (2) It is OK if a RexNode is equal to the same RexLiteral several times,
-    // (e.g. "WHERE deptno = 1 AND deptno = 1")
-    // (3) It will return false if there are inconsistent constraints (e.g.
-    // "WHERE deptno = 1 AND deptno = 2")
-    final Map<RexNode, C> map = new HashMap<>();
-    final Set<RexNode> excludeSet = new HashSet<>();
-    for (RexNode predicate : predicates.pulledUpPredicates) {
-      gatherConstraints(clazz, predicate, map, excludeSet, rexBuilder);
-    }
-    final ImmutableMap.Builder<RexNode, C> builder =
-        ImmutableMap.builder();
-    for (Map.Entry<RexNode, C> entry : map.entrySet()) {
-      RexNode rexNode = entry.getKey();
-      if (!overlap(rexNode, excludeSet)) {
-        builder.put(rexNode, entry.getValue());
-      }
-    }
-    return builder.build();
-  }
-
-  private static boolean overlap(RexNode rexNode, Set<RexNode> set) {
-    if (rexNode instanceof RexCall) {
-      for (RexNode r : ((RexCall) rexNode).getOperands()) {
-        if (overlap(r, set)) {
-          return true;
-        }
-      }
-      return false;
-    } else {
-      return set.contains(rexNode);
-    }
-  }
-
-  /** Tries to decompose the RexNode which is a RexCall into non-literal
-   * RexNodes. */
-  private static void decompose(Set<RexNode> set, RexNode rexNode) {
-    if (rexNode instanceof RexCall) {
-      for (RexNode r : ((RexCall) rexNode).getOperands()) {
-        decompose(set, r);
-      }
-    } else if (!(rexNode instanceof RexLiteral)) {
-      set.add(rexNode);
-    }
-  }
-
-  private static <C extends RexNode> void gatherConstraints(Class<C> clazz,
-      RexNode predicate, Map<RexNode, C> map, Set<RexNode> excludeSet,
-      RexBuilder rexBuilder) {
-    if (predicate.getKind() != SqlKind.EQUALS
-            && predicate.getKind() != SqlKind.IS_NULL) {
-      decompose(excludeSet, predicate);
-      return;
-    }
-    final List<RexNode> operands = ((RexCall) predicate).getOperands();
-    if (operands.size() != 2 && predicate.getKind() == SqlKind.EQUALS) {
-      decompose(excludeSet, predicate);
-      return;
-    }
-    // if it reaches here, we have rexNode equals rexNode
-    final RexNode left;
-    final RexNode right;
-    if (predicate.getKind() == SqlKind.EQUALS) {
-      left = operands.get(0);
-      right = operands.get(1);
-    } else {
-      left = operands.get(0);
-      right = rexBuilder.makeNullLiteral(left.getType().getSqlTypeName());
-    }
-    // note that literals are immutable too and they can only be compared through
-    // values.
-    gatherConstraint(clazz, left, right, map, excludeSet, rexBuilder);
-    gatherConstraint(clazz, right, left, map, excludeSet, rexBuilder);
-  }
-
-  private static <C extends RexNode> void gatherConstraint(Class<C> clazz,
-      RexNode left, RexNode right, Map<RexNode, C> map, Set<RexNode> excludeSet,
-      RexBuilder rexBuilder) {
-    if (!clazz.isInstance(right)) {
-      return;
-    }
-    if (!RexUtil.isConstant(right)) {
-      return;
-    }
-    C constant = clazz.cast(right);
-    if (excludeSet.contains(left)) {
-      return;
-    }
-    final C existedValue = map.get(left);
-    if (existedValue == null) {
-      switch (left.getKind()) {
-      case CAST:
-        // Convert "CAST(c) = literal" to "c = literal", as long as it is a
-        // widening cast.
-        final RexNode operand = ((RexCall) left).getOperands().get(0);
-        if (canAssignFrom(left.getType(), operand.getType())) {
-          final RexNode castRight =
-              rexBuilder.makeCast(operand.getType(), constant);
-          if (castRight instanceof RexLiteral) {
-            left = operand;
-            constant = clazz.cast(castRight);
-          }
-        }
-      }
-      map.put(left, constant);
-    } else {
-      if (existedValue instanceof RexLiteral
-          && constant instanceof RexLiteral
-          && !((RexLiteral) existedValue).getValue()
-              .equals(((RexLiteral) constant).getValue())) {
-        // we found conflicting values, e.g. left = 10 and left = 20
-        map.remove(left);
-        excludeSet.add(left);
-      }
-    }
-  }
-
-  /** Returns whether a value of {@code type2} can be assigned to a variable
-   * of {@code type1}.
    *
-   * <p>For example:
-   * <ul>
-   *   <li>{@code canAssignFrom(BIGINT, TINYINT)} returns {@code true}</li>
-   *   <li>{@code canAssignFrom(TINYINT, BIGINT)} returns {@code false}</li>
-   *   <li>{@code canAssignFrom(BIGINT, VARCHAR)} returns {@code false}</li>
-   * </ul>
+   * @deprecated Use {@link RelOptPredicateList#constantMap}
    */
-  private static boolean canAssignFrom(RelDataType type1, RelDataType type2) {
-    final SqlTypeName name1 = type1.getSqlTypeName();
-    final SqlTypeName name2 = type2.getSqlTypeName();
-    if (name1.getFamily() == name2.getFamily()) {
-      switch (name1.getFamily()) {
-      case NUMERIC:
-        return name1.compareTo(name2) >= 0;
-      default:
-        return true;
-      }
-    }
-    return false;
+  @Deprecated // to be removed before 2.0
+  public static <C extends RexNode> ImmutableMap<RexNode, C> predicateConstants(
+      Class<C> clazz, RexBuilder rexBuilder, RelOptPredicateList predicates) {
+    return RexUtil.predicateConstants(clazz, rexBuilder,
+        predicates.pulledUpPredicates);
   }
 
   /** Pushes predicates into a CASE.

http://git-wip-us.apache.org/repos/asf/calcite/blob/0f36aec7/core/src/main/java/org/apache/calcite/rel/rules/UnionPullUpConstantsRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/UnionPullUpConstantsRule.java b/core/src/main/java/org/apache/calcite/rel/rules/UnionPullUpConstantsRule.java
index b59b08d..39fd9e1 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/UnionPullUpConstantsRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/UnionPullUpConstantsRule.java
@@ -26,6 +26,7 @@ import org.apache.calcite.rel.core.Union;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.rel.type.RelDataTypeField;
 import org.apache.calcite.rex.RexBuilder;
+import org.apache.calcite.rex.RexInputRef;
 import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.rex.RexUtil;
 import org.apache.calcite.tools.RelBuilder;
@@ -73,14 +74,10 @@ public class UnionPullUpConstantsRule extends RelOptRule {
       return;
     }
 
-    Map<RexNode, RexNode> conditionsExtracted =
-        ReduceExpressionsRule.predicateConstants(RexNode.class, rexBuilder,
-            predicates);
-    Map<RexNode, RexNode> constants = new HashMap<>();
-    for (int i = 0; i < count; i++) {
-      RexNode expr = rexBuilder.makeInputRef(union, i);
-      if (conditionsExtracted.containsKey(expr)) {
-        constants.put(expr, conditionsExtracted.get(expr));
+    final Map<Integer, RexNode> constants = new HashMap<>();
+    for (Map.Entry<RexNode, RexNode> e : predicates.constantMap.entrySet()) {
+      if (e.getKey() instanceof RexInputRef) {
+        constants.put(((RexInputRef) e.getKey()).getIndex(), e.getValue());
       }
     }
 
@@ -95,17 +92,17 @@ public class UnionPullUpConstantsRule extends RelOptRule {
     List<String> topChildExprsFields = new ArrayList<>();
     List<RexNode> refs = new ArrayList<>();
     ImmutableBitSet.Builder refsIndexBuilder = ImmutableBitSet.builder();
-    for (int i = 0; i < count; i++) {
-      RexNode expr = rexBuilder.makeInputRef(union, i);
-      RelDataTypeField field = fields.get(i);
-      if (constants.containsKey(expr)) {
-        topChildExprs.add(constants.get(expr));
+    for (RelDataTypeField field : fields) {
+      final RexNode constant = constants.get(field.getIndex());
+      if (constant != null) {
+        topChildExprs.add(constant);
         topChildExprsFields.add(field.getName());
       } else {
+        final RexNode expr = rexBuilder.makeInputRef(union, field.getIndex());
         topChildExprs.add(expr);
         topChildExprsFields.add(field.getName());
         refs.add(expr);
-        refsIndexBuilder.set(i);
+        refsIndexBuilder.set(field.getIndex());
       }
     }
     ImmutableBitSet refsIndex = refsIndexBuilder.build();

http://git-wip-us.apache.org/repos/asf/calcite/blob/0f36aec7/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 fd971bd..5766177 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexUtil.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexUtil.java
@@ -49,6 +49,7 @@ import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
 import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
@@ -309,6 +310,164 @@ public class RexUtil {
     return false;
   }
 
+  /** Creates a map containing each (e, constant) pair that occurs within
+   * a predicate list.
+   *
+   * @param clazz Class of expression that is considered constant
+   * @param rexBuilder Rex builder
+   * @param predicates Predicate list
+   * @param <C> what to consider a constant: {@link RexLiteral} to use a narrow
+   *           definition of constant, or {@link RexNode} to use
+   *           {@link RexUtil#isConstant(RexNode)}
+   * @return Map from values to constants
+   */
+  public static <C extends RexNode> ImmutableMap<RexNode, C> predicateConstants(
+      Class<C> clazz, RexBuilder rexBuilder, List<RexNode> predicates) {
+    // We cannot use an ImmutableMap.Builder here. If there are multiple entries
+    // with the same key (e.g. "WHERE deptno = 1 AND deptno = 2"), it doesn't
+    // matter which we take, so the latter will replace the former.
+    // The basic idea is to find all the pairs of RexNode = RexLiteral
+    // (1) If 'predicates' contain a non-EQUALS, we bail out.
+    // (2) It is OK if a RexNode is equal to the same RexLiteral several times,
+    // (e.g. "WHERE deptno = 1 AND deptno = 1")
+    // (3) It will return false if there are inconsistent constraints (e.g.
+    // "WHERE deptno = 1 AND deptno = 2")
+    final Map<RexNode, C> map = new HashMap<>();
+    final Set<RexNode> excludeSet = new HashSet<>();
+    for (RexNode predicate : predicates) {
+      gatherConstraints(clazz, predicate, map, excludeSet, rexBuilder);
+    }
+    final ImmutableMap.Builder<RexNode, C> builder =
+        ImmutableMap.builder();
+    for (Map.Entry<RexNode, C> entry : map.entrySet()) {
+      RexNode rexNode = entry.getKey();
+      if (!overlap(rexNode, excludeSet)) {
+        builder.put(rexNode, entry.getValue());
+      }
+    }
+    return builder.build();
+  }
+
+  private static boolean overlap(RexNode rexNode, Set<RexNode> set) {
+    if (rexNode instanceof RexCall) {
+      for (RexNode r : ((RexCall) rexNode).getOperands()) {
+        if (overlap(r, set)) {
+          return true;
+        }
+      }
+      return false;
+    } else {
+      return set.contains(rexNode);
+    }
+  }
+
+  /** Tries to decompose the RexNode which is a RexCall into non-literal
+   * RexNodes. */
+  private static void decompose(Set<RexNode> set, RexNode rexNode) {
+    if (rexNode instanceof RexCall) {
+      for (RexNode r : ((RexCall) rexNode).getOperands()) {
+        decompose(set, r);
+      }
+    } else if (!(rexNode instanceof RexLiteral)) {
+      set.add(rexNode);
+    }
+  }
+
+  private static <C extends RexNode> void gatherConstraints(Class<C> clazz,
+      RexNode predicate, Map<RexNode, C> map, Set<RexNode> excludeSet,
+      RexBuilder rexBuilder) {
+    if (predicate.getKind() != SqlKind.EQUALS
+            && predicate.getKind() != SqlKind.IS_NULL) {
+      decompose(excludeSet, predicate);
+      return;
+    }
+    final List<RexNode> operands = ((RexCall) predicate).getOperands();
+    if (operands.size() != 2 && predicate.getKind() == SqlKind.EQUALS) {
+      decompose(excludeSet, predicate);
+      return;
+    }
+    // if it reaches here, we have rexNode equals rexNode
+    final RexNode left;
+    final RexNode right;
+    if (predicate.getKind() == SqlKind.EQUALS) {
+      left = operands.get(0);
+      right = operands.get(1);
+    } else {
+      left = operands.get(0);
+      right = rexBuilder.makeNullLiteral(left.getType().getSqlTypeName());
+    }
+    // Note that literals are immutable too, and they can only be compared
+    // through values.
+    gatherConstraint(clazz, left, right, map, excludeSet, rexBuilder);
+    gatherConstraint(clazz, right, left, map, excludeSet, rexBuilder);
+  }
+
+  private static <C extends RexNode> void gatherConstraint(Class<C> clazz,
+      RexNode left, RexNode right, Map<RexNode, C> map, Set<RexNode> excludeSet,
+      RexBuilder rexBuilder) {
+    if (!clazz.isInstance(right)) {
+      return;
+    }
+    if (!isConstant(right)) {
+      return;
+    }
+    C constant = clazz.cast(right);
+    if (excludeSet.contains(left)) {
+      return;
+    }
+    final C existedValue = map.get(left);
+    if (existedValue == null) {
+      switch (left.getKind()) {
+      case CAST:
+        // Convert "CAST(c) = literal" to "c = literal", as long as it is a
+        // widening cast.
+        final RexNode operand = ((RexCall) left).getOperands().get(0);
+        if (canAssignFrom(left.getType(), operand.getType())) {
+          final RexNode castRight =
+              rexBuilder.makeCast(operand.getType(), constant);
+          if (castRight instanceof RexLiteral) {
+            left = operand;
+            constant = clazz.cast(castRight);
+          }
+        }
+      }
+      map.put(left, constant);
+    } else {
+      if (existedValue instanceof RexLiteral
+          && constant instanceof RexLiteral
+          && !((RexLiteral) existedValue).getValue()
+              .equals(((RexLiteral) constant).getValue())) {
+        // we found conflicting values, e.g. left = 10 and left = 20
+        map.remove(left);
+        excludeSet.add(left);
+      }
+    }
+  }
+
+  /** Returns whether a value of {@code type2} can be assigned to a variable
+   * of {@code type1}.
+   *
+   * <p>For example:
+   * <ul>
+   *   <li>{@code canAssignFrom(BIGINT, TINYINT)} returns {@code true}</li>
+   *   <li>{@code canAssignFrom(TINYINT, BIGINT)} returns {@code false}</li>
+   *   <li>{@code canAssignFrom(BIGINT, VARCHAR)} returns {@code false}</li>
+   * </ul>
+   */
+  private static boolean canAssignFrom(RelDataType type1, RelDataType type2) {
+    final SqlTypeName name1 = type1.getSqlTypeName();
+    final SqlTypeName name2 = type2.getSqlTypeName();
+    if (name1.getFamily() == name2.getFamily()) {
+      switch (name1.getFamily()) {
+      case NUMERIC:
+        return name1.compareTo(name2) >= 0;
+      default:
+        return true;
+      }
+    }
+    return false;
+  }
+
   /**
    * Walks over an expression and determines whether it is constant.
    */

http://git-wip-us.apache.org/repos/asf/calcite/blob/0f36aec7/core/src/test/java/org/apache/calcite/test/RexProgramTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/RexProgramTest.java b/core/src/test/java/org/apache/calcite/test/RexProgramTest.java
index 9481d2f..641346d 100644
--- a/core/src/test/java/org/apache/calcite/test/RexProgramTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RexProgramTest.java
@@ -44,6 +44,7 @@ import org.apache.calcite.util.TestUtil;
 import org.apache.calcite.util.Util;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.LinkedHashMultimap;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Multimap;
@@ -56,6 +57,8 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Calendar;
 import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
 
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.is;
@@ -1186,6 +1189,64 @@ public class RexProgramTest {
     c.set(Calendar.SECOND, s);
     return c;
   }
+
+  @Test public void testConstantMap() {
+    final RelDataType intType = typeFactory.createSqlType(SqlTypeName.INTEGER);
+    final RelDataType rowType = typeFactory.builder()
+        .add("a", intType)
+        .add("b", intType)
+        .add("c", intType)
+        .add("d", intType)
+        .add("e", intType)
+        .build();
+
+    final RexDynamicParam range = rexBuilder.makeDynamicParam(rowType, 0);
+    final RexNode aRef = rexBuilder.makeFieldAccess(range, 0);
+    final RexNode bRef = rexBuilder.makeFieldAccess(range, 1);
+    final RexNode cRef = rexBuilder.makeFieldAccess(range, 2);
+    final RexNode dRef = rexBuilder.makeFieldAccess(range, 3);
+    final RexNode eRef = rexBuilder.makeFieldAccess(range, 4);
+    final RexLiteral literal1 = rexBuilder.makeExactLiteral(BigDecimal.ONE);
+    final RexLiteral literal2 = rexBuilder.makeExactLiteral(BigDecimal.valueOf(2));
+
+    final ImmutableMap<RexNode, RexNode> map =
+        RexUtil.predicateConstants(RexNode.class, rexBuilder,
+            ImmutableList.of(eq(aRef, bRef),
+                eq(cRef, literal1),
+                eq(cRef, aRef),
+                eq(dRef, eRef)));
+    assertThat(getString(map),
+        is("{1=?0.c, ?0.a=?0.b, ?0.b=?0.a, ?0.c=1, ?0.d=?0.e, ?0.e=?0.d}"));
+
+    // Contradictory constraints yield no constants
+    final RexNode ref0 = rexBuilder.makeInputRef(rowType, 0);
+    final RexNode ref1 = rexBuilder.makeInputRef(rowType, 1);
+    final ImmutableMap<RexNode, RexNode> map2 =
+        RexUtil.predicateConstants(RexNode.class, rexBuilder,
+            ImmutableList.of(eq(ref0, literal1),
+                eq(ref0, literal2)));
+    assertThat(getString(map2), is("{}"));
+
+    // Contradictory constraints on field accesses SHOULD yield no constants
+    // but currently there's a bug
+    final ImmutableMap<RexNode, RexNode> map3 =
+        RexUtil.predicateConstants(RexNode.class, rexBuilder,
+            ImmutableList.of(eq(aRef, literal1),
+                eq(aRef, literal2)));
+    assertThat(getString(map3), is("{1=?0.a, 2=?0.a}"));
+
+
+  }
+
+  /** Converts a map to a string, sorting on the string representation of its
+   * keys. */
+  private static String getString(ImmutableMap<RexNode, RexNode> map) {
+    final TreeMap<String, RexNode> map2 = new TreeMap<>();
+    for (Map.Entry<RexNode, RexNode> entry : map.entrySet()) {
+      map2.put(entry.getKey().toString(), entry.getValue());
+    }
+    return map2.toString();
+  }
 }
 
 // End RexProgramTest.java