Skip to content

Commit

Permalink
[CALCITE-6741] The type of a comparison is nullable when either opera…
Browse files Browse the repository at this point in the history
…nd is nullable

Signed-off-by: Mihai Budiu <mbudiu@feldera.com>
  • Loading branch information
mihaibudiu committed Dec 20, 2024
1 parent 0badcce commit 90ba0a8
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,8 @@ private RelDataType getTightestCommonTypeOrThrow(
return null;
}

boolean anyNullable = type1.isNullable() || type2.isNullable();

// this prevents the conversion between JavaType and normal RelDataType,
// as well as between JavaType and JavaType.
if (type1 instanceof RelDataTypeFactoryImpl.JavaType
Expand All @@ -516,35 +518,36 @@ private RelDataType getTightestCommonTypeOrThrow(

// DATETIME < CHARACTER -> DATETIME
if (SqlTypeUtil.isCharacter(type1) && SqlTypeUtil.isDatetime(type2)) {
return factory.createTypeWithNullability(type2, type1.isNullable());
return factory.createTypeWithNullability(type2, anyNullable);
}

if (SqlTypeUtil.isDatetime(type1) && SqlTypeUtil.isCharacter(type2)) {
return factory.createTypeWithNullability(type1, type2.isNullable());
return factory.createTypeWithNullability(type1, anyNullable);
}

// DATE < TIMESTAMP -> TIMESTAMP
if (SqlTypeUtil.isDate(type1) && SqlTypeUtil.isTimestamp(type2)) {
return factory.createTypeWithNullability(type2, type1.isNullable());
return factory.createTypeWithNullability(type2, anyNullable);
}

if (SqlTypeUtil.isDate(type2) && SqlTypeUtil.isTimestamp(type1)) {
return factory.createTypeWithNullability(type1, type2.isNullable());
return factory.createTypeWithNullability(type1, anyNullable);
}

if (SqlTypeUtil.isString(type1) && typeName2 == SqlTypeName.NULL) {
return factory.createTypeWithNullability(type1, type2.isNullable());
return factory.createTypeWithNullability(type1, true);
}

if (typeName1 == SqlTypeName.NULL && SqlTypeUtil.isString(type2)) {
return factory.createTypeWithNullability(type2, type1.isNullable());
return factory.createTypeWithNullability(type2, true);
}

if (SqlTypeUtil.isDecimal(type1) && SqlTypeUtil.isCharacter(type2)
|| SqlTypeUtil.isCharacter(type1) && SqlTypeUtil.isDecimal(type2)) {
// There is no proper DECIMAL type for VARCHAR, using max precision/scale DECIMAL
// as the best we can do.
return SqlTypeUtil.getMaxPrecisionScaleDecimal(factory);
return factory.createTypeWithNullability(
SqlTypeUtil.getMaxPrecisionScaleDecimal(factory), anyNullable);
}

// Keep sync with MS-SQL:
Expand All @@ -562,13 +565,13 @@ private RelDataType getTightestCommonTypeOrThrow(
if (SqlTypeUtil.isString(type1) && SqlTypeUtil.isString(type2)) {
// Return the string with the larger precision
if (type1.getPrecision() == RelDataType.PRECISION_NOT_SPECIFIED) {
return factory.createTypeWithNullability(type1, type2.isNullable());
return factory.createTypeWithNullability(type1, anyNullable);
} else if (type2.getPrecision() == RelDataType.PRECISION_NOT_SPECIFIED) {
return factory.createTypeWithNullability(type2, type1.isNullable());
return factory.createTypeWithNullability(type2, anyNullable);
} else if (type1.getPrecision() > type2.getPrecision()) {
return factory.createTypeWithNullability(type1, type2.isNullable());
return factory.createTypeWithNullability(type1, anyNullable);
} else {
return factory.createTypeWithNullability(type2, type1.isNullable());
return factory.createTypeWithNullability(type2, anyNullable);
}
}

Expand All @@ -577,40 +580,40 @@ private RelDataType getTightestCommonTypeOrThrow(
if (SqlTypeUtil.isTimestamp(type1)) {
return null;
}
return factory.createTypeWithNullability(type1, type2.isNullable());
return factory.createTypeWithNullability(type1, anyNullable);
}

if (SqlTypeUtil.isCharacter(type1) && SqlTypeUtil.isAtomic(type2)) {
if (SqlTypeUtil.isTimestamp(type2)) {
return null;
}
return factory.createTypeWithNullability(type2, type1.isNullable());
return factory.createTypeWithNullability(type2, anyNullable);
}

if (validator.config().conformance().allowLenientCoercion()) {
if (SqlTypeUtil.isString(type1) && SqlTypeUtil.isArray(type2)) {
return factory.createTypeWithNullability(type2, type1.isNullable());
return factory.createTypeWithNullability(type2, anyNullable);
}

if (SqlTypeUtil.isString(type2) && SqlTypeUtil.isArray(type1)) {
return factory.createTypeWithNullability(type1, type2.isNullable());
return factory.createTypeWithNullability(type1, anyNullable);
}
}

if (SqlTypeUtil.isApproximateNumeric(type1) && SqlTypeUtil.isApproximateNumeric(type2)) {
if (type1.getPrecision() > type2.getPrecision()) {
return factory.createTypeWithNullability(type1, type2.isNullable());
return factory.createTypeWithNullability(type1, anyNullable);
} else {
return factory.createTypeWithNullability(type2, type1.isNullable());
return factory.createTypeWithNullability(type2, anyNullable);
}
}

if (SqlTypeUtil.isApproximateNumeric(type1) && SqlTypeUtil.isExactNumeric(type2)) {
return factory.createTypeWithNullability(type1, type2.isNullable());
return factory.createTypeWithNullability(type1, anyNullable);
}

if (SqlTypeUtil.isApproximateNumeric(type2) && SqlTypeUtil.isExactNumeric(type1)) {
return factory.createTypeWithNullability(type2, type1.isNullable());
return factory.createTypeWithNullability(type2, anyNullable);
}

if (SqlTypeUtil.isExactNumeric(type1) && SqlTypeUtil.isExactNumeric(type2)) {
Expand All @@ -622,12 +625,12 @@ private RelDataType getTightestCommonTypeOrThrow(
Math.max(type1.getPrecision() - type1.getScale(),
type2.getPrecision() - type2.getScale()) + maxScale,
maxScale);
return factory.createTypeWithNullability(result, type1.isNullable() || type2.isNullable());
return factory.createTypeWithNullability(result, anyNullable);
}
if (type1.getPrecision() > type2.getPrecision()) {
return factory.createTypeWithNullability(type1, type2.isNullable());
return factory.createTypeWithNullability(type1, anyNullable);
} else {
return factory.createTypeWithNullability(type2, type1.isNullable());
return factory.createTypeWithNullability(type2, anyNullable);
}
}

Expand All @@ -643,8 +646,7 @@ private RelDataType getTightestCommonTypeOrThrow(
}
// The only maxCardinality that seems to be supported is -1, i.e., unlimited.
RelDataType resultType = factory.createArrayType(type, -1);
return factory.createTypeWithNullability(
resultType, type1.isNullable() || type2.isNullable());
return factory.createTypeWithNullability(resultType, anyNullable);
}

if (typeName1 == SqlTypeName.MAP) {
Expand All @@ -664,8 +666,7 @@ private RelDataType getTightestCommonTypeOrThrow(
return null;
}
RelDataType resultType = factory.createMapType(keyType, valueType);
return factory.createTypeWithNullability(
resultType, type1.isNullable() || type2.isNullable());
return factory.createTypeWithNullability(resultType, anyNullable);
}

if (typeName1 == SqlTypeName.ROW) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4480,7 +4480,7 @@ private void checkNegWindow(String s, String msg) {
expr("(1,2) in ((1,2), (3,4))")
.columnType("BOOLEAN NOT NULL");
expr("'medium' in (cast(null as varchar(10)), 'bc')")
.columnType("BOOLEAN NOT NULL");
.columnType("BOOLEAN");

// nullability depends on nullability of both sides
sql("select empno in (1, 2) from emp")
Expand Down

0 comments on commit 90ba0a8

Please sign in to comment.