This is an automated email from the ASF dual-hosted git repository.

danny0405 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/calcite.git


The following commit(s) were added to refs/heads/master by this push:
     new 551e3f5  [CALCITE-4073] Add a new component RexNormalize for more 
effect rex nodes normalization (part1)
551e3f5 is described below

commit 551e3f5a182a48e20586e40c378224bb63e4bfb3
Author: yuzhao.cyz <yuzhao....@gmail.com>
AuthorDate: Tue Jul 14 15:38:58 2020 +0800

    [CALCITE-4073] Add a new component RexNormalize for more effect rex nodes 
normalization (part1)
    
    * Add a new component named RexNormalize
    * By default, we only normalize the RexCalls during planning phrase, and
    there is no way to normalize it when constructing the calls now
    * Recover the plan diffs
    * Changes the RexCall#equals to be semantic equivalent, which i think is
    not a good design, we should promote to have a digest abstraction just
    like RelNode
---
 .../main/java/org/apache/calcite/rex/RexCall.java  | 108 +++------------
 .../main/java/org/apache/calcite/rex/RexNode.java  |  14 --
 .../java/org/apache/calcite/rex/RexNormalize.java  | 150 +++++++++++++++++++++
 .../main/java/org/apache/calcite/sql/SqlKind.java  |   6 +-
 .../java/org/apache/calcite/sql/SqlOperator.java   |  11 ++
 .../org/apache/calcite/sql/type/SqlTypeUtil.java   |  28 ++++
 .../calcite/rex/RexCallNormalizationTest.java      |  85 ------------
 .../org/apache/calcite/rex/RexNormalizeTest.java   | 141 +++++++++++++++++++
 .../org/apache/calcite/test/JdbcAdapterTest.java   |   4 +-
 .../java/org/apache/calcite/test/LatticeTest.java  |   6 +-
 .../org/apache/calcite/test/RelBuilderTest.java    |   4 +-
 .../test/enumerable/EnumerableCorrelateTest.java   |   4 +-
 .../org/apache/calcite/test/RelOptRulesTest.xml    |  74 +++++-----
 .../apache/calcite/test/SqlToRelConverterTest.xml  |  24 ++--
 .../org/apache/calcite/test/TopDownOptTest.xml     |   4 +-
 core/src/test/resources/sql/blank.iq               |   2 +-
 core/src/test/resources/sql/sub-query.iq           |   4 +-
 17 files changed, 418 insertions(+), 251 deletions(-)

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 e604820..776fb29 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexCall.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexCall.java
@@ -18,12 +18,11 @@ package org.apache.calcite.rex;
 
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.type.RelDataType;
-import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.SqlOperator;
 import org.apache.calcite.sql.SqlSyntax;
-import org.apache.calcite.sql.fun.SqlStdOperatorTable;
 import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.sql.type.SqlTypeUtil;
 import org.apache.calcite.util.Litmus;
 import org.apache.calcite.util.Pair;
 
