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

mihaibudiu 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 a11dab1c98 [CALCITE-7932] INTERSECT causes a validator crash
a11dab1c98 is described below

commit a11dab1c985276d06c981967242a02c2d001c581
Author: Zhen Chen <[email protected]>
AuthorDate: Wed Nov 11 19:01:25 2026 +0800

    [CALCITE-7932] INTERSECT causes a validator crash
---
 .../validate/implicit/AbstractTypeCoercion.java    |  5 ++-
 .../org/apache/calcite/test/TypeCoercionTest.java  | 51 ++++++++++++++++++++++
 2 files changed, 55 insertions(+), 1 deletion(-)

diff --git 
a/core/src/main/java/org/apache/calcite/sql/validate/implicit/AbstractTypeCoercion.java
 
b/core/src/main/java/org/apache/calcite/sql/validate/implicit/AbstractTypeCoercion.java
index 72fa78d728..ed39423f3f 100644
--- 
a/core/src/main/java/org/apache/calcite/sql/validate/implicit/AbstractTypeCoercion.java
+++ 
b/core/src/main/java/org/apache/calcite/sql/validate/implicit/AbstractTypeCoercion.java
@@ -358,7 +358,10 @@ protected void updateInferredColumnType(
       final RelDataTypeField field = rowType.getFieldList().get(i);
       final String name = field.getName();
       final RelDataType type = field.getType();
-      final RelDataType targetType = i == columnIndex ? desiredType : type;
+      // Implicit casts preserve the source column's nullability.
+      final RelDataType targetType = i == columnIndex
+          ? factory.createTypeWithNullability(desiredType, type.isNullable())
+          : type;
       fieldList.add(name, targetType);
     }
     updateInferredType(query, factory.createStructType(fieldList));
diff --git a/core/src/test/java/org/apache/calcite/test/TypeCoercionTest.java 
b/core/src/test/java/org/apache/calcite/test/TypeCoercionTest.java
index f77096b36b..8f3e09b5cb 100644
--- a/core/src/test/java/org/apache/calcite/test/TypeCoercionTest.java
+++ b/core/src/test/java/org/apache/calcite/test/TypeCoercionTest.java
@@ -270,6 +270,57 @@ private static ImmutableList<RelDataType> combine(
             + " DOUBLE NOT NULL T1_DOUBLE) NOT NULL");
   }
 
+  /** Type widening must preserve the nullability of nested set operations. */
+  @Test void testSetOperationCoercionNullability() {
+    final String notNullInt = "select x from (values (1)) as t(x)";
+    final String nullableInt =
+        "select x from (values (1), (cast(null as integer))) as t(x)";
+    final String nullableBigint =
+        "select x from (values (cast(1 as bigint)), (cast(null as bigint))) as 
t(x)";
+
+    // union
+    sql(notNullInt + " union " + nullableBigint)
+        .columnType("BIGINT");
+    sql(notNullInt + " union " + nullableInt + " union " + nullableBigint)
+        .columnType("BIGINT");
+    sql(nullableInt + " union " + nullableInt + " union " + nullableBigint)
+        .columnType("BIGINT");
+    sql(notNullInt + " union all " + nullableBigint)
+        .columnType("BIGINT");
+    sql(notNullInt + " union all " + nullableInt + " union all " + 
nullableBigint)
+        .columnType("BIGINT");
+    sql(nullableInt + " union all " + nullableInt + " union all " + 
nullableBigint)
+        .columnType("BIGINT");
+
+    // intersect
+    sql(notNullInt + " intersect " + nullableBigint)
+        .columnType("BIGINT NOT NULL");
+    sql(notNullInt + " intersect " + nullableInt + " intersect " + 
nullableBigint)
+        .columnType("BIGINT NOT NULL");
+    sql(nullableInt + " intersect " + nullableInt + " intersect " + 
nullableBigint)
+        .columnType("BIGINT");
+    sql(notNullInt + " intersect all " + nullableBigint)
+        .columnType("BIGINT NOT NULL");
+    sql(notNullInt + " intersect all " + nullableInt + " intersect all " + 
nullableBigint)
+        .columnType("BIGINT NOT NULL");
+    sql(nullableInt + " intersect all " + nullableInt + " intersect all " + 
nullableBigint)
+        .columnType("BIGINT");
+
+    // except
+    sql(notNullInt + " except " + nullableBigint)
+        .columnType("BIGINT NOT NULL");
+    sql(notNullInt + " except " + nullableInt + " except " + nullableBigint)
+        .columnType("BIGINT NOT NULL");
+    sql(nullableInt + " except " + nullableInt + " except " + nullableBigint)
+        .columnType("BIGINT");
+    sql(notNullInt + " except all " + nullableBigint)
+        .columnType("BIGINT NOT NULL");
+    sql(notNullInt + " except all " + nullableInt + " except all " + 
nullableBigint)
+        .columnType("BIGINT NOT NULL");
+    sql(nullableInt + " except all " + nullableInt + " except all " + 
nullableBigint)
+        .columnType("BIGINT");
+  }
+
   /** Test arithmetic expressions with string type arguments. */
   @Test void testArithmeticExpressionsWithStrings() {
     SqlValidatorFixture f = validatorFixture();

Reply via email to