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 2018/12/02 00:54:57 UTC

[05/15] calcite git commit: [CALCITE-2632] Ensure that RexNode and its sub-classes implement hashCode and equals methods (Zoltan Haindrich)

[CALCITE-2632] Ensure that RexNode and its sub-classes implement hashCode and equals methods (Zoltan Haindrich)

Previously there was no default hashCode/equals implementations. To
ensure that they are working properly is crucial while working with
basic collections like sets/maps.

Fix ups (Julian Hyde):

* Use Objects.equals(x, y) in preference to x.equals(y) only when x or y
 may be null; and use x == y when objects are primitive.

* When implementing Object.equals, use the pattern

  return this == obj
    || obj instanceof Type
    && a.equals(((Type) obj).a) ...

  whenever possible.

* In the many cases where RexNode.toString() is used as a key in
  collections, use the raw RexNode instead.

Close apache/calcite#943


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

Branch: refs/heads/master
Commit: 847e76cde2894ea7540749f68669ce2d910c2fa9
Parents: ce47088
Author: Zoltan Haindrich <ki...@rxd.hu>
Authored: Mon Nov 26 15:58:50 2018 +0100
Committer: Julian Hyde <jh...@apache.org>
Committed: Fri Nov 30 20:29:35 2018 -0800

----------------------------------------------------------------------
 .../calcite/plan/RexImplicationChecker.java     |   4 +-
 .../calcite/plan/SubstitutionVisitor.java       |   8 +-
 .../java/org/apache/calcite/rel/core/Match.java |   2 +-
 .../org/apache/calcite/rel/core/Window.java     |  13 ++
 .../calcite/rel/metadata/RelMdPredicates.java   | 137 +++++++++----------
 .../apache/calcite/rel/metadata/RelMdUtil.java  |  39 +-----
 .../calcite/rel/mutable/MutableFilter.java      |   5 +-
 .../apache/calcite/rel/mutable/MutableJoin.java |   6 +-
 .../calcite/rel/mutable/MutableSemiJoin.java    |   6 +-
 .../rel/rules/AbstractMaterializedViewRule.java |  97 ++++++-------
 .../calcite/rel/rules/DateRangeRules.java       |  21 ++-
 .../rel/rules/JoinPushExpressionsRule.java      |   2 +-
 .../apache/calcite/rel/rules/PushProjector.java |  39 ++----
 .../calcite/rel/rules/ReduceDecimalsRule.java   |   8 +-
 .../rel/rules/ReduceExpressionsRule.java        |   2 +-
 .../calcite/rel/rules/SubQueryRemoveRule.java   |   2 +-
 .../org/apache/calcite/rex/LogicVisitor.java    |   2 +-
 .../java/org/apache/calcite/rex/RexCall.java    |  22 ++-
 .../apache/calcite/rex/RexCorrelVariable.java   |  12 ++
 .../org/apache/calcite/rex/RexDynamicParam.java |  14 ++
 .../java/org/apache/calcite/rex/RexNode.java    |  13 ++
 .../java/org/apache/calcite/rex/RexOver.java    |   6 +-
 .../apache/calcite/rex/RexProgramBuilder.java   |   4 +-
 .../org/apache/calcite/rex/RexRangeRef.java     |  13 ++
 .../org/apache/calcite/rex/RexSimplify.java     |  85 ++++++------
 .../org/apache/calcite/rex/RexSubQuery.java     |   7 +-
 .../java/org/apache/calcite/rex/RexUtil.java    |  46 +++----
 .../org/apache/calcite/rex/RexVariable.java     |  10 +-
 .../calcite/sql2rel/SqlToRelConverter.java      |  16 +--
 .../org/apache/calcite/test/RelOptRulesTest.xml |   9 +-
 .../calcite/test/SqlToRelConverterTest.xml      |  34 ++---
 .../elasticsearch/ElasticsearchRules.java       |   4 +-
 32 files changed, 336 insertions(+), 352 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/847e76cd/core/src/main/java/org/apache/calcite/plan/RexImplicationChecker.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/RexImplicationChecker.java b/core/src/main/java/org/apache/calcite/plan/RexImplicationChecker.java
index f2b32f2..d769b7e 100644
--- a/core/src/main/java/org/apache/calcite/plan/RexImplicationChecker.java
+++ b/core/src/main/java/org/apache/calcite/plan/RexImplicationChecker.java
@@ -168,7 +168,7 @@ public class RexImplicationChecker {
     }
 
     // E.g. "x is null" implies "x is null".
