Vladimir Steshin created CALCITE-7062:
-----------------------------------------

             Summary: Row type of UNION may ignore a column's nullability
                 Key: CALCITE-7062
                 URL: https://issues.apache.org/jira/browse/CALCITE-7062
             Project: Calcite
          Issue Type: Bug
    Affects Versions: 1.40.0, 1.39.0
            Reporter: Vladimir Steshin


I'm not sure this is a problem. Is that ok? I found it by one of our type 
casting tests.

Consider:

 
{code:java}
package org.apache.calcite.test;

class ServerTest {
   @Test void testNullableCoercionInUnion() throws Exception {
     try (Connection c = connect(); Statement s = c.createStatement()) {
       s.execute("create table t1 (i smallint not null)");
       s.execute("create table t2 (i bigint)");

       s.executeUpdate("insert into t1 values (1)");
       s.executeUpdate("insert into t2 values (10), (null)");

       try (ResultSet r = s.executeQuery("select i from t1 union all select i 
from t2")) {
         assertTrue(r.next());

         assertThat(r.getLong("i"), is(1L));

         assertTrue(r.next());
         assertThat(r.getLong("i"), is(10L));

         assertTrue(r.next());
         // The result has a null value. It is ok.
         assertNull(r.getObject("i"));
       }

       // The plan.
       try (ResultSet r = s.executeQuery("explain plan for select i from t1 
union all select i from t2")) {
         assertTrue(r.next());

         String plan = r.getString(1);

         // Fails here. It actually casts to a BIGINT NOT NULL whereas the 
actual resuls contains a NULL.
         assertTrue(plan.contains("[CAST($t0):BIGINT]"));
       }
     }
   }
} {code}
 

The least restrictive type is _nullable BIGINT._ Looks ok. However, 
_StandardConvertletTable#convertCast(...)_ produces a _CAST_ to a {_}NOT 
NULLABLE{_}.

Regarding my research, the nullability is lost somewhere around  
{code:java}
class AbstractTypeCoercion

RelDataType syncAttributes(
    RelDataType fromType,
    RelDataType toType) {
  RelDataType syncedType = toType;
  if (fromType != null) {
    syncedType = factory.createTypeWithNullability(syncedType, 
fromType.isNullable());
   ...
}{code}
 

It doesn't take in account {_}toType.isNullable(){_}. 

And also in 

 
{code:java}
class SqlCastFunction

private static RelDataType createTypeWithNullabilityFromExpr(RelDataTypeFactory 
typeFactory,
    RelDataType expressionType, RelDataType targetType, boolean safe) {
  boolean isNullable = expressionType.isNullable() || safe;
...

} {code}
 

The same: _targetType.isNullable()_ is ignored.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to