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() {