Jefffrey commented on code in PR #11764:
URL: https://github.com/apache/arrow-rs/pull/11764#discussion_r4442061137


##########
arrow-row/src/run.rs:
##########
@@ -140,25 +142,45 @@ pub unsafe fn decode<R: RunEndIndexType>(
 
     // Convert the unique decoded values using the row converter
     let mut unique_rows: Vec<&[u8]> = decoded_values.iter().map(|v| 
v.as_slice()).collect();
-    let values = if unique_rows.is_empty() {
-        unsafe { converter.convert_raw(&mut [], validate_utf8) }?
-    } else {
-        unsafe { converter.convert_raw(&mut unique_rows, validate_utf8) }?
-    };
+    let values = unsafe { converter.convert_raw(&mut unique_rows, 
validate_utf8) }?;
 
-    // Create run ends array
-    let run_ends_array = 
PrimitiveArray::<R>::try_new(ScalarBuffer::from(run_ends), None)?;
+    let run_ends = PrimitiveArray::<R>::try_new(ScalarBuffer::from(run_ends), 
None)?;
+    let (_, run_ends, values) = RunArray::<R>::try_new(&run_ends, 
&values[0])?.into_parts();
+    let data_type = decoded_run_type(&field.data_type, values.data_type());
+    // SAFETY: run ends and value count are validated above; fields match the 
decoded types.
+    Ok(unsafe { RunArray::<R>::new_unchecked(data_type, run_ends, values) })

Review Comment:
   it seems confusing we build the run array for validation, then deconstruct 
it so we can replace the type; is there a reason we cant calculate the correct 
type upfront?



##########
arrow-row/src/run.rs:
##########
@@ -140,25 +142,45 @@ pub unsafe fn decode<R: RunEndIndexType>(
 
     // Convert the unique decoded values using the row converter
     let mut unique_rows: Vec<&[u8]> = decoded_values.iter().map(|v| 
v.as_slice()).collect();
-    let values = if unique_rows.is_empty() {
-        unsafe { converter.convert_raw(&mut [], validate_utf8) }?
-    } else {
-        unsafe { converter.convert_raw(&mut unique_rows, validate_utf8) }?
-    };
+    let values = unsafe { converter.convert_raw(&mut unique_rows, 
validate_utf8) }?;
 
-    // Create run ends array
-    let run_ends_array = 
PrimitiveArray::<R>::try_new(ScalarBuffer::from(run_ends), None)?;
+    let run_ends = PrimitiveArray::<R>::try_new(ScalarBuffer::from(run_ends), 
None)?;
+    let (_, run_ends, values) = RunArray::<R>::try_new(&run_ends, 
&values[0])?.into_parts();
+    let data_type = decoded_run_type(&field.data_type, values.data_type());
+    // SAFETY: run ends and value count are validated above; fields match the 
decoded types.
+    Ok(unsafe { RunArray::<R>::new_unchecked(data_type, run_ends, values) })
+}
 
-    // Create the RunEndEncodedArray
-    RunArray::<R>::try_new(&run_ends_array, &values[0])
+fn decoded_run_type(data_type: &DataType, values_type: &DataType) -> DataType {
+    match data_type {
+        DataType::RunEndEncoded(run_ends_field, values_field)
+            if values_field.data_type() != values_type =>

Review Comment:
   is this function supposed to be like what we do for lists?
   
   
https://github.com/apache/arrow-rs/blob/e2c48ef0dd83fa464d643e25f8e9f9177ca7316e/arrow-row/src/list.rs#L258-L269
   
   if so, why do we have this equality check here whereas for lists we always 
overwrite the type?



##########
arrow-row/src/run.rs:
##########
@@ -730,6 +793,67 @@ mod tests {
         assert_eq!(result_ree.len(), 0);
     }
 
+    #[test]
+    fn test_run_end_encoded_dictionary_values() {

Review Comment:
   this test code doesnt read well, with how it constructs the test data via 
mapping the iterators. can we simplify it; maybe its easier to just have them 
as separate tests, etc.



##########
arrow-row/src/run.rs:
##########
@@ -730,6 +793,67 @@ mod tests {
         assert_eq!(result_ree.len(), 0);
     }
 
+    #[test]
+    fn test_run_end_encoded_dictionary_values() {
+        let dictionary: DictionaryArray<Int32Type> = ["a", 
"b"].into_iter().collect();
+        let ends = PrimitiveArray::<Int32Type>::from(vec![2, 4]).to_data();
+        for nested in [false, true] {
+            let values = [
+                Arc::new(dictionary.clone()) as ArrayRef,
+                Arc::new(StringArray::from(vec!["a", "b"])) as ArrayRef,
+            ]
+            .map(|values| {
+                if nested {
+                    Arc::new(StructArray::from(vec![(
+                        Arc::new(Field::new("child", 
values.data_type().clone(), false)),
+                        values,
+                    )])) as ArrayRef
+                } else {
+                    values
+                }
+            });
+            let [input, expected] = values.map(|values| {
+                let data_type = DataType::RunEndEncoded(
+                    Arc::new(Field::new("ends", DataType::Int32, false)),
+                    Arc::new(
+                        Field::new("labels", values.data_type().clone(), false)
+                            .with_metadata([("source".to_string(), 
"test".to_string())]),
+                    ),
+                );
+                RunArray::<Int32Type>::from(
+                    ArrayData::builder(data_type)
+                        .len(4)
+                        .child_data(vec![ends.clone(), values.to_data()])
+                        .build()
+                        .unwrap(),
+                )
+            });
+            let converter =
+                
RowConverter::new(vec![SortField::new(input.data_type().clone())]).unwrap();
+            for (offset, len) in [(0, 4), (1, 3), (4, 0)] {
+                let rows = converter
+                    .convert_columns(&[Arc::new(input.slice(offset, len))])
+                    .unwrap();
+                let actual = converter.convert_rows(&rows).unwrap();
+                assert_eq!(actual[0].to_data(), expected.slice(offset, 
len).to_data());
+                actual[0].to_data().validate_full().unwrap();
+            }
+        }
+    }
+
+    #[test]
+    fn test_run_end_encoded_decode_overflow() {
+        let array = RunArray::<Int16Type>::try_new(
+            &PrimitiveArray::from(vec![1]),
+            &Int64Array::from(vec![42]),
+        )
+        .unwrap();
+        let converter = 
RowConverter::new(vec![SortField::new(array.data_type().clone())]).unwrap();
+        let rows = converter.convert_columns(&[Arc::new(array)]).unwrap();
+        let too_many_rows = std::iter::repeat_with(|| 
rows.row(0)).take(i16::MAX as usize + 1);
+        assert!(converter.convert_rows(too_many_rows).is_err());

Review Comment:
   how much memory would this test allocate if we're trying to push the bounds?



##########
arrow-select/src/interleave.rs:
##########
@@ -1004,6 +1003,23 @@ mod tests {
     use arrow_buffer::ScalarBuffer;
     use arrow_schema::{Field, Schema, TimeUnit};
 
+    #[test]
+    fn test_interleave_preserves_run_end_type() {
+        for array in crate::test_util::run_arrays() {
+            let selected: Vec<u32> = (0..array.len() as 
u32).step_by(2).collect();
+            let indices: Vec<_> = selected
+                .iter()
+                .enumerate()
+                .map(|(i, row)| (i % 2, *row as usize))
+                .collect();
+            let expected = crate::take::take(&array, 
&UInt32Array::from(selected), None).unwrap();

Review Comment:
   im hesistant on having take as the oracle; can we have proper expected data 
created, instead of relying on take being correct here? same for filter



##########
arrow-select/src/coalesce.rs:
##########
@@ -943,14 +943,37 @@ mod tests {
     use arrow_array::types::{Int32Type, UInt8Type};
     use arrow_array::{
         BinaryViewArray, DictionaryArray, Int32Array, Int64Array, 
RecordBatchOptions, StringArray,
-        StringViewArray, TimestampNanosecondArray, UInt8Array, UInt32Array, 
UInt64Array,
-        make_array, record_batch,
+        StringViewArray, StructArray, TimestampNanosecondArray, UInt8Array, 
UInt32Array,
+        UInt64Array, make_array, record_batch,
     };
     use arrow_buffer::BooleanBufferBuilder;
     use arrow_schema::{DataType, Field, Schema};
     use rand::{RngExt, SeedableRng};
     use std::ops::Range;
 
+    #[test]
+    fn test_filter_preserves_nested_run_end_type() {

Review Comment:
   is this necessary if we're testing filter? isn't that covered in filter.rs?



##########
arrow-select/src/interleave.rs:
##########
@@ -701,12 +701,11 @@ fn interleave_run_end<R: RunEndIndexType>(
     run_ends_buf.push(R::Native::from_usize(n).unwrap());
 
     let taken_values = interleave(&value_arrays, &dedup_pairs)?;
-    let run_ends = PrimitiveArray::<R>::from_iter_values(run_ends_buf);
-
-    Ok(Arc::new(RunArray::<R>::try_new(
-        &run_ends,
-        taken_values.as_ref(),
-    )?))
+    let run_ends = RunEndBuffer::new(run_ends_buf.into(), 0, n);
+    // SAFETY: inputs have the same type and interleaving produces one value 
per run.

Review Comment:
   `and interleaving produces one value per run.` isnt a strong enough safety 
comment i feel; maybe we are better off having a better api for run array to 
allow creating with fields we want, in a safe api, instead of relying on the 
unchecked constructor



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to