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

asolimando 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 e25688995d [CALCITE-7636] Materialized view union rewriting drops rows 
when the view filter is not null-rejecting
e25688995d is described below

commit e25688995dab5ea13a8eea5cad14ae6e27e0ea29
Author: Alessandro Solimando <[email protected]>
AuthorDate: Fri Jul 3 11:26:50 2026 +0200

    [CALCITE-7636] Materialized view union rewriting drops rows when the view 
filter is not null-rejecting
---
 .../materialize/MaterializedViewAggregateRule.java |  2 +-
 .../materialize/MaterializedViewJoinRule.java      |  3 ++-
 .../apache/calcite/test/MaterializationTest.java   | 23 +++++++++++++++++
 .../test/MaterializedViewRelOptRulesTest.java      | 30 ++++++++++++++++++++++
 4 files changed, 56 insertions(+), 2 deletions(-)

diff --git 
a/core/src/main/java/org/apache/calcite/rel/rules/materialize/MaterializedViewAggregateRule.java
 
b/core/src/main/java/org/apache/calcite/rel/rules/materialize/MaterializedViewAggregateRule.java
index e6cf9e1058..404d0200ba 100644
--- 
a/core/src/main/java/org/apache/calcite/rel/rules/materialize/MaterializedViewAggregateRule.java
+++ 
b/core/src/main/java/org/apache/calcite/rel/rules/materialize/MaterializedViewAggregateRule.java
@@ -291,7 +291,7 @@ public abstract class MaterializedViewAggregateRule<C 
extends MaterializedViewAg
       otherCompensationPred = newOtherCompensationPred;
     }
     final RexNode queryCompensationPred =
-        RexUtil.not(
+        rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_TRUE,
             RexUtil.composeConjunction(rexBuilder,
                 ImmutableList.of(compensationColumnsEquiPred,
                     otherCompensationPred)));
diff --git 
a/core/src/main/java/org/apache/calcite/rel/rules/materialize/MaterializedViewJoinRule.java
 
b/core/src/main/java/org/apache/calcite/rel/rules/materialize/MaterializedViewJoinRule.java
index 803033a6cc..b420811a55 100644
--- 
a/core/src/main/java/org/apache/calcite/rel/rules/materialize/MaterializedViewJoinRule.java
+++ 
b/core/src/main/java/org/apache/calcite/rel/rules/materialize/MaterializedViewJoinRule.java
@@ -29,6 +29,7 @@
 import org.apache.calcite.rex.RexSimplify;
 import org.apache.calcite.rex.RexTableInputRef.RelTableRef;
 import org.apache.calcite.rex.RexUtil;
+import org.apache.calcite.sql.fun.SqlStdOperatorTable;
 import org.apache.calcite.tools.RelBuilder;
 import org.apache.calcite.util.Pair;
 
@@ -203,7 +204,7 @@ public abstract class MaterializedViewJoinRule<C extends 
MaterializedViewRule.Co
       otherCompensationPred = newOtherCompensationPred;
     }
     final RexNode queryCompensationPred =
-        RexUtil.not(
+        rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_TRUE,
             RexUtil.composeConjunction(rexBuilder,
                 ImmutableList.of(compensationColumnsEquiPred,
                     otherCompensationPred)));