@@ -58,11 +57,16 @@ public class RexCall extends RexNode {
   public final ImmutableList<RexNode> operands;
   public final RelDataType type;
   public final int nodeCount;
+
   /**
    * Cache of hash code.
    */
   protected int hash = 0;
 
+  /**
+   * Cache of normalized variables used for #equals and #hashCode.
+   */
+  private Pair<SqlOperator, List<RexNode>> normalized;
 
   //~ Constructors -----------------------------------------------------------
 
@@ -71,11 +75,8 @@ public class RexCall extends RexNode {
       SqlOperator op,
       List<? extends RexNode> operands) {
     this.type = Objects.requireNonNull(type, "type");
-    Objects.requireNonNull(op, "operator");
-    final Pair<SqlOperator, ImmutableList<RexNode>> normalized =
-        normalize(op, operands);
-    this.op = normalized.left;
-    this.operands = normalized.right;
+    this.op = Objects.requireNonNull(op, "operator");
+    this.operands = ImmutableList.copyOf(operands);
     this.nodeCount = RexUtil.nodeCount(1, this.operands);
     assert op.getKind() != null : op;
     assert op.validRexOperands(operands.size(), Litmus.THROW) : this;
@@ -119,7 +120,7 @@ public class RexCall extends RexNode {
         RexNode otherArg = operands.get(1 - i);
         if ((!(otherArg instanceof RexLiteral)
             || ((RexLiteral) otherArg).digestIncludesType() == 
RexDigestIncludeType.NO_TYPE)
-            && equalSansNullability(operand.getType(), otherArg.getType())) {
+            && SqlTypeUtil.equalSansNullability(operand.getType(), 
otherArg.getType())) {
           includeType = RexDigestIncludeType.NO_TYPE;
         }
       }
@@ -139,81 +140,6 @@ public class RexCall extends RexNode {
     }
   }
 
-  private Pair<SqlOperator, ImmutableList<RexNode>> normalize(
-      SqlOperator operator,
-      List<? extends RexNode> operands) {
-    final ImmutableList<RexNode> oldOperands = ImmutableList.copyOf(operands);
-    final Pair<SqlOperator, ImmutableList<RexNode>> original = 
Pair.of(operator, oldOperands);
-    if (!needNormalize()) {
-      return original;
-    }
-    if (operands.size() != 2
-        || !operands.stream().allMatch(operand -> operand.getClass() == 
RexInputRef.class)) {
-      return original;
-    }
-    final RexInputRef operand0 = (RexInputRef) operands.get(0);
-    final RexInputRef operand1 = (RexInputRef) operands.get(1);
-    final SqlKind kind = operator.getKind();
-    if (operand0.getIndex() < operand1.getIndex()) {
-      return original;
-    }
-    // If arguments are the same, then we normalize < vs >
-    // '<' == 60, '>' == 62, so we prefer <.
-    if (operand0.getIndex() == operand1.getIndex()) {
-      if (kind.reverse().compareTo(kind) < 0) {
-        return Pair.of(SqlStdOperatorTable.reverse(operator), oldOperands);
-      } else {
-        return original;
-      }
-    }
-
-    if (SqlKind.SYMMETRICAL_SAME_ARG_TYPE.contains(kind)) {
-      final RelDataType firstType = operands.get(0).getType();
-      for (int i = 1; i < operands.size(); i++) {
-        if (!equalSansNullability(firstType, operands.get(i).getType())) {
-          // Arguments have different type, thus they must not be sorted
-          return original;
-        }
-      }
-      // fall through: order arguments below
-    } else if (!SqlKind.SYMMETRICAL.contains(kind)
-        && kind == kind.reverse()) {
-      // The operations have to be either symmetrical or reversible
-      // Nothing matched => we skip argument sorting
-      return original;
-    }
-    // $0=$1 is the same as $1=$0, so we make sure the digest is the same for 
them.
-
-    // When $1 > $0 is normalized, the operation needs to be flipped
-    // So we sort arguments first, then flip the sign.
-    final SqlOperator newOp = SqlStdOperatorTable.reverse(operator);
-    final ImmutableList<RexNode> newOperands = ImmutableList.of(operand1, 
operand0);
-    return Pair.of(newOp, newOperands);
-  }
-
-  /**
-   * This is a poorman's
-   * {@link 
org.apache.calcite.sql.type.SqlTypeUtil#equalSansNullability(RelDataTypeFactory,
 RelDataType, RelDataType)}
-   * <p>{@code SqlTypeUtil} requires {@link RelDataTypeFactory} which we 
haven't, so we assume that
-   * "not null" is represented in the type's digest as a trailing "NOT NULL" 
(case sensitive)
-   * @param a first type
-   * @param b second type
-   * @return true if the types are equal or the only difference is nullability
-   */
-  private static boolean equalSansNullability(RelDataType a, RelDataType b) {
-    String x = a.getFullTypeString();
-    String y = b.getFullTypeString();
-    if (x.length() < y.length()) {
-      String c = x;
-      x = y;
-      y = c;
-    }
-
-    return (x.length() == y.length()
-        || x.length() == y.length() + 9 && x.endsWith(" NOT NULL"))
-        && x.startsWith(y);
-  }
-
   protected @Nonnull String computeDigest(boolean withType) {
     final StringBuilder sb = new StringBuilder(op.getName());
     if ((operands.size() == 0)
@@ -318,6 +244,13 @@ public class RexCall extends RexNode {
     return new RexCall(type, op, operands);
   }
 
+  private Pair<SqlOperator, List<RexNode>> getNormalized() {
+    if (this.normalized == null) {
+      this.normalized = RexNormalize.normalize(this.op, this.operands);
+    }
+    return this.normalized;
+  }
+
   @Override public boolean equals(Object o) {
     if (this == o) {
       return true;
@@ -325,15 +258,18 @@ public class RexCall extends RexNode {
     if (o == null || getClass() != o.getClass()) {
       return false;
     }
+    Pair<SqlOperator, List<RexNode>> thisNormalized = getNormalized();
     RexCall rexCall = (RexCall) o;
-    return op.equals(rexCall.op)
-        && operands.equals(rexCall.operands)
+    Pair<SqlOperator, List<RexNode>> thatNormalized = rexCall.getNormalized();
+    return thisNormalized.left.equals(thatNormalized.left)
+        && thisNormalized.right.equals(thatNormalized.right)
         && type.equals(rexCall.type);
   }
 
   @Override public int hashCode() {
     if (hash == 0) {
-      hash = Objects.hash(op, operands);
+      final Pair<SqlOperator, List<RexNode>> normalized = getNormalized();
+      hash = Objects.hash(normalized.left, normalized.right);
     }
     return hash;
   }
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 88332a2..8a42b9d 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexNode.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexNode.java
@@ -16,12 +16,9 @@
  */
 package org.apache.calcite.rex;
 
-import org.apache.calcite.config.CalciteSystemProperty;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.sql.SqlKind;
 
-import org.apiguardian.api.API;
-
 import java.util.Collection;
 
 /**
@@ -40,17 +37,6 @@ import java.util.Collection;
  */
 public abstract class RexNode {
 
-  /**
-   * The digest of {@code RexNode} is normalized by default, however, 
sometimes a non-normalized
-   * representation is required.
-   * This method enables subclasses to identify if normalization is required.
-   * @return true if the digest needs to be normalized
-   */
-  @API(since = "1.22", status = API.Status.EXPERIMENTAL)
-  protected static boolean needNormalize() {
-    return CalciteSystemProperty.ENABLE_REX_DIGEST_NORMALIZE.value();
-  }
-
   //~ Instance fields --------------------------------------------------------
 
   // Effectively final. Set in each sub-class constructor, and never re-set.
diff --git a/core/src/main/java/org/apache/calcite/rex/RexNormalize.java 
b/core/src/main/java/org/apache/calcite/rex/RexNormalize.java
new file mode 100644
index 0000000..b5dc562
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/rex/RexNormalize.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.rex;
+
+import org.apache.calcite.config.CalciteSystemProperty;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.fun.SqlStdOperatorTable;
+import org.apache.calcite.sql.type.SqlTypeUtil;
+import org.apache.calcite.util.Pair;
+
+import com.google.common.collect.ImmutableList;
+
+import org.apiguardian.api.API;
+
+import java.util.List;
+
+/**
+ * Context required to normalize a row-expression.
+ *
+ * <p>Currently, only simple normalization is supported, such as:
+ *
+ * <ul>
+ *   <li>$2 = $1 &rarr; $1 = $2</li>
+ *   <li>$2 &gt; $1 &rarr; $1 &lt; $2</li>
+ *   <li>1.23 = $1 &rarr; $1 = 1.23</li>
+ *   <li>OR(OR(udf($1), $2), $3) &rarr; OR($3, OR($2, udf($1)))</li>
+ * </ul>
+ *
+ * <p>In the future, this component may extend to support more normalization 
cases
+ * for general promotion. e.g. the strategy to decide which operand is more 
complex
+ * should be more smart.
+ *
+ * <p>There is no one normalization strategy that works for all cases, and no 
consensus about what
+ * the desired strategies should be. So by default, the normalization is 
disabled. We do force
+ * normalization when computing the digest of {@link RexCall}s during planner 
planning.
+ */
+public class RexNormalize {
+
+  private RexNormalize() {}
+
+  /**
+   * Normalizes the variables of a rex call.
+   *
+   * @param operator The operator
+   * @param operands The operands
+   *
+   * @return normalized variables of the call or the original
+   * if there is no need to normalize
+   */
+  @API(since = "1.24", status = API.Status.EXPERIMENTAL)
+  public static Pair<SqlOperator, List<RexNode>> normalize(
+      SqlOperator operator,
+      List<RexNode> operands) {
+    final Pair<SqlOperator, List<RexNode>> original = Pair.of(operator, 
operands);
+    if (!allowsNormalize()) {
+      return original;
+    }
+    // Quick check.
+    if (operands.size() != 2) {
+      return original;
+    }
+
+    final RexNode operand0 = operands.get(0);
+    final RexNode operand1 = operands.get(1);
+
+    // If arguments are the same, then we normalize < vs >
+    // '<' == 60, '>' == 62, so we prefer <.
+    final SqlKind kind = operator.getKind();
+    final SqlKind reversedKind = kind.reverse();
+    final int x = reversedKind.compareTo(kind);
+    if (x < 0) {
+      return Pair.of(
+          SqlStdOperatorTable.reverse(operator),
+          ImmutableList.of(operand1, operand0));
+    }
+    if (x > 0) {
+      return original;
+    }
+
+    if (!isSymmetricalCall(operator, operand0, operand1)) {
+      return original;
+    }
+
+    if (reorderOperands(operand0, operand1) < 0) {
+      // $0=$1 is the same as $1=$0, so we make sure the digest is the same 
for them.
+
+      // When $1 > $0 is normalized, the operation needs to be flipped
+      // so we sort arguments first, then flip the sign.
+      return Pair.of(
+          SqlStdOperatorTable.reverse(operator),
+          ImmutableList.of(operand1, operand0));
+    }
+    return original;
+  }
+
+  /**
+   * Compares two operands to see which one should be normalized to be in 
front of the other.
+   *
+   * <p>We can always use the #hashCode to reorder the operands, do it as a 
fallback to keep
+   * good readability.
+   *
+   * @param operand0  First operand
+   * @param operand1  Second operand
+   *
+   * @return non-negative (>=0) if {@code operand0} should be in the front,
+   * negative if {@code operand1} should be in the front
+   */
+  private static int reorderOperands(RexNode operand0, RexNode operand1) {
+    // Reorder the operands based on the SqlKind enumeration sequence,
+    // smaller is in the behind, e.g. the literal is behind of input ref and 
AND, OR.
+    int x = operand0.getKind().compareTo(operand1.getKind());
+    // If the operands are same kind, use the hashcode to reorder.
+    // Note: the RexInputRef's hash code is its index.
+    return x != 0 ? x : operand1.hashCode() - operand0.hashCode();
+  }
+
+  /** Returns whether a call is symmetrical. **/
+  private static boolean isSymmetricalCall(
+      SqlOperator operator,
+      RexNode operand0,
+      RexNode operand1) {
+    return operator.isSymmetrical()
+        || SqlKind.SYMMETRICAL_SAME_ARG_TYPE.contains(operator.getKind())
+            && SqlTypeUtil.equalSansNullability(operand0.getType(), 
operand1.getType());
+  }
+
+  /**
+   * The digest of {@code RexNode} is normalized by default.
+   *
+   * @return true if the digest allows normalization
+   */
+  private static boolean allowsNormalize() {
+    return CalciteSystemProperty.ENABLE_REX_DIGEST_NORMALIZE.value();
+  }
+}
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlKind.java 
b/core/src/main/java/org/apache/calcite/sql/SqlKind.java
index f1cfb08..247c985 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlKind.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlKind.java
@@ -1387,9 +1387,9 @@ public enum SqlKind {
   public static final Set<SqlKind> SIMPLE_BINARY_OPS;
 
   static {
-    EnumSet<SqlKind> kinds = EnumSet.of(SqlKind.PLUS, SqlKind.MINUS, 
SqlKind.TIMES, SqlKind.DIVIDE);
-    kinds.addAll(SqlKind.COMPARISON);
-    kinds.remove(SqlKind.IN);
+    EnumSet<SqlKind> kinds = EnumSet.copyOf(SqlKind.BINARY_ARITHMETIC);
+    kinds.remove(SqlKind.MOD);
+    kinds.addAll(SqlKind.BINARY_COMPARISON);
     SIMPLE_BINARY_OPS = Sets.immutableEnumSet(kinds);
   }
 
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlOperator.java 
b/core/src/main/java/org/apache/calcite/sql/SqlOperator.java
index 199681c..14d9edd 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlOperator.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlOperator.java
@@ -942,6 +942,17 @@ public abstract class SqlOperator {
   }
 
   /**
+   * Returns whether a call to this operator is not sensitive to the operands 
input order.
+   * An operator is symmetrical if the call returns the same result when
+   * the operands are shuffled.
+   *
+   * <p>By default, returns true for {@link SqlKind#SYMMETRICAL}.
+   */
+  public boolean isSymmetrical() {
+    return SqlKind.SYMMETRICAL.contains(kind);
+  }
+
+  /**
    * Returns whether it is unsafe to cache query plans referencing this
    * operator; false is assumed by default
    */
diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeUtil.java 
b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeUtil.java
index 9a093c9..bbad1ca 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeUtil.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeUtil.java
@@ -1188,6 +1188,34 @@ public abstract class SqlTypeUtil {
   }
 
   /**
+   * This is a poorman's
+   * {@link #equalSansNullability(RelDataTypeFactory, RelDataType, 
RelDataType)}.
+   *
+   * <p>We assume that "not null" is represented in the type's digest
+   * as a trailing "NOT NULL" (case sensitive).
+   *
+   * <p>If you got a type factory, {@link 
#equalSansNullability(RelDataTypeFactory, RelDataType, RelDataType)}
+   * is preferred.
+   *
+   * @param type1 First type
+   * @param type2 Second type
+   * @return true if the types are equal or the only difference is nullability
+   */
+  public static boolean equalSansNullability(RelDataType type1, RelDataType 
type2) {
+    String x = type1.getFullTypeString();
+    String y = type2.getFullTypeString();
+    if (x.length() < y.length()) {
+      String c = x;
+      x = y;
+      y = c;
+    }
+
+    return (x.length() == y.length()
+        || x.length() == y.length() + 9 && x.endsWith(" NOT NULL"))
+        && x.startsWith(y);
+  }
+
+  /**
    * Returns whether two collection types are equal, ignoring nullability.
    *
    * <p>They need not come from the same factory.
diff --git 
a/core/src/test/java/org/apache/calcite/rex/RexCallNormalizationTest.java 
b/core/src/test/java/org/apache/calcite/rex/RexCallNormalizationTest.java
deleted file mode 100644
index 38d08c1..0000000
--- a/core/src/test/java/org/apache/calcite/rex/RexCallNormalizationTest.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to you under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.calcite.rex;
-
-import org.junit.jupiter.api.Test;
-
-class RexCallNormalizationTest extends RexProgramTestBase {
-  @Test void digestIsNormalized() {
-    final RexNode node = and(or(input(tBool(), 1), input(tBool(), 0)), 
input(tBool(), 0));
-    checkDigest(node, "AND(OR($0, $1), $0)");
-
-    checkDigest(eq(input(tVarchar(), 0), literal("0123456789012345")),
-        "=($0, '0123456789012345')");
-    checkDigest(eq(input(tVarchar(), 0), literal("01")), "=($0, '01')");
-  }
-
-  @Test void reversibleSameArgOpsNormalizedToLess() {
-    checkDigest(lt(input(tBool(), 0), input(tBool(), 0)), "<($0, $0)");
-    checkDigest(gt(input(tBool(), 0), input(tBool(), 0)), "<($0, $0)");
-    checkDigest(le(input(tBool(), 0), input(tBool(), 0)), "<=($0, $0)");
-    checkDigest(ge(input(tBool(), 0), input(tBool(), 0)), "<=($0, $0)");
-  }
-
-  @Test void reversibleDifferentArgTypesShouldNotBeShuffled() {
-    checkDigest(plus(input(tSmallInt(), 0), input(tInt(), 1)), "+($0, $1)");
-    checkDigest(plus(input(tInt(), 0), input(tSmallInt(), 1)), "+($0, $1)");
-    checkDigest(mul(input(tSmallInt(), 0), input(tInt(), 1)), "*($0, $1)");
-    checkDigest(mul(input(tInt(), 0), input(tSmallInt(), 1)), "*($0, $1)");
-  }
-
-  @Test void reversibleDifferentNullabilityArgsAreNormalized() {
-    checkDigest(plus(input(tInt(false), 1), input(tInt(), 0)), "+($0, $1)");
-    checkDigest(plus(input(tInt(), 1), input(tInt(false), 0)), "+($0, $1)");
-    checkDigest(mul(input(tInt(false), 1), input(tInt(), 0)), "*($0, $1)");
-    checkDigest(mul(input(tInt(), 1), input(tInt(false), 0)), "*($0, $1)");
-  }
-
-  @Test void symmetricalDifferentArgOps() {
-    for (int i = 0; i < 2; i++) {
-      int j = 1 - i;
-      checkDigest(eq(input(tBool(), i), input(tBool(), j)), "=($0, $1)");
-      checkDigest(ne(input(tBool(), i), input(tBool(), j)), "<>($0, $1)");
-    }
-  }
-
-  @Test void reversibleDifferentArgOps() {
-    for (int i = 0; i < 2; i++) {
-      int j = 1 - i;
-      checkDigest(
-          lt(input(tBool(), i), input(tBool(), j)),
-          i < j
-              ? "<($0, $1)"
-              : ">($0, $1)");
-      checkDigest(
-          le(input(tBool(), i), input(tBool(), j)),
-          i < j
-              ? "<=($0, $1)"
-              : ">=($0, $1)");
-      checkDigest(
-          gt(input(tBool(), i), input(tBool(), j)),
-          i < j
-              ? ">($0, $1)"
-              : "<($0, $1)");
-      checkDigest(
-          ge(input(tBool(), i), input(tBool(), j)),
-          i < j
-              ? ">=($0, $1)"
-              : "<=($0, $1)");
-    }
-  }
-}
diff --git a/core/src/test/java/org/apache/calcite/rex/RexNormalizeTest.java 
b/core/src/test/java/org/apache/calcite/rex/RexNormalizeTest.java
new file mode 100644
index 0000000..03bb8ee
--- /dev/null
+++ b/core/src/test/java/org/apache/calcite/rex/RexNormalizeTest.java
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.rex;
+
+import org.hamcrest.CoreMatchers;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/** Test cases for {@link RexNormalize}. */
+class RexNormalizeTest extends RexProgramTestBase {
+
+  @Test void digestIsNormalized() {
+    assertNodeEquals(
+        and(or(vBool(1), vBool(0)), vBool(0)),
+        and(vBool(0), or(vBool(0), vBool(1))));
+
+    assertNodeEquals(
+        and(or(vBool(1), vBool(0)), vBool(0)),
+        and(or(vBool(0), vBool(1)), vBool(0)));
+
+    assertNodeEquals(
+        eq(vVarchar(0), literal("0123456789012345")),
+        eq(literal("0123456789012345"), vVarchar(0)));
+
+    assertNodeEquals(
+        eq(vVarchar(0), literal("01")),
+        eq(literal("01"), vVarchar(0)));
+  }
+
+  @Test void reversibleNormalizedToLess() {
+    // Same type operands.
+    assertNodeEquals(
+        lt(vBool(0), vBool(0)),
+        gt(vBool(0), vBool(0)));
+
+    assertNodeEquals(
+        le(vBool(0), vBool(0)),
+        ge(vBool(0), vBool(0)));
+
+    // Different type operands.
+    assertNodeEquals(
+        lt(vSmallInt(0), vInt(1)),
+        gt(vInt(1), vSmallInt(0)));
+
+    assertNodeEquals(
+        le(vSmallInt(0), vInt(1)),
+        ge(vInt(1), vSmallInt(0)));
+  }
+
+  @Test void reversibleDifferentArgTypesShouldNotBeShuffled() {
+    assertNodeNotEqual(
+        plus(vSmallInt(1), vInt(0)),
+        plus(vInt(0), vSmallInt(1)));
+
+    assertNodeNotEqual(
+        mul(vSmallInt(0), vInt(1)),
+        mul(vInt(1), vSmallInt(0)));
+  }
+
+  @Test void reversibleDifferentNullabilityArgsAreNormalized() {
+    assertNodeEquals(
+        plus(vIntNotNull(0), vInt(1)),
+        plus(vInt(1), vIntNotNull(0)));
+
+    assertNodeEquals(
+        mul(vIntNotNull(1), vInt(0)),
+        mul(vInt(0), vIntNotNull(1)));
+  }
+
+  @Test void symmetricalDifferentArgOps() {
+    assertNodeEquals(
+        eq(vBool(0), vBool(1)),
+        eq(vBool(1), vBool(0)));
+
+    assertNodeEquals(
+        ne(vBool(0), vBool(1)),
+        ne(vBool(1), vBool(0)));
+  }
+
+  @Test void reversibleDifferentArgOps() {
+    assertNodeNotEqual(
+        lt(vBool(0), vBool(1)),
+        lt(vBool(1), vBool(0)));
+
+    assertNodeNotEqual(
+        le(vBool(0), vBool(1)),
+        le(vBool(1), vBool(0)));
+
+    assertNodeNotEqual(
+        gt(vBool(0), vBool(1)),
+        gt(vBool(1), vBool(0)));
+
+    assertNodeNotEqual(
+        ge(vBool(0), vBool(1)),
+        ge(vBool(1), vBool(0)));
+  }
+
+  /** Asserts two rex nodes are equal. */
+  private static void assertNodeEquals(RexNode node1, RexNode node2) {
+    final String reason = getReason(node1, node2, true);
+    assertThat(reason, node1, equalTo(node2));
+    assertThat(reason, node1.hashCode(), equalTo(node2.hashCode()));
+  }
+
+  /** Asserts two rex nodes are not equal. */
+  private static void assertNodeNotEqual(RexNode node1, RexNode node2) {
+    final String reason = getReason(node1, node2, false);
+    assertThat(reason, node1, CoreMatchers.not(equalTo(node2)));
+    assertThat(reason, node1.hashCode(), 
CoreMatchers.not(equalTo(node2.hashCode())));
+  }
+
+  /** Returns the assertion reason. */
+  private static String getReason(RexNode node1, RexNode node2, boolean equal) 
{
+    StringBuilder reason = new StringBuilder("Rex nodes [");
+    reason.append(node1);
+    reason.append("] and [");
+    reason.append(node2);
+    reason.append("] expect to be ");
+    if (!equal) {
+      reason.append("not ");
+    }
+    reason.append("equal");
+    return reason.toString();
+  }
+}
diff --git a/core/src/test/java/org/apache/calcite/test/JdbcAdapterTest.java 
b/core/src/test/java/org/apache/calcite/test/JdbcAdapterTest.java
index 2b32b52..67b3938 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcAdapterTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcAdapterTest.java
@@ -252,7 +252,7 @@ class JdbcAdapterTest {
             + "on s.losal <= e.sal and s.hisal >= e.sal")
         .explainContains("PLAN=JdbcToEnumerableConverter\n"
             + "  JdbcProject(EMPNO=[$0], ENAME=[$1], GRADE=[$3])\n"
-            + "    JdbcJoin(condition=[AND(>=($2, $4), <=($2, $5))], 
joinType=[inner])\n"
+            + "    JdbcJoin(condition=[AND(<=($4, $2), >=($5, $2))], 
joinType=[inner])\n"
             + "      JdbcProject(EMPNO=[$0], ENAME=[$1], SAL=[$5])\n"
             + "        JdbcTableScan(table=[[SCOTT, EMP]])\n"
             + "      JdbcTableScan(table=[[SCOTT, SALGRADE]])")
@@ -295,7 +295,7 @@ class JdbcAdapterTest {
             + "e.mgr = m.empno and (e.sal > m.sal or m.hiredate > e.hiredate)")
         .explainContains("PLAN=JdbcToEnumerableConverter\n"
             + "  JdbcProject(EMPNO=[$0], ENAME=[$1], EMPNO0=[$0], 
ENAME0=[$1])\n"
-            + "    JdbcJoin(condition=[AND(=($2, $5), OR(>($4, $7), <($3, 
$6)))], joinType=[inner])\n"
+            + "    JdbcJoin(condition=[AND(=($2, $5), OR(>($4, $7), >($6, 
$3)))], joinType=[inner])\n"
             + "      JdbcProject(EMPNO=[$0], ENAME=[$1], MGR=[$3], 
