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 516aa56ff7 [57_maintenance] fix: use correct length in runarray
validation (#11801)
516aa56ff7 is described below
commit 516aa56ff7952c0d955b4cd99646ba172cec01ac
Author: Jeffrey Vo <[email protected]>
AuthorDate: Fri Oct 23 00:19:44 2026 +0900
[57_maintenance] fix: use correct length in runarray validation (#11801)
- Backport https://github.com/apache/arrow-rs/pull/11668
- For inclusion in https://github.com/apache/arrow-rs/issues/11666
Cherry-pick of
https://github.com/apache/arrow-rs/commit/a9455d4a269d1edce4891c0927fed1c2bf24fb72
Co-authored-by: Andrew Lamb <[email protected]>
---
arrow-data/src/data.rs | 111 +++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 103 insertions(+), 8 deletions(-)
diff --git a/arrow-data/src/data.rs b/arrow-data/src/data.rs
index b3e005ee61..981f0e696f 100644
--- a/arrow-data/src/data.rs
+++ b/arrow-data/src/data.rs
@@ -1402,10 +1402,18 @@ impl ArrayData {
}
DataType::RunEndEncoded(run_ends, _values) => {
let run_ends_data = self.child_data()[0].clone();
+ let len_plus_offset =
+ checked_len_plus_offset(&self.data_type, self.len,
self.offset)?;
match run_ends.data_type() {
- DataType::Int16 => run_ends_data.check_run_ends::<i16>(),
- DataType::Int32 => run_ends_data.check_run_ends::<i32>(),
- DataType::Int64 => run_ends_data.check_run_ends::<i64>(),
+ DataType::Int16 => {
+ run_ends_data.check_run_ends::<i16>(self.len,
len_plus_offset)
+ }
+ DataType::Int32 => {
+ run_ends_data.check_run_ends::<i32>(self.len,
len_plus_offset)
+ }
+ DataType::Int64 => {
+ run_ends_data.check_run_ends::<i64>(self.len,
len_plus_offset)
+ }
_ => unreachable!(),
}
}
@@ -1553,7 +1561,11 @@ impl ArrayData {
}
/// Validates that each value in run_ends array is positive and strictly
increasing.
- fn check_run_ends<T>(&self) -> Result<(), ArrowError>
+ fn check_run_ends<T>(
+ &self,
+ parent_len: usize,
+ parent_len_plus_offset: usize,
+ ) -> Result<(), ArrowError>
where
T: ArrowNativeType + TryInto<i64> + num_traits::Num +
std::fmt::Display,
{
@@ -1580,11 +1592,9 @@ impl ArrayData {
Ok(())
})?;
- let len_plus_offset = checked_len_plus_offset(&self.data_type,
self.len, self.offset)?;
- if prev_value.as_usize() < len_plus_offset {
+ if parent_len > 0 && prev_value.as_usize() < parent_len_plus_offset {
return Err(ArrowError::InvalidArgumentError(format!(
- "The offset + length of array should be less or equal to last
value in the run_ends array. The last value of run_ends array is {prev_value}
and offset + length of array is {}.",
- len_plus_offset
+ "The offset + length of array should be less or equal to last
value in the run_ends array. The last value of run_ends array is {prev_value}
and offset + length of array is {parent_len_plus_offset}."
)));
}
Ok(())
@@ -2622,4 +2632,89 @@ mod tests {
assert!(array.is_null(i));
}
}
+
+ /// Returns a `RunEndEncoded<Int32, Float32>` data type and run ends /
values
+ /// children whose runs end at `[1, 3]`, i.e. a logical length of 3.
+ fn run_array_test_children() -> (DataType, Vec<ArrayData>) {
+ let dt = DataType::RunEndEncoded(
+ Arc::new(Field::new("run_ends", DataType::Int32, false)),
+ Arc::new(Field::new("values", DataType::Float32, true)),
+ );
+
+ let runs = ArrayData::builder(DataType::Int32)
+ .len(2)
+ .add_buffer(vec![1_i32, 3].into())
+ .build()
+ .unwrap();
+ let values = ArrayData::builder(DataType::Float32)
+ .len(2)
+ .add_buffer(vec![1.5_f32, 2.5].into())
+ .build()
+ .unwrap();
+
+ (dt, vec![runs, values])
+ }
+
+ #[test]
+ fn test_run_array_validates_last_run() {
+ let (dt, children) = run_array_test_children();
+
+ let err = ArrayDataBuilder::new(dt)
+ .len(4)
+ .offset(0)
+ .child_data(children)
+ .build()
+ .unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "Invalid argument error: The offset + length of array should be
less or equal to last value in the run_ends array. The last value of run_ends
array is 3 and offset + length of array is 4."
+ );
+ }
+
+ #[test]
+ fn test_run_array_empty_validates() {
+ let (dt, children) = run_array_test_children();
+
+ let data = ArrayDataBuilder::new(dt)
+ .len(0)
+ .offset(4)
+ .child_data(children)
+ .build()
+ .unwrap();
+ assert_eq!(data.len(), 0);
+ }
+
+ /// The parent's offset must be included when checking the last run end:
+ /// offset 2 + length 2 = 4 exceeds the last run end of 3.
+ #[test]
+ fn test_run_array_validates_last_run_with_offset() {
+ let (dt, children) = run_array_test_children();
+
+ let err = ArrayDataBuilder::new(dt)
+ .len(2)
+ .offset(2)
+ .child_data(children)
+ .build()
+ .unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "Invalid argument error: The offset + length of array should be
less or equal to last value in the run_ends array. The last value of run_ends
array is 3 and offset + length of array is 4."
+ );
+ }
+
+ /// A slice that stays within the last run end is valid:
+ /// offset 1 + length 2 = 3 equals the last run end of 3.
+ #[test]
+ fn test_run_array_sliced_validates() {
+ let (dt, children) = run_array_test_children();
+
+ let data = ArrayDataBuilder::new(dt)
+ .len(2)
+ .offset(1)
+ .child_data(children)
+ .build()
+ .unwrap();
+ assert_eq!(data.len(), 2);
+ assert_eq!(data.offset(), 1);
+ }
}