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

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


The following commit(s) were added to refs/heads/main by this push:
     new 906a17f40f [CALCITE-6534] Adjust type when pulling up Calc in 
JoinUnifyRule
906a17f40f is described below

commit 906a17f40f00550e3347b3459c7c2de7e439b3c2
Author: suibianwanwan <[email protected]>
AuthorDate: Wed Aug 21 17:01:23 2024 +0800

    [CALCITE-6534] Adjust type when pulling up Calc in JoinUnifyRule
---
 .../apache/calcite/plan/SubstitutionVisitor.java   | 252 +++++++++++++--------
 .../MaterializedViewSubstitutionVisitorTest.java   | 178 ++++++++++++---
 2 files changed, 312 insertions(+), 118 deletions(-)

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 fba9e92fdd..b8acb6c991 100644
--- a/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
+++ b/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
@@ -77,6 +77,7 @@ import com.google.common.collect.Sets;
 import org.checkerframework.checker.nullness.qual.Nullable;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Comparator;
 import java.util.HashMap;
@@ -127,6 +128,14 @@ import static java.util.Objects.requireNonNull;
  */
 public class SubstitutionVisitor {
   private static final boolean DEBUG = CalciteSystemProperty.DEBUG.value();
+  private static final Strong STRONG = new Strong() {
+    @Override public boolean isNull(RexInputRef ref) {
+      // Here strong is used to determine if the nullable side calc can be 
pulled up to the join,
+      // and we will adjust nullability if RexInputRef is not null,
+      // so here RexInputRef is always satisfies null-if-null
+      return true;
+    }
+  };
 
   public static final ImmutableList<UnifyRule> DEFAULT_RULES =
       ImmutableList.of(
@@ -1209,6 +1218,7 @@ public class SubstitutionVisitor {
       final MutableJoin query = (MutableJoin) call.query;
       final MutableCalc qInput0 = (MutableCalc) query.getLeft();
       final MutableRel qInput1 = query.getRight();
+      MutableRel qInput0Input = qInput0.getInput();
       final Pair<RexNode, List<RexNode>> qInput0Explained = 
explainCalc(qInput0);
       final RexNode qInput0Cond = qInput0Explained.left;
       final List<RexNode> qInput0Projs = qInput0Explained.right;
@@ -1222,13 +1232,8 @@ public class SubstitutionVisitor {
       if (joinRelType == null) {
         return null;
       }
-      // Check if filter under join can be pulled up.
-      if (!canPullUpFilterUnderJoin(joinRelType, qInput0Cond, null)) {
-        return null;
-      }
-
-      // Check if join project nullable can be pull up
-      if (!canPullUpProjectUnderJoin(joinRelType, qInput0Projs, null)) {
+      // Check if calc under join can be pulled up.
+      if (!canPullUpCalcUnderJoin(joinRelType, qInput0Explained, null)) {
         return null;
       }
 
@@ -1246,7 +1251,7 @@ public class SubstitutionVisitor {
             final int newIdx = ((RexInputRef) 
qInput0Projs.get(idx)).getIndex();
             return new RexInputRef(newIdx, inputRef.getType());
           } else {
-            int newIdx = idx - fieldCnt(qInput0) + 
fieldCnt(qInput0.getInput());
+            int newIdx = idx - fieldCnt(qInput0) + fieldCnt(qInput0Input);
             return new RexInputRef(newIdx, inputRef.getType());
           }
         }
@@ -1257,16 +1262,10 @@ public class SubstitutionVisitor {
       // MutableJoin matches only when the conditions are analyzed to be same.
       if (splitted != null && splitted.isAlwaysTrue()) {
         final RexNode compenCond = qInput0Cond;
-        final List<RexNode> compenProjs = new ArrayList<>();
-        for (int i = 0; i < fieldCnt(query); i++) {
-          if (i < fieldCnt(qInput0)) {
-            compenProjs.add(qInput0Projs.get(i));
-          } else {
-            final int newIdx = i - fieldCnt(qInput0) + 
fieldCnt(qInput0.getInput());
-            compenProjs.add(
-                new RexInputRef(newIdx, 
query.rowType.getFieldList().get(i).getType()));
-          }
-        }
+        final List<RexNode> compenProjs =
+            shiftAndAdjustProjectExpr(query, target, rexBuilder,
+                qInput0Projs, qInput0Input.rowType.getFieldList(), null, null);
+
         final RexProgram compenRexProgram =
             RexProgram.create(target.rowType, compenProjs, compenCond,
                 query.rowType, rexBuilder);
@@ -1312,13 +1311,8 @@ public class SubstitutionVisitor {
       if (joinRelType == null) {
         return null;
       }
-      // Check if filter under join can be pulled up.
-      if (!canPullUpFilterUnderJoin(joinRelType, null, qInput1Cond)) {
-        return null;
-      }
-
-      // Check if join project nullable can be pull up
-      if (!canPullUpProjectUnderJoin(joinRelType, null, qInput1Projs)) {
+      // Check if calc under join can be pulled up.
+      if (!canPullUpCalcUnderJoin(joinRelType, null, qInput1Explained)) {
         return null;
       }
 
@@ -1348,18 +1342,11 @@ public class SubstitutionVisitor {
       if (splitted != null && splitted.isAlwaysTrue()) {
         final RexNode compenCond =
             RexUtil.shift(qInput1Cond, qInput0.rowType.getFieldCount());
-        final List<RexNode> compenProjs = new ArrayList<>();
-        for (int i = 0; i < query.rowType.getFieldCount(); i++) {
-          if (i < fieldCnt(qInput0)) {
-            compenProjs.add(
-                new RexInputRef(i, 
query.rowType.getFieldList().get(i).getType()));
-          } else {
-            final RexNode shifted =
-                RexUtil.shift(qInput1Projs.get(i - fieldCnt(qInput0)),
-                    qInput0.rowType.getFieldCount());
-            compenProjs.add(shifted);
-          }
-        }
+
+        final List<RexNode> compenProjs =
+            shiftAndAdjustProjectExpr(query, target, rexBuilder,
+                null, null, qInput1Projs, 
qInput1.getInput().rowType.getFieldList());
+
         final RexProgram compensatingRexProgram =
             RexProgram.create(target.rowType, compenProjs, compenCond,
                 query.rowType, rexBuilder);
@@ -1408,13 +1395,8 @@ public class SubstitutionVisitor {
       if (joinRelType == null) {
         return null;
       }
-      // Check if filter under join can be pulled up.
-      if (!canPullUpFilterUnderJoin(joinRelType, qInput0Cond, qInput1Cond)) {
-        return null;
-      }
-
-      // Check if join project nullable can be pull up
-      if (!canPullUpProjectUnderJoin(joinRelType, qInput0Projs, qInput1Projs)) 
{
+      // Check if calc under join can be pulled up.
+      if (!canPullUpCalcUnderJoin(joinRelType, qInput0Explained, 
qInput1Explained)) {
         return null;
       }
 
@@ -1445,17 +1427,11 @@ public class SubstitutionVisitor {
             RexUtil.composeConjunction(rexBuilder,
                 ImmutableList.of(qInput0Cond, qInput1CondShifted));
 
-        final List<RexNode> compenProjs = new ArrayList<>();
-        for (int i = 0; i < query.rowType.getFieldCount(); i++) {
-          if (i < fieldCnt(qInput0)) {
-            compenProjs.add(qInput0Projs.get(i));
-          } else {
-            RexNode shifted =
-                RexUtil.shift(qInput1Projs.get(i - fieldCnt(qInput0)),
-                    fieldCnt(qInput0.getInput()));
-            compenProjs.add(shifted);
-          }
-        }
+        final List<RexNode> compenProjs =
+            shiftAndAdjustProjectExpr(query, target, rexBuilder,
+                qInput0Projs, qInput0.getInput().rowType.getFieldList(),
+                qInput1Projs, qInput1.getInput().rowType.getFieldList());
+
         final RexProgram compensatingRexProgram =
             RexProgram.create(target.rowType, compenProjs, compenCond,
                 query.rowType, rexBuilder);
@@ -2169,57 +2145,155 @@ public class SubstitutionVisitor {
   }
 
   /**
-   * Check if filter under join can be pulled up,
+   * Check if calc under join can be pulled up,
    * when meeting JoinOnCalc of query unify to Join of target.
    * Working in rules: {@link JoinOnLeftCalcToJoinUnifyRule} <br/>
    * {@link JoinOnRightCalcToJoinUnifyRule} <br/>
    * {@link JoinOnCalcsToJoinUnifyRule} <br/>
    */
-  private static boolean canPullUpFilterUnderJoin(JoinRelType joinType,
-      @Nullable RexNode leftFilterRexNode, @Nullable RexNode 
rightFilterRexNode) {
-    if (joinType == JoinRelType.INNER) {
-      return true;
-    }
-    if (joinType == JoinRelType.LEFT
-        && (rightFilterRexNode == null || rightFilterRexNode.isAlwaysTrue())) {
-      return true;
-    }
-    if (joinType == JoinRelType.RIGHT
-        && (leftFilterRexNode == null || leftFilterRexNode.isAlwaysTrue())) {
-      return true;
+  private static boolean canPullUpCalcUnderJoin(JoinRelType joinType,
+      @Nullable Pair<RexNode, List<RexNode>> qInput0Explained,
+      @Nullable Pair<RexNode, List<RexNode>> qInput1Explained) {
+    if (qInput0Explained != null
+        && joinType.generatesNullsOn(0)
+        && !isCalcStrong(qInput0Explained)) {
+      return false;
     }
-    if (joinType == JoinRelType.FULL
-        && ((rightFilterRexNode == null || rightFilterRexNode.isAlwaysTrue())
-        && (leftFilterRexNode == null || leftFilterRexNode.isAlwaysTrue()))) {
-      return true;
+    if (qInput1Explained != null
+        && joinType.generatesNullsOn(1)
+        && !isCalcStrong(qInput1Explained)) {
+      return false;
     }
-    return false;
+    return true;
+  }
+
+  /** Determines if all projects are strong and the condition is always true. 
*/
+  private static boolean isCalcStrong(Pair<RexNode, List<RexNode>> 
inputExplained) {
+    final RexNode cond = requireNonNull(inputExplained.left, "condition");
+    final List<RexNode> projs = requireNonNull(inputExplained.right, 
"projects");
+    return cond.isAlwaysTrue() && projs.stream().allMatch(STRONG::isNull);
   }
 
   /**
-   * Check if project under join can be pulled up,
-   * when meeting JoinOnCalc of query unify to Join of target.
+
+   */
+
+  /**
+   * Generates project expressions by shifting and adjusting the nullability 
of expressions
+   * based on the provided join targets and inputs.
+   *
+   * <p>Used in the Join rewrite to pull up the calc in query
+   * to the join in mv to ensure operator equivalence. (Already make sure that 
pull up is valid).
    * Working in rules: {@link JoinOnLeftCalcToJoinUnifyRule} <br/>
    * {@link JoinOnRightCalcToJoinUnifyRule} <br/>
    * {@link JoinOnCalcsToJoinUnifyRule} <br/>
+   *
+   * @param query MutableRel of query
+   * @param target MutableRel of target
+   * @param rexBuilder Rex builder
+   * @param qInput0Projs Project expressions from the left calc of the query 
join if exist
+   * @param qInput0InputFields Input fields from the left input of the query 
join if exist
+   * @param qInput1Projs Project expressions from the right calc of the query 
join if exist
+   * @param qInput1InputFields Input fields from the right input of the query 
join if exist
+   * @return The Project expression that makes target equivalent to query
    */
-  private static boolean canPullUpProjectUnderJoin(JoinRelType joinType,
-      @Nullable List<RexNode> leftProjects, @Nullable List<RexNode> 
rightProjects) {
-    if (leftProjects != null && joinType.generatesNullsOn(0)
-        && !allProjectsNullable(leftProjects)) {
-      return false;
-    }
-    if (rightProjects != null && joinType.generatesNullsOn(1)
-        && !allProjectsNullable(rightProjects)) {
-      return false;
+  private static List<RexNode> shiftAndAdjustProjectExpr(MutableJoin query, 
MutableJoin target,
+      RexBuilder rexBuilder,
+      @Nullable List<RexNode> qInput0Projs, @Nullable List<RelDataTypeField> 
qInput0InputFields,
+      @Nullable List<RexNode> qInput1Projs, @Nullable List<RelDataTypeField> 
qInput1InputFields) {
+    int queryLeftCount = fieldCnt(query.getLeft());
+    int targetLeftCount = fieldCnt(target.getLeft());
+    int[] adjustments0 = new int[target.rowType.getFieldCount()];
+    int[] adjustments1 = new int[target.rowType.getFieldCount()];
+
+    if (qInput1Projs != null) {
+      Arrays.fill(adjustments1, targetLeftCount);
+    }
+
+    // In cases such as JoinOnLeftCalcToJoinUnifyRule and 
JoinOnCalcsToJoinUnifyRule rules,
+    // initialize the converter where the left calc needs to be pulled up.
+    RelOptUtil.RexInputConverter converter0 =
+        new RelOptUtil.RexInputConverter(rexBuilder, qInput0InputFields,
+            target.rowType.getFieldList(), adjustments0);
+
+    // In cases such as JoinOnRightCalcToJoinUnifyRule and 
JoinOnCalcsToJoinUnifyRule rules,
+    // initialize the converter where the left calc needs to be pulled up.
+    RelOptUtil.RexInputConverter converter1 =
+        new RelOptUtil.RexInputConverter(rexBuilder, qInput1InputFields,
+            target.rowType.getFieldList(), adjustments1);
+
+    final List<RexNode> compenProjs = new ArrayList<>();
+    for (int i = 0; i < fieldCnt(query); i++) {
+      RelDataType type = query.rowType.getFieldList().get(i).getType();
+      if (i < queryLeftCount) {
+        if (qInput0Projs == null) {
+          compenProjs.add(new RexInputRef(i, type));
+        } else {
+          // Before:
+          //        QueryJoin      TargetJoin
+          //        /     \         /     \
+          //      Calc   Right    Left   Right
+          //        |
+          //      Left
+          //
+          // After:
+          //        QueryJoin         Calc
+          //        /     \            |
+          //      Calc   Right     TargetJoin
+          //        |               /     \
+          //      Left            Left   Right
+          //
+          // Since Calc is on the left side of the Join, no shift is required.
+          // However, due to nullability caused by the Join,
+          // we must adjust the type of RexInputRef.
+          RexNode apply = converter0.apply(qInput0Projs.get(i));
+          compenProjs.add(adjustNullability(apply, type, rexBuilder));
+        }
+      } else {
+        if (qInput1Projs == null) {
+          // This is equivalent to a shift, but without a calc on the right 
side to pull up,
+          // we know it's a RexInputRef, so converter isn't needed.
+          compenProjs.add(
+              new RexInputRef(i - queryLeftCount + targetLeftCount, type));
+        } else {
+          // Before:
+          //        QueryJoin      TargetJoin
+          //        /     \         /     \
+          //      Left   Calc    Left   Right
+          //               |
+          //             Right
+          //
+          // After, We pull up the Calc of Query to target to make it 
equivalent to Query:
+          //        QueryJoin          Calc
+          //        /     \             |
+          //      Left   Calc       TargetJoin
+          //               |         /     \
+          //             Right     Left   Right
+          //
+          // With regard to type adjustments, as above.
+          // And since Query's Right is equivalent to Target's Right and now 
calc
+          // will be pulled up above the join, we need to shift join's 
leftCount,
+          // which is what we did in initializing converter1.
+          RexNode apply = converter1.apply(qInput1Projs.get(i - 
queryLeftCount));
+          compenProjs.add(adjustNullability(apply, type, rexBuilder));
+        }
+      }
     }
-    return true;
+    return compenProjs;
   }
 
-  /** Returns whether all projects are nullable. */
-  private static boolean allProjectsNullable(List<RexNode> projects) {
-    return projects.stream()
-        .allMatch(project -> project.getType().isNullable());
+  /** Cast RexNode to the given type if only nullability differs, otherwise 
throw. */
+  private static RexNode adjustNullability(RexNode rexNode,
+      RelDataType type, RexBuilder rexBuilder) {
+    if (rexNode.getType().equals(type)) {
+      return rexNode;
+    }
+    final RelDataType adjustedType =
+        
rexBuilder.getTypeFactory().createTypeWithNullability(rexNode.getType(), 
type.isNullable());
+    if (type.equals(adjustedType)) {
+      return rexBuilder.makeCast(adjustedType, rexNode);
+    }
+    throw new AssertionError("Adjust nullability failed:" + rexNode.getType() 
+ " " + type);
   }
 
   /** Operand to a {@link UnifyRule}. */
diff --git 
a/core/src/test/java/org/apache/calcite/test/MaterializedViewSubstitutionVisitorTest.java
 
b/core/src/test/java/org/apache/calcite/test/MaterializedViewSubstitutionVisitorTest.java
index ea7c7258e0..c907c1d405 100644
--- 
a/core/src/test/java/org/apache/calcite/test/MaterializedViewSubstitutionVisitorTest.java
+++ 
b/core/src/test/java/org/apache/calcite/test/MaterializedViewSubstitutionVisitorTest.java
@@ -1137,9 +1137,9 @@ public class MaterializedViewSubstitutionVisitorTest {
   }
 
   /** Test case for
-   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-6501";>[CALCITE-6501]
-   * Assertion Error in JoinUnifyRule Due to Type Mismatch</a>. */
-  @Test void testLeftProjectOnRightJoinToJoinFailed() {
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-6534";>[CALCITE-6534]
+   * Adjust type when pulling up Calc in JoinUnifyRule</a>. */
+  @Test void testLeftProjectOnRightJoinToJoinOk() {
     String mv = "select * from \n"
         + "(select \"empid\", \"deptno\", \"name\" from \"emps\") \"t1\"\n"
         + "right join (select \"deptno\", \"name\" from \"depts\") \"t2\"\n"
@@ -1148,13 +1148,13 @@ public class MaterializedViewSubstitutionVisitorTest {
         + "(select \"empid\", \"empid\" a, \"deptno\", \"name\" from \"emps\") 
\"t1\"\n"
         + "right join (select \"deptno\", \"name\" from \"depts\") \"t2\"\n"
         + "on \"t1\".\"deptno\" = \"t2\".\"deptno\"";
-    sql(mv, query).noMat();
+    sql(mv, query).ok();
   }
 
   /** Test case for
-   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-6501";>[CALCITE-6501]
-   * Assertion Error in JoinUnifyRule Due to Type Mismatch</a>. */
-  @Test void testLeftProjectOnFullJoinToJoinFailed() {
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-6534";>[CALCITE-6534]
+   * Adjust type when pulling up Calc in JoinUnifyRule</a>. */
+  @Test void testLeftProjectOnFullJoinToJoinOk() {
     String mv = "select * from \n"
         + "(select \"empid\", \"deptno\", \"name\" from \"emps\") \"t1\"\n"
         + "full join (select \"deptno\", \"name\" from \"depts\") \"t2\"\n"
@@ -1163,13 +1163,13 @@ public class MaterializedViewSubstitutionVisitorTest {
         + "(select \"empid\", \"empid\" a, \"deptno\", \"name\" from \"emps\") 
\"t1\"\n"
         + "full join (select \"deptno\", \"name\" from \"depts\") \"t2\"\n"
         + "on \"t1\".\"deptno\" = \"t2\".\"deptno\"";
-    sql(mv, query).noMat();
+    sql(mv, query).ok();
   }
 
   /** Test case for
-   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-6501";>[CALCITE-6501]
-   * Assertion Error in JoinUnifyRule Due to Type Mismatch</a>. */
-  @Test void testRightProjectOnLeftJoinToJoinFailed() {
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-6534";>[CALCITE-6534]
+   * Adjust type when pulling up Calc in JoinUnifyRule</a>. */
+  @Test void testRightProjectOnLeftJoinToJoinOk2() {
     String mv = "select * from \n"
         + "(select \"empid\", \"deptno\", \"name\" from \"emps\") \"t1\"\n"
         + "left join (select \"deptno\", \"name\" from \"depts\") \"t2\"\n"
@@ -1178,13 +1178,25 @@ public class MaterializedViewSubstitutionVisitorTest {
         + "(select \"empid\", \"deptno\", \"name\" from \"emps\") \"t1\"\n"
         + "left join (select \"deptno\", \"deptno\" a, \"name\" from 
\"depts\") \"t2\"\n"
         + "on \"t1\".\"deptno\" = \"t2\".\"deptno\"";
-    sql(mv, query).noMat();
+    sql(mv, query).ok();
+  }
+
+  @Test void testRightProjectOnLeftJoinToJoinOk() {
+    String mv = "select * from \n"
+        + "(select \"empid\", \"deptno\", \"name\" from \"emps\") \"t1\"\n"
+        + "left join (select \"deptno\", \"name\" from \"depts\") \"t2\"\n"
+        + "on \"t1\".\"deptno\" = \"t2\".\"deptno\"";
+    String query = "select * from \n"
+        + "(select \"empid\", \"deptno\", \"name\" from \"emps\") \"t1\"\n"
+        + "left join (select \"deptno\", \"deptno\" a, \"name\" from 
\"depts\") \"t2\"\n"
+        + "on \"t1\".\"deptno\" = \"t2\".\"deptno\"";
+    sql(mv, query).ok();
   }
 
   /** Test case for
-   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-6501";>[CALCITE-6501]
-   * Assertion Error in JoinUnifyRule Due to Type Mismatch</a>. */
-  @Test void testRightProjectOnFullJoinToJoinFailed() {
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-6534";>[CALCITE-6534]
+   * Adjust type when pulling up Calc in JoinUnifyRule</a>. */
+  @Test void testRightProjectOnFullJoinToJoinOk() {
     String mv = "select * from \n"
         + "(select \"empid\", \"deptno\", \"name\" from \"emps\") \"t1\"\n"
         + "full join (select \"deptno\", \"name\" from \"depts\") \"t2\"\n"
@@ -1193,13 +1205,13 @@ public class MaterializedViewSubstitutionVisitorTest {
         + "(select \"empid\", \"deptno\", \"name\" from \"emps\") \"t1\"\n"
         + "full join (select \"deptno\", \"deptno\" a, \"name\" from 
\"depts\") \"t2\"\n"
         + "on \"t1\".\"deptno\" = \"t2\".\"deptno\"";
-    sql(mv, query).noMat();
+    sql(mv, query).ok();
   }
 
   /** Test case for
-   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-6501";>[CALCITE-6501]
-   * Assertion Error in JoinUnifyRule Due to Type Mismatch</a>. */
-  @Test void testLeftProjectAndRightProjectOnLeftJoinToJoinFailed() {
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-6534";>[CALCITE-6534]
+   * Adjust type when pulling up Calc in JoinUnifyRule</a>. */
+  @Test void testLeftProjectAndRightProjectOnLeftJoinToJoinOk() {
     String mv = "select * from \n"
         + "(select \"empid\", \"deptno\", \"name\" from \"emps\") \"t1\"\n"
         + "left join (select \"deptno\", \"name\" from \"depts\") \"t2\"\n"
@@ -1208,13 +1220,13 @@ public class MaterializedViewSubstitutionVisitorTest {
         + "(select \"empid\", \"empid\" a, \"deptno\", \"name\" from \"emps\") 
\"t1\"\n"
         + "left join (select \"deptno\", \"deptno\" a, \"name\" from 
\"depts\") \"t2\"\n"
         + "on \"t1\".\"deptno\" = \"t2\".\"deptno\"";
-    sql(mv, query).noMat();
+    sql(mv, query).ok();
   }
 
   /** Test case for
-   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-6501";>[CALCITE-6501]
-   * Assertion Error in JoinUnifyRule Due to Type Mismatch</a>. */
-  @Test void testLeftProjectAndRightProjectOnRightJoinToJoinFailed() {
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-6534";>[CALCITE-6534]
+   * Adjust type when pulling up Calc in JoinUnifyRule</a>. */
+  @Test void testLeftProjectAndRightProjectOnRightJoinToJoinOk() {
     String mv = "select * from \n"
         + "(select \"empid\", \"deptno\", \"name\" from \"emps\") \"t1\"\n"
         + "right join (select \"deptno\", \"name\" from \"depts\") \"t2\"\n"
@@ -1223,22 +1235,130 @@ public class MaterializedViewSubstitutionVisitorTest {
         + "(select \"empid\", \"empid\" a, \"deptno\", \"name\" from \"emps\") 
\"t1\"\n"
         + "right join (select \"deptno\", \"deptno\" a, \"name\" from 
\"depts\") \"t2\"\n"
         + "on \"t1\".\"deptno\" = \"t2\".\"deptno\"";
-    sql(mv, query).noMat();
+    sql(mv, query).ok();
   }
 
   /** Test case for
-   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-6501";>[CALCITE-6501]
-   * Assertion Error in JoinUnifyRule Due to Type Mismatch</a>. */
-  @Test void testLeftProjectAndRightProjectOnFullJoinToJoinFailed() {
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-6534";>[CALCITE-6534]
+   * Adjust type when pulling up Calc in JoinUnifyRule</a>. */
+  @Test void testLeftProjectAndRightProjectOnFullJoinToJoinOk() {
     String mv = "select * from \n"
         + "(select \"empid\", \"deptno\", \"name\" from \"emps\") \"t1\"\n"
         + "full join (select \"deptno\", \"name\" from \"depts\") \"t2\"\n"
         + "on \"t1\".\"deptno\" = \"t2\".\"deptno\"";
     String query = "select * from \n"
         + "(select \"empid\", \"deptno\", \"name\" from \"emps\") \"t1\"\n"
-        + "full join (select \"deptno\", \"deptno\" a, \"name\" from 
\"depts\") \"t2\"\n"
+        + "full join (select \"deptno\", FLOOR(\"deptno\") a, \"name\" from 
\"depts\") \"t2\"\n"
         + "on \"t1\".\"deptno\" = \"t2\".\"deptno\"";
-    sql(mv, query).noMat();
+    sql(mv, query).ok();
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-6534";>[CALCITE-6534]
+   * Adjust type when pulling up Calc in JoinUnifyRule</a>. */
+  @Test void testInnerJoinAdjustType() {
+    String mv = "select * from \n"
+        + "(select \"empid\", \"deptno\", \"name\" from \"emps\") \"t1\"\n"
+        + "join (select \"deptno\", \"name\" from \"depts\") \"t2\"\n"
+        + "on \"t1\".\"deptno\" = \"t2\".\"deptno\"";
+    String leftCalcQuery = "select * from \n"
+        + "(select \"empid\", CASE WHEN \"empid\" = 1 THEN 0 ELSE 1 END,"
+        + "\"deptno\", \"name\" from \"emps\") \"t1\"\n"
+        + "join (select \"deptno\", \"name\" from \"depts\") \"t2\"\n"
+        + "on \"t1\".\"deptno\" = \"t2\".\"deptno\"";
+    String rightCalcQuery = "select * from \n"
+        + "(select \"empid\", \"deptno\", \"name\" from \"emps\") \"t1\"\n"
+        + "join (select \"deptno\", \"deptno\" IS NULL,\"name\" from 
\"depts\") \"t2\"\n"
+        + "on \"t1\".\"deptno\" = \"t2\".\"deptno\"";
+    String leftAndRightCalcQuery = "select * from \n"
+        + "(select \"empid\", CASE WHEN \"empid\" = 1 THEN 0 ELSE \"empid\" 
END,"
+        + "\"deptno\", \"name\" from \"emps\") \"t1\"\n"
+        + "join (select \"deptno\", \"deptno\" IS NOT NULL, \"name\" from 
\"depts\") \"t2\"\n"
+        + "on \"t1\".\"deptno\" = \"t2\".\"deptno\"";
+    sql(mv, leftCalcQuery).ok();
+    sql(mv, rightCalcQuery).ok();
+    sql(mv, leftAndRightCalcQuery).ok();
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-6534";>[CALCITE-6534]
+   * Adjust type when pulling up Calc in JoinUnifyRule</a>. */
+  @Test void testLeftJoinAdjustType() {
+    String mv = "select * from \n"
+        + "(select \"empid\", \"deptno\", \"name\" from \"emps\") \"t1\"\n"
+        + "left join (select \"deptno\", \"name\" from \"depts\") \"t2\"\n"
+        + "on \"t1\".\"deptno\" = \"t2\".\"deptno\"";
+    String leftCalcQuery = "select * from \n"
+        + "(select \"empid\", CASE WHEN \"empid\" = 1 THEN 0 ELSE 1 END,"
+        + "\"deptno\", \"name\" from \"emps\") \"t1\"\n"
+        + "left join (select \"deptno\", \"name\" from \"depts\") \"t2\"\n"
+        + "on \"t1\".\"deptno\" = \"t2\".\"deptno\"";
+    String rightCalcQuery = "select * from \n"
+        + "(select \"empid\", \"deptno\", \"name\" from \"emps\") \"t1\"\n"
+        + "left join (select \"deptno\", \"deptno\" IS NULL,\"name\" from 
\"depts\") \"t2\"\n"
+        + "on \"t1\".\"deptno\" = \"t2\".\"deptno\"";
+    String leftAndRightCalcQuery = "select * from \n"
+        + "(select \"empid\", CASE WHEN \"empid\" = 1 THEN 0 ELSE \"empid\" 
END,"
+        + "\"deptno\", \"name\" from \"emps\") \"t1\"\n"
+        + "left join (select \"deptno\", \"deptno\" IS NOT NULL, \"name\" from 
\"depts\") \"t2\"\n"
+        + "on \"t1\".\"deptno\" = \"t2\".\"deptno\"";
+    sql(mv, leftCalcQuery).ok();
+    sql(mv, rightCalcQuery).noMat();
+    sql(mv, leftAndRightCalcQuery).noMat();
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-6534";>[CALCITE-6534]
+   * Adjust type when pulling up Calc in JoinUnifyRule</a>. */
+  @Test void testRightJoinAdjustType() {
+    String mv = "select * from \n"
+        + "(select \"empid\", \"deptno\", \"name\" from \"emps\") \"t1\"\n"
+        + "right join (select \"deptno\", \"name\" from \"depts\") \"t2\"\n"
+        + "on \"t1\".\"deptno\" = \"t2\".\"deptno\"";
+    String leftCalcQuery = "select * from \n"
+        + "(select \"empid\", CASE WHEN \"empid\" = 1 THEN 0 ELSE 1 END,"
+        + "\"deptno\", \"name\" from \"emps\") \"t1\"\n"
+        + "right join (select \"deptno\", \"name\" from \"depts\") \"t2\"\n"
+        + "on \"t1\".\"deptno\" = \"t2\".\"deptno\"";
+    String rightCalcQuery = "select * from \n"
+        + "(select \"empid\", \"deptno\", \"name\" from \"emps\") \"t1\"\n"
+        + "right join (select \"deptno\", \"deptno\" IS NULL,\"name\" from 
\"depts\") \"t2\"\n"
+        + "on \"t1\".\"deptno\" = \"t2\".\"deptno\"";
+    String leftAndRightCalcQuery = "select * from \n"
+        + "(select \"empid\", CASE WHEN \"empid\" = 1 THEN 0 ELSE \"empid\" 
END,"
+        + "\"deptno\", \"name\" from \"emps\") \"t1\"\n"
+        + "right join (select \"deptno\", \"deptno\" IS NOT NULL, \"name\" 
from \"depts\") \"t2\"\n"
+        + "on \"t1\".\"deptno\" = \"t2\".\"deptno\"";
+    sql(mv, leftCalcQuery).noMat();
+    sql(mv, rightCalcQuery).ok();
+    sql(mv, leftAndRightCalcQuery).noMat();
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-6534";>[CALCITE-6534]
+   * Adjust type when pulling up Calc in JoinUnifyRule</a>. */
+  @Test void testFullJoinAdjustType() {
+    String mv = "select * from \n"
+        + "(select \"empid\", \"deptno\", \"name\" from \"emps\") \"t1\"\n"
+        + "full join (select \"deptno\", \"name\" from \"depts\") \"t2\"\n"
+        + "on \"t1\".\"deptno\" = \"t2\".\"deptno\"";
+    String leftCalcQuery = "select * from \n"
+        + "(select \"empid\", CASE WHEN \"empid\" = 1 THEN 0 ELSE 1 END,"
+        + "\"deptno\", \"name\" from \"emps\") \"t1\"\n"
+        + "full join (select \"deptno\", \"name\" from \"depts\") \"t2\"\n"
+        + "on \"t1\".\"deptno\" = \"t2\".\"deptno\"";
+    String rightCalcQuery = "select * from \n"
+        + "(select \"empid\", \"deptno\", \"name\" from \"emps\") \"t1\"\n"
+        + "full join (select \"deptno\", \"deptno\" IS NULL,\"name\" from 
\"depts\") \"t2\"\n"
+        + "on \"t1\".\"deptno\" = \"t2\".\"deptno\"";
+    String leftAndRightCalcQuery = "select * from \n"
+        + "(select \"empid\", CASE WHEN \"empid\" = 1 THEN 0 ELSE \"empid\" 
END,"
+        + "\"deptno\", \"name\" from \"emps\") \"t1\"\n"
+        + "full join (select \"deptno\", \"deptno\" IS NOT NULL, \"name\" from 
\"depts\") \"t2\"\n"
+        + "on \"t1\".\"deptno\" = \"t2\".\"deptno\"";
+    sql(mv, leftCalcQuery).noMat();
+    sql(mv, rightCalcQuery).noMat();
+    sql(mv, leftAndRightCalcQuery).noMat();
   }
 
   @Test void testLeftFilterOnRightJoinToJoinFail() {

Reply via email to