HIREDATE=[$4], SAL=[$5])\n"
             + "        JdbcTableScan(table=[[SCOTT, EMP]])\n"
             + "      JdbcProject(EMPNO=[$0], HIREDATE=[$4], SAL=[$5])\n"
diff --git a/core/src/test/java/org/apache/calcite/test/LatticeTest.java 
b/core/src/test/java/org/apache/calcite/test/LatticeTest.java
index 9d672ab..987081a 100644
--- a/core/src/test/java/org/apache/calcite/test/LatticeTest.java
+++ b/core/src/test/java/org/apache/calcite/test/LatticeTest.java
@@ -234,9 +234,9 @@ class LatticeTest {
           final Map.Entry<String, CalciteSchema.LatticeEntry> entry =
               adhoc.unwrap(CalciteSchema.class).getLatticeMap().firstEntry();
           final Lattice lattice = entry.getValue().getLattice();
-          assertThat(lattice.firstColumn("S"), is(0));
-          assertThat(lattice.firstColumn("P"), is(8));
-          assertThat(lattice.firstColumn("T"), is(23));
+          assertThat(lattice.firstColumn("S"), is(10));
+          assertThat(lattice.firstColumn("P"), is(18));
+          assertThat(lattice.firstColumn("T"), is(0));
           assertThat(lattice.firstColumn("PC"), is(-1));
           assertThat(lattice.defaultMeasures.size(), is(1));
           assertThat(lattice.rootNode.descendants.size(), is(3));
diff --git a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java 
b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
index d00228f..dd1463d 100644
--- a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
@@ -1978,7 +1978,7 @@ public class RelBuilderTest {
         + "  LogicalTableScan(table=[[scott, DEPT]])\n";
     final String expectedWithoutSimplify = ""
         + "LogicalJoin(condition=[OR(null:NULL, "
-        + "AND(=($7, 1), =($7, 2), =($7, $8)))], joinType=[inner])\n"
+        + "AND(=($7, 1), =($7, 2), =($8, $7)))], joinType=[inner])\n"
         + "  LogicalTableScan(table=[[scott, EMP]])\n"
         + "  LogicalTableScan(table=[[scott, DEPT]])\n";
     assertThat(f.apply(createBuilder()), hasTree(expected));
@@ -2151,7 +2151,7 @@ public class RelBuilderTest {
                     builder.field("e", "MGR")))
             .build();
     final String expected = ""
-        + "LogicalFilter(condition=[AND(=($7, $16), =($3, $8))])\n"
+        + "LogicalFilter(condition=[AND(=($7, $16), =($8, $3))])\n"
         + "  LogicalJoin(condition=[true], joinType=[inner])\n"
         + "    LogicalTableScan(table=[[scott, EMP]])\n"
         + "    LogicalJoin(condition=[true], joinType=[inner])\n"
diff --git 
a/core/src/test/java/org/apache/calcite/test/enumerable/EnumerableCorrelateTest.java
 
b/core/src/test/java/org/apache/calcite/test/enumerable/EnumerableCorrelateTest.java
index dce7015..1432d0e 100644
--- 
a/core/src/test/java/org/apache/calcite/test/enumerable/EnumerableCorrelateTest.java
+++ 
b/core/src/test/java/org/apache/calcite/test/enumerable/EnumerableCorrelateTest.java
@@ -99,7 +99,7 @@ class EnumerableCorrelateTest {
             + "  EnumerableCorrelate(correlation=[$cor1], joinType=[inner], 
requiredColumns=[{0}])\n"
             + "    EnumerableAggregate(group=[{0}])\n"
             + "      EnumerableTableScan(table=[[s, depts]])\n"
-            + "    EnumerableCalc(expr#0..4=[{inputs}], expr#5=[$cor1], 
expr#6=[$t5.deptno], expr#7=[=($t6, $t1)], proj#0..2=[{exprs}], 
$condition=[$t7])\n"
+            + "    EnumerableCalc(expr#0..4=[{inputs}], expr#5=[$cor1], 
expr#6=[$t5.deptno], expr#7=[=($t1, $t6)], proj#0..2=[{exprs}], 
$condition=[$t7])\n"
             + "      EnumerableTableScan(table=[[s, emps]])")
         .returnsUnordered(
             "empid=100; name=Bill",
@@ -129,7 +129,7 @@ class EnumerableCorrelateTest {
             + "  EnumerableCorrelate(correlation=[$cor1], joinType=[inner], 
requiredColumns=[{0}])\n"
             + "    EnumerableAggregate(group=[{0}])\n"
             + "      EnumerableTableScan(table=[[s, depts]])\n"
-            + "    EnumerableCalc(expr#0..4=[{inputs}], expr#5=[$cor1], 
expr#6=[$t5.deptno], expr#7=[=($t6, $t1)], expr#8=[100], expr#9=[>($t0, $t8)], 
expr#10=[AND($t7, $t9)], proj#0..2=[{exprs}], $condition=[$t10])\n"
+            + "    EnumerableCalc(expr#0..4=[{inputs}], expr#5=[$cor1], 
expr#6=[$t5.deptno], expr#7=[=($t1, $t6)], expr#8=[100], expr#9=[>($t0, $t8)], 
expr#10=[AND($t7, $t9)], proj#0..2=[{exprs}], $condition=[$t10])\n"
             + "      EnumerableTableScan(table=[[s, emps]])")
         .returnsUnordered(
             "empid=110; name=Theodore",
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 e8c29b6..57fd8c4 100644
--- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
@@ -638,7 +638,7 @@ LogicalProject(MGR=[$3])
         </Resource>
         <Resource name="planMid">
             <![CDATA[
-LogicalProject(DEPTNO=[$0], EXPR$1=[OR(AND(IS NOT NULL($5), <>($2, 0)), 
AND(>($2, $3), null, <>($2, 0), IS NULL($5)))])
+LogicalProject(DEPTNO=[$0], EXPR$1=[OR(AND(IS NOT NULL($5), <>($2, 0)), 
AND(<($3, $2), null, <>($2, 0), IS NULL($5)))])
   LogicalJoin(condition=[=($1, $4)], joinType=[left])
     LogicalJoin(condition=[true], joinType=[inner])
       LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
@@ -652,7 +652,7 @@ LogicalProject(DEPTNO=[$0], EXPR$1=[OR(AND(IS NOT NULL($5), 
<>($2, 0)), AND(>($2
         </Resource>
         <Resource name="planAfter">
             <![CDATA[
-LogicalProject(DEPTNO=[$0], EXPR$1=[OR(AND(IS NOT NULL($5), <>($2, 0)), 
AND(>($2, $3), null, <>($2, 0), IS NULL($5)))])
+LogicalProject(DEPTNO=[$0], EXPR$1=[OR(AND(IS NOT NULL($5), <>($2, 0)), 
AND(<($3, $2), null, <>($2, 0), IS NULL($5)))])
   LogicalJoin(condition=[=($1, $4)], joinType=[left])
     LogicalJoin(condition=[true], joinType=[inner])
       LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
@@ -1837,7 +1837,7 @@ group by case when e.sal < 11 then 11 else -1 * e.sal 
end]]>
 LogicalProject(EXPR$0=[$1], EXPR$1=[$0])
   LogicalAggregate(group=[{0}], EXPR$0=[COUNT()])
     LogicalProject(EXPR$1=[CASE(<($9, 11), 11, *(-1, $9))])
-      LogicalJoin(condition=[=($0, $5)], joinType=[left])
+      LogicalJoin(condition=[=($5, $0)], joinType=[left])
         LogicalTableScan(table=[[CATALOG, SALES, BONUS]])
         LogicalTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
@@ -1847,7 +1847,7 @@ LogicalProject(EXPR$0=[$1], EXPR$1=[$0])
 LogicalProject(EXPR$0=[$1], EXPR$1=[$0])
   LogicalAggregate(group=[{0}], EXPR$0=[COUNT()])
     LogicalProject(EXPR$1=[CASE($2, 11, $3)])
-      LogicalJoin(condition=[=($0, $1)], joinType=[left])
+      LogicalJoin(condition=[=($1, $0)], joinType=[left])
         LogicalProject(ENAME=[$0])
           LogicalTableScan(table=[[CATALOG, SALES, BONUS]])
         LogicalProject(ENAME=[$1], <=[<($5, 11)], *=[*(-1, $5)])
@@ -1866,7 +1866,7 @@ group by case when e.sal < 11 then -1 * e.sal else e.sal 
end]]>
 LogicalProject(EXPR$0=[$1], EXPR$1=[$0])
   LogicalAggregate(group=[{0}], EXPR$0=[COUNT()])
     LogicalProject(EXPR$1=[CASE(<($9, 11), *(-1, $9), $9)])
-      LogicalJoin(condition=[=($0, $5)], joinType=[left])
+      LogicalJoin(condition=[=($5, $0)], joinType=[left])
         LogicalTableScan(table=[[CATALOG, SALES, BONUS]])
         LogicalTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
@@ -1876,7 +1876,7 @@ LogicalProject(EXPR$0=[$1], EXPR$1=[$0])
 LogicalProject(EXPR$0=[$1], EXPR$1=[$0])
   LogicalAggregate(group=[{0}], EXPR$0=[COUNT()])
     LogicalProject(EXPR$1=[$2])
-      LogicalJoin(condition=[=($0, $1)], joinType=[left])
+      LogicalJoin(condition=[=($1, $0)], joinType=[left])
         LogicalProject(ENAME=[$0])
           LogicalTableScan(table=[[CATALOG, SALES, BONUS]])
         LogicalProject(ENAME=[$1], CASE=[CASE(<($5, 11), *(-1, $5), $5)])
@@ -1983,7 +1983,7 @@ group by case when e.sal < 11 then 11 else -1 * e.sal 
end]]>
 LogicalProject(EXPR$0=[$1], EXPR$1=[$0])
   LogicalAggregate(group=[{0}], EXPR$0=[COUNT()])
     LogicalProject(EXPR$1=[CASE(<($9, 11), 11, *(-1, $9))])
-      LogicalJoin(condition=[=($0, $5)], joinType=[right])
+      LogicalJoin(condition=[=($5, $0)], joinType=[right])
         LogicalTableScan(table=[[CATALOG, SALES, BONUS]])
         LogicalTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
@@ -1993,7 +1993,7 @@ LogicalProject(EXPR$0=[$1], EXPR$1=[$0])
 LogicalProject(EXPR$0=[$1], EXPR$1=[$0])
   LogicalAggregate(group=[{0}], EXPR$0=[COUNT()])
     LogicalProject(EXPR$1=[$2])
-      LogicalJoin(condition=[=($0, $1)], joinType=[right])
+      LogicalJoin(condition=[=($1, $0)], joinType=[right])
         LogicalProject(ENAME=[$0])
           LogicalTableScan(table=[[CATALOG, SALES, BONUS]])
         LogicalProject(ENAME=[$1], CASE=[CASE(<($5, 11), 11, *(-1, $5))])
@@ -2012,7 +2012,7 @@ group by case when e.sal < 11 then -1 * e.sal else e.sal 
end]]>
 LogicalProject(EXPR$0=[$1], EXPR$1=[$0])
   LogicalAggregate(group=[{0}], EXPR$0=[COUNT()])
     LogicalProject(EXPR$1=[CASE(<($9, 11), *(-1, $9), $9)])
-      LogicalJoin(condition=[=($0, $5)], joinType=[right])
+      LogicalJoin(condition=[=($5, $0)], joinType=[right])
         LogicalTableScan(table=[[CATALOG, SALES, BONUS]])
         LogicalTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
@@ -2022,7 +2022,7 @@ LogicalProject(EXPR$0=[$1], EXPR$1=[$0])
 LogicalProject(EXPR$0=[$1], EXPR$1=[$0])
   LogicalAggregate(group=[{0}], EXPR$0=[COUNT()])
     LogicalProject(EXPR$1=[$2])
-      LogicalJoin(condition=[=($0, $1)], joinType=[right])
+      LogicalJoin(condition=[=($1, $0)], joinType=[right])
         LogicalProject(ENAME=[$0])
           LogicalTableScan(table=[[CATALOG, SALES, BONUS]])
         LogicalProject(ENAME=[$1], CASE=[CASE(<($5, 11), *(-1, $5), $5)])
@@ -3582,7 +3582,7 @@ join sales.emp e on e.deptno = d.deptno and d.deptno not 
in (4, 6)]]>
         <Resource name="planBefore">
             <![CDATA[
 LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], EMPNO0=[$9], ENAME0=[$10], 
JOB0=[$11], MGR0=[$12], HIREDATE0=[$13], SAL0=[$14], COMM0=[$15], 
DEPTNO0=[$16], SLACKER0=[$17])
-  LogicalJoin(condition=[AND(=($7, $16), NOT(OR(=($7, 4), =($7, 6))))], 
joinType=[inner])
+  LogicalJoin(condition=[AND(=($16, $7), NOT(OR(=($7, 4), =($7, 6))))], 
joinType=[inner])
     LogicalTableScan(table=[[CATALOG, SALES, EMP]])
     LogicalTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
