TypeConverter.toSubstrait(RelDataType, List<String>) dispatches on switch (type.getSqlTypeName()), and Calcite does not guarantee that accessor is non-null. RelDataTypeImpl's base implementation is return castNonNull(null); — a no-op cast at runtime — and RelCrossType, which RelDataTypeFactory.createJoinType(...) returns, does not override it. So the switch throws before the unsupported-type arm can report anything:
RelDataType cross = TYPE_FACTORY.createJoinType(structA, structB);
// cross.getSqlTypeName() == null, cross.toString() == CrossType(RecordType(INTEGER a), RecordType(VARCHAR b))
TypeConverter.DEFAULT.toSubstrait(cross)
-> NullPointerException: Cannot invoke "org.apache.calcite.sql.type.SqlTypeName.ordinal()" because the return value of "org.apache.calcite.rel.type.RelDataType.getSqlTypeName()" is null
createJoinType is called nowhere in this repo and CROSS JOIN does not produce a RelCrossType — Calcite derives join row types as RelRecordType — so this needs a caller-supplied type through the public entry point, the same reachability class #1244 described for its multiset route. A sweep of the RelDataType implementations on the isthmus test classpath found RelCrossType the only concrete type that inherits the null-returning accessor.
The cost is only diagnostics, but it is the same cost #1244 was about: toSubstrait(RelDataType) documents @throws UnsupportedOperationException, and a caller who follows that still sees an unrelated unchecked exception escape. A null check before the switch, routing into the same default: message, closes it — and the message can name the type, since toString() works fine.
Measured on main at 944b921.
TypeConverter.toSubstrait(RelDataType, List<String>)dispatches onswitch (type.getSqlTypeName()), and Calcite does not guarantee that accessor is non-null.RelDataTypeImpl's base implementation isreturn castNonNull(null);— a no-op cast at runtime — andRelCrossType, whichRelDataTypeFactory.createJoinType(...)returns, does not override it. So the switch throws before the unsupported-type arm can report anything:createJoinTypeis called nowhere in this repo andCROSS JOINdoes not produce aRelCrossType— Calcite derives join row types asRelRecordType— so this needs a caller-supplied type through the public entry point, the same reachability class #1244 described for its multiset route. A sweep of theRelDataTypeimplementations on the isthmus test classpath foundRelCrossTypethe only concrete type that inherits the null-returning accessor.The cost is only diagnostics, but it is the same cost #1244 was about:
toSubstrait(RelDataType)documents@throws UnsupportedOperationException, and a caller who follows that still sees an unrelated unchecked exception escape. A null check before the switch, routing into the samedefault:message, closes it — and the message can name the type, sincetoString()works fine.Measured on
mainat 944b921.