blob: 635f23967a1985e8fbcebe18378d7479d8cdd50c [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//! [`ScalarUDFImpl`] definitions for array_reverse function.
use crate::utils::make_scalar_function;
use arrow::array::{
Array, ArrayRef, Capacities, FixedSizeListArray, GenericListArray,
GenericListViewArray, MutableArrayData, OffsetSizeTrait, UInt32Array,
};
use arrow::buffer::{OffsetBuffer, ScalarBuffer};
use arrow::compute::take;
use arrow::datatypes::DataType::{
FixedSizeList, LargeList, LargeListView, List, ListView, Null,
};
use arrow::datatypes::{DataType, FieldRef};
use datafusion_common::cast::{
as_fixed_size_list_array, as_large_list_array, as_large_list_view_array,
as_list_array, as_list_view_array,
};
use datafusion_common::{exec_err, utils::take_function_args, Result};
use datafusion_expr::{
ColumnarValue, Documentation, ScalarUDFImpl, Signature, Volatility,
};
use datafusion_macros::user_doc;
use itertools::Itertools;
use std::any::Any;
use std::sync::Arc;
make_udf_expr_and_func!(
ArrayReverse,
array_reverse,
array,
"reverses the order of elements in the array.",
array_reverse_udf
);
#[user_doc(
doc_section(label = "Array Functions"),
description = "Returns the array with the order of the elements reversed.",
syntax_example = "array_reverse(array)",
sql_example = r#"```sql
> select array_reverse([1, 2, 3, 4]);
+------------------------------------------------------------+
| array_reverse(List([1, 2, 3, 4])) |
+------------------------------------------------------------+
| [4, 3, 2, 1] |
+------------------------------------------------------------+
```"#,
argument(
name = "array",
description = "Array expression. Can be a constant, column, or function, and any combination of array operators."
)
)]
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct ArrayReverse {
signature: Signature,
aliases: Vec<String>,
}
impl Default for ArrayReverse {
fn default() -> Self {
Self::new()
}
}
impl ArrayReverse {
pub fn new() -> Self {
Self {
signature: Signature::array(Volatility::Immutable),
aliases: vec!["list_reverse".to_string()],
}
}
}
impl ScalarUDFImpl for ArrayReverse {
fn as_any(&self) -> &dyn Any {
self
}
fn name(&self) -> &str {
"array_reverse"
}
fn signature(&self) -> &Signature {
&self.signature
}
fn return_type(&self, arg_types: &[DataType]) -> Result<DataType> {
Ok(arg_types[0].clone())
}
fn invoke_with_args(
&self,
args: datafusion_expr::ScalarFunctionArgs,
) -> Result<ColumnarValue> {
make_scalar_function(array_reverse_inner)(&args.args)
}
fn aliases(&self) -> &[String] {
&self.aliases
}
fn documentation(&self) -> Option<&Documentation> {
self.doc()
}
}
/// array_reverse SQL function
pub fn array_reverse_inner(arg: &[ArrayRef]) -> Result<ArrayRef> {
let [input_array] = take_function_args("array_reverse", arg)?;
match &input_array.data_type() {
List(field) => {
let array = as_list_array(input_array)?;
general_array_reverse::<i32>(array, field)
}
LargeList(field) => {
let array = as_large_list_array(input_array)?;
general_array_reverse::<i64>(array, field)
}
FixedSizeList(field, _) => {
let array = as_fixed_size_list_array(input_array)?;
fixed_size_array_reverse(array, field)
}
Null => Ok(Arc::clone(input_array)),
ListView(field) => {
let array = as_list_view_array(input_array)?;
list_view_reverse::<i32>(array, field)
}
LargeListView(field) => {
let array = as_large_list_view_array(input_array)?;
list_view_reverse::<i64>(array, field)
}
array_type => exec_err!("array_reverse does not support type '{array_type}'."),
}
}
fn general_array_reverse<O: OffsetSizeTrait>(
array: &GenericListArray<O>,
field: &FieldRef,
) -> Result<ArrayRef> {
let values = array.values();
let original_data = values.to_data();
let capacity = Capacities::Array(original_data.len());
let mut offsets = vec![O::usize_as(0)];
let mut mutable =
MutableArrayData::with_capacities(vec![&original_data], false, capacity);
for (row_index, (&start, &end)) in array.offsets().iter().tuple_windows().enumerate()
{
// skip the null value
if array.is_null(row_index) {
offsets.push(offsets[row_index]);
continue;
}
let mut index = end - O::one();
while index >= start {
mutable.extend(0, index.to_usize().unwrap(), index.to_usize().unwrap() + 1);
index = index - O::one();
}
let size = end - start;
offsets.push(offsets[row_index] + size);
}
let data = mutable.freeze();
Ok(Arc::new(GenericListArray::<O>::try_new(
Arc::clone(field),
OffsetBuffer::<O>::new(offsets.into()),
arrow::array::make_array(data),
array.nulls().cloned(),
)?))
}
/// Reverses a list view array.
///
/// Construct indices, sizes and offsets for the reversed array by iterating over
/// the list view array in the logical order, and reversing the order of the elements.
/// We end up with a list view array where the elements are in order,
/// even if the original array had elements out of order.
fn list_view_reverse<O: OffsetSizeTrait>(
array: &GenericListViewArray<O>,
field: &FieldRef,
) -> Result<ArrayRef> {
let offsets = array.offsets();
let values = array.values();
let sizes = array.sizes();
let mut new_offsets: Vec<O> = Vec::with_capacity(offsets.len());
let mut indices: Vec<O> = Vec::with_capacity(values.len());
let mut new_sizes = Vec::with_capacity(sizes.len());
let mut current_offset = O::zero();
for (row_index, offset) in offsets.iter().enumerate() {
new_offsets.push(current_offset);
// If this array is null, we set its size to 0 and continue
if array.is_null(row_index) {
new_sizes.push(O::zero());
continue;
}
let size = sizes[row_index];
new_sizes.push(size);
// Each array is located at [offset, offset + size), collect indices in the reverse order
let array_start = *offset;
let array_end = array_start + size;
let mut idx = array_end - O::one();
while idx >= array_start {
indices.push(idx);
idx = idx - O::one();
}
current_offset += size;
}
// Materialize values from underlying array with take
let indices_array: ArrayRef = if O::IS_LARGE {
Arc::new(arrow::array::UInt64Array::from(
indices
.iter()
.map(|i| i.as_usize() as u64)
.collect::<Vec<_>>(),
))
} else {
Arc::new(UInt32Array::from(
indices
.iter()
.map(|i| i.as_usize() as u32)
.collect::<Vec<_>>(),
))
};
let values_reversed = take(&values, &indices_array, None)?;
Ok(Arc::new(GenericListViewArray::<O>::try_new(
Arc::clone(field),
ScalarBuffer::from(new_offsets),
ScalarBuffer::from(new_sizes),
values_reversed,
array.nulls().cloned(),
)?))
}
fn fixed_size_array_reverse(
array: &FixedSizeListArray,
field: &FieldRef,
) -> Result<ArrayRef> {
let values = array.values();
let original_data = values.to_data();
let capacity = Capacities::Array(original_data.len());
let mut mutable =
MutableArrayData::with_capacities(vec![&original_data], false, capacity);
let value_length = array.value_length() as usize;
for row_index in 0..array.len() {
// skip the null value
if array.is_null(row_index) {
mutable.extend(0, 0, value_length);
continue;
}
let start = row_index * value_length;
let end = start + value_length;
for idx in (start..end).rev() {
mutable.extend(0, idx, idx + 1);
}
}
let data = mutable.freeze();
Ok(Arc::new(FixedSizeListArray::try_new(
Arc::clone(field),
array.value_length(),
arrow::array::make_array(data),
array.nulls().cloned(),
)?))
}
#[cfg(test)]
mod tests {
use crate::reverse::list_view_reverse;
use arrow::{
array::{
AsArray, GenericListViewArray, Int32Array, LargeListViewArray, ListViewArray,
OffsetSizeTrait,
},
buffer::{NullBuffer, ScalarBuffer},
datatypes::{DataType, Field, Int32Type},
};
use datafusion_common::Result;
use std::sync::Arc;
fn list_view_values<O: OffsetSizeTrait>(
array: &GenericListViewArray<O>,
) -> Vec<Option<Vec<i32>>> {
array
.iter()
.map(|x| x.map(|x| x.as_primitive::<Int32Type>().values().to_vec()))
.collect()
}
#[test]
fn test_reverse_list_view() -> Result<()> {
let field = Arc::new(Field::new("a", DataType::Int32, false));
let offsets = ScalarBuffer::from(vec![0, 1, 6, 6]);
let sizes = ScalarBuffer::from(vec![1, 5, 0, 3]);
let values = Arc::new(Int32Array::from(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]));
let nulls = Some(NullBuffer::from(vec![true, true, false, true]));
let list_view = ListViewArray::new(field, offsets, sizes, values, nulls);
let result = list_view_reverse(
&list_view,
&Arc::new(Field::new("test", DataType::Int32, true)),
)?;
let reversed = list_view_values(result.as_list_view::<i32>());
let expected = vec![
Some(vec![1]),
Some(vec![6, 5, 4, 3, 2]),
None,
Some(vec![9, 8, 7]),
];
assert_eq!(expected, reversed);
Ok(())
}
#[test]
fn test_reverse_large_list_view() -> Result<()> {
let field = Arc::new(Field::new("a", DataType::Int32, false));
let offsets = ScalarBuffer::from(vec![0, 1, 6, 6]);
let sizes = ScalarBuffer::from(vec![1, 5, 0, 3]);
let values = Arc::new(Int32Array::from(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]));
let nulls = Some(NullBuffer::from(vec![true, true, false, true]));
let list_view = LargeListViewArray::new(field, offsets, sizes, values, nulls);
let result = list_view_reverse(
&list_view,
&Arc::new(Field::new("test", DataType::Int32, true)),
)?;
let reversed = list_view_values(result.as_list_view::<i64>());
let expected = vec![
Some(vec![1]),
Some(vec![6, 5, 4, 3, 2]),
None,
Some(vec![9, 8, 7]),
];
assert_eq!(expected, reversed);
Ok(())
}
#[test]
fn test_reverse_list_view_out_of_order() -> Result<()> {
let field = Arc::new(Field::new("a", DataType::Int32, false));
let offsets = ScalarBuffer::from(vec![6, 1, 6, 0]); // out of order
let sizes = ScalarBuffer::from(vec![3, 5, 0, 1]);
let values = Arc::new(Int32Array::from(vec![
1, // fourth array: offset 0, size 1
2, 3, 4, 5, 6, // second array: offset 1, size 5
// third array: offset 6, size 0 (and null)
7, 8, 9, // first array: offset 6, size 3
]));
let nulls = Some(NullBuffer::from(vec![true, true, false, true]));
let list_view = ListViewArray::new(field, offsets, sizes, values, nulls);
let result = list_view_reverse(
&list_view,
&Arc::new(Field::new("test", DataType::Int32, true)),
)?;
let reversed = list_view_values(result.as_list_view::<i32>());
let expected = vec![
Some(vec![9, 8, 7]),
Some(vec![6, 5, 4, 3, 2]),
None,
Some(vec![1]),
];
assert_eq!(expected, reversed);
Ok(())
}
#[test]
fn test_reverse_list_view_with_nulls() -> Result<()> {
let field = Arc::new(Field::new("a", DataType::Int32, false));
let offsets = ScalarBuffer::from(vec![16, 1, 6, 0]); // out of order
let sizes = ScalarBuffer::from(vec![3, 5, 10, 1]);
let values = Arc::new(Int32Array::from(vec![
1, // fourth array: offset 0, size 1
2, 3, 4, 5, 6, // second array: offset 1, size 5
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, // third array: offset 6, size 10
7, 8, 9, // first array: offset 6, size 3
]));
let nulls = Some(NullBuffer::from(vec![true, true, false, true]));
let list_view = ListViewArray::new(field, offsets, sizes, values, nulls);
let result = list_view_reverse(
&list_view,
&Arc::new(Field::new("test", DataType::Int32, true)),
)?;
let reversed = list_view_values(result.as_list_view::<i32>());
let expected = vec![
Some(vec![9, 8, 7]),
Some(vec![6, 5, 4, 3, 2]),
None,
Some(vec![1]),
];
assert_eq!(expected, reversed);
Ok(())
}
#[test]
fn test_reverse_list_view_empty() -> Result<()> {
let field = Arc::new(Field::new("a", DataType::Int32, false));
let offsets = ScalarBuffer::from(vec![]);
let sizes = ScalarBuffer::from(vec![]);
let empty_array: Vec<i32> = vec![];
let values = Arc::new(Int32Array::from(empty_array));
let nulls = None;
let list_view = ListViewArray::new(field, offsets, sizes, values, nulls);
let result = list_view_reverse(
&list_view,
&Arc::new(Field::new("test", DataType::Int32, true)),
)?;
let reversed = list_view_values(result.as_list_view::<i32>());
let expected: Vec<Option<Vec<i32>>> = vec![];
assert_eq!(expected, reversed);
Ok(())
}
#[test]
fn test_reverse_list_view_all_nulls() -> Result<()> {
let field = Arc::new(Field::new("a", DataType::Int32, false));
let offsets = ScalarBuffer::from(vec![0, 1, 2, 3]);
let sizes = ScalarBuffer::from(vec![0, 1, 1, 1]);
let values = Arc::new(Int32Array::from(vec![1, 2, 3, 4]));
let nulls = Some(NullBuffer::from(vec![false, false, false, false]));
let list_view = ListViewArray::new(field, offsets, sizes, values, nulls);
let result = list_view_reverse(
&list_view,
&Arc::new(Field::new("test", DataType::Int32, true)),
)?;
let reversed = list_view_values(result.as_list_view::<i32>());
let expected: Vec<Option<Vec<i32>>> = vec![None, None, None, None];
assert_eq!(expected, reversed);
Ok(())
}
}