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);
+    }
 }

Reply via email to