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-11643-a2b405fe22b6657fd4f69eb139e6836cafa3b55f
in repository https://gitbox.apache.org/repos/asf/arrow-rs.git

commit 24c53c7b0803d32575e5bcb302bf78065ded31b6
Author: Chang Yong Lik <[email protected]>
AuthorDate: Sun Oct 18 02:36:03 2026 +0000

    fix(arrow-array): use the field's type id in dense union logical_nulls 
(#11643)
    
    # Which issue does this PR close?
    
    - Closes #11595.
    
    # Rationale for this change
    
    A dense union with a single field whose type id is not 0 reported no
    logical nulls, so `logical_null_count`, `is_null` and `is_not_null` were
    wrong for it.
    
    # What changes are included in this PR?
    
    The single-field path of `UnionArray::logical_nulls` passed the child
    nulls to `gather_nulls` under type id 0. It now passes the field's own
    type id, enumerating `self.fields` the same way `fields_logical_nulls`
    does.
    
    # Are these changes tested?
    
    Yes, `test_dense_union_one_field_nonzero_type_id_logical_nulls`. It
    fails 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-array/src/array/union_array.rs | 46 ++++++++++++++++++++++++++----------
 1 file changed, 34 insertions(+), 12 deletions(-)

diff --git a/arrow-array/src/array/union_array.rs 
b/arrow-array/src/array/union_array.rs
index 17b740c29c..bdac19272e 100644
--- a/arrow-array/src/array/union_array.rs
+++ b/arrow-array/src/array/union_array.rs
@@ -795,18 +795,23 @@ unsafe impl Array for UnionArray {
         };
 
         if fields.len() <= 1 {
-            return self.fields.iter().find_map(|field_opt| {
-                field_opt
-                    .as_ref()
-                    .and_then(|field| field.logical_nulls())
-                    .map(|logical_nulls| {
-                        if self.is_dense() {
-                            self.gather_nulls(vec![(0, logical_nulls)]).into()
-                        } else {
-                            logical_nulls
-                        }
-                    })
-            });
+            return self
+                .fields
+                .iter()
+                .enumerate()
+                .find_map(|(type_id, field_opt)| {
+                    field_opt
+                        .as_ref()
+                        .and_then(|field| field.logical_nulls())
+                        .map(|logical_nulls| {
+                            if self.is_dense() {
+                                self.gather_nulls(vec![(type_id as i8, 
logical_nulls)])
+                                    .into()
+                            } else {
+                                logical_nulls
+                            }
+                        })
+                });
         }
 
         let logical_nulls = self.fields_logical_nulls();
@@ -2050,6 +2055,23 @@ mod tests {
         assert_eq!(expected, array.gather_nulls(array.fields_logical_nulls()));
     }
 
+    #[test]
+    fn test_dense_union_one_field_nonzero_type_id_logical_nulls() {
+        let fields = UnionFields::try_new([5], [Field::new("a", 
DataType::Int32, true)]).unwrap();
+        let child = Arc::new(Int32Array::from(vec![None, Some(1)]));
+        let array = UnionArray::try_new(
+            fields,
+            vec![5, 5, 5].into(),
+            Some(vec![1, 0, 1].into()),
+            vec![child],
+        )
+        .unwrap();
+
+        let expected = NullBuffer::from(vec![true, false, true]);
+        assert_eq!(array.logical_nulls(), Some(expected));
+        assert_eq!(array.logical_null_count(), 1);
+    }
+
     #[test]
     fn test_sparse_union_logical_nulls_mask_all_nulls_skip_one() {
         let fields: UnionFields = [

Reply via email to