diff --git 
a/core/src/test/java/org/apache/calcite/test/MaterializationTest.java 
b/core/src/test/java/org/apache/calcite/test/MaterializationTest.java
index d23e1eecba..c91f0e75ec 100644
--- a/core/src/test/java/org/apache/calcite/test/MaterializationTest.java
+++ b/core/src/test/java/org/apache/calcite/test/MaterializationTest.java
@@ -128,6 +128,29 @@ public class MaterializationTest {
         .sameResultWithMaterializationsDisabled();
   }
 
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7636";>[CALCITE-7636]
+   * Materialized view union rewriting drops rows when the view filter is not
+   * null-rejecting</a>.
+   *
+   * <p>The view filters on {@code commission > 400}, which is UNKNOWN for the
+   * null-commission employee. The union-rewritten result must keep that row 
and
+   * therefore match the result computed without materializations. */
+  @Test void testUnionRewritingNullableAggregatePredicate() {
+    try (TryThreadLocal.Memo ignored = Prepare.THREAD_TRIM.push(true)) {
+      MaterializationService.setThreadLocal();
+      CalciteAssert.that()
+          .withMaterializations(HR_FKUK_MODEL, "m0",
+              "select \"deptno\", sum(\"salary\") as s from \"emps\" "
+              + "where \"deptno\" > 5 and \"commission\" > 400 group by 
\"deptno\"")
+          .query("select \"deptno\", sum(\"salary\") as s from \"emps\" "
+              + "where \"deptno\" > 5 group by \"deptno\"")
+          .enableMaterializations(true)
+          .explainContains("EnumerableUnion(all=[true])")
+          .sameResultWithMaterializationsDisabled();
+    }
+  }
+
   @Test void testViewMaterialization() {
     try (TryThreadLocal.Memo ignored = Prepare.THREAD_TRIM.push(true)) {
       MaterializationService.setThreadLocal();
diff --git 
a/core/src/test/java/org/apache/calcite/test/MaterializedViewRelOptRulesTest.java
 
b/core/src/test/java/org/apache/calcite/test/MaterializedViewRelOptRulesTest.java
index a7a088081e..febf47cbbe 100644
--- 
a/core/src/test/java/org/apache/calcite/test/MaterializedViewRelOptRulesTest.java
+++ 
b/core/src/test/java/org/apache/calcite/test/MaterializedViewRelOptRulesTest.java
@@ -364,6 +364,36 @@ protected final MaterializedViewFixture sql(String 
materialize,
         .ok();
   }
 
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7636";>[CALCITE-7636]
+   * Materialized view union rewriting drops rows when the view filter is not
+   * null-rejecting</a>.
+   *
+   * <p>The view is filtered on a predicate over the nullable {@code 
commission}
+   * column, so the predicate is UNKNOWN for rows where {@code commission} is
+   * null. The query-branch of the union must keep those rows (using
+   * {@code IS NOT TRUE}), otherwise they are dropped from both branches and
+   * whole groups disappear from the result. */
+  @Test void testAggregateMaterializationUnionRewritingNullablePredicate() {
+    sql("select \"deptno\", sum(\"salary\") as s\n"
+            + "from \"emps\" where \"deptno\" > 5 and \"commission\" > 1000\n"
+            + "group by \"deptno\"",
+        "select \"deptno\", sum(\"salary\") as s\n"
+            + "from \"emps\" where \"deptno\" > 5\n"
+            + "group by \"deptno\"")
+        .checkingThatResultContains(""
+            + "EnumerableAggregate(group=[{0}], S=[$SUM0($1)])\n"
+            + "  EnumerableUnion(all=[true])\n"
+            + "    EnumerableAggregate(group=[{1}], S=[$SUM0($3)])\n"
+            + "      EnumerableCalc(expr#0..4=[{inputs}], 
expr#5=[CAST($t1):INTEGER NOT NULL], "
+            + "expr#6=[5], expr#7=[>($t5, $t6)], expr#8=[1000], 
expr#9=[CAST($t4):INTEGER], "
+            + "expr#10=[<($t8, $t9)], expr#11=[IS NOT TRUE($t10)], 
expr#12=[AND($t7, $t11)], "
+            + "proj#0..4=[{exprs}], $condition=[$t12])\n"
+            + "        EnumerableTableScan(table=[[hr, emps]])\n"
+            + "    EnumerableTableScan(table=[[hr, MV0]])")
+        .ok();
+  }
+
   @Test void testJoinAggregateMaterializationNoAggregateFuncs1() {
     sql("select \"empid\", \"depts\".\"deptno\" from \"emps\"\n"
             + "join \"depts\" using (\"deptno\") where \"depts\".\"deptno\" > 
10\n"

Reply via email to