-    if (RexUtil.eq(first, second)) {
+    if (first.equals(second)) {
       return true;
     }
 
@@ -184,7 +184,7 @@ public class RexImplicationChecker {
       final RexNode operand = ((RexCall) second).getOperands().get(0);
       final Strong strong = new Strong() {
         @Override public boolean isNull(RexNode node) {
-          return RexUtil.eq(node, operand)
+          return node.equals(operand)
               || super.isNull(node);
         }
       };

http://git-wip-us.apache.org/repos/asf/calcite/blob/847e76cd/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java b/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
index 4212fae..b30763c 100644
--- a/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
+++ b/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
@@ -1362,13 +1362,13 @@ public class SubstitutionVisitor {
   /** Builds a shuttle that stores a list of expressions, and can map incoming
    * expressions to references to them. */
   protected static RexShuttle getRexShuttle(MutableProject target) {
-    final Map<String, Integer> map = new HashMap<>();
+    final Map<RexNode, Integer> map = new HashMap<>();
     for (RexNode e : target.projects) {
-      map.put(e.toString(), map.size());
+      map.put(e, map.size());
     }
     return new RexShuttle() {
       @Override public RexNode visitInputRef(RexInputRef ref) {
-        final Integer integer = map.get(ref.getName());
+        final Integer integer = map.get(ref);
         if (integer != null) {
           return new RexInputRef(integer, ref.getType());
         }
@@ -1376,7 +1376,7 @@ public class SubstitutionVisitor {
       }
 
       @Override public RexNode visitCall(RexCall call) {
-        final Integer integer = map.get(call.toString());
+        final Integer integer = map.get(call);
         if (integer != null) {
           return new RexInputRef(integer, call.getType());
         }

http://git-wip-us.apache.org/repos/asf/calcite/blob/847e76cd/core/src/main/java/org/apache/calcite/rel/core/Match.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Match.java b/core/src/main/java/org/apache/calcite/rel/core/Match.java
index d2c170e..742fa81 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Match.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Match.java
@@ -277,7 +277,7 @@ public abstract class Match extends SingleRel {
           }
           boolean update = true;
           for (RexMRAggCall rex : set) {
-            if (rex.toString().equals(aggCall.toString())) {
+            if (rex.equals(aggCall)) {
               update = false;
               break;
             }

http://git-wip-us.apache.org/repos/asf/calcite/blob/847e76cd/core/src/main/java/org/apache/calcite/rel/core/Window.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Window.java b/core/src/main/java/org/apache/calcite/rel/core/Window.java
index 186f7d3..013e7cc 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Window.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Window.java
@@ -47,6 +47,7 @@ import com.google.common.collect.ImmutableList;
 
 import java.util.AbstractList;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * A relational expression representing a set of window aggregates.
@@ -361,6 +362,18 @@ public abstract class Window extends SingleRel {
       this.distinct = distinct;
     }
 
+    /** {@inheritDoc}
+     *
+     * <p>Override {@link RexCall}, defining equality based on identity.
+     */
+    @Override public boolean equals(Object obj) {
+      return this == obj;
+    }
+
+    @Override public int hashCode() {
+      return Objects.hash(digest, ordinal, distinct);
+    }
+
     @Override public RexCall clone(RelDataType type, List<RexNode> operands) {
       throw new UnsupportedOperationException();
     }

http://git-wip-us.apache.org/repos/asf/calcite/blob/847e76cd/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 985464c..a7993cb 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
@@ -59,9 +59,9 @@ import org.apache.calcite.util.mapping.Mappings;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.BitSet;
 import java.util.Collections;
 import java.util.HashMap;
@@ -69,7 +69,6 @@ import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.Objects;
 import java.util.Set;
 import java.util.SortedMap;
@@ -358,58 +357,56 @@ public class RelMdPredicates
    * Infers predicates for a Union.
    */
   public RelOptPredicateList getPredicates(Union union, RelMetadataQuery mq) {
-    RexBuilder rexBuilder = union.getCluster().getRexBuilder();
+    final RexBuilder rexBuilder = union.getCluster().getRexBuilder();
 
-    Map<String, RexNode> finalPreds = new HashMap<>();
-    List<RexNode> finalResidualPreds = new ArrayList<>();
-    for (int i = 0; i < union.getInputs().size(); i++) {
-      RelNode input = union.getInputs().get(i);
-      RelOptPredicateList info = mq.getPulledUpPredicates(input);
+    Set<RexNode> finalPredicates = new HashSet<>();
+    final List<RexNode> finalResidualPredicates = new ArrayList<>();
+    for (Ord<RelNode> input : Ord.zip(union.getInputs())) {
+      RelOptPredicateList info = mq.getPulledUpPredicates(input.e);
       if (info.pulledUpPredicates.isEmpty()) {
         return RelOptPredicateList.EMPTY;
       }
-      Map<String, RexNode> preds = new HashMap<>();
-      List<RexNode> residualPreds = new ArrayList<>();
+      final Set<RexNode> predicates = new HashSet<>();
+      final List<RexNode> residualPredicates = new ArrayList<>();
       for (RexNode pred : info.pulledUpPredicates) {
-        final String predDigest = pred.toString();
-        if (i == 0) {
-          preds.put(predDigest, pred);
+        if (input.i == 0) {
+          predicates.add(pred);
           continue;
         }
-        if (finalPreds.containsKey(predDigest)) {
-          preds.put(predDigest, pred);
+        if (finalPredicates.contains(pred)) {
+          predicates.add(pred);
         } else {
-          residualPreds.add(pred);
+          residualPredicates.add(pred);
         }
       }
-      // Add new residual preds
-      finalResidualPreds.add(RexUtil.composeConjunction(rexBuilder, residualPreds));
+      // Add new residual predicates
+      finalResidualPredicates.add(RexUtil.composeConjunction(rexBuilder, residualPredicates));
       // Add those that are not part of the final set to residual
-      for (Entry<String, RexNode> e : finalPreds.entrySet()) {
-        if (!preds.containsKey(e.getKey())) {
+      for (RexNode e : finalPredicates) {
+        if (!predicates.contains(e)) {
           // This node was in previous union inputs, but it is not in this one
-          for (int j = 0; j < i; j++) {
-            finalResidualPreds.set(j,
+          for (int j = 0; j < input.i; j++) {
+            finalResidualPredicates.set(j,
                 RexUtil.composeConjunction(rexBuilder,
-                    Lists.newArrayList(finalResidualPreds.get(j), e.getValue())));
+                    Arrays.asList(finalResidualPredicates.get(j), e)));
           }
         }
       }
-      // Final preds
-      finalPreds = preds;
+      // Final predicates
+      finalPredicates = predicates;
     }
 
-    List<RexNode> preds = new ArrayList<>(finalPreds.values());
+    final List<RexNode> predicates = new ArrayList<>(finalPredicates);
     final RelOptCluster cluster = union.getCluster();
     final RexExecutor executor =
         Util.first(cluster.getPlanner().getExecutor(), RexUtil.EXECUTOR);
-    final RelOptPredicateList predicates = RelOptPredicateList.EMPTY;
-    RexNode disjPred = new RexSimplify(rexBuilder, predicates, executor)
-        .simplifyOrs(finalResidualPreds);
-    if (!disjPred.isAlwaysTrue()) {
-      preds.add(disjPred);
+    RexNode disjunctivePredicate =
+        new RexSimplify(rexBuilder, RelOptPredicateList.EMPTY, executor)
+            .simplifyOrs(finalResidualPredicates);
+    if (!disjunctivePredicate.isAlwaysTrue()) {
+      predicates.add(disjunctivePredicate);
     }
-    return RelOptPredicateList.of(rexBuilder, preds);
+    return RelOptPredicateList.of(rexBuilder, predicates);
   }
 
   /**
@@ -479,20 +476,20 @@ public class RelMdPredicates
     final ImmutableBitSet rightFieldsBitSet;
     final ImmutableBitSet allFieldsBitSet;
     SortedMap<Integer, BitSet> equivalence;
-    final Map<String, ImmutableBitSet> exprFields;
-    final Set<String> allExprDigests;
-    final Set<String> equalityPredicates;
+    final Map<RexNode, ImmutableBitSet> exprFields;
+    final Set<RexNode> allExprs;
+    final Set<RexNode> equalityPredicates;
     final RexNode leftChildPredicates;
     final RexNode rightChildPredicates;
     final RexSimplify simplify;
 
     JoinConditionBasedPredicateInference(Join joinRel,
-        RexNode lPreds, RexNode rPreds, RexSimplify simplify) {
-      this(joinRel, joinRel instanceof SemiJoin, lPreds, rPreds, simplify);
+        RexNode leftPredicates, RexNode rightPredicates, RexSimplify simplify) {
+      this(joinRel, joinRel instanceof SemiJoin, leftPredicates, rightPredicates, simplify);
     }
 
     private JoinConditionBasedPredicateInference(Join joinRel, boolean isSemiJoin,
-        RexNode lPreds, RexNode rPreds, RexSimplify simplify) {
+        RexNode leftPredicates, RexNode rightPredicates, RexSimplify simplify) {
       super();
       this.joinRel = joinRel;
       this.isSemiJoin = isSemiJoin;
@@ -508,35 +505,35 @@ public class RelMdPredicates
           nSysFields + nFieldsLeft + nFieldsRight);
 
       exprFields = new HashMap<>();
-      allExprDigests = new HashSet<>();
+      allExprs = new HashSet<>();
 
-      if (lPreds == null) {
+      if (leftPredicates == null) {
         leftChildPredicates = null;
       } else {
         Mappings.TargetMapping leftMapping = Mappings.createShiftMapping(
             nSysFields + nFieldsLeft, nSysFields, 0, nFieldsLeft);
-        leftChildPredicates = lPreds.accept(
+        leftChildPredicates = leftPredicates.accept(
             new RexPermuteInputsShuttle(leftMapping, joinRel.getInput(0)));
 
-        allExprDigests.add(leftChildPredicates.toString());
+        allExprs.add(leftChildPredicates);
         for (RexNode r : RelOptUtil.conjunctions(leftChildPredicates)) {
-          exprFields.put(r.toString(), RelOptUtil.InputFinder.bits(r));
-          allExprDigests.add(r.toString());
+          exprFields.put(r, RelOptUtil.InputFinder.bits(r));
+          allExprs.add(r);
         }
       }
-      if (rPreds == null) {
+      if (rightPredicates == null) {
         rightChildPredicates = null;
       } else {
         Mappings.TargetMapping rightMapping = Mappings.createShiftMapping(
             nSysFields + nFieldsLeft + nFieldsRight,
             nSysFields + nFieldsLeft, 0, nFieldsRight);
-        rightChildPredicates = rPreds.accept(
+        rightChildPredicates = rightPredicates.accept(
             new RexPermuteInputsShuttle(rightMapping, joinRel.getInput(1)));
 
-        allExprDigests.add(rightChildPredicates.toString());
+        allExprs.add(rightChildPredicates);
         for (RexNode r : RelOptUtil.conjunctions(rightChildPredicates)) {
-          exprFields.put(r.toString(), RelOptUtil.InputFinder.bits(r));
-          allExprDigests.add(r.toString());
+          exprFields.put(r, RelOptUtil.InputFinder.bits(r));
+          allExprs.add(r);
         }
       }
 
@@ -555,8 +552,7 @@ public class RelMdPredicates
               compose(rexBuilder, ImmutableList.of(joinRel.getCondition())));
 
       final EquivalenceFinder eF = new EquivalenceFinder();
-      new ArrayList<>(
-          Lists.transform(exprs, input -> input.accept(eF)));
+      exprs.forEach(input -> input.accept(eF));
 
       equivalence = BitSets.closure(equivalence);
     }
@@ -577,12 +573,12 @@ public class RelMdPredicates
     public RelOptPredicateList inferPredicates(
         boolean includeEqualityInference) {
       final List<RexNode> inferredPredicates = new ArrayList<>();
-      final Set<String> allExprDigests = new HashSet<>(this.allExprDigests);
+      final Set<RexNode> allExprs = new HashSet<>(this.allExprs);
       final JoinRelType joinType = joinRel.getJoinType();
       switch (joinType) {
       case INNER:
       case LEFT:
-        infer(leftChildPredicates, allExprDigests, inferredPredicates,
+        infer(leftChildPredicates, allExprs, inferredPredicates,
             includeEqualityInference,
             joinType == JoinRelType.LEFT ? rightFieldsBitSet
                 : allFieldsBitSet);
@@ -591,7 +587,7 @@ public class RelMdPredicates
       switch (joinType) {
       case INNER:
       case RIGHT:
-        infer(rightChildPredicates, allExprDigests, inferredPredicates,
+        infer(rightChildPredicates, allExprs, inferredPredicates,
             includeEqualityInference,
             joinType == JoinRelType.RIGHT ? leftFieldsBitSet
                 : allFieldsBitSet);
@@ -659,12 +655,12 @@ public class RelMdPredicates
       return rightChildPredicates;
     }
 
-    private void infer(RexNode predicates, Set<String> allExprsDigests,
+    private void infer(RexNode predicates, Set<RexNode> allExprs,
         List<RexNode> inferredPredicates, boolean includeEqualityInference,
         ImmutableBitSet inferringFields) {
       for (RexNode r : RelOptUtil.conjunctions(predicates)) {
         if (!includeEqualityInference
-            && equalityPredicates.contains(r.toString())) {
+            && equalityPredicates.contains(r)) {
           continue;
         }
         for (Mapping m : mappings(r)) {
@@ -676,33 +672,31 @@ public class RelMdPredicates
           // some duplicates in in result pulledUpPredicates
           RexNode simplifiedTarget =
               simplify.simplifyFilterPredicates(RelOptUtil.conjunctions(tr));
-          if (checkTarget(inferringFields, allExprsDigests, tr)
-              && checkTarget(inferringFields, allExprsDigests, simplifiedTarget)) {
+          if (checkTarget(inferringFields, allExprs, tr)
+              && checkTarget(inferringFields, allExprs, simplifiedTarget)) {
             inferredPredicates.add(simplifiedTarget);
-            allExprsDigests.add(simplifiedTarget.toString());
+            allExprs.add(simplifiedTarget);
           }
         }
       }
     }
 
     Iterable<Mapping> mappings(final RexNode predicate) {
-      return () -> {
-        ImmutableBitSet fields = exprFields.get(predicate.toString());
-        if (fields.cardinality() == 0) {
-          return Collections.emptyIterator();
-        }
-        return new ExprsItr(fields);
-      };
+      final ImmutableBitSet fields = exprFields.get(predicate);
+      if (fields.cardinality() == 0) {
+        return Collections.emptyList();
+      }
+      return () -> new ExprsItr(fields);
     }
 
     private boolean checkTarget(ImmutableBitSet inferringFields,
-        Set<String> allExprsDigests, RexNode tr) {
+        Set<RexNode> allExprs, RexNode tr) {
       return inferringFields.contains(RelOptUtil.InputFinder.bits(tr))
-          && !allExprsDigests.contains(tr.toString())
+          && !allExprs.contains(tr)
           && !isAlwaysTrue(tr);
     }
 
-    private void equivalent(int p1, int p2) {
+    private void markAsEquivalent(int p1, int p2) {
       BitSet b = equivalence.get(p1);
       b.set(p2);
 
@@ -728,9 +722,8 @@ public class RelMdPredicates
           int lPos = pos(call.getOperands().get(0));
           int rPos = pos(call.getOperands().get(1));
           if (lPos != -1 && rPos != -1) {
-            JoinConditionBasedPredicateInference.this.equivalent(lPos, rPos);
-            JoinConditionBasedPredicateInference.this.equalityPredicates
-                .add(call.toString());
+            markAsEquivalent(lPos, rPos);
+            equalityPredicates.add(call);
           }
         }
         return null;

http://git-wip-us.apache.org/repos/asf/calcite/blob/847e76cd/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUtil.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUtil.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUtil.java
index 6229f4e..73cae72 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUtil.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUtil.java
@@ -49,7 +49,7 @@ import com.google.common.collect.ImmutableList;
 
 import java.math.BigDecimal;
 import java.util.ArrayList;
-import java.util.HashSet;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
 
@@ -433,20 +433,9 @@ public class RelMdUtil {
       RexBuilder rexBuilder,
       RexNode pred1,
       RexNode pred2) {
-    final List<RexNode> unionList = new ArrayList<>();
-    final Set<String> strings = new HashSet<>();
-
-    for (RexNode rex : RelOptUtil.conjunctions(pred1)) {
-      if (strings.add(rex.toString())) {
-        unionList.add(rex);
-      }
-    }
-    for (RexNode rex2 : RelOptUtil.conjunctions(pred2)) {
-      if (strings.add(rex2.toString())) {
-        unionList.add(rex2);
-      }
-    }
-
+    final Set<RexNode> unionList = new LinkedHashSet<>();
+    unionList.addAll(RelOptUtil.conjunctions(pred1));
+    unionList.addAll(RelOptUtil.conjunctions(pred2));
     return RexUtil.composeConjunction(rexBuilder, unionList, true);
   }
 
@@ -463,23 +452,9 @@ public class RelMdUtil {
       RexBuilder rexBuilder,
       RexNode pred1,
       RexNode pred2) {
-    final List<RexNode> list1 = RelOptUtil.conjunctions(pred1);
-    final List<RexNode> list2 = RelOptUtil.conjunctions(pred2);
-    final List<RexNode> minusList = new ArrayList<>();
-
-    for (RexNode rex1 : list1) {
-      boolean add = true;
-      for (RexNode rex2 : list2) {
-        if (rex2.toString().compareTo(rex1.toString()) == 0) {
-          add = false;
-          break;
-        }
-      }
-      if (add) {
-        minusList.add(rex1);
-      }
-    }
-
+    final List<RexNode> minusList =
+        new ArrayList<>(RelOptUtil.conjunctions(pred1));
+    minusList.removeAll(RelOptUtil.conjunctions(pred2));
     return RexUtil.composeConjunction(rexBuilder, minusList, true);
   }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/847e76cd/core/src/main/java/org/apache/calcite/rel/mutable/MutableFilter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/mutable/MutableFilter.java b/core/src/main/java/org/apache/calcite/rel/mutable/MutableFilter.java
index 787487f..ef9f8d7 100644
--- a/core/src/main/java/org/apache/calcite/rel/mutable/MutableFilter.java
+++ b/core/src/main/java/org/apache/calcite/rel/mutable/MutableFilter.java
@@ -43,13 +43,12 @@ public class MutableFilter extends MutableSingleRel {
   @Override public boolean equals(Object obj) {
     return obj == this
         || obj instanceof MutableFilter
-        && condition.toString().equals(
-            ((MutableFilter) obj).condition.toString())
+        && condition.equals(((MutableFilter) obj).condition)
         && input.equals(((MutableFilter) obj).input);
   }
 
   @Override public int hashCode() {
-    return Objects.hash(input, condition.toString());
+    return Objects.hash(input, condition);
   }
 
   @Override public StringBuilder digest(StringBuilder buf) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/847e76cd/core/src/main/java/org/apache/calcite/rel/mutable/MutableJoin.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/mutable/MutableJoin.java b/core/src/main/java/org/apache/calcite/rel/mutable/MutableJoin.java
index 09d5a1c..0a49c8c 100644
--- a/core/src/main/java/org/apache/calcite/rel/mutable/MutableJoin.java
+++ b/core/src/main/java/org/apache/calcite/rel/mutable/MutableJoin.java
@@ -66,8 +66,7 @@ public class MutableJoin extends MutableBiRel {
     return obj == this
         || obj instanceof MutableJoin
         && joinType == ((MutableJoin) obj).joinType
-        && condition.toString().equals(
-            ((MutableJoin) obj).condition.toString())
+        && condition.equals(((MutableJoin) obj).condition)
         && Objects.equals(variablesSet,
             ((MutableJoin) obj).variablesSet)
         && left.equals(((MutableJoin) obj).left)
@@ -75,8 +74,7 @@ public class MutableJoin extends MutableBiRel {
   }
 
   @Override public int hashCode() {
-    return Objects.hash(left, right,
-        condition.toString(), joinType, variablesSet);
+    return Objects.hash(left, right, condition, joinType, variablesSet);
   }
 
   @Override public StringBuilder digest(StringBuilder buf) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/847e76cd/core/src/main/java/org/apache/calcite/rel/mutable/MutableSemiJoin.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/mutable/MutableSemiJoin.java b/core/src/main/java/org/apache/calcite/rel/mutable/MutableSemiJoin.java
index c2e81b7..5806696 100644
--- a/core/src/main/java/org/apache/calcite/rel/mutable/MutableSemiJoin.java
+++ b/core/src/main/java/org/apache/calcite/rel/mutable/MutableSemiJoin.java
@@ -61,8 +61,7 @@ public class MutableSemiJoin extends MutableBiRel {
   @Override public boolean equals(Object obj) {
     return obj == this
         || obj instanceof MutableSemiJoin
-        && condition.toString().equals(
-            ((MutableSemiJoin) obj).condition.toString())
+        && condition.equals(((MutableSemiJoin) obj).condition)
         && leftKeys.equals(((MutableSemiJoin) obj).leftKeys)
         && rightKeys.equals(((MutableSemiJoin) obj).rightKeys)
         && left.equals(((MutableSemiJoin) obj).left)
@@ -70,8 +69,7 @@ public class MutableSemiJoin extends MutableBiRel {
   }
 
   @Override public int hashCode() {
-    return Objects.hash(left, right,
-        condition.toString(), leftKeys, rightKeys);
+    return Objects.hash(left, right, condition, leftKeys, rightKeys);
   }
 
   @Override public StringBuilder digest(StringBuilder buf) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/847e76cd/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java
index 89e444a..7eedc93 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java
@@ -1359,7 +1359,7 @@ public abstract class AbstractMaterializedViewRule extends RelOptRule {
       }
 
       // Generate result rewriting
-      List<String> additionalViewExprs = new ArrayList<>();
+      final List<RexNode> additionalViewExprs = new ArrayList<>();
       Mapping rewritingMapping = null;
       RelNode result = relBuilder.push(input).build();
       // We create view expressions that will be used in a Project on top of the
@@ -1389,7 +1389,7 @@ public abstract class AbstractMaterializedViewRule extends RelOptRule {
             RexNode targetNode = rollupNodes.get(
                 targetIdx - viewInputFieldCount - viewInputDifferenceViewFieldCount);
             // We need to rollup this expression
-            final Multimap<String, Integer> exprsLineage = ArrayListMultimap.create();
+            final Multimap<RexNode, Integer> exprsLineage = ArrayListMultimap.create();
             final ImmutableBitSet refs = RelOptUtil.InputFinder.bits(targetNode);
             for (int childTargetIdx : refs) {
               added = false;
@@ -1398,10 +1398,10 @@ public abstract class AbstractMaterializedViewRule extends RelOptRule {
                 if (!n.isA(SqlKind.INPUT_REF)) {
                   continue;
                 }
-                int ref = ((RexInputRef) n).getIndex();
+                final int ref = ((RexInputRef) n).getIndex();
                 if (ref == childTargetIdx) {
                   exprsLineage.put(
-                      new RexInputRef(childTargetIdx, targetNode.getType()).toString(), k);
+                      new RexInputRef(ref, targetNode.getType()), k);
                   added = true;
                 }
               }
@@ -1414,7 +1414,7 @@ public abstract class AbstractMaterializedViewRule extends RelOptRule {
             groupSetB.set(inputViewExprs.size());
             rewritingMapping.set(inputViewExprs.size(), i);
             additionalViewExprs.add(
-                new RexInputRef(targetIdx, targetNode.getType()).toString());
+                new RexInputRef(targetIdx, targetNode.getType()));
             // We need to create the rollup expression
             inputViewExprs.add(
                 shuttleReferences(rexBuilder, targetNode, exprsLineage));
@@ -1536,12 +1536,12 @@ public abstract class AbstractMaterializedViewRule extends RelOptRule {
         topRowType = queryAggregate.getRowType();
       }
       // Available in view.
-      final Multimap<String, Integer> viewExprs = ArrayListMultimap.create();
+      final Multimap<RexNode, Integer> viewExprs = ArrayListMultimap.create();
       int numberViewExprs = 0;
       for (RexNode viewExpr : topViewProject.getChildExps()) {
-        viewExprs.put(viewExpr.toString(), numberViewExprs++);
+        viewExprs.put(viewExpr, numberViewExprs++);
       }
-      for (String additionalViewExpr : additionalViewExprs) {
+      for (RexNode additionalViewExpr : additionalViewExprs) {
         viewExprs.put(additionalViewExpr, numberViewExprs++);
       }
       final List<RexNode> rewrittenExprs = new ArrayList<>(topExprs.size());
@@ -1587,7 +1587,7 @@ public abstract class AbstractMaterializedViewRule extends RelOptRule {
       Multimap<Integer, Integer> m = ArrayListMultimap.create();
       Map<RexTableInputRef, Set<RexTableInputRef>> equivalenceClassesMap =
           sourceEC.getEquivalenceClassesMap();
-      Multimap<String, Integer> exprsLineage = ArrayListMultimap.create();
+      Multimap<RexNode, Integer> exprsLineage = ArrayListMultimap.create();
       List<RexNode> timestampExprs = new ArrayList<>();
       for (int i = 0; i < target.getRowType().getFieldCount(); i++) {
         Set<RexNode> s = mq.getExpressionLineage(target, rexBuilder.makeInputRef(target, i));
@@ -1604,7 +1604,7 @@ public abstract class AbstractMaterializedViewRule extends RelOptRule {
             simplified,
             tableMapping.inverse(),
             equivalenceClassesMap);
-        exprsLineage.put(expr.toString(), i);
+        exprsLineage.put(expr, i);
         SqlTypeName sqlTypeName = expr.getType().getSqlTypeName();
         if (sqlTypeName == SqlTypeName.TIMESTAMP
             || sqlTypeName == SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE) {
@@ -1633,7 +1633,7 @@ public abstract class AbstractMaterializedViewRule extends RelOptRule {
             // can try to find a match
             final RexNode simplified =
                 simplify.simplifyUnknownAsFalse(ceilExpr);
-            exprsLineage.put(simplified.toString(),
+            exprsLineage.put(simplified,
                 target.getRowType().getFieldCount() + additionalExprs.size() - 1);
           }
           // FLOOR
@@ -1651,7 +1651,7 @@ public abstract class AbstractMaterializedViewRule extends RelOptRule {
             // can try to find a match
             final RexNode simplified =
                 simplify.simplifyUnknownAsFalse(floorExpr);
-            exprsLineage.put(simplified.toString(),
+            exprsLineage.put(simplified,
                 target.getRowType().getFieldCount() + additionalExprs.size() - 1);
           }
         }
@@ -1670,7 +1670,7 @@ public abstract class AbstractMaterializedViewRule extends RelOptRule {
         final RexNode simplified = simplify.simplifyUnknownAsFalse(e);
         RexNode targetExpr = RexUtil.swapColumnReferences(rexBuilder,
             simplified, equivalenceClassesMap);
-        Collection<Integer> c = exprsLineage.get(targetExpr.toString());
+        final Collection<Integer> c = exprsLineage.get(targetExpr);
         if (!c.isEmpty()) {
           for (Integer j : c) {
             m.put(i, j);
@@ -2010,8 +2010,8 @@ public abstract class AbstractMaterializedViewRule extends RelOptRule {
                 parentTRef.getTable().getRowType().getFieldList().get(uniqueKeyPos).getType());
             if (!foreignKeyColumnType.isNullable()
                 && sourceEC.getEquivalenceClassesMap().containsKey(uniqueKeyColumnRef)
-                && sourceEC.getEquivalenceClassesMap().get(uniqueKeyColumnRef).contains(
-                    foreignKeyColumnRef)) {
+                && sourceEC.getEquivalenceClassesMap().get(uniqueKeyColumnRef)
+                    .contains(foreignKeyColumnRef)) {
               equiColumns.put(foreignKeyColumnRef, uniqueKeyColumnRef);
             } else {
               canBeRewritten = false;
@@ -2310,8 +2310,8 @@ public abstract class AbstractMaterializedViewRule extends RelOptRule {
       BiMap<RelTableRef, RelTableRef> tableMapping,
       EquivalenceClasses ec,
       List<RexNode> nodeExprs) {
-    Map<String, Integer> exprsLineage = new HashMap<>();
-    Map<String, Integer> exprsLineageLosslessCasts = new HashMap<>();
+    final Map<RexNode, Integer> exprsLineage = new HashMap<>();
+    final Map<RexNode, Integer> exprsLineageLosslessCasts = new HashMap<>();
     for (int i = 0; i < nodeExprs.size(); i++) {
       final Set<RexNode> s = mq.getExpressionLineage(node, nodeExprs.get(i));
       if (s == null) {
@@ -2325,12 +2325,12 @@ public abstract class AbstractMaterializedViewRule extends RelOptRule {
       // mapping, then we take first element from the corresponding equivalence class
       final RexNode e = RexUtil.swapTableColumnReferences(rexBuilder,
           s.iterator().next(), tableMapping, ec.getEquivalenceClassesMap());
-      exprsLineage.put(e.toString(), i);
+      exprsLineage.put(e, i);
       if (RexUtil.isLosslessCast(e)) {
-        exprsLineageLosslessCasts.put(((RexCall) e).getOperands().get(0).toString(), i);
+        exprsLineageLosslessCasts.put(((RexCall) e).getOperands().get(0), i);
       }
     }
-    return NodeLineage.of(exprsLineage, exprsLineageLosslessCasts);
+    return new NodeLineage(exprsLineage, exprsLineageLosslessCasts);
   }
 
   /**
@@ -2344,8 +2344,8 @@ public abstract class AbstractMaterializedViewRule extends RelOptRule {
       BiMap<RelTableRef, RelTableRef> tableMapping,
       EquivalenceClasses ec,
       List<RexNode> nodeExprs) {
-    Map<String, Integer> exprsLineage = new HashMap<>();
-    Map<String, Integer> exprsLineageLosslessCasts = new HashMap<>();
+    final Map<RexNode, Integer> exprsLineage = new HashMap<>();
+    final Map<RexNode, Integer> exprsLineageLosslessCasts = new HashMap<>();
     for (int i = 0; i < nodeExprs.size(); i++) {
       final Set<RexNode> s = mq.getExpressionLineage(node, nodeExprs.get(i));
       if (s == null) {
@@ -2354,17 +2354,17 @@ public abstract class AbstractMaterializedViewRule extends RelOptRule {
       }
       // We only support project - filter - join, thus it should map to
       // a single expression
-      assert s.size() == 1;
+      final RexNode node2 = Iterables.getOnlyElement(s);
       // Rewrite expr. First we take first element from the corresponding equivalence class,
       // then we swap the table references following the table mapping
-      final RexNode e = RexUtil.swapColumnTableReferences(
-          rexBuilder, s.iterator().next(), ec.getEquivalenceClassesMap(), tableMapping);
-      exprsLineage.put(e.toString(), i);
+      final RexNode e = RexUtil.swapColumnTableReferences(rexBuilder, node2,
+          ec.getEquivalenceClassesMap(), tableMapping);
+      exprsLineage.put(e, i);
       if (RexUtil.isLosslessCast(e)) {
-        exprsLineageLosslessCasts.put(((RexCall) e).getOperands().get(0).toString(), i);
+        exprsLineageLosslessCasts.put(((RexCall) e).getOperands().get(0), i);
       }
     }
-    return NodeLineage.of(exprsLineage, exprsLineageLosslessCasts);
+    return new NodeLineage(exprsLineage, exprsLineageLosslessCasts);
   }
 
   /**
@@ -2392,12 +2392,12 @@ public abstract class AbstractMaterializedViewRule extends RelOptRule {
           }
 
           private RexNode replace(RexNode e) {
-            Integer pos = nodeLineage.exprsLineage.get(e.toString());
+            Integer pos = nodeLineage.exprsLineage.get(e);
             if (pos != null) {
               // Found it
               return rexBuilder.makeInputRef(node, pos);
             }
-            pos = nodeLineage.exprsLineageLosslessCasts.get(e.toString());
+            pos = nodeLineage.exprsLineageLosslessCasts.get(e);
             if (pos != null) {
               // Found it
               return rexBuilder.makeCast(
@@ -2436,29 +2436,29 @@ public abstract class AbstractMaterializedViewRule extends RelOptRule {
   }
 
   /**
-   * Replaces all the possible subexpressions by input references
+   * Replaces all the possible sub-expressions by input references
    * to the input node.
    */
   private static RexNode shuttleReferences(final RexBuilder rexBuilder,
-      final RexNode expr, final Multimap<String, Integer> exprsLineage) {
+      final RexNode expr, final Multimap<RexNode, Integer> exprsLineage) {
     return shuttleReferences(rexBuilder, expr,
         exprsLineage, null, null);
   }
 
   /**
-   * Replaces all the possible subexpressions by input references
+   * Replaces all the possible sub-expressions by input references
    * to the input node. If available, it uses the rewriting mapping
    * to change the position to reference. Takes the reference type
    * from the input node.
    */
   private static RexNode shuttleReferences(final RexBuilder rexBuilder,
-      final RexNode expr, final Multimap<String, Integer> exprsLineage,
+      final RexNode expr, final Multimap<RexNode, Integer> exprsLineage,
       final RelNode node, final Mapping rewritingMapping) {
     try {
       RexShuttle visitor =
           new RexShuttle() {
             @Override public RexNode visitTableInputRef(RexTableInputRef ref) {
-              Collection<Integer> c = exprsLineage.get(ref.toString());
+              Collection<Integer> c = exprsLineage.get(ref);
               if (c.isEmpty()) {
                 // Cannot map expression
                 throw Util.FoundOne.NULL;
@@ -2478,7 +2478,7 @@ public abstract class AbstractMaterializedViewRule extends RelOptRule {
             }
 
             @Override public RexNode visitInputRef(RexInputRef inputRef) {
-              Collection<Integer> c = exprsLineage.get(inputRef.toString());
+              Collection<Integer> c = exprsLineage.get(inputRef);
               if (c.isEmpty()) {
                 // Cannot map expression
                 throw Util.FoundOne.NULL;
@@ -2498,7 +2498,7 @@ public abstract class AbstractMaterializedViewRule extends RelOptRule {
             }
 
             @Override public RexNode visitCall(final RexCall call) {
-              Collection<Integer> c = exprsLineage.get(call.toString());
+              Collection<Integer> c = exprsLineage.get(call);
               if (c.isEmpty()) {
                 // Cannot map expression
                 return super.visitCall(call);
@@ -2612,28 +2612,21 @@ public abstract class AbstractMaterializedViewRule extends RelOptRule {
     }
   }
 
-  /**
-   * Class to encapsulate expression lineage details
-   */
+  /** Expression lineage details. */
   private static class NodeLineage {
-    private final Map<String, Integer> exprsLineage;
-    private final Map<String, Integer> exprsLineageLosslessCasts;
+    private final Map<RexNode, Integer> exprsLineage;
+    private final Map<RexNode, Integer> exprsLineageLosslessCasts;
 
-    private NodeLineage(Map<String, Integer> exprsLineage,
-        Map<String, Integer> exprsLineageLosslessCasts) {
-      this.exprsLineage = Collections.unmodifiableMap(exprsLineage);
-      this.exprsLineageLosslessCasts = Collections.unmodifiableMap(exprsLineageLosslessCasts);
-    }
-
-    protected static NodeLineage of(
-        Map<String, Integer> exprsLineage, Map<String, Integer> exprsLineageLosslessCasts) {
-      return new NodeLineage(exprsLineage, exprsLineageLosslessCasts);
+    private NodeLineage(Map<RexNode, Integer> exprsLineage,
+        Map<RexNode, Integer> exprsLineageLosslessCasts) {
+      this.exprsLineage = ImmutableMap.copyOf(exprsLineage);
+      this.exprsLineageLosslessCasts =
+          ImmutableMap.copyOf(exprsLineageLosslessCasts);
     }
   }
 
   /** Edge for graph */
   private static class Edge extends DefaultEdge {
-
     final Multimap<RexTableInputRef, RexTableInputRef> equiColumns =
         ArrayListMultimap.create();
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/847e76cd/core/src/main/java/org/apache/calcite/rel/rules/DateRangeRules.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/DateRangeRules.java b/core/src/main/java/org/apache/calcite/rel/rules/DateRangeRules.java
index e9a968e..8d2337f 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/DateRangeRules.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/DateRangeRules.java
@@ -158,7 +158,7 @@ public abstract class DateRangeRules {
       timeUnits = ImmutableSortedSet.<TimeUnitRange>naturalOrder()
           .addAll(timeUnits).add(TimeUnitRange.YEAR).build();
     }
-    final Map<String, RangeSet<Calendar>> operandRanges = new HashMap<>();
+    final Map<RexNode, RangeSet<Calendar>> operandRanges = new HashMap<>();
     for (TimeUnitRange timeUnit : timeUnits) {
       e = e.accept(
           new ExtractShuttle(rexBuilder, timeUnit, operandRanges, timeUnits,
@@ -183,7 +183,7 @@ public abstract class DateRangeRules {
           .unwrap(CalciteConnectionConfig.class).timeZone();
       final RexNode condition =
           replaceTimeUnits(rexBuilder, filter.getCondition(), timeZone);
-      if (RexUtil.eq(condition, filter.getCondition())) {
+      if (condition.equals(filter.getCondition())) {
         return;
       }
       final RelBuilder relBuilder =
@@ -238,14 +238,14 @@ public abstract class DateRangeRules {
   static class ExtractShuttle extends RexShuttle {
     private final RexBuilder rexBuilder;
     private final TimeUnitRange timeUnit;
-    private final Map<String, RangeSet<Calendar>> operandRanges;
+    private final Map<RexNode, RangeSet<Calendar>> operandRanges;
     private final Deque<RexCall> calls = new ArrayDeque<>();
     private final ImmutableSortedSet<TimeUnitRange> timeUnitRanges;
     private final String timeZone;
 
     @VisibleForTesting
     ExtractShuttle(RexBuilder rexBuilder, TimeUnitRange timeUnit,
-        Map<String, RangeSet<Calendar>> operandRanges,
+        Map<RexNode, RangeSet<Calendar>> operandRanges,
         ImmutableSortedSet<TimeUnitRange> timeUnitRanges, String timeZone) {
       this.rexBuilder = Objects.requireNonNull(rexBuilder);
       this.timeUnit = Objects.requireNonNull(timeUnit);
@@ -332,8 +332,7 @@ public abstract class DateRangeRules {
       if (timeUnit == TimeUnitRange.YEAR) {
         return true;
       }
-      final RangeSet<Calendar> calendarRangeSet =
-          operandRanges.get(operand.toString());
+      final RangeSet<Calendar> calendarRangeSet = operandRanges.get(operand);
       if (calendarRangeSet == null || calendarRangeSet.isEmpty()) {
         return false;
       }
@@ -361,7 +360,7 @@ public abstract class DateRangeRules {
           //noinspection unchecked
           return (List<RexNode>) exprs;
         }
-        final Map<String, RangeSet<Calendar>> save =
+        final Map<RexNode, RangeSet<Calendar>> save =
             ImmutableMap.copyOf(operandRanges);
         final ImmutableList.Builder<RexNode> clonedOperands =
             ImmutableList.builder();
@@ -400,7 +399,7 @@ public abstract class DateRangeRules {
 
     RexNode compareExtract(SqlKind comparison, RexNode operand,
         RexLiteral literal) {
-      RangeSet<Calendar> rangeSet = operandRanges.get(operand.toString());
+      RangeSet<Calendar> rangeSet = operandRanges.get(operand);
       if (rangeSet == null) {
         rangeSet = ImmutableRangeSet.<Calendar>of().complement();
       }
@@ -439,7 +438,7 @@ public abstract class DateRangeRules {
       }
       // Intersect old range set with new.
       s2.removeAll(rangeSet.complement());
-      operandRanges.put(operand.toString(), ImmutableRangeSet.copyOf(s2));
+      operandRanges.put(operand, ImmutableRangeSet.copyOf(s2));
       final List<RexNode> nodes = new ArrayList<>();
       for (Range<Calendar> r : s2.asRanges()) {
         nodes.add(toRex(operand, r));
@@ -569,7 +568,7 @@ public abstract class DateRangeRules {
 
     private RexNode compareFloorCeil(SqlKind comparison, RexNode operand,
         RexLiteral timeLiteral, TimeUnitRange timeUnit, boolean floor) {
-      RangeSet<Calendar> rangeSet = operandRanges.get(operand.toString());
+      RangeSet<Calendar> rangeSet = operandRanges.get(operand);
       if (rangeSet == null) {
         rangeSet = ImmutableRangeSet.<Calendar>of().complement();
       }
@@ -581,7 +580,7 @@ public abstract class DateRangeRules {
       s2.add(range);
       // Intersect old range set with new.
       s2.removeAll(rangeSet.complement());
-      operandRanges.put(operand.toString(), ImmutableRangeSet.copyOf(s2));
+      operandRanges.put(operand, ImmutableRangeSet.copyOf(s2));
       if (range.isEmpty()) {
         return rexBuilder.makeLiteral(false);
       }

http://git-wip-us.apache.org/repos/asf/calcite/blob/847e76cd/core/src/main/java/org/apache/calcite/rel/rules/JoinPushExpressionsRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/JoinPushExpressionsRule.java b/core/src/main/java/org/apache/calcite/rel/rules/JoinPushExpressionsRule.java
index 4fb33e5..923809f 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/JoinPushExpressionsRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/JoinPushExpressionsRule.java
@@ -61,7 +61,7 @@ public class JoinPushExpressionsRule extends RelOptRule {
     // If the join is the same, we bail out
     if (newJoin instanceof Join) {
       final RexNode newCondition = ((Join) newJoin).getCondition();
-      if (join.getCondition().toString().equals(newCondition.toString())) {
+      if (join.getCondition().equals(newCondition)) {
         return;
       }
     }

http://git-wip-us.apache.org/repos/asf/calcite/blob/847e76cd/core/src/main/java/org/apache/calcite/rel/rules/PushProjector.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/PushProjector.java b/core/src/main/java/org/apache/calcite/rel/rules/PushProjector.java
index e0d6b71..e34a4a8 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/PushProjector.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/PushProjector.java
@@ -700,11 +700,15 @@ public class PushProjector {
         final ImmutableBitSet exprArgs = RelOptUtil.InputFinder.bits(call);
         if (exprArgs.cardinality() > 0) {
           if (leftFields.contains(exprArgs) && isStrong(exprArgs, call)) {
-            addExpr(preserveLeft, call);
+            if (!preserveLeft.contains(call)) {
+              preserveLeft.add(call);
+            }
             return true;
           } else if (rightFields.contains(exprArgs) && isStrong(exprArgs, call)) {
             assert preserveRight != null;
-            addExpr(preserveRight, call);
+            if (!preserveRight.contains(call)) {
+              preserveRight.add(call);
+            }
             return true;
           }
         }
@@ -721,22 +725,6 @@ public class PushProjector {
       return null;
     }
 
-    /**
-     * Adds an expression to a list if the same expression isn't already in
-     * the list. Expressions are identical if their digests are the same.
-     *
-     * @param exprList current list of expressions
-     * @param newExpr  new expression to be added
-     */
-    private void addExpr(List<RexNode> exprList, RexNode newExpr) {
-      String newExprString = newExpr.toString();
-      for (RexNode expr : exprList) {
-        if (newExprString.compareTo(expr.toString()) == 0) {
-          return;
-        }
-      }
-      exprList.add(newExpr);
-    }
   }
 
   /**
@@ -804,13 +792,13 @@ public class PushProjector {
         int adjust1,
         List<RexNode> rexList2,
         int adjust2) {
-      int match = findExprInList(rex, rexList1);
+      int match = rexList1.indexOf(rex);
       if (match >= 0) {
         return match + adjust1;
       }
 
       if (rexList2 != null) {
-        match = findExprInList(rex, rexList2);
+        match = rexList2.indexOf(rex);
         if (match >= 0) {
           return match + adjust2;
         }
@@ -818,17 +806,6 @@ public class PushProjector {
 
       return -1;
     }
-
-    private int findExprInList(RexNode rex, List<RexNode> rexList) {
-      int match = 0;
-      for (RexNode rexElement : rexList) {
-        if (rexElement.toString().compareTo(rex.toString()) == 0) {
-          return match;
-        }
-        match++;
-      }
-      return -1;
-    }
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/calcite/blob/847e76cd/core/src/main/java/org/apache/calcite/rel/rules/ReduceDecimalsRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/ReduceDecimalsRule.java b/core/src/main/java/org/apache/calcite/rel/rules/ReduceDecimalsRule.java
index 1242d51..c8dd509 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/ReduceDecimalsRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/ReduceDecimalsRule.java
@@ -122,8 +122,8 @@ public class ReduceDecimalsRule extends RelOptRule {
    * longs.
    */
   public class DecimalShuttle extends RexShuttle {
-    private final Map<Pair<String, String>, RexNode> irreducible;
-    private final Map<Pair<String, String>, RexNode> results;
+    private final Map<Pair<RexNode, String>, RexNode> irreducible;
+    private final Map<Pair<RexNode, String>, RexNode> results;
     private final ExpanderMap expanderMap;
 
     public DecimalShuttle(RexBuilder rexBuilder) {
@@ -173,7 +173,7 @@ public class ReduceDecimalsRule extends RelOptRule {
      * Registers node so it will not be computed again
      */
     private void register(RexNode node, RexNode reducedNode) {
-      Pair<String, String> key = RexUtil.makeKey(node);
+      Pair<RexNode, String> key = RexUtil.makeKey(node);
       if (node == reducedNode) {
         irreducible.put(key, reducedNode);
       } else {
@@ -185,7 +185,7 @@ public class ReduceDecimalsRule extends RelOptRule {
      * Lookup registered node
      */
     private RexNode lookup(RexNode node) {
-      Pair<String, String> key = RexUtil.makeKey(node);
+      Pair<RexNode, String> key = RexUtil.makeKey(node);
       if (irreducible.get(key) != null) {
         return node;
       }

http://git-wip-us.apache.org/repos/asf/calcite/blob/847e76cd/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 741d083..50823f4 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
@@ -547,7 +547,7 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
     boolean simplified = false;
     for (int i = 0; i < expList.size(); i++) {
       RexNode expr2 = simplifier.apply(expList.get(i));
-      if (!expr2.toString().equals(expList.get(i).toString())) {
+      if (!expr2.equals(expList.get(i))) {
         expList.set(i, expr2);
         simplified = true;
       }

http://git-wip-us.apache.org/repos/asf/calcite/blob/847e76cd/core/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java b/core/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java
index b993184..9eebeb3 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java
@@ -599,7 +599,7 @@ public abstract class SubQueryRemoveRule extends RelOptRule {
     }
 
     @Override public RexNode visitSubQuery(RexSubQuery subQuery) {
-      return RexUtil.eq(subQuery, this.subQuery) ? replacement : subQuery;
+      return subQuery.equals(this.subQuery) ? replacement : subQuery;
     }
   }
 }

http://git-wip-us.apache.org/repos/asf/calcite/blob/847e76cd/core/src/main/java/org/apache/calcite/rex/LogicVisitor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/LogicVisitor.java b/core/src/main/java/org/apache/calcite/rex/LogicVisitor.java
index f632e94..7d4f494 100644
--- a/core/src/main/java/org/apache/calcite/rex/LogicVisitor.java
+++ b/core/src/main/java/org/apache/calcite/rex/LogicVisitor.java
@@ -115,7 +115,7 @@ public class LogicVisitor implements RexBiVisitor<Logic, Logic> {
   }
 
   private Logic end(RexNode node, Logic arg) {
-    if (RexUtil.eq(node, seek)) {
+    if (node.equals(seek)) {
       logicCollection.add(arg);
     }
     return arg;

http://git-wip-us.apache.org/repos/asf/calcite/blob/847e76cd/core/src/main/java/org/apache/calcite/rex/RexCall.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexCall.java b/core/src/main/java/org/apache/calcite/rex/RexCall.java
index d2c4ac2..26b095d 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexCall.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexCall.java
@@ -26,6 +26,7 @@ import com.google.common.collect.ImmutableList;
 
 import java.util.List;
 import java.util.Objects;
+import javax.annotation.Nonnull;
 
 /**
  * An expression formed by a call to an operator with zero or more expressions
@@ -65,8 +66,8 @@ public class RexCall extends RexNode {
 
   //~ Methods ----------------------------------------------------------------
 
-  protected String computeDigest(boolean withType) {
-    StringBuilder sb = new StringBuilder(op.getName());
+  protected @Nonnull String computeDigest(boolean withType) {
+    final StringBuilder sb = new StringBuilder(op.getName());
     if ((operands.size() == 0)
         && (op.getSyntax() == SqlSyntax.FUNCTION_ID)) {
       // Don't print params for empty arg list. For example, we want
@@ -77,8 +78,7 @@ public class RexCall extends RexNode {
         if (i > 0) {
           sb.append(", ");
         }
-        RexNode operand = operands.get(i);
-        sb.append(operand.toString());
+        sb.append(operands.get(i));
       }
       sb.append(")");
     }
@@ -92,13 +92,13 @@ public class RexCall extends RexNode {
     return sb.toString();
   }
 
-  public String toString() {
+  @Override public final @Nonnull String toString() {
     // This data race is intentional
     String localDigest = digest;
     if (localDigest == null) {
       localDigest = computeDigest(
           isA(SqlKind.CAST) || isA(SqlKind.NEW_SPECIFICATION));
-      digest = localDigest;
+      digest = Objects.requireNonNull(localDigest);
     }
     return localDigest;
   }
@@ -173,6 +173,16 @@ public class RexCall extends RexNode {
   public RexCall clone(RelDataType type, List<RexNode> operands) {
     return new RexCall(type, op, operands);
   }
+
+  @Override public boolean equals(Object obj) {
+    return obj == this
+        || obj instanceof RexCall
+        && toString().equals(obj.toString());
+  }
+
+  @Override public int hashCode() {
+    return toString().hashCode();
+  }
 }
 
 // End RexCall.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/847e76cd/core/src/main/java/org/apache/calcite/rex/RexCorrelVariable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexCorrelVariable.java b/core/src/main/java/org/apache/calcite/rex/RexCorrelVariable.java
index 750a97b..a16b30d 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexCorrelVariable.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexCorrelVariable.java
@@ -54,6 +54,18 @@ public class RexCorrelVariable extends RexVariable {
   @Override public SqlKind getKind() {
     return SqlKind.CORREL_VARIABLE;
   }
+
+  @Override public boolean equals(Object obj) {
+    return this == obj
+        || obj instanceof RexCorrelVariable
+        && digest.equals(((RexCorrelVariable) obj).digest)
+        && type.equals(((RexCorrelVariable) obj).type)
+        && id.equals(((RexCorrelVariable) obj).id);
+  }
+
+  @Override public int hashCode() {
+    return Objects.hash(digest, type, id);
+  }
 }
 
 // End RexCorrelVariable.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/847e76cd/core/src/main/java/org/apache/calcite/rex/RexDynamicParam.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexDynamicParam.java b/core/src/main/java/org/apache/calcite/rex/RexDynamicParam.java
index 6bbd692..7e92d4a 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexDynamicParam.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexDynamicParam.java
@@ -19,6 +19,8 @@ package org.apache.calcite.rex;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.sql.SqlKind;
 
+import java.util.Objects;
+
 /**
  * Dynamic parameter reference in a row-expression.
  */
@@ -59,6 +61,18 @@ public class RexDynamicParam extends RexVariable {
   public <R, P> R accept(RexBiVisitor<R, P> visitor, P arg) {
     return visitor.visitDynamicParam(this, arg);
   }
+
+  @Override public boolean equals(Object obj) {
+    return this == obj
+        || obj instanceof RexDynamicParam
+        && digest.equals(((RexDynamicParam) obj).digest)
+        && type.equals(((RexDynamicParam) obj).type)
+        && index == ((RexDynamicParam) obj).index;
+  }
+
+  @Override public int hashCode() {
+    return Objects.hash(digest, type, index);
+  }
 }
 
 // End RexDynamicParam.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/847e76cd/core/src/main/java/org/apache/calcite/rex/RexNode.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexNode.java b/core/src/main/java/org/apache/calcite/rex/RexNode.java
index 95df460..ca382c0 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexNode.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexNode.java
@@ -96,6 +96,19 @@ public abstract class RexNode {
    * {@link RexBiVisitor#visitInputRef(RexInputRef, Object)} visitXxx} method.
    */
   public abstract <R, P> R accept(RexBiVisitor<R, P> visitor, P arg);
+
+  /** {@inheritDoc}
+   *
+   * <p>Every node must implement {@link #equals} based on its content
+   */
+  @Override public abstract boolean equals(Object obj);
+
+  /** {@inheritDoc}
+   *
+   * <p>Every node must implement {@link #hashCode} consistent with
+   * {@link #equals}
+   */
+  @Override public abstract int hashCode();
 }
 
 // End RexNode.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/847e76cd/core/src/main/java/org/apache/calcite/rex/RexOver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexOver.java b/core/src/main/java/org/apache/calcite/rex/RexOver.java
index fe7f7d9..7737ccb 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexOver.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexOver.java
@@ -26,6 +26,7 @@ import com.google.common.base.Preconditions;
 
 import java.util.List;
 import java.util.Objects;
+import javax.annotation.Nonnull;
 
 /**
  * Call to an aggregate function over a window.
@@ -88,7 +89,7 @@ public class RexOver extends RexCall {
     return distinct;
   }
 
-  @Override protected String computeDigest(boolean withType) {
+  @Override protected @Nonnull String computeDigest(boolean withType) {
     final StringBuilder sb = new StringBuilder(op.getName());
     sb.append("(");
     if (distinct) {
@@ -98,8 +99,7 @@ public class RexOver extends RexCall {
       if (i > 0) {
         sb.append(", ");
       }
-      RexNode operand = operands.get(i);
-      sb.append(operand.toString());
+      sb.append(operands.get(i));
     }
     sb.append(")");
     if (withType) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/847e76cd/core/src/main/java/org/apache/calcite/rex/RexProgramBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexProgramBuilder.java b/core/src/main/java/org/apache/calcite/rex/RexProgramBuilder.java
index f871b9d..9feb711 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexProgramBuilder.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexProgramBuilder.java
@@ -43,7 +43,7 @@ public class RexProgramBuilder {
   private final RexBuilder rexBuilder;
   private final RelDataType inputRowType;
   private final List<RexNode> exprList = new ArrayList<>();
-  private final Map<Pair<String, String>, RexLocalRef> exprMap =
+  private final Map<Pair<RexNode, String>, RexLocalRef> exprMap =
       new HashMap<>();
   private final List<RexLocalRef> localRefList = new ArrayList<>();
   private final List<RexLocalRef> projectRefList = new ArrayList<>();
@@ -329,7 +329,7 @@ public class RexProgramBuilder {
     expr = simplify.simplifyPreservingType(expr);
 
     RexLocalRef ref;
-    final Pair<String, String> key;
+    final Pair<RexNode, String> key;
     if (expr instanceof RexLocalRef) {
       key = null;
       ref = (RexLocalRef) expr;

http://git-wip-us.apache.org/repos/asf/calcite/blob/847e76cd/core/src/main/java/org/apache/calcite/rex/RexRangeRef.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexRangeRef.java b/core/src/main/java/org/apache/calcite/rex/RexRangeRef.java
index 04aa277..97d5e46 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexRangeRef.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexRangeRef.java
@@ -18,6 +18,8 @@ package org.apache.calcite.rex;
 
 import org.apache.calcite.rel.type.RelDataType;
 
+import java.util.Objects;
+
 /**
  * Reference to a range of columns.
  *
@@ -74,6 +76,17 @@ public class RexRangeRef extends RexNode {
   public <R, P> R accept(RexBiVisitor<R, P> visitor, P arg) {
     return visitor.visitRangeRef(this, arg);
   }
+
+  @Override public boolean equals(Object obj) {
+    return this == obj
+        || obj instanceof RexRangeRef
+        && type.equals(((RexRangeRef) obj).type)
+        && offset == ((RexRangeRef) obj).offset;
+  }
+
+  @Override public int hashCode() {
+    return Objects.hash(type, offset);
+  }
 }
 
 // End RexRangeRef.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/847e76cd/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexSimplify.java b/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
index a8e95e9..f78a90c 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
@@ -300,7 +300,7 @@ public class RexSimplify {
     // Simplify "x <op> x"
     final RexNode o0 = operands.get(0);
     final RexNode o1 = operands.get(1);
-    if (RexUtil.eq(o0, o1)
+    if (o0.equals(o1)
         && (unknownAs == FALSE
             || (!o0.getType().isNullable()
                 && !o1.getType().isNullable()))) {
@@ -514,7 +514,7 @@ public class RexSimplify {
 
     for (RexNode p : predicates.pulledUpPredicates) {
       IsPredicate pred = IsPredicate.of(p);
-      if (pred == null || !a.toString().equals(pred.ref.toString())) {
+      if (pred == null || !a.equals(pred.ref)) {
         continue;
       }
       if (kind == pred.kind) {
@@ -649,12 +649,12 @@ public class RexSimplify {
   }
 
   private RexNode simplifyCoalesce(RexCall call) {
-    final Set<String> digests = new HashSet<>();
+    final Set<RexNode> operandSet = new HashSet<>();
     final List<RexNode> operands = new ArrayList<>();
     for (RexNode operand : call.getOperands()) {
       operand = simplify(operand, UNKNOWN);
       if (!RexUtil.isNull(operand)
-          && digests.add(operand.toString())) {
+          && operandSet.add(operand)) {
         operands.add(operand);
       }
       if (!operand.getType().isNullable()) {
@@ -699,7 +699,7 @@ public class RexSimplify {
 
       // create new branch
       if (lastBranch != null) {
-        if (lastBranch.value.toString().equals(newValue.toString())
+        if (lastBranch.value.equals(newValue)
             && isSafeExpression(newCond)) {
           // in this case, last branch and new branch have the same conclusion,
           // hence we create a new composite condition and we do not add it to
@@ -809,7 +809,7 @@ public class RexSimplify {
     }
 
     @Override public String toString() {
-      return new StringBuilder(cond.toString()).append(" => ").append(value).toString();
+      return cond + " => " + value;
     }
 
     /** Given "CASE WHEN p1 THEN v1 ... ELSE e END"
@@ -1120,14 +1120,15 @@ public class RexSimplify {
       return simplify(terms.get(0), FALSE);
     }
     // Try to simplify the expression
-    final Multimap<String, Pair<String, RexNode>> equalityTerms = ArrayListMultimap.create();
-    final Map<String, Pair<Range<C>, List<RexNode>>> rangeTerms =
+    final Multimap<RexNode, Pair<RexNode, RexNode>> equalityTerms =
+        ArrayListMultimap.create();
+    final Map<RexNode, Pair<Range<C>, List<RexNode>>> rangeTerms =
         new HashMap<>();
-    final Map<String, String> equalityConstantTerms = new HashMap<>();
-    final Set<String> negatedTerms = new HashSet<>();
-    final Set<String> nullOperands = new HashSet<>();
+    final Map<RexNode, RexLiteral> equalityConstantTerms = new HashMap<>();
+    final Set<RexNode> negatedTerms = new HashSet<>();
+    final Set<RexNode> nullOperands = new HashSet<>();
     final Set<RexNode> notNullOperands = new LinkedHashSet<>();
-    final Set<String> comparedOperands = new HashSet<>();
+    final Set<RexNode> comparedOperands = new HashSet<>();
 
     // Add the predicates from the source to the range terms.
     for (RexNode predicate : predicates.pulledUpPredicates) {
@@ -1173,19 +1174,19 @@ public class RexSimplify {
       case LESS_THAN_OR_EQUAL:
       case GREATER_THAN_OR_EQUAL:
         RexCall call = (RexCall) term;
-        RexNode left = call.getOperands().get(0);
-        comparedOperands.add(left.toString());
+        final RexNode left = call.getOperands().get(0);
+        comparedOperands.add(left);
         // if it is a cast, we include the inner reference
         if (left.getKind() == SqlKind.CAST) {
           RexCall leftCast = (RexCall) left;
-          comparedOperands.add(leftCast.getOperands().get(0).toString());
+          comparedOperands.add(leftCast.getOperands().get(0));
         }
-        RexNode right = call.getOperands().get(1);
-        comparedOperands.add(right.toString());
+        final RexNode right = call.getOperands().get(1);
+        comparedOperands.add(right);
         // if it is a cast, we include the inner reference
         if (right.getKind() == SqlKind.CAST) {
           RexCall rightCast = (RexCall) right;
-          comparedOperands.add(rightCast.getOperands().get(0).toString());
+          comparedOperands.add(rightCast.getOperands().get(0));
         }
         final Comparison comparison = Comparison.of(term);
         // Check for comparison with null values
@@ -1198,15 +1199,15 @@ public class RexSimplify {
         // and hence it can be evaluated to FALSE
         if (term.getKind() == SqlKind.EQUALS) {
           if (comparison != null) {
-            final String literal = comparison.literal.toString();
-            final String prevLiteral =
-                equalityConstantTerms.put(comparison.ref.toString(), literal);
+            final RexLiteral literal = comparison.literal;
+            final RexLiteral prevLiteral =
+                equalityConstantTerms.put(comparison.ref, literal);
             if (prevLiteral != null && !literal.equals(prevLiteral)) {
               return rexBuilder.makeLiteral(false);
             }
           } else if (RexUtil.isReferenceOrAccess(left, true)
               && RexUtil.isReferenceOrAccess(right, true)) {
-            equalityTerms.put(left.toString(), Pair.of(right.toString(), term));
+            equalityTerms.put(left, Pair.of(right, term));
           }
         }
         // Assume the expression a > 5 is part of a Filter condition.
@@ -1216,10 +1217,10 @@ public class RexSimplify {
         // Observe that for creating the inverted term we invert the list of operands.
         RexNode negatedTerm = RexUtil.negate(rexBuilder, call);
         if (negatedTerm != null) {
-          negatedTerms.add(negatedTerm.toString());
+          negatedTerms.add(negatedTerm);
           RexNode invertNegatedTerm = RexUtil.invert(rexBuilder, (RexCall) negatedTerm);
           if (invertNegatedTerm != null) {
-            negatedTerms.add(invertNegatedTerm.toString());
+            negatedTerms.add(invertNegatedTerm);
           }
         }
         // Remove terms that are implied by predicates on the input,
@@ -1243,10 +1244,10 @@ public class RexSimplify {
         }
         break;
       case IN:
-        comparedOperands.add(((RexCall) term).operands.get(0).toString());
+        comparedOperands.add(((RexCall) term).operands.get(0));
         break;
       case BETWEEN:
-        comparedOperands.add(((RexCall) term).operands.get(1).toString());
+        comparedOperands.add(((RexCall) term).operands.get(1));
         break;
       case IS_NOT_NULL:
         notNullOperands.add(((RexCall) term).getOperands().get(0));
@@ -1254,7 +1255,7 @@ public class RexSimplify {
         --i;
         break;
       case IS_NULL:
-        nullOperands.add(((RexCall) term).getOperands().get(0).toString());
+        nullOperands.add(((RexCall) term).getOperands().get(0));
       }
     }
     // If one column should be null and is in a comparison predicate,
@@ -1266,14 +1267,14 @@ public class RexSimplify {
     // Check for equality of two refs wrt equality with constants
     // Example #1. x=5 AND y=5 AND x=y : x=5 AND y=5
     // Example #2. x=5 AND y=6 AND x=y - not satisfiable
-    for (String ref1 : equalityTerms.keySet()) {
-      final String literal1 = equalityConstantTerms.get(ref1);
+    for (RexNode ref1 : equalityTerms.keySet()) {
+      final RexLiteral literal1 = equalityConstantTerms.get(ref1);
       if (literal1 == null) {
         continue;
       }
-      Collection<Pair<String, RexNode>> references = equalityTerms.get(ref1);
-      for (Pair<String, RexNode> ref2 : references) {
-        final String literal2 = equalityConstantTerms.get(ref2.left);
+      Collection<Pair<RexNode, RexNode>> references = equalityTerms.get(ref1);
+      for (Pair<RexNode, RexNode> ref2 : references) {
+        final RexLiteral literal2 = equalityConstantTerms.get(ref2.left);
         if (literal2 == null) {
           continue;
         }
@@ -1291,7 +1292,7 @@ public class RexSimplify {
     //
     // Example. IS NOT NULL(x) AND x < 5  : x < 5
     for (RexNode operand : notNullOperands) {
-      if (!comparedOperands.contains(operand.toString())) {
+      if (!comparedOperands.contains(operand)) {
         terms.add(
             rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_NULL, operand));
       }
@@ -1303,12 +1304,12 @@ public class RexSimplify {
     // Example #1. x AND y AND z AND NOT (x AND y)  - not satisfiable
     // Example #2. x AND y AND NOT (x AND y)        - not satisfiable
     // Example #3. x AND y AND NOT (x AND y AND z)  - may be satisfiable
-    final Set<String> termsSet = new HashSet<>(RexUtil.strings(terms));
+    final Set<RexNode> termsSet = new HashSet<>(terms);
     for (RexNode notDisjunction : notTerms) {
       if (!RexUtil.isDeterministic(notDisjunction)) {
         continue;
       }
-      final List<String> terms2Set = RexUtil.strings(RelOptUtil.conjunctions(notDisjunction));
+      final List<RexNode> terms2Set = RelOptUtil.conjunctions(notDisjunction);
       if (termsSet.containsAll(terms2Set)) {
         return rexBuilder.makeLiteral(false);
       }
@@ -1320,7 +1321,7 @@ public class RexSimplify {
       terms.add(simplify(call, FALSE));
     }
     // The negated terms: only deterministic expressions
-    for (String negatedTerm : negatedTerms) {
+    for (RexNode negatedTerm : negatedTerms) {
       if (termsSet.contains(negatedTerm)) {
         return rexBuilder.makeLiteral(false);
       }
@@ -1673,11 +1674,11 @@ public class RexSimplify {
 
   private static <C extends Comparable<C>> RexNode processRange(
       RexBuilder rexBuilder, List<RexNode> terms,
-      Map<String, Pair<Range<C>, List<RexNode>>> rangeTerms, RexNode term,
+      Map<RexNode, Pair<Range<C>, List<RexNode>>> rangeTerms, RexNode term,
       RexNode ref, C v0, SqlKind comparison) {
-    Pair<Range<C>, List<RexNode>> p = rangeTerms.get(ref.toString());
+    Pair<Range<C>, List<RexNode>> p = rangeTerms.get(ref);
     if (p == null) {
-      rangeTerms.put(ref.toString(),
+      rangeTerms.put(ref,
           Pair.of(range(comparison, v0),
               (List<RexNode>) ImmutableList.of(term)));
     } else {
@@ -1691,7 +1692,7 @@ public class RexSimplify {
           // Range is empty, not satisfiable
           return rexBuilder.makeLiteral(false);
         }
-        rangeTerms.put(ref.toString(),
+        rangeTerms.put(ref,
             Pair.of(Range.singleton(v0),
                 (List<RexNode>) ImmutableList.of(term)));
         // remove
@@ -1862,7 +1863,7 @@ public class RexSimplify {
           }
         }
         newBounds.add(term);
-        rangeTerms.put(ref.toString(),
+        rangeTerms.put(ref,
             Pair.of(r, (List<RexNode>) newBounds.build()));
       } else if (removeLowerBound) {
         ImmutableList.Builder<RexNode> newBounds = ImmutableList.builder();
@@ -1874,7 +1875,7 @@ public class RexSimplify {
           }
         }
         newBounds.add(term);
-        rangeTerms.put(ref.toString(),
+        rangeTerms.put(ref,
             Pair.of(r, (List<RexNode>) newBounds.build()));
       }
     }

http://git-wip-us.apache.org/repos/asf/calcite/blob/847e76cd/core/src/main/java/org/apache/calcite/rex/RexSubQuery.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexSubQuery.java b/core/src/main/java/org/apache/calcite/rex/RexSubQuery.java
index 05fd176..fceb58e 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexSubQuery.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexSubQuery.java
@@ -30,6 +30,7 @@ import org.apache.calcite.sql.type.SqlTypeName;
 import com.google.common.collect.ImmutableList;
 
 import java.util.List;
+import javax.annotation.Nonnull;
 
 /**
  * Scalar expression that represents an IN, EXISTS or scalar sub-query.
@@ -108,11 +109,11 @@ public class RexSubQuery extends RexCall {
     return visitor.visitSubQuery(this, arg);
   }
 
-  @Override protected String computeDigest(boolean withType) {
-    StringBuilder sb = new StringBuilder(op.getName());
+  @Override protected @Nonnull String computeDigest(boolean withType) {
+    final StringBuilder sb = new StringBuilder(op.getName());
     sb.append("(");
     for (RexNode operand : operands) {
-      sb.append(operand.toString());
+      sb.append(operand);
       sb.append(", ");
     }
     sb.append("{\n");

http://git-wip-us.apache.org/repos/asf/calcite/blob/847e76cd/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 2eed8d2..56eabb6 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexUtil.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexUtil.java
@@ -968,8 +968,8 @@ public class RexUtil {
    * representation. For example, "10" integer and "10" bigint result in
    * different keys.
    */
-  public static Pair<String, String> makeKey(RexNode expr) {
-    return Pair.of(expr.toString(), expr.getType().getFullTypeString());
+  public static Pair<RexNode, String> makeKey(RexNode expr) {
+    return Pair.of(expr, expr.getType().getFullTypeString());
   }
 
   /**
@@ -1054,17 +1054,17 @@ public class RexUtil {
       return ImmutableList.of();
     }
     final ImmutableList.Builder<RexNode> builder = ImmutableList.builder();
-    final Set<String> digests = new HashSet<>(); // to eliminate duplicates
+    final Set<RexNode> set = new HashSet<>(); // to eliminate duplicates
     for (RexNode node : nodes) {
       if (node != null) {
-        addAnd(builder, digests, node);
+        addAnd(builder, set, node);
       }
     }
     return builder.build();
   }
 
   private static void addAnd(ImmutableList.Builder<RexNode> builder,
-      Set<String> digests, RexNode node) {
+      Set<RexNode> digests, RexNode node) {
     switch (node.getKind()) {
     case AND:
       for (RexNode operand : ((RexCall) node).getOperands()) {
@@ -1072,7 +1072,7 @@ public class RexUtil {
       }
       return;
     default:
-      if (!node.isAlwaysTrue() && digests.add(node.toString())) {
+      if (!node.isAlwaysTrue() && digests.add(node)) {
         builder.add(node);
       }
     }
@@ -1122,23 +1122,23 @@ public class RexUtil {
       return ImmutableList.of();
     }
     final ImmutableList.Builder<RexNode> builder = ImmutableList.builder();
-    final Set<String> digests = new HashSet<>(); // to eliminate duplicates
+    final Set<RexNode> set = new HashSet<>(); // to eliminate duplicates
     for (RexNode node : nodes) {
-      addOr(builder, digests, node);
+      addOr(builder, set, node);
     }
     return builder.build();
   }
 
   private static void addOr(ImmutableList.Builder<RexNode> builder,
-      Set<String> digests, RexNode node) {
+      Set<RexNode> set, RexNode node) {
     switch (node.getKind()) {
     case OR:
       for (RexNode operand : ((RexCall) node).getOperands()) {
-        addOr(builder, digests, operand);
+        addOr(builder, set, operand);
       }
       return;
     default:
-      if (!node.isAlwaysFalse() && digests.add(node.toString())) {
+      if (!node.isAlwaysFalse() && set.add(node)) {
         builder.add(node);
       }
     }
@@ -1637,7 +1637,7 @@ public class RexUtil {
     Iterator<RexNode> iterator = targets.iterator();
     while (iterator.hasNext()) {
       RexNode next = iterator.next();
-      if (eq(next, e)) {
+      if (next.equals(e)) {
         ++count;
         iterator.remove();
       }
@@ -1650,6 +1650,7 @@ public class RexUtil {
    * <p>This method considers structure, not semantics. 'x &lt; y' is not
    * equivalent to 'y &gt; x'.
    */
+  @Deprecated // use e1.equals(e2)
   public static boolean eq(RexNode e1, RexNode e2) {
     return e1 == e2 || e1.toString().equals(e2.toString());
   }
@@ -2071,7 +2072,7 @@ public class RexUtil {
    * Walks over expressions and builds a bank of common sub-expressions.
    */
   private static class ExpressionNormalizer extends RexVisitorImpl<RexNode> {
-    final Map<String, RexNode> mapDigestToExpr = new HashMap<>();
+    final Map<RexNode, RexNode> map = new HashMap<>();
     final boolean allowDups;
 
     protected ExpressionNormalizer(boolean allowDups) {
@@ -2080,8 +2081,7 @@ public class RexUtil {
     }
 
     protected RexNode register(RexNode expr) {
-      final String key = expr.toString();
-      final RexNode previous = mapDigestToExpr.put(key, expr);
+      final RexNode previous = map.put(expr, expr);
       if (!allowDups && (previous != null)) {
         throw new SubExprExistsException(expr);
       }
@@ -2089,7 +2089,7 @@ public class RexUtil {
     }
 
     protected RexNode lookup(RexNode expr) {
-      return mapDigestToExpr.get(expr.toString());
+      return map.get(expr);
     }
 
     public RexNode visitInputRef(RexInputRef inputRef) {
@@ -2327,7 +2327,7 @@ public class RexUtil {
         return and(pullList(operands));
       case OR:
         operands = flattenOr(((RexCall) rex).getOperands());
-        final Map<String, RexNode> factors = commonFactors(operands);
+        final Map<RexNode, RexNode> factors = commonFactors(operands);
         if (factors.isEmpty()) {
           return or(operands);
         }
@@ -2356,25 +2356,25 @@ public class RexUtil {
       return list;
     }
 
-    private Map<String, RexNode> commonFactors(List<RexNode> nodes) {
-      final Map<String, RexNode> map = new HashMap<>();
+    private Map<RexNode, RexNode> commonFactors(List<RexNode> nodes) {
+      final Map<RexNode, RexNode> map = new HashMap<>();
       int i = 0;
       for (RexNode node : nodes) {
         if (i++ == 0) {
           for (RexNode conjunction : RelOptUtil.conjunctions(node)) {
-            map.put(conjunction.toString(), conjunction);
+            map.put(conjunction, conjunction);
           }
         } else {
-          map.keySet().retainAll(strings(RelOptUtil.conjunctions(node)));
+          map.keySet().retainAll(RelOptUtil.conjunctions(node));
         }
       }
       return map;
     }
 
-    private RexNode removeFactor(Map<String, RexNode> factors, RexNode node) {
+    private RexNode removeFactor(Map<RexNode, RexNode> factors, RexNode node) {
       List<RexNode> list = new ArrayList<>();
       for (RexNode operand : RelOptUtil.conjunctions(node)) {
-        if (!factors.containsKey(operand.toString())) {
+        if (!factors.containsKey(operand)) {
           list.add(operand);
         }
       }

http://git-wip-us.apache.org/repos/asf/calcite/blob/847e76cd/core/src/main/java/org/apache/calcite/rex/RexVariable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexVariable.java b/core/src/main/java/org/apache/calcite/rex/RexVariable.java
index 717a569..6a702c0 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexVariable.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexVariable.java
@@ -18,6 +18,8 @@ package org.apache.calcite.rex;
 
 import org.apache.calcite.rel.type.RelDataType;
 
+import java.util.Objects;
+
 /**
  * A row-expression which references a field.
  */
@@ -32,11 +34,9 @@ public abstract class RexVariable extends RexNode {
   protected RexVariable(
       String name,
       RelDataType type) {
-    assert type != null;
-    assert name != null;
-    this.name = name;
-    this.digest = name;
-    this.type = type;
+    this.name = Objects.requireNonNull(name);
+    this.digest = Objects.requireNonNull(name);
+    this.type = Objects.requireNonNull(type);
   }
 
   //~ Methods ----------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/calcite/blob/847e76cd/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
index 9512312..2e58719 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
@@ -706,8 +706,8 @@ public class SqlToRelConverter {
       final List<Integer> origins = new ArrayList<>();
       int dupCount = 0;
       for (int i = 0; i < projectExprs.size(); i++) {
-        int x = findExpr(projectExprs.get(i), projectExprs, i);
-        if (x >= 0) {
+        int x = projectExprs.indexOf(projectExprs.get(i));
+        if (x >= 0 && x < i) {
           origins.add(x);
           ++dupCount;
         } else {
@@ -770,16 +770,6 @@ public class SqlToRelConverter {
         false);
   }
 
-  private int findExpr(RexNode seek, List<RexNode> exprs, int count) {
-    for (int i = 0; i < count; i++) {
-      RexNode expr = exprs.get(i);
-      if (expr.toString().equals(seek.toString())) {
-        return i;
-      }
-    }
-    return -1;
-  }
-
   /**
    * Converts a query's ORDER BY clause, if any.
    *
@@ -5095,7 +5085,7 @@ public class SqlToRelConverter {
     private int lookupOrCreateGroupExpr(RexNode expr) {
       int index = 0;
       for (RexNode convertedInputExpr : Pair.left(convertedInputExprs)) {
-        if (expr.toString().equals(convertedInputExpr.toString())) {
+        if (expr.equals(convertedInputExpr)) {
           return index;
         }
         ++index;

http://git-wip-us.apache.org/repos/asf/calcite/blob/847e76cd/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 ba38082..dd775ff 100644
--- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
@@ -5245,9 +5245,9 @@ LogicalAggregate(group=[{0}], EXPR$1=[STDDEV_POP($1)], EXPR$2=[AVG($1)], EXPR$3=
         </Resource>
         <Resource name="planAfter">
             <![CDATA[
-LogicalProject(NAME=[$0], EXPR$1=[CAST(POWER(/(-($1, /(*($2, $2), $3)), $3), 0.5)):INTEGER NOT NULL], EXPR$2=[CAST(/($2, $3)):INTEGER NOT NULL], EXPR$3=[CAST(POWER(/(-($4, /(*($2, $2), $3)), CASE(=($3, 1), null, -($3, 1))), 0.5)):INTEGER NOT NULL], EXPR$4=[CAST(/(-($5, /(*($2, $2), $3)), $3)):INTEGER NOT NULL], EXPR$5=[CAST(/(-($6, /(*($2, $2), $3)), CASE(=($3, 1), null, -($3, 1)))):INTEGER NOT NULL])
-  LogicalAggregate(group=[{0}], agg#0=[$SUM0($2)], agg#1=[$SUM0($1)], agg#2=[COUNT()], agg#3=[$SUM0($3)], agg#4=[$SUM0($4)], agg#5=[$SUM0($5)])
-    LogicalProject(NAME=[$0], DEPTNO=[$1], $f2=[*($1, $1)], $f3=[*($1, $1)], $f4=[*($1, $1)], $f5=[*($1, $1)])
+LogicalProject(NAME=[$0], EXPR$1=[CAST(POWER(/(-($1, /(*($2, $2), $3)), $3), 0.5)):INTEGER NOT NULL], EXPR$2=[CAST(/($2, $3)):INTEGER NOT NULL], EXPR$3=[CAST(POWER(/(-($1, /(*($2, $2), $3)), CASE(=($3, 1), null, -($3, 1))), 0.5)):INTEGER NOT NULL], EXPR$4=[CAST(/(-($1, /(*($2, $2), $3)), $3)):INTEGER NOT NULL], EXPR$5=[CAST(/(-($1, /(*($2, $2), $3)), CASE(=($3, 1), null, -($3, 1)))):INTEGER NOT NULL])
+  LogicalAggregate(group=[{0}], agg#0=[$SUM0($2)], agg#1=[$SUM0($1)], agg#2=[COUNT()])
+    LogicalProject(NAME=[$0], DEPTNO=[$1], $f2=[*($1, $1)])
       LogicalProject(NAME=[$1], DEPTNO=[$0])
         LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
 ]]>
@@ -5775,8 +5775,7 @@ LogicalProject(DEPTNO=[$0], ENAME=[$1])
   LogicalProject(DEPTNO=[$9], ENAME=[$1])
     LogicalJoin(condition=[=($7, $9)], joinType=[inner])
       LogicalTableScan(table=[[CATALOG, SALES, EMP]])
-      LogicalFilter(condition=[=($0, 10)])
-        LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+      LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
 ]]>
         </Resource>
     </TestCase>