You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
infer_expression_type takes IfThen and SwitchExpression nullability from the first branch only #277
infer_expression_type derives an IfThen's type from ifs[0].then alone (src/substrait/type_inference.py:426) and a SwitchExpression's from switch_expression.ifs[0].then (type_inference.py:433). The remaining then branches and the else branch are never consulted, so when the branches disagree on nullability the answer depends on branch order — and in one order it is unsound, declaring REQUIRED for an expression that can evaluate to null.
A COALESCE-style rewrite over a left join is exactly this shape: one branch is a required column, the other a null-padded one.
Reproducer
importsubstrait.algebra_pb2asstalg, substrait.type_pb2assttfromsubstrait.type_inferenceimportinfer_expression_typeR, N=stt.Type.NULLABILITY_REQUIRED, stt.Type.NULLABILITY_NULLABLENAMES= {R: "REQUIRED", N: "NULLABLE"}
# An input row where field 0 is required and field 1 is nullable.scope=stt.Type.Struct(
types=[stt.Type(i64=stt.Type.I64(nullability=R)), stt.Type(i64=stt.Type.I64(nullability=N))],
nullability=R,
)
defref(i):
returnstalg.Expression(selection=stalg.Expression.FieldReference(
root_reference=stalg.Expression.FieldReference.RootReference(),
direct_reference=stalg.Expression.ReferenceSegment(
struct_field=stalg.Expression.ReferenceSegment.StructField(field=i))))
defif_then(then, else_):
true=stalg.Expression(literal=stalg.Expression.Literal(boolean=True))
returnstalg.Expression(if_then=stalg.Expression.IfThen(
ifs=[stalg.Expression.IfThen.IfClause(**{"if": true, "then": then})],
**{"else": else_}))
forlabel, exprin (
("then=f0 REQUIRED, else=f1 NULLABLE", if_then(ref(0), ref(1))),
("then=f1 NULLABLE, else=f0 REQUIRED", if_then(ref(1), ref(0))),
):
t=infer_expression_type(expr, scope)
print(f"{label}: {NAMES[getattr(t, t.WhichOneof('kind')).nullability]}")
The output nullability should be nullable if any branch — including else — is nullable. This is the same shape as the Expand switching-field gap in #269 and the grouping-key gap in #268.
🤖 Filed with AI assistance as a follow-up to review of #272.
Activity