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

commit 862b1e065b602400784268df9ded5d0a18710eed
Author: Neil Conway <[email protected]>
AuthorDate: Sat Oct 3 02:58:49 2026 +0000

    fix(arrow-data): fix gaps in ListView equality (#11345)
    
    # Which issue does this PR close?
    
    - N/A
    
    # Rationale for this change
    
    `list_view_equal` had two bugs:
    
    * When both arrays had null rows, it compared each non-null row using
    the size of the LHS only. For example, `[[1, 2], null]` was considered
    equal to `[[1, 2, 3], null]`.
    * Rows that differed only in their null masks were not handled
    correctly: they were either incorrectly considered equal, or the
    comparison resulted in a panic.
    
    # What changes are included in this PR?
    
    * Fix ListView equality bugs
    * Add unit tests
    
    # Are these changes tested?
    
    Yes; existing tests pass, new tests added.
    
    # Are there any significant user-facing changes?
    
    No.
    
    # AI usage
    
    Developed with Claude Code (Opus 5.5).
---
 arrow-data/src/equal/list_view.rs | 23 +++++++++++++++--------
 arrow-data/src/equal/mod.rs       |  3 +++
 arrow/tests/array_equal.rs        | 26 ++++++++++++++++++++++++++
 3 files changed, 44 insertions(+), 8 deletions(-)

diff --git a/arrow-data/src/equal/list_view.rs 
b/arrow-data/src/equal/list_view.rs
index f431e84589..285fbc8a9a 100644
--- a/arrow-data/src/equal/list_view.rs
+++ b/arrow-data/src/equal/list_view.rs
@@ -17,7 +17,7 @@
 
 use crate::ArrayData;
 use crate::data::count_nulls;
-use crate::equal::equal_values;
+use crate::equal::equal_range;
 use arrow_buffer::ArrowNativeType;
 use num_integer::Integer;
 
@@ -66,8 +66,7 @@ pub(super) fn list_view_equal<T: ArrowNativeType + Integer>(
             let rhs_offset = rhs_offset.to_usize().unwrap();
             let size = size.to_usize().unwrap();
 
-            // Check if offsets are valid for the given range
-            if !equal_values(lhs_data, rhs_data, lhs_offset, rhs_offset, size) 
{
+            if !equal_range(lhs_data, rhs_data, lhs_offset, rhs_offset, size) {
                 return false;
             }
         }
@@ -77,10 +76,10 @@ pub(super) fn list_view_equal<T: ArrowNativeType + Integer>(
         let rhs_nulls = rhs.nulls().unwrap().slice(rhs_start, len);
 
         // Check values for equality, with null checking
-        for (index, ((&lhs_offset, &rhs_offset), &size)) in lhs_range_offsets
+        for (index, ((&lhs_offset, &rhs_offset), (&lhs_size, &rhs_size))) in 
lhs_range_offsets
             .iter()
             .zip(rhs_range_offsets)
-            .zip(lhs_range_sizes)
+            .zip(lhs_range_sizes.iter().zip(rhs_range_sizes))
             .enumerate()
         {
             let lhs_is_null = lhs_nulls.is_null(index);
@@ -90,12 +89,20 @@ pub(super) fn list_view_equal<T: ArrowNativeType + Integer>(
                 return false;
             }
 
+            // Null rows are equal whatever their sizes and values
+            if lhs_is_null {
+                continue;
+            }
+
+            if lhs_size != rhs_size {
+                return false;
+            }
+
             let lhs_offset = lhs_offset.to_usize().unwrap();
             let rhs_offset = rhs_offset.to_usize().unwrap();
-            let size = size.to_usize().unwrap();
+            let size = lhs_size.to_usize().unwrap();
 
-            // Check if values match in the range
-            if !lhs_is_null && !equal_values(lhs_data, rhs_data, lhs_offset, 
rhs_offset, size) {
+            if !equal_range(lhs_data, rhs_data, lhs_offset, rhs_offset, size) {
                 return false;
             }
         }
diff --git a/arrow-data/src/equal/mod.rs b/arrow-data/src/equal/mod.rs
index b241b8cf66..011f591d10 100644
--- a/arrow-data/src/equal/mod.rs
+++ b/arrow-data/src/equal/mod.rs
@@ -58,6 +58,9 @@ use variable_size::variable_sized_equal;
 
 /// Compares the values of two [ArrayData] starting at `lhs_start` and 
`rhs_start` respectively
 /// for `len` slots.
+///
+/// Assumes the null masks of the two ranges are already known to be equal. To 
compare
+/// ranges of child data, use [`equal_range`], which also compares the null 
masks.
 #[inline]
 fn equal_values(
     lhs: &ArrayData,
diff --git a/arrow/tests/array_equal.rs b/arrow/tests/array_equal.rs
index c108cc9670..cc45526a02 100644
--- a/arrow/tests/array_equal.rs
+++ b/arrow/tests/array_equal.rs
@@ -822,6 +822,22 @@ fn test_test_list_view_array<T: OffsetSizeTrait>() {
     test_equal(&a, &b, true);
 }
 
+fn test_list_view_sizes_and_child_nulls<T: OffsetSizeTrait>() {
+    // Same first elements but different sizes, with a null row
+    let a = create_list_view_array::<T, _, _>([Some(vec![Some(1), Some(2)]), 
None]);
+    let b = create_list_view_array::<T, _, _>([Some(vec![Some(1), Some(2), 
Some(3)]), None]);
+    test_equal(&a, &b, false);
+
+    // Child values that differ only in their null masks, without and with a 
null row
+    let a = create_list_view_array::<T, _, _>([Some(vec![Some(1), Some(0)])]);
+    let b = create_list_view_array::<T, _, _>([Some(vec![Some(1), None])]);
+    test_equal(&a, &b, false);
+
+    let a = create_list_view_array::<T, _, _>([None, Some(vec![Some(1), 
Some(0)])]);
+    let b = create_list_view_array::<T, _, _>([None, Some(vec![Some(1), 
None])]);
+    test_equal(&a, &b, false);
+}
+
 // Special test for List<ListView<i32>>.
 // This tests the equal_ranges kernel
 fn test_sliced_list_of_list_view<T: OffsetSizeTrait>() {
@@ -871,6 +887,16 @@ fn test_large_list_view_array() {
     test_test_list_view_array::<i64>();
 }
 
+#[test]
+fn test_list_view_array_sizes_and_child_nulls() {
+    test_list_view_sizes_and_child_nulls::<i32>();
+}
+
+#[test]
+fn test_large_list_view_array_sizes_and_child_nulls() {
+    test_list_view_sizes_and_child_nulls::<i64>();
+}
+
 #[test]
 fn test_nested_list_view_array() {
     test_sliced_list_of_list_view::<i32>();

Reply via email to