This is an automated email from the ASF dual-hosted git repository. github-merge-queue[bot] pushed a commit to branch gh-readonly-queue/main/pr-11644-8a9f96070f405e5a5f85a63cdc4a3d9c32a20086 in repository https://gitbox.apache.org/repos/asf/arrow-rs.git
commit 0a2b0498c96121812686d8b626591a929c87ee06 Author: Chang Yong Lik <[email protected]> AuthorDate: Sun Oct 18 02:26:01 2026 +0000 fix(arrow-row): map type ids to field indexes when decoding dense union offsets (#11644) # Which issue does this PR close? - Closes #11599. # Rationale for this change `RowConverter::convert_rows` panicked with an index out of bounds on a dense union whose type ids are not below the number of fields, for example a single field with type id 5, although `convert_columns` accepted it. # What changes are included in this PR? When building the dense offsets, the decoder now looks up the field index with `type_id_to_field_idx`, which the same function already builds and uses to group the rows, instead of indexing its per-field counters by type id. # Are these changes tested? Yes, `test_dense_union_type_ids_not_below_field_count` round-trips a dense union with type ids 2 and 7. It panics without the fix. # Are there any significant user-facing changes? No. AI use: I used Claude Code to find the cause and write the fix and test. I reviewed the change and checked that the test fails without the fix. --- arrow-row/src/lib.rs | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/arrow-row/src/lib.rs b/arrow-row/src/lib.rs index fa0d710e6c..fa2ed015c6 100644 --- a/arrow-row/src/lib.rs +++ b/arrow-row/src/lib.rs @@ -2475,7 +2475,7 @@ unsafe fn decode_column( if let Some(ref mut offsets_vec) = offsets { let mut count = vec![0i32; converters.len()]; for type_id in &type_ids { - let field_idx = *type_id as usize; + let field_idx = type_id_to_field_idx[*type_id as usize]; offsets_vec.push(count[field_idx]); count[field_idx] += 1; @@ -5718,6 +5718,39 @@ mod tests { } } + #[test] + fn test_dense_union_type_ids_not_below_field_count() { + // [1, "a", 3, "b"] with type ids that are not field indexes + let union_fields = [ + (2, Arc::new(Field::new("int", DataType::Int32, false))), + (7, Arc::new(Field::new("str", DataType::Utf8, false))), + ] + .into_iter() + .collect(); + + let union_array: ArrayRef = Arc::new( + UnionArray::try_new( + union_fields, + vec![2, 7, 2, 7].into(), + Some(vec![0, 0, 1, 1].into()), + vec![ + Arc::new(Int32Array::from(vec![1, 3])) as ArrayRef, + Arc::new(StringArray::from(vec!["a", "b"])), + ], + ) + .unwrap(), + ); + + let converter = + RowConverter::new(vec![SortField::new(union_array.data_type().clone())]).unwrap(); + let rows = converter + .convert_columns(std::slice::from_ref(&union_array)) + .unwrap(); + let back = converter.convert_rows(&rows).unwrap(); + + assert_eq!(back[0].to_data(), union_array.to_data()); + } + #[test] fn test_dense_union_with_nulls() { // create a dense union with Int32 (type_id = 0) and Utf8 (type_id = 1)
