This is an automated email from the ASF dual-hosted git repository.
Jefffrey pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-rs.git
The following commit(s) were added to refs/heads/main by this push:
new 8205c5421f fix: don't panic on `ArrayData::try_new` on bad input even
when `force_validate` feature is on (#10282)
8205c5421f is described below
commit 8205c5421f2f08c22c74be909beaf8afb2774011
Author: Raz Luvaton <[email protected]>
AuthorDate: Sun Jul 5 11:13:21 2026 +0300
fix: don't panic on `ArrayData::try_new` on bad input even when
`force_validate` feature is on (#10282)
# Which issue does this PR close?
N/A
# Rationale for this change
the caller of `try_new` is responsible for handling errors, so it should
not panic even when `force_validate` feature is on.
the added benefit is that validation is not called twice on `try_new`
when `force_validate` feature is on
# What changes are included in this PR?
use builder validation call
# Are these changes tested?
yes
# Are there any user-facing changes?
error instead of panic
-----
Found while testing:
- #10272
---
arrow-data/src/data.rs | 91 +++++++++++++++++++++++++++++++++++---------------
1 file changed, 65 insertions(+), 26 deletions(-)
diff --git a/arrow-data/src/data.rs b/arrow-data/src/data.rs
index 0ce98aa090..19e3037846 100644
--- a/arrow-data/src/data.rs
+++ b/arrow-data/src/data.rs
@@ -294,24 +294,18 @@ impl ArrayData {
buffers: Vec<Buffer>,
child_data: Vec<ArrayData>,
) -> Self {
- let mut skip_validation = UnsafeFlag::new();
- // SAFETY: caller responsible for ensuring data is valid
- unsafe { skip_validation.set(true) };
-
- ArrayDataBuilder {
+ let builder = Self::inner_new_builder(
data_type,
len,
null_count,
null_bit_buffer,
- nulls: None,
offset,
buffers,
child_data,
- align_buffers: false,
- skip_validation,
- }
- .build()
- .unwrap()
+ );
+
+ // SAFETY: caller responsible for ensuring data is valid
+ unsafe { builder.build_unchecked() }
}
/// Create a new ArrayData, validating that the provided buffers form a
valid
@@ -337,7 +331,7 @@ impl ArrayData {
) -> Result<Self, ArrowError> {
// we must check the length of `null_bit_buffer` first
// because we use this buffer to calculate `null_count`
- // in `Self::new_unchecked`.
+ // in `ArrayDataBuilder::build`.
if let Some(null_bit_buffer) = null_bit_buffer.as_ref() {
let len_plus_offset = checked_len_plus_offset(&data_type, len,
offset)?;
let needed_len = bit_util::ceil(len_plus_offset, 8);
@@ -349,25 +343,47 @@ impl ArrayData {
)));
}
}
- // Safety justification: `validate_full` is called below
- let new_self = unsafe {
- Self::new_unchecked(
- data_type,
- len,
- None,
- null_bit_buffer,
- offset,
- buffers,
- child_data,
- )
- };
+
+ let builder = Self::inner_new_builder(
+ data_type,
+ len,
+ None,
+ null_bit_buffer,
+ offset,
+ buffers,
+ child_data,
+ );
+
+ assert!(!builder.skip_validation.get());
// As the data is not trusted, do a full validation of its contents
// We don't need to validate children as we can assume that the
// [`ArrayData`] in `child_data` have already been validated through
// a call to `ArrayData::try_new` or created using unsafe
- new_self.validate_data()?;
- Ok(new_self)
+ builder.build()
+ }
+
+ fn inner_new_builder(
+ data_type: DataType,
+ len: usize,
+ null_count: Option<usize>,
+ null_bit_buffer: Option<Buffer>,
+ offset: usize,
+ buffers: Vec<Buffer>,
+ child_data: Vec<ArrayData>,
+ ) -> ArrayDataBuilder {
+ ArrayDataBuilder {
+ data_type,
+ len,
+ null_count,
+ null_bit_buffer,
+ nulls: None,
+ offset,
+ buffers,
+ child_data,
+ align_buffers: false,
+ skip_validation: UnsafeFlag::new(),
+ }
}
/// Return the constituent parts of this ArrayData
@@ -2727,4 +2743,27 @@ mod tests {
assert!(array.is_null(i));
}
}
+
+ // Even when `force_validate` feature is on
+ #[test]
+ fn test_dont_panic_on_bad_input_when_using_try_new() {
+ let empty_bytes = Buffer::default();
+
+ let array_data = ArrayData::try_new(
+ DataType::Utf8,
+ 1, // len
+ None,
+ 0,
+ // the offsets says that we have 2 bytes but the buffer is empty
+ vec![Buffer::from_vec(vec![0i32, 2i32]), empty_bytes],
+ vec![],
+ );
+
+ let res = array_data.expect_err("should get error");
+
+ assert_eq!(
+ res.to_string(),
+ format!("Invalid argument error: Last offset 2 of Utf8 is larger
than values length 0",)
+ );
+ }
}