@@ -3590,7 +3590,7 @@ LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], 
MGR=[$3], HIREDATE=[$4], SAL=[$
         <Resource name="planAfter">
             <![CDATA[
 LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], EMPNO0=[$9], ENAME0=[$10], 
JOB0=[$11], MGR0=[$12], HIREDATE0=[$13], SAL0=[$14], COMM0=[$15], 
DEPTNO0=[$16], SLACKER0=[$17])
-  LogicalJoin(condition=[=($7, $16)], joinType=[inner])
+  LogicalJoin(condition=[=($16, $7)], joinType=[inner])
     LogicalFilter(condition=[AND(<>($7, 4), <>($7, 6))])
       LogicalTableScan(table=[[CATALOG, SALES, EMP]])
     LogicalFilter(condition=[AND(<>($7, 4), <>($7, 6))])
@@ -3750,10 +3750,10 @@ LogicalProject(SAL=[$5])
     LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], SLACKER=[$8])
       LogicalFilter(condition=[AND(=($7, 20), >($5, 1000))])
         LogicalTableScan(table=[[CATALOG, SALES, EMPNULLABLES]])
-    LogicalFilter(condition=[=($0, $1)])
+    LogicalFilter(condition=[=($1, $0)])
       LogicalAggregate(group=[{0, 1}])
         LogicalProject(SAL=[$5], SAL0=[$8])
