Skip to content

substrait: add an extension point for per-field Arrow metadata on user-defined types - #176

Closed
moraistejerina wants to merge 1 commit into
branch-54from
jorge.morais/json-udt-field-metadata
Closed

moraistejerina wants to merge 1 commit into
branch-54from
jorge.morais/json-udt-field-metadata

Conversation

@moraistejerina

@moraistejerina moraistejerina commented Sep 4, 2026 •

Copy link
Copy Markdown

Summary

  • consume_user_defined_type maps a Substrait UDT straight to a bare DataType, so a consumer has no way to mark the resulting Field for a downstream reader to recover semantic type lost in that mapping (e.g. the json UDT becomes plain Utf8, indistinguishable from a real string column).
  • Adds consume_user_defined_type_metadata, a second, additive extension point on SubstraitConsumer defaulting to Ok(None), and applies its result to the Field built in from_substrait_struct_type.
  • Existing consumers are unaffected: the default keeps today's behavior exactly (no metadata attached, same as before this change).

Motivation

In dd-source, retriever streams query results over Arrow Flight through DataFusion/Substrait. JSON-typed columns come back as plain string instead of json, because Arrow has no native JSON type and nothing marks the column as semantically JSON. The synchronous (non-streaming) path avoids this by reconciling against the query planner's own richer type, but that reconciliation isn't available to a pure Arrow Flight consumer. This extension point lets a consumer (in a follow-up dd-source change) attach metadata analogous to what the existing Trino downstream already does for its own Arrow output, so a Flight client can recover the original type.

…strait types

Arrow cannot represent some Substrait types natively. Our "json",
"group", and "inet" user-defined types are all mapped to plain Utf8,
which is then indistinguishable from a real string column once it
reaches a consumer that only sees the Arrow schema (e.g. over Arrow
Flight, with no access to the original plan). Retriever's streaming
shadow variant was comparing against control_group results without
this distinction, producing false-positive column_types_differ diffs.

Add SubstraitConsumer::consume_type_metadata, an additive extension
point defaulting to Ok(None), that lets a consumer attach Arrow field
metadata for a Substrait type it recognizes. It takes the whole Type
rather than just a UserDefined variant, so a consumer can also key off
a type nested inside a list, map, or struct.

Apply it at a single point, substrait_type_to_field, that every
Arrow Field built from a Substrait type now goes through: the two
public field_from_substrait_type* entry points, the list element
field, the map key/value fields, the struct child fields, and the
EmptyMap literal. This keeps metadata attached for a type nested
inside a list, map, or struct without every call site having to ask
for it, and avoids a signature change to from_substrait_type, which
keeps returning a bare DataType. A Dictionary-variation map is the one
case that still can't carry metadata, since DataType::Dictionary has
no field to hold it.

Existing consumers are unaffected: the default keeps today's behavior
exactly, and no public signature changes.

Submitted upstream as apache#25015; this cherry-picks onto
branch-54 ahead of that landing so dd-datafusion can consume it now.
@moraistejerina
moraistejerina force-pushed the jorge.morais/json-udt-field-metadata branch from 8a4fd34 to a1d1741 Compare September 7, 2026 12:52
@moraistejerina

Copy link
Copy Markdown
Author

Superseded by #178, a proper cherry-pick (with -x) of the now-merged apache#25015, per the confluence "upstream first" process. This branch predated the merge and manually recreated the diff.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant