// 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, BooleanBuilder, Decimal128Builder, Int32Array, Int32Builder, Int64Array, StringArray, StructBuilder, UInt64Array, make_array, }; use arrow_array::Decimal128Array; use arrow_buffer::{ArrowNativeType, Buffer}; use arrow_data::ArrayData; use arrow_schema::{DataType, Field, UnionFields, UnionMode}; use std::ptr::NonNull; use std::sync::Arc; #[test] #[should_panic(expected = "Need at least 80 bytes in buffers[0] in array of type Int64, but got 8")] fn test_buffer_too_small() { let buffer = Buffer::from_slice_ref([0i32, 2i32]); // should fail as the declared size (10*8 = 80) is larger than the underlying bfufer (8) ArrayData::try_new(DataType::Int64, 10, None, 0, vec![buffer], vec![]).unwrap(); } #[test] #[should_panic(expected = "Need at least 16 bytes in buffers[0] in array of type Int64, but got 8")] fn test_buffer_too_small_offset() { let buffer = Buffer::from_slice_ref([0i32, 2i32]); // should fail -- size is ok, but also has offset ArrayData::try_new(DataType::Int64, 1, None, 1, vec![buffer], vec![]).unwrap(); } #[test] #[should_panic(expected = "Expected 1 buffers in array of type Int64, got 2")] fn test_bad_number_of_buffers() { let buffer1 = Buffer::from_slice_ref([0i32, 2i32]); let buffer2 = Buffer::from_slice_ref([0i32, 2i32]); ArrayData::try_new(DataType::Int64, 1, None, 0, vec![buffer1, buffer2], vec![]).unwrap(); } #[test] #[should_panic( expected = "Need at least 18446744073709551615 bytes in buffers[0] in array of type Int64, but got 8" )] fn test_fixed_width_overflow() { let buffer = Buffer::from_slice_ref([0i32, 2i32]); ArrayData::try_new(DataType::Int64, usize::MAX, None, 0, vec![buffer], vec![]).unwrap(); } #[test] #[should_panic(expected = "null_bit_buffer size too small. got 1 needed 2")] fn test_bitmap_too_small() { let buffer = make_i32_buffer(9); let null_bit_buffer = Buffer::from([0b11111111]); ArrayData::try_new( DataType::Int32, 9, Some(null_bit_buffer), 0, vec![buffer], vec![], ) .unwrap(); } // Test creating a dictionary with a non integer type #[test] #[should_panic(expected = "Dictionary key type must be integer, but was Utf8")] fn test_non_int_dictionary() { let i32_buffer = Buffer::from_slice_ref([0i32, 2i32]); let data_type = DataType::Dictionary(Box::new(DataType::Utf8), Box::new(DataType::Int32)); let child_data = ArrayData::try_new( DataType::Int32, 1, None, 0, vec![i32_buffer.clone()], vec![], ) .unwrap(); ArrayData::try_new( data_type, 1, None, 0, vec![i32_buffer.clone(), i32_buffer], vec![child_data], ) .unwrap(); } #[test] #[should_panic(expected = "Expected LargeUtf8 but child data had Utf8")] fn test_mismatched_dictionary_types() { // test w/ dictionary created with a child array data that has type different than declared let string_array: StringArray = vec![Some("foo"), Some("bar")].into_iter().collect(); let i32_buffer = Buffer::from_slice_ref([0i32, 1i32]); // Dict says LargeUtf8 but array is Utf8 let data_type = DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::LargeUtf8)); let child_data = string_array.into_data(); ArrayData::try_new(data_type, 1, None, 0, vec![i32_buffer], vec![child_data]).unwrap(); } #[test] fn test_empty_utf8_array_with_empty_offsets_buffer() { let data_buffer = Buffer::from(&[]); let offsets_buffer = Buffer::from(&[]); ArrayData::try_new( DataType::Utf8, 0, None, 0, vec![offsets_buffer, data_buffer], vec![], ) .unwrap(); } #[test] fn test_empty_utf8_array_with_single_zero_offset() { let data_buffer = Buffer::from(&[]); let offsets_buffer = Buffer::from_slice_ref([0i32]); ArrayData::try_new( DataType::Utf8, 0, None, 0, vec![offsets_buffer, data_buffer], vec![], ) .unwrap(); } #[test] #[should_panic(expected = "First offset 1 of Utf8 is larger than values length 0")] fn test_empty_utf8_array_with_invalid_offset() { let data_buffer = Buffer::from(&[]); let offsets_buffer = Buffer::from_slice_ref([1i32]); ArrayData::try_new( DataType::Utf8, 0, None, 0, vec![offsets_buffer, data_buffer], vec![], ) .unwrap(); } #[test] fn test_empty_utf8_array_with_non_zero_offset() { let data_buffer = Buffer::from_slice_ref("abcdef".as_bytes()); let offsets_buffer = Buffer::from_slice_ref([0i32, 2, 6, 0]); ArrayData::try_new( DataType::Utf8, 0, None, 3, vec![offsets_buffer, data_buffer], vec![], ) .unwrap(); } #[test] #[should_panic(expected = "Buffer 0 of LargeUtf8 isn't large enough. Expected 8 bytes got 4")] fn test_empty_large_utf8_array_with_wrong_type_offsets() { let data_buffer = Buffer::from(&[]); let offsets_buffer = Buffer::from_slice_ref([0i32]); ArrayData::try_new( DataType::LargeUtf8, 0, None, 0, vec![offsets_buffer, data_buffer], vec![], ) .unwrap(); } #[test] #[should_panic(expected = "Buffer 0 of Utf8 isn't large enough. Expected 12 bytes got 8")] fn test_validate_offsets_i32() { let data_buffer = Buffer::from_slice_ref("abcdef".as_bytes()); let offsets_buffer = Buffer::from_slice_ref([0i32, 2i32]); ArrayData::try_new( DataType::Utf8, 2, None, 0, vec![offsets_buffer, data_buffer], vec![], ) .unwrap(); } #[test] #[should_panic(expected = "Buffer 0 of LargeUtf8 isn't large enough. Expected 24 bytes got 16")] fn test_validate_offsets_i64() { let data_buffer = Buffer::from_slice_ref("abcdef".as_bytes()); let offsets_buffer = Buffer::from_slice_ref([0i64, 2i64]); ArrayData::try_new( DataType::LargeUtf8, 2, None, 0, vec![offsets_buffer, data_buffer], vec![], ) .unwrap(); } #[test] #[should_panic(expected = "Error converting offset[0] (-2) to usize for Utf8")] fn test_validate_offsets_negative_first_i32() { let data_buffer = Buffer::from_slice_ref("abcdef".as_bytes()); let offsets_buffer = Buffer::from_slice_ref([-2i32, 1i32, 3i32]); ArrayData::try_new( DataType::Utf8, 2, None, 0, vec![offsets_buffer, data_buffer], vec![], ) .unwrap(); } #[test] #[should_panic(expected = "Error converting offset[2] (-3) to usize for Utf8")] fn test_validate_offsets_negative_last_i32() { let data_buffer = Buffer::from_slice_ref("abcdef".as_bytes()); let offsets_buffer = Buffer::from_slice_ref([0i32, 2i32, -3i32]); ArrayData::try_new( DataType::Utf8, 2, None, 0, vec![offsets_buffer, data_buffer], vec![], ) .unwrap(); } #[test] #[should_panic(expected = "First offset 4 in Utf8 is smaller than last offset 3")] fn test_validate_offsets_range_too_small() { let data_buffer = Buffer::from_slice_ref("abcdef".as_bytes()); // start offset is larger than end let offsets_buffer = Buffer::from_slice_ref([4i32, 2i32, 3i32]); ArrayData::try_new( DataType::Utf8, 2, None, 0, vec![offsets_buffer, data_buffer], vec![], ) .unwrap(); } #[test] #[should_panic(expected = "Last offset 10 of Utf8 is larger than values length 6")] fn test_validate_offsets_range_too_large() { let data_buffer = Buffer::from_slice_ref("abcdef".as_bytes()); // 10 is off the end of the buffer let offsets_buffer = Buffer::from_slice_ref([0i32, 2i32, 10i32]); ArrayData::try_new( DataType::Utf8, 2, None, 0, vec![offsets_buffer, data_buffer], vec![], ) .unwrap(); } #[test] #[should_panic(expected = "First offset 10 of Utf8 is larger than values length 6")] fn test_validate_offsets_first_too_large() { let data_buffer = Buffer::from_slice_ref("abcdef".as_bytes()); // 10 is off the end of the buffer let offsets_buffer = Buffer::from_slice_ref([10i32, 2i32, 10i32]); ArrayData::try_new( DataType::Utf8, 2, None, 0, vec![offsets_buffer, data_buffer], vec![], ) .unwrap(); } #[test] fn test_validate_offsets_first_too_large_skipped() { let data_buffer = Buffer::from_slice_ref("abcdef".as_bytes()); // 10 is off the end of the buffer, but offset starts at 1 so it is skipped let offsets_buffer = Buffer::from_slice_ref([10i32, 2i32, 3i32, 4i32]); let data = ArrayData::try_new( DataType::Utf8, 2, None, 1, vec![offsets_buffer, data_buffer], vec![], ) .unwrap(); let array: StringArray = data.into(); let expected: StringArray = vec![Some("c"), Some("d")].into_iter().collect(); assert_eq!(array, expected); } #[test] #[should_panic(expected = "Last offset 8 of Utf8 is larger than values length 6")] fn test_validate_offsets_last_too_large() { let data_buffer = Buffer::from_slice_ref("abcdef".as_bytes()); // 10 is off the end of the buffer let offsets_buffer = Buffer::from_slice_ref([5i32, 7i32, 8i32]); ArrayData::try_new( DataType::Utf8, 2, None, 0, vec![offsets_buffer, data_buffer], vec![], ) .unwrap(); } /// Test that the list of type `data_type` generates correct offset and size out of bounds errors fn check_list_view_offsets_sizes( data_type: DataType, offsets: Vec, sizes: Vec, ) { let values: Int32Array = [Some(1), Some(2), Some(3), Some(4)].into_iter().collect(); let offsets_buffer = Buffer::from_slice_ref(offsets); let sizes_buffer = Buffer::from_slice_ref(sizes); ArrayData::try_new( data_type, 4, None, 0, vec![offsets_buffer, sizes_buffer], vec![values.into_data()], ) .unwrap(); } #[test] #[should_panic(expected = "Size 3 at index 3 is larger than the remaining values for ListView")] fn test_validate_list_view_offsets_sizes() { let field_type = Field::new("f", DataType::Int32, true); check_list_view_offsets_sizes::( DataType::ListView(Arc::new(field_type)), vec![0, 1, 1, 2], vec![1, 1, 1, 3], ); } #[test] #[should_panic( expected = "Size 3 at index 3 is larger than the remaining values for LargeListView" )] fn test_validate_large_list_view_offsets_sizes() { let field_type = Field::new("f", DataType::Int32, true); check_list_view_offsets_sizes::( DataType::LargeListView(Arc::new(field_type)), vec![0, 1, 1, 2], vec![1, 1, 1, 3], ); } #[test] #[should_panic(expected = "Error converting offset[1] (-1) to usize for ListView")] fn test_validate_list_view_negative_offsets() { let field_type = Field::new("f", DataType::Int32, true); check_list_view_offsets_sizes::( DataType::ListView(Arc::new(field_type)), vec![0, -1, 1, 2], vec![1, 1, 1, 3], ); } #[test] #[should_panic(expected = "Error converting size[2] (-1) to usize for ListView")] fn test_validate_list_view_negative_sizes() { let field_type = Field::new("f", DataType::Int32, true); check_list_view_offsets_sizes::( DataType::ListView(Arc::new(field_type)), vec![0, 1, 1, 2], vec![1, 1, -1, 3], ); } #[test] #[should_panic(expected = "Error converting offset[1] (-1) to usize for LargeListView")] fn test_validate_large_list_view_negative_offsets() { let field_type = Field::new("f", DataType::Int32, true); check_list_view_offsets_sizes::( DataType::LargeListView(Arc::new(field_type)), vec![0, -1, 1, 2], vec![1, 1, 1, 3], ); } #[test] #[should_panic(expected = "Error converting size[2] (-1) to usize for LargeListView")] fn test_validate_large_list_view_negative_sizes() { let field_type = Field::new("f", DataType::Int32, true); check_list_view_offsets_sizes::( DataType::LargeListView(Arc::new(field_type)), vec![0, 1, 1, 2], vec![1, 1, -1, 3], ); } #[test] #[should_panic( expected = "Values length 4 is less than the length (3) multiplied by the value size (2) for FixedSizeList" )] fn test_validate_fixed_size_list() { // child has 4 elements, let child_array = vec![Some(1), Some(2), Some(3), None] .into_iter() .collect::(); // but claim we have 3 elements for a fixed size of 2 // 10 is off the end of the buffer let field = Field::new("field", DataType::Int32, true); ArrayData::try_new( DataType::FixedSizeList(Arc::new(field), 2), 3, None, 0, vec![], vec![child_array.into_data()], ) .unwrap(); } #[test] #[should_panic(expected = "Child type mismatch for Struct")] fn test_validate_struct_child_type() { let field1 = vec![Some(1), Some(2), Some(3), None] .into_iter() .collect::(); // validate the the type of struct fields matches child fields ArrayData::try_new( DataType::Struct(vec![Field::new("field1", DataType::Int64, true)].into()), 3, None, 0, vec![], vec![field1.into_data()], ) .unwrap(); } #[test] #[should_panic( expected = "child array #0 for field field1 has length smaller than expected for struct array (4 < 6)" )] fn test_validate_struct_child_length() { // field length only has 4 items, but array claims to have 6 let field1 = vec![Some(1), Some(2), Some(3), None] .into_iter() .collect::(); ArrayData::try_new( DataType::Struct(vec![Field::new("field1", DataType::Int32, true)].into()), 6, None, 0, vec![], vec![field1.into_data()], ) .unwrap(); } /// Test that the array of type `data_type` that has invalid utf8 data errors fn check_utf8_validation(data_type: DataType) { // 0x80 is a utf8 continuation sequence and is not a valid utf8 sequence itself let data_buffer = Buffer::from_slice_ref([b'a', b'a', 0x80, 0x00]); let offsets: Vec = [0, 2, 3] .iter() .map(|&v| T::from_usize(v).unwrap()) .collect(); let offsets_buffer = Buffer::from_slice_ref(offsets); ArrayData::try_new( data_type, 2, None, 0, vec![offsets_buffer, data_buffer], vec![], ) .unwrap(); } #[test] #[should_panic(expected = "Invalid UTF8 sequence at string index 1 (2..3)")] fn test_validate_utf8_content() { check_utf8_validation::(DataType::Utf8); } #[test] #[should_panic(expected = "Invalid UTF8 sequence at string index 1 (2..3)")] fn test_validate_large_utf8_content() { check_utf8_validation::(DataType::LargeUtf8); } /// Tests that offsets are at valid codepoint boundaries fn check_utf8_char_boundary(data_type: DataType) { let data_buffer = Buffer::from("🙀".as_bytes()); let offsets: Vec = [0, 1, data_buffer.len()] .iter() .map(|&v| T::from_usize(v).unwrap()) .collect(); let offsets_buffer = Buffer::from_slice_ref(offsets); ArrayData::try_new( data_type, 2, None, 0, vec![offsets_buffer, data_buffer], vec![], ) .unwrap(); } #[test] #[should_panic(expected = "incomplete utf-8 byte sequence from index 0")] fn test_validate_utf8_char_boundary() { check_utf8_char_boundary::(DataType::Utf8); } #[test] #[should_panic(expected = "incomplete utf-8 byte sequence from index 0")] fn test_validate_large_utf8_char_boundary() { check_utf8_char_boundary::(DataType::LargeUtf8); } /// Test that the array of type `data_type` that has invalid indexes (out of bounds) fn check_index_out_of_bounds_validation(data_type: DataType) { let data_buffer = Buffer::from_slice_ref([b'a', b'b', b'c', b'd']); // First two offsets are fine, then 5 is out of bounds let offsets: Vec = [0, 1, 2, 5, 2] .iter() .map(|&v| T::from_usize(v).unwrap()) .collect(); let offsets_buffer = Buffer::from_slice_ref(offsets); ArrayData::try_new( data_type, 4, None, 0, vec![offsets_buffer, data_buffer], vec![], ) .unwrap(); } #[test] #[should_panic(expected = "Offset invariant failure: offset at position 3 out of bounds: 5 > 4")] fn test_validate_utf8_out_of_bounds() { check_index_out_of_bounds_validation::(DataType::Utf8); } #[test] #[should_panic(expected = "Offset invariant failure: offset at position 3 out of bounds: 5 > 4")] fn test_validate_large_utf8_out_of_bounds() { check_index_out_of_bounds_validation::(DataType::LargeUtf8); } #[test] #[should_panic(expected = "Offset invariant failure: offset at position 3 out of bounds: 5 > 4")] fn test_validate_binary_out_of_bounds() { check_index_out_of_bounds_validation::(DataType::Binary); } #[test] #[should_panic(expected = "Offset invariant failure: offset at position 3 out of bounds: 5 > 4")] fn test_validate_large_binary_out_of_bounds() { check_index_out_of_bounds_validation::(DataType::LargeBinary); } // validate that indexes don't go bacwards check indexes that go backwards fn check_index_backwards_validation(data_type: DataType) { let data_buffer = Buffer::from_slice_ref([b'a', b'b', b'c', b'd']); // First three offsets are fine, then 1 goes backwards let offsets: Vec = [0, 1, 2, 2, 1] .iter() .map(|&v| T::from_usize(v).unwrap()) .collect(); let offsets_buffer = Buffer::from_slice_ref(offsets); ArrayData::try_new( data_type, 4, None, 0, vec![offsets_buffer, data_buffer], vec![], ) .unwrap(); } #[test] #[should_panic(expected = "Offset invariant failure: non-monotonic offset at slot 3: 2 > 1")] fn test_validate_utf8_index_backwards() { check_index_backwards_validation::(DataType::Utf8); } #[test] #[should_panic(expected = "Offset invariant failure: non-monotonic offset at slot 3: 2 > 1")] fn test_validate_large_utf8_index_backwards() { check_index_backwards_validation::(DataType::LargeUtf8); } #[test] #[should_panic(expected = "Offset invariant failure: non-monotonic offset at slot 3: 2 > 1")] fn test_validate_binary_index_backwards() { check_index_backwards_validation::(DataType::Binary); } #[test] #[should_panic(expected = "Offset invariant failure: non-monotonic offset at slot 3: 2 > 1")] fn test_validate_large_binary_index_backwards() { check_index_backwards_validation::(DataType::LargeBinary); } #[test] #[should_panic(expected = "Value at position 1 out of bounds: 3 (should be in [0, 1])")] fn test_validate_dictionary_index_too_large() { let values: StringArray = [Some("foo"), Some("bar")].into_iter().collect(); // 3 is not a valid index into the values (only 0 and 1) let keys: Int32Array = [Some(1), Some(3)].into_iter().collect(); let data_type = DataType::Dictionary( Box::new(keys.data_type().clone()), Box::new(values.data_type().clone()), ); ArrayData::try_new( data_type, 2, None, 0, vec![keys.into_data().buffers()[0].clone()], vec![values.into_data()], ) .unwrap(); } #[test] #[should_panic(expected = "Value at position 1 out of bounds: -1 (should be in [0, 1]")] fn test_validate_dictionary_index_negative() { let values: StringArray = [Some("foo"), Some("bar")].into_iter().collect(); // -1 is not a valid index at all! let keys: Int32Array = [Some(1), Some(-1)].into_iter().collect(); let data_type = DataType::Dictionary( Box::new(keys.data_type().clone()), Box::new(values.data_type().clone()), ); ArrayData::try_new( data_type, 2, None, 0, vec![keys.into_data().buffers()[0].clone()], vec![values.into_data()], ) .unwrap(); } #[test] fn test_validate_dictionary_index_negative_but_not_referenced() { let values: StringArray = [Some("foo"), Some("bar")].into_iter().collect(); // -1 is not a valid index at all, but the array is length 1 // so the -1 should not be looked at let keys: Int32Array = [Some(1), Some(-1)].into_iter().collect(); let data_type = DataType::Dictionary( Box::new(keys.data_type().clone()), Box::new(values.data_type().clone()), ); // Expect this not to panic ArrayData::try_new( data_type, 1, None, 0, vec![keys.into_data().buffers()[0].clone()], vec![values.into_data()], ) .unwrap(); } #[test] #[should_panic( expected = "Value at position 0 out of bounds: 18446744073709551615 (can not convert to i64)" )] fn test_validate_dictionary_index_giant_negative() { let values: StringArray = [Some("foo"), Some("bar")].into_iter().collect(); // -1 is not a valid index at all! let keys: UInt64Array = [Some(u64::MAX), Some(1)].into_iter().collect(); let data_type = DataType::Dictionary( Box::new(keys.data_type().clone()), Box::new(values.data_type().clone()), ); ArrayData::try_new( data_type, 2, None, 0, vec![keys.into_data().buffers()[0].clone()], vec![values.into_data()], ) .unwrap(); } /// Test that the list of type `data_type` generates correct offset out of bounds errors fn check_list_offsets(data_type: DataType) { let values: Int32Array = [Some(1), Some(2), Some(3), Some(4)].into_iter().collect(); // 5 is an invalid offset into a list of only three values let offsets: Vec = [0, 2, 5, 4] .iter() .map(|&v| T::from_usize(v).unwrap()) .collect(); let offsets_buffer = Buffer::from_slice_ref(offsets); ArrayData::try_new( data_type, 3, None, 0, vec![offsets_buffer], vec![values.into_data()], ) .unwrap(); } #[test] #[should_panic(expected = "Offset invariant failure: offset at position 2 out of bounds: 5 > 4")] fn test_validate_list_offsets() { let field_type = Field::new("f", DataType::Int32, true); check_list_offsets::(DataType::List(Arc::new(field_type))); } #[test] #[should_panic(expected = "Offset invariant failure: offset at position 2 out of bounds: 5 > 4")] fn test_validate_large_list_offsets() { let field_type = Field::new("f", DataType::Int32, true); check_list_offsets::(DataType::LargeList(Arc::new(field_type))); } /// Test that the list of type `data_type` generates correct errors for negative offsets #[test] #[should_panic( expected = "Offset invariant failure: Could not convert offset -1 to usize at position 2" )] fn test_validate_list_negative_offsets() { let values: Int32Array = [Some(1), Some(2), Some(3), Some(4)].into_iter().collect(); let field_type = Field::new("f", values.data_type().clone(), true); let data_type = DataType::List(Arc::new(field_type)); // -1 is an invalid offset any way you look at it let offsets: Vec = vec![0, 2, -1, 4]; let offsets_buffer = Buffer::from_slice_ref(offsets); ArrayData::try_new( data_type, 3, None, 0, vec![offsets_buffer], vec![values.into_data()], ) .unwrap(); } /// returns a buffer initialized with some constant value for tests fn make_i32_buffer(n: usize) -> Buffer { Buffer::from_slice_ref(vec![42i32; n]) } #[test] #[should_panic(expected = "Expected Int64 but child data had Int32")] fn test_validate_union_different_types() { let field1 = vec![Some(1), Some(2)].into_iter().collect::(); let field2 = vec![Some(1), Some(2)].into_iter().collect::(); let type_ids = Buffer::from_slice_ref([0i8, 1i8]); ArrayData::try_new( DataType::Union( UnionFields::try_new( vec![0, 1], vec![ Field::new("field1", DataType::Int32, true), Field::new("field2", DataType::Int64, true), // data is int32 ], ) .unwrap(), UnionMode::Sparse, ), 2, None, 0, vec![type_ids], vec![field1.into_data(), field2.into_data()], ) .unwrap(); } // sparse with wrong sized children #[test] #[should_panic( expected = "Sparse union child array #1 has length smaller than expected for union array (1 < 2)" )] fn test_validate_union_sparse_different_child_len() { let field1 = vec![Some(1), Some(2)].into_iter().collect::(); // field 2 only has 1 item but array should have 2 let field2 = vec![Some(1)].into_iter().collect::(); let type_ids = Buffer::from_slice_ref([0i8, 1i8]); ArrayData::try_new( DataType::Union( UnionFields::try_new( vec![0, 1], vec![ Field::new("field1", DataType::Int32, true), Field::new("field2", DataType::Int64, true), ], ) .unwrap(), UnionMode::Sparse, ), 2, None, 0, vec![type_ids], vec![field1.into_data(), field2.into_data()], ) .unwrap(); } #[test] #[should_panic(expected = "Expected 2 buffers in array of type Union")] fn test_validate_union_dense_without_offsets() { let field1 = vec![Some(1), Some(2)].into_iter().collect::(); let field2 = vec![Some(1)].into_iter().collect::(); let type_ids = Buffer::from_slice_ref([0i8, 1i8]); ArrayData::try_new( DataType::Union( UnionFields::try_new( vec![0, 1], vec![ Field::new("field1", DataType::Int32, true), Field::new("field2", DataType::Int64, true), ], ) .unwrap(), UnionMode::Dense, ), 2, None, 0, vec![type_ids], // need offsets buffer here too vec![field1.into_data(), field2.into_data()], ) .unwrap(); } #[test] #[should_panic(expected = "Need at least 8 bytes in buffers[1] in array of type Union")] fn test_validate_union_dense_with_bad_len() { let field1 = vec![Some(1), Some(2)].into_iter().collect::(); let field2 = vec![Some(1)].into_iter().collect::(); let type_ids = Buffer::from_slice_ref([0i8, 1i8]); let offsets = Buffer::from_slice_ref([0i32]); // should have 2 offsets, but only have 1 ArrayData::try_new( DataType::Union( UnionFields::try_new( vec![0, 1], vec![ Field::new("field1", DataType::Int32, true), Field::new("field2", DataType::Int64, true), ], ) .unwrap(), UnionMode::Dense, ), 2, None, 0, vec![type_ids, offsets], vec![field1.into_data(), field2.into_data()], ) .unwrap(); } #[test] fn test_try_new_sliced_struct() { let mut builder = StructBuilder::new( vec![ Field::new("a", DataType::Int32, true), Field::new("b", DataType::Boolean, true), ], vec![ Box::new(Int32Builder::with_capacity(5)), Box::new(BooleanBuilder::with_capacity(5)), ], ); // struct[0] = { a: 10, b: true } builder .field_builder::(0) .unwrap() .append_option(Some(10)); builder .field_builder::(1) .unwrap() .append_option(Some(true)); builder.append(true); // struct[1] = null builder .field_builder::(0) .unwrap() .append_option(None); builder .field_builder::(1) .unwrap() .append_option(None); builder.append(false); // struct[2] = { a: null, b: false } builder .field_builder::(0) .unwrap() .append_option(None); builder .field_builder::(1) .unwrap() .append_option(Some(false)); builder.append(true); // struct[3] = { a: 21, b: null } builder .field_builder::(0) .unwrap() .append_option(Some(21)); builder .field_builder::(1) .unwrap() .append_option(None); builder.append(true); // struct[4] = { a: 18, b: false } builder .field_builder::(0) .unwrap() .append_option(Some(18)); builder .field_builder::(1) .unwrap() .append_option(Some(false)); builder.append(true); let struct_array = builder.finish(); let struct_array_slice = struct_array.slice(1, 3); assert_eq!(struct_array_slice, struct_array_slice); } #[test] fn test_string_data_from_foreign() { let mut strings = "foobarfoobar".to_owned(); let mut offsets = vec![0_i32, 0, 3, 6, 12]; let mut bitmap = vec![0b1110_u8]; let strings_buffer = unsafe { Buffer::from_custom_allocation( NonNull::new_unchecked(strings.as_mut_ptr()), strings.len(), Arc::new(strings), ) }; let offsets_buffer = unsafe { Buffer::from_custom_allocation( NonNull::new_unchecked(offsets.as_mut_ptr() as *mut u8), offsets.len() * std::mem::size_of::(), Arc::new(offsets), ) }; let null_buffer = unsafe { Buffer::from_custom_allocation( NonNull::new_unchecked(bitmap.as_mut_ptr()), bitmap.len(), Arc::new(bitmap), ) }; let data = ArrayData::try_new( DataType::Utf8, 4, Some(null_buffer), 0, vec![offsets_buffer, strings_buffer], vec![], ) .unwrap(); let array = make_array(data); let array = array.as_any().downcast_ref::().unwrap(); let expected = StringArray::from(vec![None, Some("foo"), Some("bar"), Some("foobar")]); assert_eq!(array, &expected); } #[test] fn test_decimal_full_validation() { let array = Decimal128Array::from(vec![123456_i128]) .with_precision_and_scale(5, 2) .unwrap(); let error = array.validate_decimal_precision(5).unwrap_err(); assert_eq!( "Invalid argument error: 1234.56 is too large to store in a Decimal128 of precision 5. Max is 999.99", error.to_string() ); let array = Decimal128Array::from(vec![123456_i128]); let error = array.validate_decimal_precision(5).unwrap_err(); assert_eq!( "Invalid argument error: Decimal precision 5 is less than scale 10", error.to_string() ); } #[test] fn test_decimal_validation() { let mut builder = Decimal128Builder::with_capacity(4); builder.append_value(10000); builder.append_value(20000); let array = builder.finish(); array.into_data().validate_full().unwrap(); } #[test] #[cfg(not(feature = "force_validate"))] fn test_sliced_array_child() { let values = Int32Array::from_iter_values([1, 2, 3]); let values_sliced = values.slice(1, 2); let offsets = Buffer::from_iter([1_i32, 3_i32]); let list_field = Field::new("element", DataType::Int32, false); let data_type = DataType::List(Arc::new(list_field)); let data = unsafe { ArrayData::new_unchecked( data_type, 1, None, None, 0, vec![offsets], vec![values_sliced.into_data()], ) }; let err = data.validate_values().unwrap_err(); assert_eq!( err.to_string(), "Invalid argument error: Offset invariant failure: offset at position 1 out of bounds: 3 > 2" ); }