-          LogicalJoin(condition=[OR(=($5, $8), =($8, 4))], joinType=[inner])
+          LogicalJoin(condition=[OR(=($8, $5), =($8, 4))], joinType=[inner])
             LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], 
HIREDATE=[$4], SAL=[$5], COMM=[$6], SLACKER=[$8])
               LogicalFilter(condition=[AND(=($7, 20), >($5, 1000))])
                 LogicalTableScan(table=[[CATALOG, SALES, EMPNULLABLES]])
@@ -6414,7 +6414,7 @@ group by e.job]]>
             <![CDATA[
 LogicalAggregate(group=[{2}])
   LogicalJoin(condition=[AND(=($2, $11), =($9, $12))], joinType=[full])
-    LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], $f9=[+($0, $7)])
+    LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], $f9=[+($7, $0)])
       LogicalFilter(condition=[=($1, 'A')])
         LogicalTableScan(table=[[CATALOG, SALES, EMP]])
     LogicalProject(DEPTNO=[$0], NAME=[$1], $f2=[+($0, 5)])
@@ -6426,7 +6426,7 @@ LogicalAggregate(group=[{2}])
 LogicalAggregate(group=[{0}])
   LogicalJoin(condition=[AND(=($0, $2), =($1, $3))], joinType=[full])
     LogicalAggregate(group=[{2, 9}])
-      LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], 
HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], $f9=[+($0, $7)])
+      LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], 
HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], $f9=[+($7, $0)])
         LogicalFilter(condition=[=($1, 'A')])
           LogicalTableScan(table=[[CATALOG, SALES, EMP]])
     LogicalAggregate(group=[{1, 2}])
