Skip to main content

arrow_row/
run.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use crate::{RowConverter, Rows, SortField, variable};
19use arrow_array::types::RunEndIndexType;
20use arrow_array::{PrimitiveArray, RunArray};
21use arrow_buffer::{ArrowNativeType, ScalarBuffer};
22use arrow_schema::{ArrowError, SortOptions};
23
24/// Computes the lengths of each row for a RunEndEncodedArray
25pub fn compute_lengths<R: RunEndIndexType>(
26    lengths: &mut [usize],
27    rows: &Rows,
28    array: &RunArray<R>,
29) {
30    let run_ends = array.run_ends().sliced_values();
31    let mut logical_start = 0;
32
33    // Iterate over each run and apply the same length to all logical positions in the run
34    for (physical_idx, run_end) in run_ends.enumerate() {
35        let logical_end = run_end.as_usize();
36        let row_len = rows.row_len(physical_idx);
37        let encoded_len = variable::padded_length(Some(row_len));
38
39        // Add the same length for all logical positions in this run
40        for length in &mut lengths[logical_start..logical_end] {
41            *length += encoded_len;
42        }
43
44        logical_start = logical_end;
45    }
46}
47
48/// Encodes the provided `RunEndEncodedArray` to `out` with the provided `SortOptions`
49///
50/// `rows` should contain the encoded values
51pub fn encode<R: RunEndIndexType>(
52    data: &mut [u8],
53    offsets: &mut [usize],
54    rows: &Rows,
55    opts: SortOptions,
56    array: &RunArray<R>,
57) {
58    let run_ends = array.run_ends().sliced_values();
59    let mut logical_idx = 0;
60    let mut offset_idx = 1; // Skip first offset
61
62    // Iterate over each run
63    for (physical_idx, run_end) in run_ends.enumerate() {
64        let run_end = run_end.as_usize();
65        let iteration_count = run_end - logical_idx;
66
67        let first_offset = offsets[offset_idx];
68        let out = &mut data[first_offset..];
69        let bytes_written = variable::encode_one(out, Some(rows.row(physical_idx).data), opts);
70        offsets[offset_idx] += bytes_written;
71        // now if there are multiple logical positions in this run, we can just copy the same encoded data to the next offsets without re-encoding
72        for i in 1..iteration_count {
73            let dst = offsets[offset_idx + i];
74            data.copy_within(first_offset..first_offset + bytes_written, dst);
75            offsets[offset_idx + i] += bytes_written;
76        }
77        logical_idx = run_end;
78        offset_idx += iteration_count;
79    }
80}
81
82/// Decodes a RunEndEncodedArray from `rows` with the provided `options`
83///
84/// # Safety
85///
86/// `rows` must contain valid data for the provided `converter`
87pub unsafe fn decode<R: RunEndIndexType>(
88    converter: &RowConverter,
89    rows: &mut [&[u8]],
90    field: &SortField,
91    validate_utf8: bool,
92) -> Result<RunArray<R>, ArrowError> {
93    if rows.is_empty() {
94        let values = unsafe { converter.convert_raw(&mut [], validate_utf8) }?;
95        let run_ends_array = PrimitiveArray::<R>::try_new(ScalarBuffer::from(vec![]), None)?;
96        return RunArray::<R>::try_new(&run_ends_array, &values[0]);
97    }
98
99    // Decode each row's REE data and collect the decoded values
100    let mut decoded_values = Vec::new();
101    let mut run_ends = Vec::new();
102    let mut unique_row_indices = Vec::new();
103
104    // Process each row to extract its REE data (following decode_binary pattern)
105    let mut decoded_data = Vec::new();
106    for (idx, row) in rows.iter_mut().enumerate() {
107        decoded_data.clear();
108        // Extract the decoded value data from this row
109        let consumed = variable::decode_blocks(row, field.options, |block| {
110            decoded_data.extend_from_slice(block);
111        });
112
113        // Handle bit inversion for descending sort (following decode_binary pattern)
114        if field.options.descending {
115            decoded_data.iter_mut().for_each(|b| *b = !*b);
116        }
117
118        // Update the row to point past the consumed REE data
119        *row = &row[consumed..];
120
121        // Check if this decoded value is the same as the previous one to identify runs
122        let is_new_run =
123            idx == 0 || decoded_data != decoded_values[*unique_row_indices.last().unwrap()];
124
125        if is_new_run {
126            // This is a new unique value - end the previous run if any
127            if idx > 0 {
128                run_ends.push(R::Native::usize_as(idx));
129            }
130            unique_row_indices.push(decoded_values.len());
131            let capacity = decoded_data.capacity();
132            decoded_values.push(std::mem::replace(
133                &mut decoded_data,
134                Vec::with_capacity(capacity),
135            ));
136        }
137    }
138    // Add the final run end
139    run_ends.push(R::Native::usize_as(rows.len()));
140
141    // Convert the unique decoded values using the row converter
142    let mut unique_rows: Vec<&[u8]> = decoded_values.iter().map(|v| v.as_slice()).collect();
143    let values = if unique_rows.is_empty() {
144        unsafe { converter.convert_raw(&mut [], validate_utf8) }?
145    } else {
146        unsafe { converter.convert_raw(&mut unique_rows, validate_utf8) }?
147    };
148
149    // Create run ends array
150    let run_ends_array = PrimitiveArray::<R>::try_new(ScalarBuffer::from(run_ends), None)?;
151
152    // Create the RunEndEncodedArray
153    RunArray::<R>::try_new(&run_ends_array, &values[0])
154}
155
156#[cfg(test)]
157mod tests {
158    use crate::{RowConverter, SortField};
159    use arrow_array::cast::AsArray;
160    use arrow_array::types::{Int16Type, Int32Type, Int64Type, RunEndIndexType};
161    use arrow_array::{Array, Int64Array, PrimitiveArray, RunArray, StringArray};
162    use arrow_schema::{DataType, Field, SortOptions};
163    use std::sync::Arc;
164
165    fn assert_roundtrip<R: RunEndIndexType>(
166        array: &RunArray<R>,
167        run_end_type: DataType,
168        values_type: DataType,
169        sort_options: Option<SortOptions>,
170    ) {
171        let sort_field = if let Some(options) = sort_options {
172            SortField::new_with_options(
173                DataType::RunEndEncoded(
174                    Arc::new(Field::new(
175                        Field::REE_RUN_ENDS_FIELD_DEFAULT_NAME,
176                        run_end_type,
177                        false,
178                    )),
179                    Arc::new(Field::new(
180                        Field::REE_VALUES_FIELD_DEFAULT_NAME,
181                        values_type,
182                        true,
183                    )),
184                ),
185                options,
186            )
187        } else {
188            SortField::new(DataType::RunEndEncoded(
189                Arc::new(Field::new(
190                    Field::REE_RUN_ENDS_FIELD_DEFAULT_NAME,
191                    run_end_type,
192                    false,
193                )),
194                Arc::new(Field::new(
195                    Field::REE_VALUES_FIELD_DEFAULT_NAME,
196                    values_type,
197                    true,
198                )),
199            ))
200        };
201
202        let converter = RowConverter::new(vec![sort_field]).unwrap();
203
204        let rows = converter
205            .convert_columns(&[Arc::new(array.clone())])
206            .unwrap();
207
208        let arrays = converter.convert_rows(&rows).unwrap();
209        let result = arrays[0].as_any().downcast_ref::<RunArray<R>>().unwrap();
210
211        assert_eq!(array, result);
212    }
213
214    #[test]
215    fn test_run_end_encoded_supports_datatype() {
216        // Test that the RowConverter correctly supports run-end encoded arrays
217        assert!(RowConverter::supports_datatype(&DataType::RunEndEncoded(
218            Arc::new(Field::new(
219                Field::REE_RUN_ENDS_FIELD_DEFAULT_NAME,
220                DataType::Int32,
221                false
222            )),
223            Arc::new(Field::new(
224                Field::REE_VALUES_FIELD_DEFAULT_NAME,
225                DataType::Utf8,
226                true
227            )),
228        )));
229    }
230
231    #[test]
232    fn test_run_end_encoded_round_trip_int16_int64s() {
233        // Test round-trip correctness for RunEndEncodedArray with Int64 values making sure it
234        // doesn't just work with eg. strings (which are all the other tests).
235
236        let values = Int64Array::from(vec![100, 200, 100, 300]);
237        let run_ends = vec![2, 3, 5, 6];
238        let array: RunArray<Int16Type> =
239            RunArray::try_new(&PrimitiveArray::from(run_ends), &values).unwrap();
240
241        assert_roundtrip(&array, DataType::Int16, DataType::Int64, None);
242    }
243
244    #[test]
245    fn test_run_end_encoded_round_trip_int32_int64s() {
246        // Test round-trip correctness for RunEndEncodedArray with Int64 values making sure it
247        // doesn't just work with eg. strings (which are all the other tests).
248
249        let values = Int64Array::from(vec![100, 200, 100, 300]);
250        let run_ends = vec![2, 3, 5, 6];
251        let array: RunArray<Int32Type> =
252            RunArray::try_new(&PrimitiveArray::from(run_ends), &values).unwrap();
253
254        assert_roundtrip(&array, DataType::Int32, DataType::Int64, None);
255    }
256
257    #[test]
258    fn test_run_end_encoded_round_trip_int64_int64s() {
259        // Test round-trip correctness for RunEndEncodedArray with Int64 values making sure it
260        // doesn't just work with eg. strings (which are all the other tests).
261
262        let values = Int64Array::from(vec![100, 200, 100, 300]);
263        let run_ends = vec![2, 3, 5, 6];
264        let array: RunArray<Int64Type> =
265            RunArray::try_new(&PrimitiveArray::from(run_ends), &values).unwrap();
266
267        assert_roundtrip(&array, DataType::Int64, DataType::Int64, None);
268    }
269
270    #[test]
271    fn test_run_end_encoded_round_trip_strings() {
272        // Test round-trip correctness for RunEndEncodedArray with strings
273
274        let array: RunArray<Int32Type> = vec!["b", "b", "a"].into_iter().collect();
275
276        assert_roundtrip(&array, DataType::Int32, DataType::Utf8, None);
277    }
278
279    #[test]
280    fn test_run_end_encoded_round_trip_strings_with_nulls() {
281        // Test round-trip correctness for RunEndEncodedArray with nulls
282
283        let array: RunArray<Int32Type> = vec![Some("b"), Some("b"), None, Some("a")]
284            .into_iter()
285            .collect();
286
287        assert_roundtrip(&array, DataType::Int32, DataType::Utf8, None);
288    }
289
290    #[test]
291    fn test_run_end_encoded_ascending_descending_round_trip() {
292        // Test round-trip correctness for ascending vs descending sort options
293
294        let values_asc =
295            arrow_array::StringArray::from(vec![Some("apple"), Some("banana"), Some("cherry")]);
296        let run_ends_asc = vec![2, 4, 6];
297        let run_array_asc: RunArray<Int32Type> = RunArray::try_new(
298            &arrow_array::PrimitiveArray::from(run_ends_asc),
299            &values_asc,
300        )
301        .unwrap();
302
303        // Test ascending order
304        assert_roundtrip(
305            &run_array_asc,
306            DataType::Int32,
307            DataType::Utf8,
308            Some(SortOptions {
309                descending: false,
310                nulls_first: true,
311            }),
312        );
313
314        // Test descending order
315        assert_roundtrip(
316            &run_array_asc,
317            DataType::Int32,
318            DataType::Utf8,
319            Some(SortOptions {
320                descending: true,
321                nulls_first: true,
322            }),
323        );
324    }
325
326    #[test]
327    fn test_run_end_encoded_sort_configurations_basic() {
328        // Test that different sort configurations work and can round-trip successfully
329
330        let test_array: RunArray<Int32Type> = vec!["test"].into_iter().collect();
331
332        // Test ascending order
333        assert_roundtrip(
334            &test_array,
335            DataType::Int32,
336            DataType::Utf8,
337            Some(SortOptions {
338                descending: false,
339                nulls_first: true,
340            }),
341        );
342
343        // Test descending order
344        assert_roundtrip(
345            &test_array,
346            DataType::Int32,
347            DataType::Utf8,
348            Some(SortOptions {
349                descending: true,
350                nulls_first: true,
351            }),
352        );
353    }
354
355    #[test]
356    fn test_run_end_encoded_nulls_first_last_configurations() {
357        // Test that nulls_first vs nulls_last configurations work
358
359        let simple_array: RunArray<Int32Type> = vec!["simple"].into_iter().collect();
360
361        let converter_nulls_first = RowConverter::new(vec![SortField::new_with_options(
362            DataType::RunEndEncoded(
363                Arc::new(Field::new(
364                    Field::REE_RUN_ENDS_FIELD_DEFAULT_NAME,
365                    DataType::Int32,
366                    false,
367                )),
368                Arc::new(Field::new(
369                    Field::REE_VALUES_FIELD_DEFAULT_NAME,
370                    DataType::Utf8,
371                    true,
372                )),
373            ),
374            SortOptions {
375                descending: false,
376                nulls_first: true,
377            },
378        )])
379        .unwrap();
380
381        let converter_nulls_last = RowConverter::new(vec![SortField::new_with_options(
382            DataType::RunEndEncoded(
383                Arc::new(Field::new(
384                    Field::REE_RUN_ENDS_FIELD_DEFAULT_NAME,
385                    DataType::Int32,
386                    false,
387                )),
388                Arc::new(Field::new(
389                    Field::REE_VALUES_FIELD_DEFAULT_NAME,
390                    DataType::Utf8,
391                    true,
392                )),
393            ),
394            SortOptions {
395                descending: false,
396                nulls_first: false,
397            },
398        )])
399        .unwrap();
400
401        // Test that both configurations can handle simple arrays
402        let rows_nulls_first = converter_nulls_first
403            .convert_columns(&[Arc::new(simple_array.clone())])
404            .unwrap();
405        let arrays_nulls_first = converter_nulls_first
406            .convert_rows(&rows_nulls_first)
407            .unwrap();
408        let result_nulls_first = arrays_nulls_first[0]
409            .as_any()
410            .downcast_ref::<RunArray<Int32Type>>()
411            .unwrap();
412
413        let rows_nulls_last = converter_nulls_last
414            .convert_columns(&[Arc::new(simple_array.clone())])
415            .unwrap();
416        let arrays_nulls_last = converter_nulls_last.convert_rows(&rows_nulls_last).unwrap();
417        let result_nulls_last = arrays_nulls_last[0]
418            .as_any()
419            .downcast_ref::<RunArray<Int32Type>>()
420            .unwrap();
421
422        // Both should successfully convert the simple array
423        assert_eq!(simple_array.len(), result_nulls_first.len());
424        assert_eq!(simple_array.len(), result_nulls_last.len());
425    }
426
427    #[test]
428    fn test_run_end_encoded_row_consumption() {
429        // This test verifies that ALL rows are properly consumed during decoding,
430        // not just the unique values. We test this by ensuring multi-column conversion
431        // works correctly - if rows aren't consumed properly, the second column would fail.
432
433        // Create a REE array with multiple runs
434        let array: RunArray<Int32Type> = vec!["a", "a", "b", "b", "b", "c"].into_iter().collect();
435        let string_array = StringArray::from(vec!["x", "y", "z", "w", "u", "v"]);
436
437        let multi_converter = RowConverter::new(vec![
438            SortField::new(DataType::RunEndEncoded(
439                Arc::new(Field::new(
440                    Field::REE_RUN_ENDS_FIELD_DEFAULT_NAME,
441                    DataType::Int32,
442                    false,
443                )),
444                Arc::new(Field::new(
445                    Field::REE_VALUES_FIELD_DEFAULT_NAME,
446                    DataType::Utf8,
447                    true,
448                )),
449            )),
450            SortField::new(DataType::Utf8),
451        ])
452        .unwrap();
453
454        let multi_rows = multi_converter
455            .convert_columns(&[Arc::new(array.clone()), Arc::new(string_array.clone())])
456            .unwrap();
457
458        // Convert back - this will test that all rows are consumed properly
459        let arrays = multi_converter.convert_rows(&multi_rows).unwrap();
460
461        // Verify both columns round-trip correctly
462        let result_ree = arrays[0]
463            .as_any()
464            .downcast_ref::<RunArray<Int32Type>>()
465            .unwrap();
466
467        let result_string = arrays[1].as_any().downcast_ref::<StringArray>().unwrap();
468
469        // This should pass - both arrays should be identical to originals
470        assert_eq!(result_ree.values().as_ref(), array.values().as_ref());
471        assert_eq!(result_ree.run_ends().values(), array.run_ends().values());
472        assert_eq!(*result_string, string_array);
473    }
474
475    #[test]
476    fn test_run_end_encoded_sorting_behavior() {
477        // Test that the binary row encoding actually produces the correct sort order
478
479        // Create REE arrays with different values to test sorting
480        let array1: RunArray<Int32Type> = vec!["apple", "apple"].into_iter().collect();
481        let array2: RunArray<Int32Type> = vec!["banana", "banana"].into_iter().collect();
482        let array3: RunArray<Int32Type> = vec!["cherry", "cherry"].into_iter().collect();
483
484        // Test ascending sort
485        let converter_asc = RowConverter::new(vec![SortField::new(DataType::RunEndEncoded(
486            Arc::new(Field::new(
487                Field::REE_RUN_ENDS_FIELD_DEFAULT_NAME,
488                DataType::Int32,
489                false,
490            )),
491            Arc::new(Field::new(
492                Field::REE_VALUES_FIELD_DEFAULT_NAME,
493                DataType::Utf8,
494                true,
495            )),
496        ))])
497        .unwrap();
498
499        let rows1_asc = converter_asc
500            .convert_columns(&[Arc::new(array1.clone())])
501            .unwrap();
502        let rows2_asc = converter_asc
503            .convert_columns(&[Arc::new(array2.clone())])
504            .unwrap();
505        let rows3_asc = converter_asc
506            .convert_columns(&[Arc::new(array3.clone())])
507            .unwrap();
508
509        // For ascending: apple < banana < cherry
510        // So row bytes should sort: rows1 < rows2 < rows3
511        assert!(
512            rows1_asc.row(0) < rows2_asc.row(0),
513            "apple should come before banana in ascending order"
514        );
515        assert!(
516            rows2_asc.row(0) < rows3_asc.row(0),
517            "banana should come before cherry in ascending order"
518        );
519        assert!(
520            rows1_asc.row(0) < rows3_asc.row(0),
521            "apple should come before cherry in ascending order"
522        );
523
524        // Test descending sort
525        let converter_desc = RowConverter::new(vec![SortField::new_with_options(
526            DataType::RunEndEncoded(
527                Arc::new(Field::new(
528                    Field::REE_RUN_ENDS_FIELD_DEFAULT_NAME,
529                    DataType::Int32,
530                    false,
531                )),
532                Arc::new(Field::new(
533                    Field::REE_VALUES_FIELD_DEFAULT_NAME,
534                    DataType::Utf8,
535                    true,
536                )),
537            ),
538            arrow_schema::SortOptions {
539                descending: true,
540                nulls_first: true,
541            },
542        )])
543        .unwrap();
544
545        let rows1_desc = converter_desc
546            .convert_columns(&[Arc::new(array1.clone())])
547            .unwrap();
548        let rows2_desc = converter_desc
549            .convert_columns(&[Arc::new(array2.clone())])
550            .unwrap();
551        let rows3_desc = converter_desc
552            .convert_columns(&[Arc::new(array3.clone())])
553            .unwrap();
554
555        // For descending: cherry > banana > apple
556        // So row bytes should sort: rows3 < rows2 < rows1 (because byte comparison is ascending)
557        assert!(
558            rows3_desc.row(0) < rows2_desc.row(0),
559            "cherry should come before banana in descending order (byte-wise)"
560        );
561        assert!(
562            rows2_desc.row(0) < rows1_desc.row(0),
563            "banana should come before apple in descending order (byte-wise)"
564        );
565        assert!(
566            rows3_desc.row(0) < rows1_desc.row(0),
567            "cherry should come before apple in descending order (byte-wise)"
568        );
569    }
570
571    #[test]
572    fn test_run_end_encoded_null_sorting() {
573        // Test null handling in sort order
574
575        let array_with_nulls: RunArray<Int32Type> = vec![None, None].into_iter().collect();
576        let array_with_values: RunArray<Int32Type> = vec!["apple", "apple"].into_iter().collect();
577
578        // Test nulls_first = true
579        let converter_nulls_first = RowConverter::new(vec![SortField::new_with_options(
580            DataType::RunEndEncoded(
581                Arc::new(Field::new(
582                    Field::REE_RUN_ENDS_FIELD_DEFAULT_NAME,
583                    DataType::Int32,
584                    false,
585                )),
586                Arc::new(Field::new(
587                    Field::REE_VALUES_FIELD_DEFAULT_NAME,
588                    DataType::Utf8,
589                    true,
590                )),
591            ),
592            arrow_schema::SortOptions {
593                descending: false,
594                nulls_first: true,
595            },
596        )])
597        .unwrap();
598
599        let rows_nulls = converter_nulls_first
600            .convert_columns(&[Arc::new(array_with_nulls.clone())])
601            .unwrap();
602        let rows_values = converter_nulls_first
603            .convert_columns(&[Arc::new(array_with_values.clone())])
604            .unwrap();
605
606        // nulls should come before values when nulls_first = true
607        assert!(
608            rows_nulls.row(0) < rows_values.row(0),
609            "nulls should come before values when nulls_first=true"
610        );
611
612        // Test nulls_first = false
613        let converter_nulls_last = RowConverter::new(vec![SortField::new_with_options(
614            DataType::RunEndEncoded(
615                Arc::new(Field::new(
616                    Field::REE_RUN_ENDS_FIELD_DEFAULT_NAME,
617                    DataType::Int32,
618                    false,
619                )),
620                Arc::new(Field::new(
621                    Field::REE_VALUES_FIELD_DEFAULT_NAME,
622                    DataType::Utf8,
623                    true,
624                )),
625            ),
626            arrow_schema::SortOptions {
627                descending: false,
628                nulls_first: false,
629            },
630        )])
631        .unwrap();
632
633        let rows_nulls_last = converter_nulls_last
634            .convert_columns(&[Arc::new(array_with_nulls.clone())])
635            .unwrap();
636        let rows_values_last = converter_nulls_last
637            .convert_columns(&[Arc::new(array_with_values.clone())])
638            .unwrap();
639
640        // values should come before nulls when nulls_first = false
641        assert!(
642            rows_values_last.row(0) < rows_nulls_last.row(0),
643            "values should come before nulls when nulls_first=false"
644        );
645    }
646
647    #[test]
648    fn test_run_end_encoded_mixed_sorting() {
649        // Test sorting with mixed values and nulls to ensure complex scenarios work
650
651        let array1: RunArray<Int32Type> = vec![Some("apple"), None].into_iter().collect();
652        let array2: RunArray<Int32Type> = vec![None, Some("banana")].into_iter().collect();
653        let array3: RunArray<Int32Type> =
654            vec![Some("cherry"), Some("cherry")].into_iter().collect();
655
656        let converter = RowConverter::new(vec![SortField::new_with_options(
657            DataType::RunEndEncoded(
658                Arc::new(Field::new(
659                    Field::REE_RUN_ENDS_FIELD_DEFAULT_NAME,
660                    DataType::Int32,
661                    false,
662                )),
663                Arc::new(Field::new(
664                    Field::REE_VALUES_FIELD_DEFAULT_NAME,
665                    DataType::Utf8,
666                    true,
667                )),
668            ),
669            arrow_schema::SortOptions {
670                descending: false,
671                nulls_first: true,
672            },
673        )])
674        .unwrap();
675
676        let rows1 = converter.convert_columns(&[Arc::new(array1)]).unwrap();
677        let rows2 = converter.convert_columns(&[Arc::new(array2)]).unwrap();
678        let rows3 = converter.convert_columns(&[Arc::new(array3)]).unwrap();
679
680        // With nulls_first=true, ascending:
681        // Row 0: array1[0]="apple", array2[0]=null, array3[0]="cherry" -> null < apple < cherry
682        // Row 1: array1[1]=null, array2[1]="banana", array3[1]="cherry" -> null < banana < cherry
683
684        // Compare first rows: null < apple < cherry
685        assert!(rows2.row(0) < rows1.row(0), "null should come before apple");
686        assert!(
687            rows1.row(0) < rows3.row(0),
688            "apple should come before cherry"
689        );
690
691        // Compare second rows: null < banana < cherry
692        assert!(
693            rows1.row(1) < rows2.row(1),
694            "null should come before banana"
695        );
696        assert!(
697            rows2.row(1) < rows3.row(1),
698            "banana should come before cherry"
699        );
700    }
701
702    #[test]
703    fn test_run_end_encoded_empty() {
704        // Test converting / decoding an empty RunEndEncodedArray
705        let values: Vec<&str> = vec![];
706        let array: RunArray<Int32Type> = values.into_iter().collect();
707
708        let converter = RowConverter::new(vec![SortField::new(DataType::RunEndEncoded(
709            Arc::new(Field::new(
710                Field::REE_RUN_ENDS_FIELD_DEFAULT_NAME,
711                DataType::Int32,
712                false,
713            )),
714            Arc::new(Field::new(
715                Field::REE_VALUES_FIELD_DEFAULT_NAME,
716                DataType::Utf8,
717                true,
718            )),
719        ))])
720        .unwrap();
721
722        let rows = converter.convert_columns(&[Arc::new(array)]).unwrap();
723        assert_eq!(rows.num_rows(), 0);
724
725        // Likewise converting empty rows should yield an empty RunEndEncodedArray
726        let arrays = converter.convert_rows(&rows).unwrap();
727        assert_eq!(arrays.len(), 1);
728        // Verify both columns round-trip correctly
729        let result_ree = arrays[0].as_run::<Int32Type>();
730        assert_eq!(result_ree.len(), 0);
731    }
732
733    #[test]
734    fn test_run_end_encoded_round_trip_sliced() {
735        let values = Int64Array::from(vec![100, 200, 100, 300]);
736        let run_ends = vec![2, 3, 5, 6];
737        let array: RunArray<Int16Type> =
738            RunArray::try_new(&PrimitiveArray::from(run_ends), &values).unwrap();
739        let array = array.slice(2, 3);
740
741        assert_roundtrip(&array, DataType::Int16, DataType::Int64, None);
742    }
743}