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")]);