@@ -6596,7 +6596,7 @@ group by e.job]]>
             <![CDATA[
 LogicalAggregate(group=[{2}])
   LogicalJoin(condition=[AND(=($2, $11), =($9, $12))], joinType=[left])
-    LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], $f9=[+($0, $7)])
+    LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], $f9=[+($7, $0)])
       LogicalFilter(condition=[=($1, 'A')])
         LogicalTableScan(table=[[CATALOG, SALES, EMP]])
     LogicalProject(DEPTNO=[$0], NAME=[$1], $f2=[+($0, 5)])
@@ -6608,7 +6608,7 @@ LogicalAggregate(group=[{2}])
 LogicalAggregate(group=[{0}])
   LogicalJoin(condition=[AND(=($0, $2), =($1, $3))], joinType=[left])
     LogicalAggregate(group=[{2, 9}])
-      LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], 
HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], $f9=[+($0, $7)])
+      LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], 
HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], $f9=[+($7, $0)])
         LogicalFilter(condition=[=($1, 'A')])
           LogicalTableScan(table=[[CATALOG, SALES, EMP]])
     LogicalAggregate(group=[{1, 2}])
@@ -6629,7 +6629,7 @@ group by d.name]]>
             <![CDATA[
 LogicalAggregate(group=[{11}])
   LogicalJoin(condition=[AND(=($2, $11), =($9, $12))], joinType=[left])
-    LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], $f9=[+($0, $7)])
+    LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], $f9=[+($7, $0)])
       LogicalFilter(condition=[=($1, 'A')])
         LogicalTableScan(table=[[CATALOG, SALES, EMP]])
     LogicalProject(DEPTNO=[$0], NAME=[$1], $f2=[+($0, 5)])
@@ -6641,7 +6641,7 @@ LogicalAggregate(group=[{11}])
 LogicalAggregate(group=[{2}])
   LogicalJoin(condition=[AND(=($0, $2), =($1, $3))], joinType=[left])
     LogicalAggregate(group=[{2, 9}])
-      LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], 
HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], $f9=[+($0, $7)])
+      LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], 
HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], $f9=[+($7, $0)])
         LogicalFilter(condition=[=($1, 'A')])
           LogicalTableScan(table=[[CATALOG, SALES, EMP]])
     LogicalAggregate(group=[{1, 2}])
@@ -6662,7 +6662,7 @@ group by e.job, d.name]]>
             <![CDATA[
 LogicalAggregate(group=[{2, 11}])
   LogicalJoin(condition=[AND(=($2, $11), =($9, $12))], joinType=[left])
-    LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], $f9=[+($0, $7)])
+    LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], $f9=[+($7, $0)])
       LogicalFilter(condition=[=($1, 'A')])
         LogicalTableScan(table=[[CATALOG, SALES, EMP]])
     LogicalProject(DEPTNO=[$0], NAME=[$1], $f2=[+($0, 5)])
@@ -6674,7 +6674,7 @@ LogicalAggregate(group=[{2, 11}])
 LogicalAggregate(group=[{0, 2}])
   LogicalJoin(condition=[AND(=($0, $2), =($1, $3))], joinType=[left])
     LogicalAggregate(group=[{2, 9}])
-      LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], 
HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], $f9=[+($0, $7)])
+      LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], 
HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], $f9=[+($7, $0)])
         LogicalFilter(condition=[=($1, 'A')])
           LogicalTableScan(table=[[CATALOG, SALES, EMP]])
     LogicalAggregate(group=[{1, 2}])
@@ -6743,7 +6743,7 @@ join sales.dept as d on e.empno = d.deptno and e.deptno + 
e.empno = d.deptno + 5
             <![CDATA[
 LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], DEPTNO0=[$10], NAME=[$11])
   LogicalJoin(condition=[AND(=($0, $10), =($9, $12))], joinType=[inner])
-    LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], $f9=[+($0, $7)])
+    LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], $f9=[+($7, $0)])
       LogicalFilter(condition=[=($0, 10)])
         LogicalTableScan(table=[[CATALOG, SALES, EMP]])
     LogicalProject(DEPTNO=[$0], NAME=[$1], $f2=[+($0, 5)])
@@ -6754,7 +6754,7 @@ LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], 
MGR=[$3], HIREDATE=[$4], SAL=[$
             <![CDATA[
 LogicalProject(EMPNO=[10], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], DEPTNO0=[10], NAME=[$11])
   LogicalJoin(condition=[=($9, 15)], joinType=[inner])
-    LogicalProject(EMPNO=[10], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], $f9=[+(10, $7)])
+    LogicalProject(EMPNO=[10], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], $f9=[+($7, 10)])
       LogicalFilter(condition=[=($0, 10)])
         LogicalTableScan(table=[[CATALOG, SALES, EMP]])
     LogicalProject(DEPTNO=[10], NAME=[$1], $f2=[15])
@@ -7868,13 +7868,13 @@ from emp]]>
             <![CDATA[
 LogicalProject($0=[$3], $1=[$4])
   LogicalWindow(window#0=[window(partition {1} order by [0] aggs [SUM($1), 
SUM($2)])])
-    LogicalProject(SAL=[$5], DEPTNO=[$7], $2=[+($5, $7)])
+    LogicalProject(SAL=[$5], DEPTNO=[$7], $2=[+($7, $5)])
       LogicalTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
         </Resource>
         <Resource name="planBefore">
             <![CDATA[
-LogicalProject(SUM1=[SUM($7) OVER (PARTITION BY $7 ORDER BY $5)], 
SUM2=[SUM(+($5, $7)) OVER (PARTITION BY $7 ORDER BY $5)])
+LogicalProject(SUM1=[SUM($7) OVER (PARTITION BY $7 ORDER BY $5)], 
SUM2=[SUM(+($7, $5)) OVER (PARTITION BY $7 ORDER BY $5)])
   LogicalTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
         </Resource>
@@ -7920,7 +7920,7 @@ group by e.job,d.name]]>
             <![CDATA[
 LogicalAggregate(group=[{2, 11}])
   LogicalJoin(condition=[AND(=($2, $11), =($9, $12))], joinType=[inner])
-    LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], $f9=[+($0, $7)])
+    LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], $f9=[+($7, $0)])
       LogicalFilter(condition=[=($1, 'A')])
         LogicalTableScan(table=[[CATALOG, SALES, EMP]])
     LogicalProject(DEPTNO=[$0], NAME=[$1], $f2=[+($0, 5)])
@@ -7932,7 +7932,7 @@ LogicalAggregate(group=[{2, 11}])
 LogicalAggregate(group=[{0, 2}])
   LogicalJoin(condition=[AND(=($0, $2), =($1, $3))], joinType=[inner])
     LogicalAggregate(group=[{2, 9}])
-      LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], 
HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], $f9=[+($0, $7)])
+      LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], 
HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], $f9=[+($7, $0)])
         LogicalFilter(condition=[=($1, 'A')])
           LogicalTableScan(table=[[CATALOG, SALES, EMP]])
     LogicalAggregate(group=[{1, 2}])
@@ -8466,7 +8466,7 @@ LogicalProject(EXPR$0=[1])
             <![CDATA[
 LogicalProject(EXPR$0=[1])
   LogicalProject(DEPTNO=[$9], NAME=[$10], EMPNO=[$0], ENAME=[$1], JOB=[$2], 
MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO0=[$7], SLACKER=[$8])
-    LogicalJoin(condition=[=($7, $9)], joinType=[right])
+    LogicalJoin(condition=[=($9, $7)], joinType=[right])
       LogicalTableScan(table=[[CATALOG, SALES, EMP]])
       LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
 ]]>
