This is an automated email from the ASF dual-hosted git repository.

alamb pushed a commit to branch 57_maintenance
in repository https://gitbox.apache.org/repos/asf/arrow-rs.git


The following commit(s) were added to refs/heads/57_maintenance by this push:
     new 9e45aaaa52 [57_maintenance] fix(arrow-data): check view nulls at the 
right index in `ArrayData` equality (#11811)
9e45aaaa52 is described below

commit 9e45aaaa529d7000fc01e78953b05580c7f99997
Author: Jeffrey Vo <[email protected]>
AuthorDate: Fri Oct 23 00:19:28 2026 +0900

    [57_maintenance] fix(arrow-data): check view nulls at the right index in 
`ArrayData` equality (#11811)
    
    - Backport https://github.com/apache/arrow-rs/pull/11642
    - For inclusion in https://github.com/apache/arrow-rs/issues/11666
    
    Cherry-pick of
    
https://github.com/apache/arrow-rs/commit/c0970925a2bb29efe2f855305a76986959171685
    
    Co-authored-by: ump45nose <[email protected]>
    Co-authored-by: ump45nose <[email protected]>
---
 arrow-data/src/equal/byte_view.rs |  3 ++-
 arrow/tests/array_equal.rs        | 57 +++++++++++++++++++++++++++++++++------
 2 files changed, 51 insertions(+), 9 deletions(-)

diff --git a/arrow-data/src/equal/byte_view.rs 
b/arrow-data/src/equal/byte_view.rs
index 7c6001e9a2..248328bc21 100644
--- a/arrow-data/src/equal/byte_view.rs
+++ b/arrow-data/src/equal/byte_view.rs
@@ -32,7 +32,8 @@ pub(super) fn byte_view_equal(
     for (idx, (l, r)) in lhs_views.iter().zip(rhs_views).enumerate() {
         // Only checking one null mask here because by the time the control 
flow reaches
         // this point, the equality of the two masks would have already been 
verified.
-        if lhs.is_null(idx) {
+        // `idx` counts from `lhs_start`, so offset it to index the null mask.
+        if lhs.is_null(lhs_start + idx) {
             continue;
         }
 
diff --git a/arrow/tests/array_equal.rs b/arrow/tests/array_equal.rs
index c21096c6dc..269a1b238e 100644
--- a/arrow/tests/array_equal.rs
+++ b/arrow/tests/array_equal.rs
@@ -16,20 +16,18 @@
 // under the License.
 
 use arrow::array::{
-    Array, ArrayRef, BooleanArray, Decimal128Array, FixedSizeBinaryArray, 
FixedSizeBinaryBuilder,
-    FixedSizeListBuilder, GenericBinaryArray, GenericStringArray, Int32Array, 
Int32Builder,
-    Int64Builder, ListArray, ListBuilder, NullArray, OffsetSizeTrait, 
StringArray,
-    StringDictionaryBuilder, StructArray, UnionBuilder, make_array,
+    Array, ArrayRef, BinaryViewArray, BooleanArray, Decimal128Array, 
DictionaryArray,
+    FixedSizeBinaryArray, FixedSizeBinaryBuilder, FixedSizeListArray, 
FixedSizeListBuilder,
+    GenericBinaryArray, GenericListViewArray, GenericStringArray, Int32Array, 
Int32Builder,
+    Int64Builder, ListArray, ListBuilder, NullArray, OffsetSizeTrait, 
PrimitiveArray, StringArray,
+    StringDictionaryBuilder, StringViewArray, StructArray, UnionBuilder, 
make_array,
 };
 use arrow::datatypes::{Int16Type, Int32Type};
 use arrow_array::builder::{
     GenericListViewBuilder, StringBuilder, StringViewBuilder, StructBuilder,
 };
 use arrow_array::cast::AsArray;
-use arrow_array::{
-    DictionaryArray, FixedSizeListArray, GenericListViewArray, PrimitiveArray, 
StringViewArray,
-};
-use arrow_buffer::{Buffer, ToByteSlice};
+use arrow_buffer::{Buffer, NullBuffer, OffsetBuffer, ToByteSlice};
 use arrow_data::{ArrayData, ArrayDataBuilder};
 use arrow_schema::{DataType, Field, Fields};
 use arrow_select::take::take;
@@ -357,6 +355,49 @@ fn test_string_view_equal() {
     test_equal(&a1_s, &a2_s, true);
 }
 
+/// Builds `[[child[0]], [child[1]]]` with the given child validity and slices 
it to its second
+/// row, so the equality check compares a child range that starts at 1 instead 
of 0.
+fn sliced_list_of_view(child: ArrayData, validity: [bool; 2]) -> ListArray {
+    let child = child
+        .into_builder()
+        .nulls(Some(NullBuffer::from(validity.to_vec())))
+        .build()
+        .unwrap();
+    let field = Arc::new(Field::new("item", child.data_type().clone(), true));
+    let list = ListArray::new(
+        field,
+        OffsetBuffer::from_lengths([1, 1]),
+        make_array(child),
+        None,
+    );
+    list.slice(1, 1)
+}
+
+// https://github.com/apache/arrow-rs/issues/11583
+#[test]
+fn test_list_of_view_child_nulls_past_start() {
+    let long_a = "a long string that is not inlined in the view";
+    let long_b = "another long string that is not inlined either";
+
+    let children: [fn(&str) -> ArrayData; 2] = [
+        |second| StringViewArray::from(vec!["x", second]).into_data(),
+        |second| BinaryViewArray::from(vec![b"x".as_slice(), 
second.as_bytes()]).into_data(),
+    ];
+
+    for child in children {
+        // Both are [[null]] and differ only in the data under the null slot
+        let a = sliced_list_of_view(child(long_a), [true, false]);
+        let b = sliced_list_of_view(child(long_b), [true, false]);
+        test_equal(&a, &b, true);
+
+        // Both rows are valid and hold different values; the null at child 
index 0 is
+        // outside the compared range and must not hide the difference
+        let a = sliced_list_of_view(child(long_a), [false, true]);
+        let b = sliced_list_of_view(child(long_b), [false, true]);
+        test_equal(&a, &b, false);
+    }
+}
+
 #[test]
 fn test_string_offset() {
     let a = StringArray::from(vec![Some("a"), None, Some("b")]);

Reply via email to