FieldReference.dereferenceStruct, dereferenceList and dereferenceMap type the result as the member's declared type and ignore the container's own nullability, so reading a field of a struct that may be null is typed as though it can never be null.
FieldReference.newRootStructReference(0, N.struct(R.I32, N.FP64)).dereferenceStruct(0).getType();
// I32{nullable=false}
FieldReference.newRootStructReference(0, N.list(R.I32)).dereferenceList(0).getType();
// I32{nullable=false}
FieldReference.newRootStructReference(0, N.map(R.STRING, R.I32))
.dereferenceMap(ExpressionCreator.string(false, "k")).getType();
// I32{nullable=false}
Measured on main at 4bb02974. The derivation is StructFieldFinder, ListIndexFinder and MapKeyFinder.getReferencedType in FieldReference.java.
The spec does not settle it. site/docs/expressions/field_references.md (v0.102.0) gives a struct field reference's type as "Type of field referenced" and states no nullability rule for any kind of reference. The type is never serialized (StructField carries only field and child), so every consumer derives it itself. substrait-go (StructFieldRef.GetType) and substrait-python (type_inference.py) also return the declared member type unchanged.
This matters more since #1317: a nullable struct now keeps its required fields through Calcite instead of having them widened to nullable, so this derivation now decides the type of any reference into one. The open question is whether :core should make the derived type nullable when the container is, and if so, whether the spec should say so first.
FieldReference.dereferenceStruct,dereferenceListanddereferenceMaptype the result as the member's declared type and ignore the container's own nullability, so reading a field of a struct that may be null is typed as though it can never be null.Measured on
mainat4bb02974. The derivation isStructFieldFinder,ListIndexFinderandMapKeyFinder.getReferencedTypeinFieldReference.java.The spec does not settle it.
site/docs/expressions/field_references.md(v0.102.0) gives a struct field reference's type as "Type of field referenced" and states no nullability rule for any kind of reference. The type is never serialized (StructFieldcarries onlyfieldandchild), so every consumer derives it itself. substrait-go (StructFieldRef.GetType) and substrait-python (type_inference.py) also return the declared member type unchanged.This matters more since #1317: a nullable struct now keeps its required fields through Calcite instead of having them widened to nullable, so this derivation now decides the type of any reference into one. The open question is whether
:coreshould make the derived type nullable when the container is, and if so, whether the spec should say so first.