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 ce6a160397 [CALCITE-5387] Type-mismatch on nullability in
JoinPushTransitivePredicatesRule RelRule
ce6a160397 is described below
commit ce6a160397aa967ffe85ccb65f37fae8448680d8
Author: Zhen Chen <[email protected]>
AuthorDate: Thu May 1 08:35:15 2025 +0800
[CALCITE-5387] Type-mismatch on nullability in
JoinPushTransitivePredicatesRule RelRule
---
.../calcite/rel/metadata/RelMdPredicates.java | 4 +--
.../calcite/rex/RexPermuteInputsShuttle.java | 42 +++++++++++++++++++---
.../org/apache/calcite/test/RelOptRulesTest.java | 19 ++++++++++
.../org/apache/calcite/test/RelOptRulesTest.xml | 35 ++++++++++++++++++
4 files changed, 94 insertions(+), 6 deletions(-)
diff --git
a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java
b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java
index cfdc372392..9dcfb8e92e 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java
@@ -803,12 +803,12 @@ public RelOptPredicateList inferPredicates(
Mappings.createShiftMapping(nSysFields + nFieldsLeft + nFieldsRight,
0, nSysFields + nFieldsLeft, nFieldsRight);
final RexPermuteInputsShuttle rightPermute =
- new RexPermuteInputsShuttle(rightMapping, joinRel);
+ new RexPermuteInputsShuttle(rightMapping, true, joinRel.getRight());
Mappings.TargetMapping leftMapping =
Mappings.createShiftMapping(nSysFields + nFieldsLeft, 0, nSysFields,
nFieldsLeft);
final RexPermuteInputsShuttle leftPermute =
- new RexPermuteInputsShuttle(leftMapping, joinRel);
+ new RexPermuteInputsShuttle(leftMapping, true, joinRel.getLeft());
final List<RexNode> leftInferredPredicates = new ArrayList<>();
final List<RexNode> rightInferredPredicates = new ArrayList<>();
diff --git
a/core/src/main/java/org/apache/calcite/rex/RexPermuteInputsShuttle.java
b/core/src/main/java/org/apache/calcite/rex/RexPermuteInputsShuttle.java
index 9f418b1b72..0906a6610d 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexPermuteInputsShuttle.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexPermuteInputsShuttle.java
@@ -37,6 +37,7 @@ public class RexPermuteInputsShuttle extends RexShuttle {
private final Mappings.TargetMapping mapping;
private final ImmutableList<RelDataTypeField> fields;
+ private final boolean matchTargetType;
//~ Constructors -----------------------------------------------------------
@@ -54,14 +55,41 @@ public class RexPermuteInputsShuttle extends RexShuttle {
public RexPermuteInputsShuttle(
Mappings.TargetMapping mapping,
RelNode... inputs) {
- this(mapping, fields(inputs));
+ this(mapping, fields(inputs), false);
+ }
+
+ /**
+ * Creates a RexPermuteInputsShuttle.
+ *
+ * <p>The mapping provides at most one target for every source.
+ *
+ * @param mapping Mapping
+ * @param shouldMatchTargetType If {@code false}, each input reference is
substituted
+ * with a reference retaining the original
input's type.
+ * If {@code true}, each input reference is
substituted
+ * with a reference matching the target type.
+ * @param inputs Input relational expressions
+ */
+ public RexPermuteInputsShuttle(
+ Mappings.TargetMapping mapping,
+ boolean shouldMatchTargetType,
+ RelNode... inputs) {
+ this(mapping, fields(inputs), shouldMatchTargetType);
}
private RexPermuteInputsShuttle(
Mappings.TargetMapping mapping,
ImmutableList<RelDataTypeField> fields) {
+ this(mapping, fields, false);
+ }
+
+ private RexPermuteInputsShuttle(
+ Mappings.TargetMapping mapping,
+ ImmutableList<RelDataTypeField> fields,
+ boolean matchTargetType) {
this.mapping = mapping;
this.fields = fields;
+ this.matchTargetType = matchTargetType;
}
/** Creates a shuttle with an empty field list. It cannot handle GET calls
but
@@ -85,9 +113,15 @@ private static ImmutableList<RelDataTypeField>
fields(RelNode[] inputs) {
@Override public RexNode visitInputRef(RexInputRef local) {
final int index = local.getIndex();
int target = mapping.getTarget(index);
- return new RexInputRef(
- target,
- local.getType());
+ if (!matchTargetType) {
+ return new RexInputRef(
+ target,
+ local.getType());
+ } else {
+ return new RexInputRef(
+ target,
+ fields.get(target).getType());
+ }
}
@Override public RexNode visitCall(RexCall call) {
diff --git a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
index 65c9f41723..3278dab3e5 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
@@ -6896,6 +6896,25 @@ private HepProgram getTransitiveProgram() {
.checkUnchanged();
}
+ /** Test case for
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-5387">[CALCITE-5387]
+ * Type-mismatch on nullability in JoinPushTransitivePredicatesRule
RelRule</a>. */
+ @Test void testJoinPushTransitivePredicatesNullabilityIssue() {
+ final String sql = "WITH\n"
+ + "non_null_table AS (\n"
+ + " SELECT DATE '2023-08-07' AS date_col_non_null FROM dept\n"
+ + "),\n"
+ + "null_table AS (\n"
+ + " SELECT CAST(null as DATE) AS date_col_null FROM dept\n"
+ + ")\n"
+ + "SELECT *\n"
+ + "FROM non_null_table\n"
+ + "JOIN null_table\n"
+ + "ON null_table.date_col_null = non_null_table.date_col_non_null";
+
+ sql(sql).withRule(CoreRules.JOIN_PUSH_TRANSITIVE_PREDICATES).check();
+ }
+
/** Test case of
* <a
href="https://issues.apache.org/jira/browse/CALCITE-6432">[CALCITE-6432]
* Infinite loop for JoinPushTransitivePredicatesRule</a>. */
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 1a4a98bbb8..37915af905 100644
--- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
@@ -7468,6 +7468,41 @@ LogicalProject(DEPTNO=[$0], NAME=[$1], R=[$3],
EXPR$1=[$4])
LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
LogicalProject(R=[RANK() OVER (ORDER BY $1)], EXPR$1=[+(1, 1)])
LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testJoinPushTransitivePredicatesNullabilityIssue">
+ <Resource name="sql">
+ <![CDATA[WITH
+non_null_table AS (
+ SELECT DATE '2023-08-07' AS date_col_non_null FROM dept
+),
+null_table AS (
+ SELECT CAST(null as DATE) AS date_col_null FROM dept
+)
+SELECT *
+FROM non_null_table
+JOIN null_table
+ON null_table.date_col_null = non_null_table.date_col_non_null]]>
+ </Resource>
+ <Resource name="planBefore">
+ <![CDATA[
+LogicalProject(DATE_COL_NON_NULL=[$0], DATE_COL_NULL=[$1])
+ LogicalJoin(condition=[=($1, $0)], joinType=[inner])
+ LogicalProject(DATE_COL_NON_NULL=[2023-08-07])
+ LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+ LogicalProject(DATE_COL_NULL=[null:DATE])
+ LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+]]>
+ </Resource>
+ <Resource name="planAfter">
+ <![CDATA[
+LogicalProject(DATE_COL_NON_NULL=[$0], DATE_COL_NULL=[$1])
+ LogicalJoin(condition=[=($1, $0)], joinType=[inner])
+ LogicalValues(tuples=[[]])
+ LogicalFilter(condition=[=($0, 2023-08-07)])
+ LogicalProject(DATE_COL_NULL=[null:DATE])
+ LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
]]>
</Resource>
</TestCase>