From cbcc72444c08d4b6839999f6b3093f32bd7907a9 Mon Sep 17 00:00:00 2001 From: Niels Pardon Date: Thu, 16 Jul 2026 14:58:25 +0200 Subject: [PATCH] fix: read SetRel.common in set-relation schema inference infer_rel_schema's set branch read rel.fetch.common instead of rel.set.common, so a SetRel carrying a RelCommon.Emit output_mapping had its column remapping silently dropped: on a set-typed Rel, rel.fetch is a default-empty FetchRel whose common has no emit_kind, which the emit tail treats as "direct" and returns the first input's schema verbatim. Not reachable through the library's own builders (only select() emits an output_mapping, and on a ProjectRel); this affects externally-authored or deserialized plans passed to inference. --- src/substrait/type_inference.py | 2 +- tests/test_type_inference.py | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/substrait/type_inference.py b/src/substrait/type_inference.py index a564cd37..24711f12 100644 --- a/src/substrait/type_inference.py +++ b/src/substrait/type_inference.py @@ -429,7 +429,7 @@ def infer_rel_schema(rel: stalg.Rel) -> stt.Type.Struct: (common, struct) = (rel.project.common, raw_schema) elif rel_type == "set": - (common, struct) = (rel.fetch.common, infer_rel_schema(rel.set.inputs[0])) + (common, struct) = (rel.set.common, infer_rel_schema(rel.set.inputs[0])) elif rel_type == "cross": left_schema = infer_rel_schema(rel.cross.left) right_schema = infer_rel_schema(rel.cross.right) diff --git a/tests/test_type_inference.py b/tests/test_type_inference.py index af61903b..e45b3e7a 100644 --- a/tests/test_type_inference.py +++ b/tests/test_type_inference.py @@ -66,6 +66,27 @@ def test_inference_project_emit(): assert infer_rel_schema(rel) == expected +def test_inference_set_emit(): + # A SetRel's own RelCommon.Emit must drive the output schema; regression for + # the branch reading rel.fetch.common instead of rel.set.common (issue #217). + rel = stalg.Rel( + set=stalg.SetRel( + inputs=[read_rel, read_rel], + op=stalg.SetRel.SET_OP_UNION_ALL, + common=stalg.RelCommon(emit=stalg.RelCommon.Emit(output_mapping=[2, 0])), + ) + ) + + expected = stt.Type.Struct( + types=[ + stt.Type(fp32=stt.Type.FP32(nullability=stt.Type.NULLABILITY_NULLABLE)), + stt.Type(i64=stt.Type.I64(nullability=stt.Type.NULLABILITY_REQUIRED)), + ] + ) + + assert infer_rel_schema(rel) == expected + + def test_inference_project_literal(): rel = stalg.Rel( project=stalg.ProjectRel(