// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // 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, }; 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_data::{ArrayData, ArrayDataBuilder}; use arrow_schema::{DataType, Field, Fields}; use arrow_select::take::take; use std::sync::Arc; #[test] fn test_null_equal() { let a = NullArray::new(12); let b = NullArray::new(12); test_equal(&a, &b, true); let b = NullArray::new(10); test_equal(&a, &b, false); // Test the case where offset != 0 let a_slice = a.slice(2, 3); let b_slice = b.slice(1, 3); test_equal(&a_slice, &b_slice, true); let a_slice = a.slice(5, 4); let b_slice = b.slice(3, 3); test_equal(&a_slice, &b_slice, false); } #[test] fn test_boolean_equal() { let a = BooleanArray::from(vec![false, false, true]); let b = BooleanArray::from(vec![false, false, true]); test_equal(&a, &b, true); let b = BooleanArray::from(vec![false, false, false]); test_equal(&a, &b, false); } #[test] fn test_boolean_equal_nulls() { let a = BooleanArray::from(vec![Some(false), None, None, Some(true)]); let b = BooleanArray::from(vec![Some(false), None, None, Some(true)]); test_equal(&a, &b, true); let b = BooleanArray::from(vec![None, None, None, Some(true)]); test_equal(&a, &b, false); let b = BooleanArray::from(vec![Some(true), None, None, Some(true)]); test_equal(&a, &b, false); } #[test] fn test_boolean_equal_offset() { let a = BooleanArray::from(vec![false, true, false, true, false, false, true]); let b = BooleanArray::from(vec![true, false, false, false, true, false, true, true]); test_equal(&a, &b, false); let a_slice = a.slice(2, 3); let b_slice = b.slice(3, 3); test_equal(&a_slice, &b_slice, true); let a_slice = a.slice(3, 4); let b_slice = b.slice(4, 4); test_equal(&a_slice, &b_slice, false); // Test the optimization cases where null_count == 0 and starts at 0 and len >= size_of(u8) // Elements fill in `u8`'s exactly. let mut vector = vec![false, false, true, true, true, true, true, true]; let a = BooleanArray::from(vector.clone()); let b = BooleanArray::from(vector.clone()); test_equal(&a, &b, true); // Elements fill in `u8`s + suffix bits. vector.push(true); let a = BooleanArray::from(vector.clone()); let b = BooleanArray::from(vector); test_equal(&a, &b, true); } #[test] fn test_primitive() { let cases = vec![ ( vec![Some(1), Some(2), Some(3)], vec![Some(1), Some(2), Some(3)], true, ), ( vec![Some(1), Some(2), Some(3)], vec![Some(1), Some(2), Some(4)], false, ), ( vec![Some(1), Some(2), None], vec![Some(1), Some(2), None], true, ), ( vec![Some(1), None, Some(3)], vec![Some(1), Some(2), None], false, ), ( vec![Some(1), None, None], vec![Some(1), Some(2), None], false, ), ]; for (lhs, rhs, expected) in cases { let lhs = Int32Array::from(lhs); let rhs = Int32Array::from(rhs); test_equal(&lhs, &rhs, expected); } } #[test] fn test_primitive_slice() { let cases = vec![ ( vec![Some(1), Some(2), Some(3)], (0, 1), vec![Some(1), Some(2), Some(3)], (0, 1), true, ), ( vec![Some(1), Some(2), Some(3)], (1, 1), vec![Some(1), Some(2), Some(3)], (2, 1), false, ), ( vec![Some(1), Some(2), None], (1, 1), vec![Some(1), None, Some(2)], (2, 1), true, ), ( vec![None, Some(2), None], (1, 1), vec![None, None, Some(2)], (2, 1), true, ), ( vec![Some(1), None, Some(2), None, Some(3)], (2, 2), vec![None, Some(2), None, Some(3)], (1, 2), true, ), ( vec![Some(1), Some(2), None, Some(0)], (2, 2), vec![Some(4), Some(5), Some(0), None], (2, 2), false, ), ]; for (lhs, slice_lhs, rhs, slice_rhs, expected) in cases { let lhs = Int32Array::from(lhs); let lhs = lhs.slice(slice_lhs.0, slice_lhs.1); let rhs = Int32Array::from(rhs); let rhs = rhs.slice(slice_rhs.0, slice_rhs.1); test_equal(&lhs, &rhs, expected); } } #[allow(clippy::eq_op)] fn test_equal(lhs: &dyn Array, rhs: &dyn Array, expected: bool) { // equality is symmetric assert_eq!(lhs, lhs); assert_eq!(rhs, rhs); match expected { true => { assert_eq!(lhs, rhs); assert_eq!(rhs, lhs); } false => { assert_ne!(lhs, rhs); assert_ne!(rhs, lhs); } } } type OptionString = Option; fn binary_cases() -> Vec<(Vec, Vec, bool)> { let base = vec![ Some("hello".to_owned()), None, None, Some("world".to_owned()), None, None, ]; let not_base = vec![ Some("hello".to_owned()), Some("foo".to_owned()), None, Some("world".to_owned()), None, None, ]; vec![ ( vec![Some("hello".to_owned()), Some("world".to_owned())], vec![Some("hello".to_owned()), Some("world".to_owned())], true, ), ( vec![Some("hello".to_owned()), Some("world".to_owned())], vec![Some("hello".to_owned()), Some("arrow".to_owned())], false, ), (base.clone(), base.clone(), true), (base, not_base, false), ] } fn test_generic_string_equal() { let cases = binary_cases(); for (lhs, rhs, expected) in cases { let lhs: GenericStringArray = lhs.into_iter().collect(); let rhs: GenericStringArray = rhs.into_iter().collect(); test_equal(&lhs, &rhs, expected); } } #[test] fn test_string_equal() { test_generic_string_equal::() } #[test] fn test_large_string_equal() { test_generic_string_equal::() } fn test_generic_binary_equal() { let cases = binary_cases(); for (lhs, rhs, expected) in cases { let lhs = lhs .iter() .map(|x| x.as_deref().map(|x| x.as_bytes())) .collect(); let rhs = rhs .iter() .map(|x| x.as_deref().map(|x| x.as_bytes())) .collect(); let lhs = GenericBinaryArray::::from_opt_vec(lhs); let rhs = GenericBinaryArray::::from_opt_vec(rhs); test_equal(&lhs, &rhs, expected); } } #[test] fn test_binary_equal() { test_generic_binary_equal::() } #[test] fn test_large_binary_equal() { test_generic_binary_equal::() } #[test] fn test_fixed_size_binary_array() { let a_input_arg = vec![vec![1, 2], vec![3, 4], vec![5, 6]]; let a = FixedSizeBinaryArray::try_from_iter(a_input_arg.into_iter()).unwrap(); let b_input_arg = vec![vec![1, 2], vec![3, 4], vec![5, 6]]; let b = FixedSizeBinaryArray::try_from_iter(b_input_arg.into_iter()).unwrap(); test_equal(&a, &b, true); } #[test] fn test_string_view_equal() { let a1 = StringViewArray::from(vec!["foo", "very long string over 12 bytes", "bar"]); let a2 = StringViewArray::from(vec![ "a very long string over 12 bytes", "foo", "very long string over 12 bytes", "bar", ]); test_equal(&a1, &a2.slice(1, 3), true); let a1 = StringViewArray::from(vec!["foo", "very long string over 12 bytes", "bar"]); let a2 = StringViewArray::from(vec!["foo", "very long string over 12 bytes", "bar"]); test_equal(&a1, &a2, true); let a1_s = a1.slice(1, 1); let a2_s = a2.slice(1, 1); test_equal(&a1_s, &a2_s, true); let a1_s = a1.slice(2, 1); let a2_s = a2.slice(0, 1); test_equal(&a1_s, &a2_s, false); // test will null value. let a1 = StringViewArray::from(vec!["foo", "very long string over 12 bytes", "bar"]); let a2 = { let mut builder = StringViewBuilder::new(); builder.append_value("foo"); builder.append_null(); builder.append_option(Some("very long string over 12 bytes")); builder.append_value("bar"); builder.finish() }; test_equal(&a1, &a2, false); let a1_s = a1.slice(1, 2); let a2_s = a2.slice(1, 3); test_equal(&a1_s, &a2_s, false); let a1_s = a1.slice(1, 2); let a2_s = a2.slice(2, 2); test_equal(&a1_s, &a2_s, true); } #[test] fn test_string_offset() { let a = StringArray::from(vec![Some("a"), None, Some("b")]); let a = a.slice(2, 1); let b = StringArray::from(vec![Some("b")]); test_equal(&a, &b, true); } #[test] fn test_string_offset_larger() { let a = StringArray::from(vec![Some("a"), None, Some("b"), None, Some("c")]); let b = StringArray::from(vec![None, Some("b"), None, Some("c")]); test_equal(&a.slice(2, 2), &b.slice(0, 2), false); test_equal(&a.slice(2, 2), &b.slice(1, 2), true); test_equal(&a.slice(2, 2), &b.slice(2, 2), false); } #[test] fn test_null() { let a = NullArray::new(2); let b = NullArray::new(2); test_equal(&a, &b, true); let b = NullArray::new(1); test_equal(&a, &b, false); } fn create_list_array, T: AsRef<[Option]>>(data: T) -> ListArray { let mut builder = ListBuilder::new(Int32Builder::with_capacity(10)); for d in data.as_ref() { if let Some(v) = d { builder.values().append_slice(v.as_ref()); builder.append(true); } else { builder.append(false); } } builder.finish() } #[test] fn test_list_equal() { let a = create_list_array([Some(&[1, 2, 3]), Some(&[4, 5, 6])]); let b = create_list_array([Some(&[1, 2, 3]), Some(&[4, 5, 6])]); test_equal(&a, &b, true); let b = create_list_array([Some(&[1, 2, 3]), Some(&[4, 5, 7])]); test_equal(&a, &b, false); } #[test] fn test_empty_offsets_list_equal() { let empty: Vec = vec![]; let values = Int32Array::from(empty); let empty_offsets: [u8; 0] = []; let a: ListArray = ArrayDataBuilder::new(DataType::List(Arc::new(Field::new_list_field( DataType::Int32, true, )))) .len(0) .add_buffer(Buffer::from(&empty_offsets)) .add_child_data(values.to_data()) .null_bit_buffer(Some(Buffer::from(&empty_offsets))) .build() .unwrap() .into(); let b: ListArray = ArrayDataBuilder::new(DataType::List(Arc::new(Field::new_list_field( DataType::Int32, true, )))) .len(0) .add_buffer(Buffer::from(&empty_offsets)) .add_child_data(values.to_data()) .null_bit_buffer(Some(Buffer::from(&empty_offsets))) .build() .unwrap() .into(); test_equal(&a, &b, true); let c: ListArray = ArrayDataBuilder::new(DataType::List(Arc::new(Field::new_list_field( DataType::Int32, true, )))) .len(0) .add_buffer(Buffer::from([0i32, 2, 3, 4, 6, 7, 8].to_byte_slice())) .add_child_data(Int32Array::from(vec![1, 2, -1, -2, 3, 4, -3, -4]).into_data()) .null_bit_buffer(Some(Buffer::from([0b00001001]))) .build() .unwrap() .into(); test_equal(&a, &c, true); } // Test the case where null_count > 0 #[test] fn test_list_null() { let a = create_list_array([Some(&[1, 2]), None, None, Some(&[3, 4]), None, None]); let b = create_list_array([Some(&[1, 2]), None, None, Some(&[3, 4]), None, None]); test_equal(&a, &b, true); let b = create_list_array([ Some(&[1, 2]), None, Some(&[5, 6]), Some(&[3, 4]), None, None, ]); test_equal(&a, &b, false); let b = create_list_array([Some(&[1, 2]), None, None, Some(&[3, 5]), None, None]); test_equal(&a, &b, false); // a list where the nullness of values is determined by the list's bitmap let c_values = Int32Array::from(vec![1, 2, -1, -2, 3, 4, -3, -4]); let c: ListArray = ArrayDataBuilder::new(DataType::List(Arc::new(Field::new_list_field( DataType::Int32, true, )))) .len(6) .add_buffer(Buffer::from([0i32, 2, 3, 4, 6, 7, 8].to_byte_slice())) .add_child_data(c_values.into_data()) .null_bit_buffer(Some(Buffer::from([0b00001001]))) .build() .unwrap() .into(); let d_values = Int32Array::from(vec![ Some(1), Some(2), None, None, Some(3), Some(4), None, None, ]); let d: ListArray = ArrayDataBuilder::new(DataType::List(Arc::new(Field::new_list_field( DataType::Int32, true, )))) .len(6) .add_buffer(Buffer::from([0i32, 2, 3, 4, 6, 7, 8].to_byte_slice())) .add_child_data(d_values.into_data()) .null_bit_buffer(Some(Buffer::from([0b00001001]))) .build() .unwrap() .into(); test_equal(&c, &d, true); } // Test the case where offset != 0 #[test] fn test_list_offsets() { let a = create_list_array([Some(&[1, 2]), None, None, Some(&[3, 4]), None, None]); let b = create_list_array([Some(&[1, 2]), None, None, Some(&[3, 5]), None, None]); let a_slice = a.slice(0, 3); let b_slice = b.slice(0, 3); test_equal(&a_slice, &b_slice, true); let a_slice = a.slice(0, 5); let b_slice = b.slice(0, 5); test_equal(&a_slice, &b_slice, false); let a_slice = a.slice(4, 1); let b_slice = b.slice(4, 1); test_equal(&a_slice, &b_slice, true); } fn create_fixed_size_binary_array, T: AsRef<[Option]>>( data: T, ) -> FixedSizeBinaryArray { let mut builder = FixedSizeBinaryBuilder::with_capacity(data.as_ref().len(), 5); for d in data.as_ref() { if let Some(v) = d { builder.append_value(v.as_ref()).unwrap(); } else { builder.append_null(); } } builder.finish() } #[test] fn test_fixed_size_binary_equal() { let a = create_fixed_size_binary_array([Some(b"hello"), Some(b"world")]); let b = create_fixed_size_binary_array([Some(b"hello"), Some(b"world")]); test_equal(&a, &b, true); let b = create_fixed_size_binary_array([Some(b"hello"), Some(b"arrow")]); test_equal(&a, &b, false); } // Test the case where null_count > 0 #[test] fn test_fixed_size_binary_null() { let a = create_fixed_size_binary_array([Some(b"hello"), None, Some(b"world")]); let b = create_fixed_size_binary_array([Some(b"hello"), None, Some(b"world")]); test_equal(&a, &b, true); let b = create_fixed_size_binary_array([Some(b"hello"), Some(b"world"), None]); test_equal(&a, &b, false); let b = create_fixed_size_binary_array([Some(b"hello"), None, Some(b"arrow")]); test_equal(&a, &b, false); } #[test] fn test_fixed_size_binary_offsets() { // Test the case where offset != 0 let a = create_fixed_size_binary_array([Some(b"hello"), None, None, Some(b"world"), None, None]); let b = create_fixed_size_binary_array([Some(b"hello"), None, None, Some(b"arrow"), None, None]); let a_slice = a.slice(0, 3); let b_slice = b.slice(0, 3); test_equal(&a_slice, &b_slice, true); let a_slice = a.slice(0, 5); let b_slice = b.slice(0, 5); test_equal(&a_slice, &b_slice, false); let a_slice = a.slice(4, 1); let b_slice = b.slice(4, 1); test_equal(&a_slice, &b_slice, true); let a_slice = a.slice(3, 1); let b_slice = b.slice(3, 1); test_equal(&a_slice, &b_slice, false); } fn create_decimal_array(data: Vec>) -> Decimal128Array { data.into_iter() .collect::() .with_precision_and_scale(23, 6) .unwrap() } #[test] fn test_decimal_equal() { let a = create_decimal_array(vec![Some(8_887_000_000), Some(-8_887_000_000)]); let b = create_decimal_array(vec![Some(8_887_000_000), Some(-8_887_000_000)]); test_equal(&a, &b, true); let b = create_decimal_array(vec![Some(15_887_000_000), Some(-8_887_000_000)]); test_equal(&a, &b, false); } // Test the case where null_count > 0 #[test] fn test_decimal_null() { let a = create_decimal_array(vec![Some(8_887_000_000), None, Some(-8_887_000_000)]); let b = create_decimal_array(vec![Some(8_887_000_000), None, Some(-8_887_000_000)]); test_equal(&a, &b, true); let b = create_decimal_array(vec![Some(8_887_000_000), Some(-8_887_000_000), None]); test_equal(&a, &b, false); let b = create_decimal_array(vec![Some(15_887_000_000), None, Some(-8_887_000_000)]); test_equal(&a, &b, false); } #[test] fn test_decimal_offsets() { // Test the case where offset != 0 let a = create_decimal_array(vec![ Some(8_887_000_000), None, None, Some(-8_887_000_000), None, None, ]); let b = create_decimal_array(vec![ None, Some(8_887_000_000), None, None, Some(15_887_000_000), None, None, ]); let a_slice = a.slice(0, 3); let b_slice = b.slice(1, 3); test_equal(&a_slice, &b_slice, true); let a_slice = a.slice(0, 5); let b_slice = b.slice(1, 5); test_equal(&a_slice, &b_slice, false); let a_slice = a.slice(4, 1); let b_slice = b.slice(5, 1); test_equal(&a_slice, &b_slice, true); let a_slice = a.slice(3, 3); let b_slice = b.slice(4, 3); test_equal(&a_slice, &b_slice, false); let a_slice = a.slice(1, 3); let b_slice = b.slice(2, 3); test_equal(&a_slice, &b_slice, false); let b = create_decimal_array(vec![ None, None, None, Some(-8_887_000_000), Some(-3_000), None, ]); let a_slice = a.slice(1, 3); let b_slice = b.slice(1, 3); test_equal(&a_slice, &b_slice, true); } /// Create a fixed size list of 2 value lengths fn create_fixed_size_list_array, T: AsRef<[Option]>>( data: T, ) -> FixedSizeListArray { let mut builder = FixedSizeListBuilder::new(Int32Builder::with_capacity(10), 3); for d in data.as_ref() { if let Some(v) = d { builder.values().append_slice(v.as_ref()); builder.append(true); } else { for _ in 0..builder.value_length() { builder.values().append_null(); } builder.append(false); } } builder.finish() } #[test] fn test_fixed_size_list_equal() { let a = create_fixed_size_list_array([Some(&[1, 2, 3]), Some(&[4, 5, 6])]); let b = create_fixed_size_list_array([Some(&[1, 2, 3]), Some(&[4, 5, 6])]); test_equal(&a, &b, true); let b = create_fixed_size_list_array([Some(&[1, 2, 3]), Some(&[4, 5, 7])]); test_equal(&a, &b, false); } // Test the case where null_count > 0 #[test] fn test_fixed_list_null() { let a = create_fixed_size_list_array([Some(&[1, 2, 3]), None, None, Some(&[4, 5, 6]), None, None]); let b = create_fixed_size_list_array([Some(&[1, 2, 3]), None, None, Some(&[4, 5, 6]), None, None]); test_equal(&a, &b, true); let b = create_fixed_size_list_array([ Some(&[1, 2, 3]), None, Some(&[7, 8, 9]), Some(&[4, 5, 6]), None, None, ]); test_equal(&a, &b, false); let b = create_fixed_size_list_array([Some(&[1, 2, 3]), None, None, Some(&[3, 6, 9]), None, None]); test_equal(&a, &b, false); let b = create_fixed_size_list_array([None, Some(&[4, 5, 6]), None, None]); test_equal(&a.slice(2, 4), &b, true); test_equal(&a.slice(3, 3), &b.slice(1, 3), true); } #[test] fn test_fixed_list_offsets() { // Test the case where offset != 0 let a = create_fixed_size_list_array([Some(&[1, 2, 3]), None, None, Some(&[4, 5, 6]), None, None]); let b = create_fixed_size_list_array([Some(&[1, 2, 3]), None, None, Some(&[3, 6, 9]), None, None]); let a_slice = a.slice(0, 3); let b_slice = b.slice(0, 3); test_equal(&a_slice, &b_slice, true); let a_slice = a.slice(0, 5); let b_slice = b.slice(0, 5); test_equal(&a_slice, &b_slice, false); let a_slice = a.slice(4, 1); let b_slice = b.slice(4, 1); test_equal(&a_slice, &b_slice, true); } fn create_list_view_array< O: OffsetSizeTrait, U: IntoIterator>, T: IntoIterator>, >( data: T, ) -> GenericListViewArray { let mut builder = GenericListViewBuilder::::new(Int32Builder::new()); for d in data { if let Some(v) = d { builder.append_value(v); } else { builder.append_null(); } } builder.finish() } fn test_test_list_view_array() { let a = create_list_view_array::([ None, Some(vec![Some(1), None, Some(2)]), Some(vec![Some(3), Some(4), Some(5), None]), ]); let b = create_list_view_array::([ None, Some(vec![Some(1), None, Some(2)]), Some(vec![Some(3), Some(4), Some(5), None]), ]); test_equal(&a, &b, true); // Simple non-matching arrays by reordering let b = create_list_view_array::([ Some(vec![Some(3), Some(4), Some(5), None]), Some(vec![Some(1), None, Some(2)]), ]); test_equal(&a, &b, false); // reorder using take yields equal values let indices: PrimitiveArray = vec![None, Some(1), Some(0)].into(); let b = take(&b, &indices, None) .unwrap() .as_list_view::() .clone(); test_equal(&a, &b, true); // Slicing one side yields unequal again let a = a.slice(1, 2); test_equal(&a, &b, false); // Slicing the other to match makes them equal again let b = b.slice(1, 2); test_equal(&a, &b, true); } // Special test for List>. // This tests the equal_ranges kernel fn test_sliced_list_of_list_view() { // First list view is created using the builder, with elements not deduplicated. let mut a = ListBuilder::new(GenericListViewBuilder::::new(Int32Builder::new())); a.append_value([Some(vec![Some(1), Some(2), Some(3)]), Some(vec![])]); a.append_null(); a.append_value([ Some(vec![Some(1), Some(2), Some(3)]), None, Some(vec![Some(6)]), ]); let a = a.finish(); // a = [[[1,2,3], []], null, [[4, null], [5], null, [6]]] // First list view is created using the builder, with elements not deduplicated. let mut b = ListBuilder::new(GenericListViewBuilder::::new(Int32Builder::new())); // Add an extra row that we will slice off, adjust the List offsets b.append_value([Some(vec![Some(0), Some(0), Some(0)])]); b.append_value([Some(vec![Some(1), Some(2), Some(3)]), Some(vec![])]); b.append_null(); b.append_value([ Some(vec![Some(1), Some(2), Some(3)]), None, Some(vec![Some(6)]), ]); let b = b.finish(); // b = [[[0, 0, 0]], [[1,2,3], []], null, [[4, null], [5], null, [6]]] let b = b.slice(1, 3); // b = [[[1,2,3], []], null, [[4, null], [5], null, [6]]] but the outer ListArray // has an offset test_equal(&a, &b, true); } #[test] fn test_list_view_array() { test_test_list_view_array::(); } #[test] fn test_large_list_view_array() { test_test_list_view_array::(); } #[test] fn test_nested_list_view_array() { test_sliced_list_of_list_view::(); } #[test] fn test_nested_large_list_view_array() { test_sliced_list_of_list_view::(); } #[test] fn test_struct_equal() { let strings: ArrayRef = Arc::new(StringArray::from(vec![ Some("joe"), None, None, Some("mark"), Some("doe"), ])); let ints: ArrayRef = Arc::new(Int32Array::from(vec![ Some(1), Some(2), None, Some(4), Some(5), ])); let a = StructArray::try_from(vec![("f1", strings.clone()), ("f2", ints.clone())]).unwrap(); let b = StructArray::try_from(vec![("f1", strings), ("f2", ints)]).unwrap(); test_equal(&a, &b, true); } #[test] fn test_struct_equal_null() { let strings: ArrayRef = Arc::new(StringArray::from(vec![ Some("joe"), None, None, Some("mark"), Some("doe"), ])); let ints: ArrayRef = Arc::new(Int32Array::from(vec![ Some(1), Some(2), None, Some(4), Some(5), ])); let ints_non_null: ArrayRef = Arc::new(Int32Array::from(vec![1, 2, 3, 4, 0])); let a = ArrayData::builder(DataType::Struct(Fields::from(vec![ Field::new("f1", DataType::Utf8, true), Field::new("f2", DataType::Int32, true), ]))) .null_bit_buffer(Some(Buffer::from([0b00001011]))) .len(5) .add_child_data(strings.to_data()) .add_child_data(ints.to_data()) .build() .unwrap(); let a = make_array(a); let b = ArrayData::builder(DataType::Struct(Fields::from(vec![ Field::new("f1", DataType::Utf8, true), Field::new("f2", DataType::Int32, true), ]))) .null_bit_buffer(Some(Buffer::from([0b00001011]))) .len(5) .add_child_data(strings.to_data()) .add_child_data(ints_non_null.to_data()) .build() .unwrap(); let b = make_array(b); test_equal(&a, &b, true); // test with arrays that are not equal let c_ints_non_null: ArrayRef = Arc::new(Int32Array::from(vec![1, 2, 3, 0, 4])); let c = ArrayData::builder(DataType::Struct(Fields::from(vec![ Field::new("f1", DataType::Utf8, true), Field::new("f2", DataType::Int32, true), ]))) .null_bit_buffer(Some(Buffer::from([0b00001011]))) .len(5) .add_child_data(strings.to_data()) .add_child_data(c_ints_non_null.to_data()) .build() .unwrap(); let c = make_array(c); test_equal(&a, &c, false); // test a nested struct let a = ArrayData::builder(DataType::Struct( vec![Field::new("f3", a.data_type().clone(), true)].into(), )) .null_bit_buffer(Some(Buffer::from([0b00011110]))) .len(5) .add_child_data(a.to_data()) .build() .unwrap(); let a = make_array(a); // reconstruct b, but with different data where the first struct is null let strings: ArrayRef = Arc::new(StringArray::from(vec![ Some("joanne"), // difference None, None, Some("mark"), Some("doe"), ])); let b = ArrayData::builder(DataType::Struct(Fields::from(vec![ Field::new("f1", DataType::Utf8, true), Field::new("f2", DataType::Int32, true), ]))) .null_bit_buffer(Some(Buffer::from([0b00001011]))) .len(5) .add_child_data(strings.to_data()) .add_child_data(ints_non_null.to_data()) .build() .unwrap(); let b = ArrayData::builder(DataType::Struct( vec![Field::new("f3", b.data_type().clone(), true)].into(), )) .null_bit_buffer(Some(Buffer::from([0b00011110]))) .len(5) .add_child_data(b) .build() .unwrap(); let b = make_array(b); test_equal(&a, &b, true); } #[test] fn test_struct_equal_null_variable_size() { // the string arrays differ, but where the struct array is null let strings1: ArrayRef = Arc::new(StringArray::from(vec![ Some("joe"), None, None, Some("mark"), Some("doel"), ])); let strings2: ArrayRef = Arc::new(StringArray::from(vec![ Some("joel"), None, None, Some("mark"), Some("doe"), ])); let a = ArrayData::builder(DataType::Struct( vec![Field::new("f1", DataType::Utf8, true)].into(), )) .null_bit_buffer(Some(Buffer::from([0b00001010]))) .len(5) .add_child_data(strings1.to_data()) .build() .unwrap(); let a = make_array(a); let b = ArrayData::builder(DataType::Struct( vec![Field::new("f1", DataType::Utf8, true)].into(), )) .null_bit_buffer(Some(Buffer::from([0b00001010]))) .len(5) .add_child_data(strings2.to_data()) .build() .unwrap(); let b = make_array(b); test_equal(&a, &b, true); // test with arrays that are not equal let strings3: ArrayRef = Arc::new(StringArray::from(vec![ Some("mark"), None, None, Some("doe"), Some("joe"), ])); let c = ArrayData::builder(DataType::Struct( vec![Field::new("f1", DataType::Utf8, true)].into(), )) .null_bit_buffer(Some(Buffer::from([0b00001011]))) .len(5) .add_child_data(strings3.to_data()) .build() .unwrap(); let c = make_array(c); test_equal(&a, &c, false); } fn create_dictionary_array(values: &[&str], keys: &[Option<&str>]) -> DictionaryArray { let values = StringArray::from(values.to_vec()); let mut builder = StringDictionaryBuilder::::new_with_dictionary(keys.len(), &values).unwrap(); for key in keys { if let Some(v) = key { builder.append(v).unwrap(); } else { builder.append_null() } } builder.finish() } #[test] fn test_dictionary_equal() { // (a, b, c), (1, 2, 1, 3) => (a, b, a, c) let a = create_dictionary_array( &["a", "b", "c"], &[Some("a"), Some("b"), Some("a"), Some("c")], ); // different representation (values and keys are swapped), same result let b = create_dictionary_array( &["a", "c", "b"], &[Some("a"), Some("b"), Some("a"), Some("c")], ); test_equal(&a, &b, true); // different len let b = create_dictionary_array(&["a", "c", "b"], &[Some("a"), Some("b"), Some("a")]); test_equal(&a, &b, false); // different key let b = create_dictionary_array( &["a", "c", "b"], &[Some("a"), Some("b"), Some("a"), Some("a")], ); test_equal(&a, &b, false); // different values, same keys let b = create_dictionary_array( &["a", "b", "d"], &[Some("a"), Some("b"), Some("a"), Some("d")], ); test_equal(&a, &b, false); } #[test] fn test_dictionary_equal_null() { // (a, b, c), (1, 2, 1, 3) => (a, b, a, c) let a = create_dictionary_array(&["a", "b", "c"], &[Some("a"), None, Some("a"), Some("c")]); // equal to self test_equal(&a, &a, true); // different representation (values and keys are swapped), same result let b = create_dictionary_array(&["a", "c", "b"], &[Some("a"), None, Some("a"), Some("c")]); test_equal(&a, &b, true); // different null position let b = create_dictionary_array(&["a", "c", "b"], &[Some("a"), Some("b"), Some("a"), None]); test_equal(&a, &b, false); // different key let b = create_dictionary_array(&["a", "c", "b"], &[Some("a"), None, Some("a"), Some("a")]); test_equal(&a, &b, false); // different values, same keys let b = create_dictionary_array(&["a", "b", "d"], &[Some("a"), None, Some("a"), Some("d")]); test_equal(&a, &b, false); } #[test] fn test_non_null_empty_strings() { let s1 = StringArray::from(vec![Some(""), Some(""), Some("")]); let data = s1.to_data().into_builder().nulls(None).build().unwrap(); let s2 = StringArray::from(data); // s2 is identical to s1 except that it has no validity buffer but since there // are no nulls, s1 and s2 are equal test_equal(&s1, &s2, true); } #[test] fn test_null_empty_strings() { let s1 = StringArray::from(vec![Some(""), None, Some("")]); let data = s1.to_data().into_builder().nulls(None).build().unwrap(); let s2 = StringArray::from(data); // s2 is identical to s1 except that it has no validity buffer since string1 has // nulls in it, s1 and s2 are not equal test_equal(&s1, &s2, false); } #[test] fn test_union_equal_dense() { let mut builder = UnionBuilder::new_dense(); builder.append::("a", 1).unwrap(); builder.append::("b", 2).unwrap(); builder.append::("c", 3).unwrap(); builder.append::("a", 4).unwrap(); builder.append_null::("a").unwrap(); builder.append::("a", 6).unwrap(); builder.append::("b", 7).unwrap(); let union1 = builder.build().unwrap(); builder = UnionBuilder::new_dense(); builder.append::("a", 1).unwrap(); builder.append::("b", 2).unwrap(); builder.append::("c", 3).unwrap(); builder.append::("a", 4).unwrap(); builder.append_null::("a").unwrap(); builder.append::("a", 6).unwrap(); builder.append::("b", 7).unwrap(); let union2 = builder.build().unwrap(); builder = UnionBuilder::new_dense(); builder.append::("a", 1).unwrap(); builder.append::("b", 2).unwrap(); builder.append::("c", 3).unwrap(); builder.append::("a", 5).unwrap(); builder.append::("c", 4).unwrap(); builder.append::("a", 6).unwrap(); builder.append::("b", 7).unwrap(); let union3 = builder.build().unwrap(); builder = UnionBuilder::new_dense(); builder.append::("a", 1).unwrap(); builder.append::("b", 2).unwrap(); builder.append::("c", 3).unwrap(); builder.append::("a", 4).unwrap(); builder.append_null::("c").unwrap(); builder.append_null::("b").unwrap(); builder.append::("b", 7).unwrap(); let union4 = builder.build().unwrap(); test_equal(&union1, &union2, true); test_equal(&union1, &union3, false); test_equal(&union1, &union4, false); } #[test] fn test_union_equal_sparse() { let mut builder = UnionBuilder::new_sparse(); builder.append::("a", 1).unwrap(); builder.append::("b", 2).unwrap(); builder.append::("c", 3).unwrap(); builder.append::("a", 4).unwrap(); builder.append_null::("a").unwrap(); builder.append::("a", 6).unwrap(); builder.append::("b", 7).unwrap(); let union1 = builder.build().unwrap(); builder = UnionBuilder::new_sparse(); builder.append::("a", 1).unwrap(); builder.append::("b", 2).unwrap(); builder.append::("c", 3).unwrap(); builder.append::("a", 4).unwrap(); builder.append_null::("a").unwrap(); builder.append::("a", 6).unwrap(); builder.append::("b", 7).unwrap(); let union2 = builder.build().unwrap(); builder = UnionBuilder::new_sparse(); builder.append::("a", 1).unwrap(); builder.append::("b", 2).unwrap(); builder.append::("c", 3).unwrap(); builder.append::("a", 5).unwrap(); builder.append::("c", 4).unwrap(); builder.append::("a", 6).unwrap(); builder.append::("b", 7).unwrap(); let union3 = builder.build().unwrap(); builder = UnionBuilder::new_sparse(); builder.append::("a", 1).unwrap(); builder.append::("b", 2).unwrap(); builder.append::("c", 3).unwrap(); builder.append::("a", 4).unwrap(); builder.append_null::("a").unwrap(); builder.append_null::("a").unwrap(); builder.append::("b", 7).unwrap(); let union4 = builder.build().unwrap(); test_equal(&union1, &union2, true); test_equal(&union1, &union3, false); test_equal(&union1, &union4, false); } #[test] fn test_union_equal_sparse_slice() { let mut builder = UnionBuilder::new_sparse(); builder.append::("a", 1).unwrap(); builder.append::("a", 2).unwrap(); builder.append::("b", 3).unwrap(); let a1 = builder.build().unwrap(); let mut builder = UnionBuilder::new_sparse(); builder.append::("a", 2).unwrap(); builder.append::("b", 3).unwrap(); let a2 = builder.build().unwrap(); test_equal(&a1.slice(1, 2), &a2, true) } #[test] fn test_boolean_slice() { let array = BooleanArray::from(vec![true; 32]); let slice = array.slice(4, 12); assert_eq!(&slice, &slice); let slice = array.slice(8, 12); assert_eq!(&slice, &slice); let slice = array.slice(8, 24); assert_eq!(&slice, &slice); } #[test] fn test_sliced_nullable_boolean_array() { let a = BooleanArray::from(vec![None; 32]); let b = BooleanArray::from(vec![true; 32]); let slice_a = a.slice(1, 12); let slice_b = b.slice(1, 12); assert_ne!(&slice_a, &slice_b); } #[test] fn list_array_non_zero_nulls() { // Tests handling of list arrays with non-empty null ranges let mut builder = ListBuilder::new(Int64Builder::with_capacity(10)); builder.values().append_value(1); builder.values().append_value(2); builder.values().append_value(3); builder.append(true); builder.append(false); let array1 = builder.finish(); let mut builder = ListBuilder::new(Int64Builder::with_capacity(10)); builder.values().append_value(1); builder.values().append_value(2); builder.values().append_value(3); builder.append(true); builder.values().append_null(); builder.values().append_null(); builder.append(false); let array2 = builder.finish(); assert_eq!(array1, array2); } #[test] fn test_list_different_offsets() { let a = ListArray::from_iter_primitive::([ Some([Some(0), Some(0)]), Some([Some(1), Some(2)]), Some([None, None]), ]); let b = ListArray::from_iter_primitive::([ Some([Some(1), Some(2)]), Some([None, None]), Some([None, None]), ]); let a_slice = a.slice(1, 2); let b_slice = b.slice(0, 2); assert_eq!(&a_slice, &b_slice); } fn make_struct(elements: Vec, Option)>>) -> StructArray { let mut builder = StructBuilder::new( vec![ Field::new("f1", DataType::Utf8, true), Field::new("f2", DataType::Int32, true), ], vec![ Box::new(StringBuilder::new()), Box::new(Int32Builder::new()), ], ); for element in elements { match element.and_then(|e| e.0) { None => builder .field_builder::(0) .unwrap() .append_null(), Some(s) => builder .field_builder::(0) .unwrap() .append_value(s), }; builder .field_builder::(1) .unwrap() .append_option(element.and_then(|e| e.1)); builder.append(element.is_some()); } builder.finish() } #[test] fn test_struct_equal_slice() { let a = make_struct(vec![ None, Some((Some("joe"), Some(1))), Some((None, Some(2))), Some((None, None)), Some((Some("mark"), Some(4))), Some((Some("doe"), Some(5))), ]); let a = a.slice(1, 5); let a = a.as_any().downcast_ref::().unwrap(); let b = make_struct(vec![ Some((Some("joe"), Some(1))), Some((None, Some(2))), Some((None, None)), Some((Some("mark"), Some(4))), Some((Some("doe"), Some(5))), ]); assert_eq!(a, &b); test_equal(&a, &b, true); } #[test] fn test_list_excess_children_equal() { let mut a = ListBuilder::new(FixedSizeBinaryBuilder::new(5)); a.values().append_value(b"11111").unwrap(); // Masked value a.append_null(); a.values().append_value(b"22222").unwrap(); a.values().append_null(); a.append(true); let a = a.finish(); let mut b = ListBuilder::new(FixedSizeBinaryBuilder::new(5)); b.append_null(); b.values().append_value(b"22222").unwrap(); b.values().append_null(); b.append(true); let b = b.finish(); assert_eq!(a.value_offsets(), &[0, 1, 3]); assert_eq!(b.value_offsets(), &[0, 0, 2]); assert_eq!(a, b); }