@@ -9742,7 +9742,7 @@ RIGHT JOIN sales.emp e ON e.deptno = d.deptno]]>
         <Resource name="planBefore">
             <![CDATA[
 LogicalProject(DEPTNO=[$9])
-  LogicalJoin(condition=[=($0, $9)], joinType=[right])
+  LogicalJoin(condition=[=($9, $0)], joinType=[right])
     LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
     LogicalTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
@@ -9786,7 +9786,7 @@ RIGHT JOIN sales.emp e ON e.deptno = d.deptno]]>
         <Resource name="planBefore">
             <![CDATA[
 LogicalProject(DEPTNO=[$9], NAME=[$1])
-  LogicalJoin(condition=[=($0, $9)], joinType=[right])
+  LogicalJoin(condition=[=($9, $0)], joinType=[right])
     LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
     LogicalTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
@@ -11289,7 +11289,7 @@ LogicalProject(DEPTNO=[$1])
         </Resource>
         <Resource name="planAfter">
             <![CDATA[
-LogicalProject(EMPNO=[$0], D=[CASE(=($9, 0), false, IS NULL(CASE(true, 
CAST($7):INTEGER, null:INTEGER)), null:BOOLEAN, IS NOT NULL($12), true, >($9, 
$10), null:BOOLEAN, false)])
+LogicalProject(EMPNO=[$0], D=[CASE(=($9, 0), false, IS NULL(CASE(true, 
CAST($7):INTEGER, null:INTEGER)), null:BOOLEAN, IS NOT NULL($12), true, <($10, 
$9), null:BOOLEAN, false)])
   LogicalJoin(condition=[=(CASE(true, CAST($7):INTEGER, null:INTEGER), $11)], 
joinType=[left])
     LogicalJoin(condition=[true], joinType=[inner])
       LogicalTableScan(table=[[CATALOG, SALES, EMP]])
@@ -11337,7 +11337,7 @@ LogicalProject(EXPR$0=[CASE(true, CAST($7):INTEGER, 
null:INTEGER)])
             <![CDATA[
 LogicalProject(EMPNO=[$0])
   LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
-    LogicalFilter(condition=[<($0, CASE(OR(AND(IS NOT NULL($12), <>($9, 0)), 
AND(>($9, $10), null, <>($9, 0), IS NULL($12))), 10, AND(OR(IS NULL($12), =($9, 
0)), OR(<=($9, $10), =($9, 0), IS NOT NULL($12))), 20, 30))])
+    LogicalFilter(condition=[<($0, CASE(OR(AND(IS NOT NULL($12), <>($9, 0)), 
AND(<($10, $9), null, <>($9, 0), IS NULL($12))), 10, AND(OR(IS NULL($12), =($9, 
0)), OR(>=($10, $9), =($9, 0), IS NOT NULL($12))), 20, 30))])
       LogicalJoin(condition=[=($7, $11)], joinType=[left])
         LogicalJoin(condition=[true], joinType=[inner])
           LogicalTableScan(table=[[CATALOG, SALES, EMP]])
@@ -11764,7 +11764,7 @@ LogicalProject(DEPTNO=[$0])
         <Resource name="planAfter">
             <![CDATA[
 LogicalProject(SAL=[$5])
-  LogicalFilter(condition=[OR(=($10, 0), IS NOT TRUE(OR(IS NOT NULL($13), 
>($10, $11))))])
+  LogicalFilter(condition=[OR(=($10, 0), IS NOT TRUE(OR(IS NOT NULL($13), 
<($11, $10))))])
     LogicalJoin(condition=[AND(=($0, $12), =($2, $14))], joinType=[left])
       LogicalJoin(condition=[=($2, $9)], joinType=[left])
         LogicalTableScan(table=[[CATALOG, SALES, EMP]])
@@ -11779,7 +11779,7 @@ LogicalProject(SAL=[$5])
             <![CDATA[
 LogicalProject(SAL=[$5])
   LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
-    LogicalFilter(condition=[OR(=($9, 0), IS NOT TRUE(OR(IS NOT NULL($12), 
>($9, $10))))])
+    LogicalFilter(condition=[OR(=($9, 0), IS NOT TRUE(OR(IS NOT NULL($12), 
<($10, $9))))])
       LogicalCorrelate(correlation=[$cor0], joinType=[left], 
requiredColumns=[{2}])
         LogicalCorrelate(correlation=[$cor0], joinType=[left], 
requiredColumns=[{2}])
           LogicalTableScan(table=[[CATALOG, SALES, EMP]])
@@ -11856,7 +11856,7 @@ LogicalProject(EMPNO=[$1])
             <![CDATA[
 LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
   LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
-    LogicalFilter(condition=[OR(=($9, 0), IS NOT TRUE(OR(IS NOT NULL($12), 
>($9, $10))))])
+    LogicalFilter(condition=[OR(=($9, 0), IS NOT TRUE(OR(IS NOT NULL($12), 
<($10, $9))))])
       LogicalCorrelate(correlation=[$cor0], joinType=[left], 
requiredColumns=[{1}])
         LogicalCorrelate(correlation=[$cor0], joinType=[left], 
requiredColumns=[{1}])
           LogicalTableScan(table=[[CATALOG, SALES, EMP]])
@@ -11875,7 +11875,7 @@ LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], 
MGR=[$3], HIREDATE=[$4], SAL=[$
         <Resource name="planAfter">
             <![CDATA[
 LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
-  LogicalFilter(condition=[OR(=($10, 0), IS NOT TRUE(OR(IS NOT NULL($13), 
>($10, $11))))])
+  LogicalFilter(condition=[OR(=($10, 0), IS NOT TRUE(OR(IS NOT NULL($13), 
<($11, $10))))])
     LogicalJoin(condition=[AND(=($0, $12), =($1, $14))], joinType=[left])
       LogicalJoin(condition=[=($1, $9)], joinType=[left])
         LogicalTableScan(table=[[CATALOG, SALES, EMP]])
diff --git 
a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml 
b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
index 1e81638..53ef390 100644
--- a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
@@ -104,7 +104,7 @@ LogicalAggregate(group=[{0, 1}])
             <![CDATA[
 LogicalProject(D=[$0], EXPR$1=[+($0, $1)])
   LogicalAggregate(group=[{0, 1}])
-    LogicalProject(D=[+($0, $7)], MGR=[$3])
+    LogicalProject(D=[+($7, $0)], MGR=[$3])
       LogicalTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
         </Resource>
@@ -1565,7 +1565,7 @@ order by sal + empno desc, sal * empno, sal + empno 
desc]]>
             <![CDATA[
 LogicalProject(EMPNO=[$0])
   LogicalSort(sort0=[$1], sort1=[$2], dir0=[DESC], dir1=[ASC])
-    LogicalProject(EMPNO=[$0], EXPR$1=[+($0, $5)], EXPR$2=[*($0, $5)])
+    LogicalProject(EMPNO=[$0], EXPR$1=[+($5, $0)], EXPR$2=[*($5, $0)])
       LogicalJoin(condition=[true], joinType=[inner])
         LogicalTableScan(table=[[CATALOG, SALES, EMP]])
         LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
@@ -1879,8 +1879,8 @@ order by c + a]]>
             <![CDATA[
 LogicalProject(A=[$0], B=[$1], C=[$2], DEPTNO=[$3], NAME=[$4])
   LogicalSort(sort0=[$5], dir0=[ASC])
-    LogicalProject(A=[$0], B=[$1], C=[$2], DEPTNO=[$3], NAME=[$4], 
EXPR$5=[+($0, $2)])
-      LogicalJoin(condition=[=($2, $3)], joinType=[inner])
+    LogicalProject(A=[$0], B=[$1], C=[$2], DEPTNO=[$3], NAME=[$4], 
EXPR$5=[+($2, $0)])
+      LogicalJoin(condition=[=($3, $2)], joinType=[inner])
         LogicalProject(A=[$2], B=[$1], C=[$0])
           LogicalValues(tuples=[[{ 1, 2, 3 }]])
         LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
@@ -2317,7 +2317,7 @@ JOIN dept on dept.deptno = emp.deptno + 0]]>
         <Resource name="plan">
             <![CDATA[
 LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], DEPTNO0=[$10], NAME=[$11])
-  LogicalJoin(condition=[=($9, $10)], joinType=[inner])
+  LogicalJoin(condition=[=($10, $9)], joinType=[inner])
     LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], $f9=[+($7, 0)])
       LogicalTableScan(table=[[CATALOG, SALES, EMP]])
     LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
@@ -2407,7 +2407,7 @@ LogicalProject(D2=[$0], D3=[$1])
                   LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
                 LogicalAggregate(group=[{0, 1}], agg#0=[MIN($2)])
                   LogicalProject(D4=[$0], D6=[$2], $f0=[true])
-                    LogicalFilter(condition=[=($0, $1)])
+                    LogicalFilter(condition=[=($1, $0)])
                       LogicalProject(D4=[+($0, 4)], D5=[+($0, 5)], D6=[+($0, 
6)])
                         LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
 ]]>
@@ -2590,7 +2590,7 @@ from dept]]>
         </Resource>
         <Resource name="plan">
             <![CDATA[
-LogicalProject(NAME=[$1], EXPR$1=[OR(AND(IS NOT NULL($6), <>($2, 0)), 
AND(>($2, $3), null, <>($2, 0), IS NULL($6)))])
+LogicalProject(NAME=[$1], EXPR$1=[OR(AND(IS NOT NULL($6), <>($2, 0)), 
AND(<($3, $2), null, <>($2, 0), IS NULL($6)))])
   LogicalJoin(condition=[=($4, $5)], joinType=[left])
     LogicalProject(DEPTNO=[$0], NAME=[$1], $f0=[$2], $f1=[$3], DEPTNO0=[$0])
       LogicalJoin(condition=[true], joinType=[inner])
@@ -2612,7 +2612,7 @@ from emp]]>
         </Resource>
         <Resource name="plan">
             <![CDATA[
-LogicalProject(EMPNO=[$0], EXPR$1=[OR(=($9, 0), AND(>($9, $10), null, <>($9, 
0), IS NULL($13)), AND(<>($9, 0), IS NULL($13), <=($9, $10)))])
+LogicalProject(EMPNO=[$0], EXPR$1=[OR(=($9, 0), AND(<($10, $9), null, <>($9, 
0), IS NULL($13)), AND(<>($9, 0), IS NULL($13), >=($10, $9)))])
   LogicalJoin(condition=[=($11, $12)], joinType=[left])
     LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], $f0=[$9], $f1=[$10], 
DEPTNO0=[$7])
       LogicalJoin(condition=[true], joinType=[inner])
@@ -2756,7 +2756,7 @@ from emp]]>
         </Resource>
         <Resource name="plan">
             <![CDATA[
-LogicalProject(EMPNO=[$0], EXPR$1=[OR(=($9, 0), AND(>($9, $10), null, <>($9, 
0), IS NULL($13)), AND(<>($9, 0), IS NULL($13), <=($9, $10)))])
+LogicalProject(EMPNO=[$0], EXPR$1=[OR(=($9, 0), AND(<($10, $9), null, <>($9, 
0), IS NULL($13)), AND(<>($9, 0), IS NULL($13), >=($10, $9)))])
   LogicalJoin(condition=[=($11, $12)], joinType=[left])
     LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], $f0=[$9], $f1=[$10], 
DEPTNO0=[$7])
       LogicalJoin(condition=[true], joinType=[inner])
@@ -5759,7 +5759,7 @@ LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], 
MGR=[$3], HIREDATE=[$4], SAL=[$
   LogicalJoin(condition=[=($7, $11)], joinType=[inner])
     LogicalTableScan(table=[[CATALOG, SALES, EMP]])
     LogicalProject(DEPTNO=[$0], NAME=[$1], DEPTNO0=[$2])
-      LogicalJoin(condition=[>($0, $2)], joinType=[inner])
+      LogicalJoin(condition=[<($2, $0)], joinType=[inner])
         LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
         LogicalAggregate(group=[{0}])
           LogicalProject(DEPTNO=[$7])
@@ -5807,7 +5807,7 @@ LogicalProject(D2=[$0], D3=[$1])
                   LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
                 LogicalAggregate(group=[{0, 1}], agg#0=[MIN($2)])
                   LogicalProject(D4=[$0], D6=[$2], $f0=[true])
-                    LogicalFilter(condition=[=($0, $1)])
+                    LogicalFilter(condition=[=($1, $0)])
                       LogicalProject(D4=[+($0, 4)], D5=[+($0, 5)], D6=[+($0, 
6)])
                         LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
 ]]>
@@ -5931,7 +5931,7 @@ LogicalProject(EMPNO=[$0])
           LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
       LogicalAggregate(group=[{0}], agg#0=[MIN($1)])
         LogicalProject(EMPNO0=[$9], $f0=[true])
-          LogicalJoin(condition=[>($0, $9)], joinType=[inner])
+          LogicalJoin(condition=[<($9, $0)], joinType=[inner])
             LogicalTableScan(table=[[CATALOG, SALES, EMP]])
             LogicalAggregate(group=[{0}])
               LogicalProject(EMPNO=[$0])
diff --git a/core/src/test/resources/org/apache/calcite/test/TopDownOptTest.xml 
b/core/src/test/resources/org/apache/calcite/test/TopDownOptTest.xml
index 0be66a5..9e6a6e9 100644
--- a/core/src/test/resources/org/apache/calcite/test/TopDownOptTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/TopDownOptTest.xml
@@ -579,7 +579,7 @@ on r.job=s.job and r.ename=s.ename]]>
     <Resource name="planBefore">
       <![CDATA[
 LogicalProject(ENAME=[$0], JOB=[$1], SAL=[$2], COMM=[$3], ENAME0=[$4], 
JOB0=[$5], EXPR$2=[$6])
-  LogicalJoin(condition=[AND(=($1, $5), =($0, $4))], joinType=[inner])
+  LogicalJoin(condition=[AND(=($5, $1), =($4, $0))], joinType=[inner])
     LogicalTableScan(table=[[CATALOG, SALES, BONUS]])
     LogicalAggregate(group=[{0, 1}], EXPR$2=[MAX($2)])
       LogicalProject(ENAME=[$1], JOB=[$2], SAL=[$5])
@@ -606,7 +606,7 @@ on r.job=s.job and r.ename=s.ename]]>
     <Resource name="planBefore">
       <![CDATA[
 LogicalProject(ENAME=[$0], JOB=[$1], SAL=[$2], COMM=[$3], ENAME0=[$4], 
JOB0=[$5], MGR=[$6], EXPR$3=[$7])
-  LogicalJoin(condition=[AND(=($1, $5), =($0, $4))], joinType=[inner])
+  LogicalJoin(condition=[AND(=($5, $1), =($4, $0))], joinType=[inner])
     LogicalTableScan(table=[[CATALOG, SALES, BONUS]])
     LogicalAggregate(group=[{0, 1, 2}], EXPR$3=[MAX($3)])
       LogicalProject(ENAME=[$1], JOB=[$2], MGR=[$3], SAL=[$5])
diff --git a/core/src/test/resources/sql/blank.iq 
b/core/src/test/resources/sql/blank.iq
index 32d3bcb..2d69aaf 100644
--- a/core/src/test/resources/sql/blank.iq
+++ b/core/src/test/resources/sql/blank.iq
@@ -89,7 +89,7 @@ insert into table2 values (NULL, 1), (2, 1);
 # Checked on Oracle
 !set lateDecorrelate true
 select i, j from table1 where table1.j NOT IN (select i from table2 where 
table1.i=table2.j);
-EnumerableCalc(expr#0..7=[{inputs}], expr#8=[0], expr#9=[=($t3, $t8)], 
expr#10=[IS NULL($t1)], expr#11=[IS NOT NULL($t7)], expr#12=[>($t3, $t4)], 
expr#13=[OR($t10, $t11, $t12)], expr#14=[IS NOT TRUE($t13)], expr#15=[OR($t9, 
$t14)], proj#0..1=[{exprs}], $condition=[$t15])
+EnumerableCalc(expr#0..7=[{inputs}], expr#8=[0], expr#9=[=($t3, $t8)], 
expr#10=[IS NULL($t1)], expr#11=[IS NOT NULL($t7)], expr#12=[<($t4, $t3)], 
expr#13=[OR($t10, $t11, $t12)], expr#14=[IS NOT TRUE($t13)], expr#15=[OR($t9, 
$t14)], proj#0..1=[{exprs}], $condition=[$t15])
   EnumerableHashJoin(condition=[AND(=($0, $6), =($1, $5))], joinType=[left])
     EnumerableHashJoin(condition=[=($0, $2)], joinType=[left])
       EnumerableTableScan(table=[[BLANK, TABLE1]])
diff --git a/core/src/test/resources/sql/sub-query.iq 
b/core/src/test/resources/sql/sub-query.iq
index 254738d..9a9df2f 100644
--- a/core/src/test/resources/sql/sub-query.iq
+++ b/core/src/test/resources/sql/sub-query.iq
@@ -32,7 +32,7 @@ where t1.x not in (select t2.x from t2);
 (0 rows)
 
 !ok
-EnumerableCalc(expr#0..4=[{inputs}], expr#5=[0], expr#6=[=($t1, $t5)], 
expr#7=[IS NULL($t4)], expr#8=[<=($t1, $t2)], expr#9=[IS NOT NULL($t0)], 
expr#10=[AND($t7, $t8, $t9)], expr#11=[OR($t6, $t10)], X=[$t0], 
$condition=[$t11])
+EnumerableCalc(expr#0..4=[{inputs}], expr#5=[0], expr#6=[=($t1, $t5)], 
expr#7=[IS NULL($t4)], expr#8=[>=($t2, $t1)], expr#9=[IS NOT NULL($t0)], 
expr#10=[AND($t7, $t8, $t9)], expr#11=[OR($t6, $t10)], X=[$t0], 
$condition=[$t11])
   EnumerableHashJoin(condition=[=($0, $3)], joinType=[left])
     EnumerableNestedLoopJoin(condition=[true], joinType=[inner])
       EnumerableUnion(all=[true])
@@ -2048,7 +2048,7 @@ where sal + 100 not in (
 
 !ok
 EnumerableAggregate(group=[{}], C=[COUNT()])
-  EnumerableCalc(expr#0..9=[{inputs}], expr#10=[0], expr#11=[=($t4, $t10)], 
expr#12=[IS NULL($t2)], expr#13=[IS NOT NULL($t7)], expr#14=[>($t4, $t5)], 
expr#15=[OR($t12, $t13, $t14)], expr#16=[IS NOT TRUE($t15)], expr#17=[OR($t11, 
$t16)], proj#0..9=[{exprs}], $condition=[$t17])
+  EnumerableCalc(expr#0..9=[{inputs}], expr#10=[0], expr#11=[=($t4, $t10)], 
expr#12=[IS NULL($t2)], expr#13=[IS NOT NULL($t7)], expr#14=[<($t5, $t4)], 
expr#15=[OR($t12, $t13, $t14)], expr#16=[IS NOT TRUE($t15)], expr#17=[OR($t11, 
$t16)], proj#0..9=[{exprs}], $condition=[$t17])
     EnumerableHashJoin(condition=[AND(=($1, $8), =($2, $9))], joinType=[left])
       EnumerableHashJoin(condition=[=($1, $3)], joinType=[left])
         EnumerableCalc(expr#0..7=[{inputs}], proj#0..1=[{exprs}], SAL=[$t5])

Reply via email to