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