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
Is your feature request related to a problem or challenge? Please describe what you are trying to do.
RowConverter accepts types on encode whose decode output type differs from the encode input type: dictionaries (and dictionary-containing nested types) are flattened to their value type on decode via the internal corrected_type step. For example:
Dictionary(Int32, Utf8) decodes as Utf8
ListView(Dictionary(Int32, Utf8)) decodes as ListView(Utf8)
List(Dictionary(Int32, Utf8)) decodes as List(Utf8)
This mapping is arrow-row internal knowledge with no public API to query it. Downstreams that need to round-trip arrays through the row format (e.g. DataFusion's group-by, which stores group keys as rows and re-emits them as arrays) must mirror decoder internals by hand to know which output types to expect and reconstruct.
This has produced two real bugs of the same class:
apache/datafusion#23523 review — DataFusion accepted ListView<Dictionary> for its rows-backed group storage but missed the reconstruction arm for view-lists, silently emitting ListView(Utf8) where the schema declared ListView(Dictionary(Int32, Utf8)).
Both stem from the same gap: each downstream has to re-derive, per container type, "what type will decode hand me back?" — and every missed container is a bug.
Describe the solution you'd like
Expose the mapping as a public API, e.g.:
/// Returns the data type produced by `RowConverter::convert_rows` for/// an input column of `data_type` — identical to the input except that/// dictionary-encoded values (at any nesting level) are replaced by/// their value type.pubfndecoded_type(data_type:&DataType) -> DataType
(placement flexible: free function in arrow_row, method on SortField, or RowConverter::decoded_types(&self) -> Vec<DataType>).
Together with a property test in arrow-row asserting the contract for every supported type shape:
The invariant test is arguably the bigger win: it would have caught #10413 automatically (panic = contract violation), and prevents any future decoder from silently diverging from the others the way decode_fixed_size_list did.
With this API, downstream code reduces to a mechanical rule: decoded_type(t) == t → array round-trips as-is; otherwise recurse over the structure and re-encode exactly where the two types differ.
Describe alternatives you've considered
A boolean round_trips_losslessly(&DataType) -> bool — simpler, but only says whether the type changes, not what comes back, so downstreams still can't derive reconstruction mechanically.
Making decode preserve dictionary types — would eliminate the mapping entirely, but re-encoding dictionaries on decode is expensive, changes long-standing semantics, and the flattened output is often what callers want.
Status quo — downstreams mirror decoder internals; two bugs in two months suggests this doesn't scale.
Is your feature request related to a problem or challenge? Please describe what you are trying to do.
RowConverteraccepts types on encode whose decode output type differs from the encode input type: dictionaries (and dictionary-containing nested types) are flattened to their value type on decode via the internalcorrected_typestep. For example:Dictionary(Int32, Utf8)decodes asUtf8ListView(Dictionary(Int32, Utf8))decodes asListView(Utf8)List(Dictionary(Int32, Utf8))decodes asList(Utf8)This mapping is arrow-row internal knowledge with no public API to query it. Downstreams that need to round-trip arrays through the row format (e.g. DataFusion's group-by, which stores group keys as rows and re-emits them as arrays) must mirror decoder internals by hand to know which output types to expect and reconstruct.
This has produced two real bugs of the same class:
decode_fixed_size_listpanics onFixedSizeList<Dictionary>(missing corrected_type) #10413 —decode_fixed_size_listdiverged from the other list-like decoders by not applying the correction, panicking onFixedSizeList<Dictionary>(fixed by arrow-row: Fix decode_fixed_size_list to apply the corrected_type step for dictionary children #10414).ListView<Dictionary>for its rows-backed group storage but missed the reconstruction arm for view-lists, silently emittingListView(Utf8)where the schema declaredListView(Dictionary(Int32, Utf8)).Both stem from the same gap: each downstream has to re-derive, per container type, "what type will decode hand me back?" — and every missed container is a bug.
Describe the solution you'd like
Expose the mapping as a public API, e.g.:
(placement flexible: free function in
arrow_row, method onSortField, orRowConverter::decoded_types(&self) -> Vec<DataType>).Together with a property test in arrow-row asserting the contract for every supported type shape:
The invariant test is arguably the bigger win: it would have caught #10413 automatically (panic = contract violation), and prevents any future decoder from silently diverging from the others the way
decode_fixed_size_listdid.With this API, downstream code reduces to a mechanical rule:
decoded_type(t) == t→ array round-trips as-is; otherwise recurse over the structure and re-encode exactly where the two types differ.Describe alternatives you've considered
round_trips_losslessly(&DataType) -> bool— simpler, but only says whether the type changes, not what comes back, so downstreams still can't derive reconstruction mechanically.Additional context
decode_fixed_size_listpanics onFixedSizeList<Dictionary>(missing corrected_type) #10413 / arrow-row: Fix decode_fixed_size_list to apply the corrected_type step for dictionary children #10414 (thedecode_fixed_size_listdivergence)Happy to implement this (API + property test) if the direction sounds good.