datafusion_functions_nested/
utils.rs1use std::sync::Arc;
21
22use arrow::datatypes::{DataType, Field, FieldRef, Fields};
23
24use arrow::array::{
25 Array, ArrayRef, BooleanArray, Float64Array, GenericListArray, NullBufferBuilder,
26 OffsetSizeTrait, Scalar,
27};
28use arrow::buffer::{NullBuffer, OffsetBuffer};
29use datafusion_common::cast::{
30 as_fixed_size_list_array, as_float64_array, as_generic_list_array,
31 as_large_list_array, as_large_list_view_array, as_list_array, as_list_view_array,
32};
33use datafusion_common::{Result, ScalarValue, exec_err, internal_err, plan_err};
34
35use datafusion_expr::ColumnarValue;
36use itertools::Itertools as _;
37
38pub(crate) fn list_type_with_element(
51 array_type: &DataType,
52 element_nullable: bool,
53) -> DataType {
54 match array_type {
55 DataType::List(field) => {
56 DataType::List(widen_nullability(field, element_nullable))
57 }
58 DataType::LargeList(field) => {
59 DataType::LargeList(widen_nullability(field, element_nullable))
60 }
61 other => other.clone(),
62 }
63}
64
65fn widen_nullability(field: &FieldRef, nullable: bool) -> FieldRef {
66 if nullable && !field.is_nullable() {
67 Arc::new(field.as_ref().clone().with_nullable(true))
68 } else {
69 Arc::clone(field)
70 }
71}
72
73pub(crate) fn list_inner_field(context: &str, data_type: &DataType) -> Result<FieldRef> {
83 match data_type {
84 DataType::List(field) | DataType::LargeList(field) => Ok(Arc::clone(field)),
85 other => internal_err!("{context} got unexpected data type: {other}"),
86 }
87}
88
89pub(crate) fn check_datatypes(name: &str, args: &[&ArrayRef]) -> Result<()> {
90 let data_type = args[0].data_type();
91 if !args.iter().all(|arg| {
92 arg.data_type().equals_datatype(data_type)
93 || arg.data_type().equals_datatype(&DataType::Null)
94 }) {
95 let types = args.iter().map(|arg| arg.data_type()).collect::<Vec<_>>();
96 return plan_err!(
97 "{name} received incompatible types: {}",
98 types.iter().join(", ")
99 );
100 }
101
102 Ok(())
103}
104
105pub(crate) fn make_scalar_function<F>(
107 inner: F,
108) -> impl Fn(&[ColumnarValue]) -> Result<ColumnarValue>
109where
110 F: Fn(&[ArrayRef]) -> Result<ArrayRef>,
111{
112 move |args: &[ColumnarValue]| {
113 let len = args
116 .iter()
117 .fold(Option::<usize>::None, |acc, arg| match arg {
118 ColumnarValue::Scalar(_) => acc,
119 ColumnarValue::Array(a) => Some(a.len()),
120 });
121
122 let is_scalar = len.is_none();
123
124 let args = ColumnarValue::values_to_arrays(args)?;
125
126 let result = (inner)(&args);
127
128 if is_scalar {
129 let result = result.and_then(|arr| ScalarValue::try_from_array(&arr, 0));
131 result.map(ColumnarValue::Scalar)
132 } else {
133 result.map(ColumnarValue::Array)
134 }
135 }
136}
137
138pub(crate) fn align_array_dimensions<O: OffsetSizeTrait>(
139 args: Vec<ArrayRef>,
140) -> Result<Vec<ArrayRef>> {
141 let args_ndim = args
142 .iter()
143 .map(|arg| datafusion_common::utils::list_ndims(arg.data_type()))
144 .collect::<Vec<_>>();
145 let max_ndim = args_ndim.iter().max().unwrap_or(&0);
146
147 let aligned_args: Result<Vec<ArrayRef>> = args
149 .into_iter()
150 .zip(args_ndim.iter())
151 .map(|(array, ndim)| {
152 if ndim < max_ndim {
153 let mut aligned_array = Arc::clone(&array);
154 for _ in 0..(max_ndim - ndim) {
155 let data_type = aligned_array.data_type().to_owned();
156 let array_lengths = vec![1; aligned_array.len()];
157 let offsets = OffsetBuffer::<O>::from_lengths(array_lengths);
158
159 aligned_array = Arc::new(GenericListArray::<O>::try_new(
160 Arc::new(Field::new_list_field(data_type, true)),
161 offsets,
162 aligned_array,
163 None,
164 )?)
165 }
166 Ok(aligned_array)
167 } else {
168 Ok(Arc::clone(&array))
169 }
170 })
171 .collect();
172
173 aligned_args
174}
175
176pub(crate) fn compare_element_to_list(
204 list_array_row: &dyn Array,
205 element_array: &dyn Array,
206 row_index: usize,
207 eq: bool,
208) -> Result<BooleanArray> {
209 if list_array_row.data_type() != element_array.data_type() {
210 return exec_err!(
211 "compare_element_to_list received incompatible types: '{:?}' and '{:?}'.",
212 list_array_row.data_type(),
213 element_array.data_type()
214 );
215 }
216
217 let element_array_row = element_array.slice(row_index, 1);
218
219 let res = match element_array_row.data_type() {
222 DataType::List(_) => {
224 let element_array_row_inner = as_list_array(&element_array_row)?.value(0);
226 let list_array_row_inner = as_list_array(list_array_row)?;
227
228 list_array_row_inner
229 .iter()
230 .map(|row| {
232 row.map(|row| {
233 if eq {
234 row.eq(&element_array_row_inner)
235 } else {
236 row.ne(&element_array_row_inner)
237 }
238 })
239 })
240 .collect::<BooleanArray>()
241 }
242 DataType::LargeList(_) => {
243 let element_array_row_inner =
245 as_large_list_array(&element_array_row)?.value(0);
246 let list_array_row_inner = as_large_list_array(list_array_row)?;
247
248 list_array_row_inner
249 .iter()
250 .map(|row| {
252 row.map(|row| {
253 if eq {
254 row.eq(&element_array_row_inner)
255 } else {
256 row.ne(&element_array_row_inner)
257 }
258 })
259 })
260 .collect::<BooleanArray>()
261 }
262 _ => {
263 let element_arr = Scalar::new(element_array_row);
264 if eq {
266 arrow_ord::cmp::not_distinct(&list_array_row, &element_arr)?
267 } else {
268 arrow_ord::cmp::distinct(&list_array_row, &element_arr)?
269 }
270 }
271 };
272
273 Ok(res)
274}
275
276pub(crate) fn compute_array_dims(
278 arr: Option<ArrayRef>,
279) -> Result<Option<Vec<Option<u64>>>> {
280 let mut value = match arr {
281 Some(arr) => arr,
282 None => return Ok(None),
283 };
284 if value.is_empty() {
285 return Ok(None);
286 }
287 let mut res = vec![Some(value.len() as u64)];
288
289 loop {
290 match value.data_type() {
291 DataType::List(_) => {
292 value = as_list_array(&value)?.value(0);
293 res.push(Some(value.len() as u64));
294 }
295 DataType::LargeList(_) => {
296 value = as_large_list_array(&value)?.value(0);
297 res.push(Some(value.len() as u64));
298 }
299 DataType::ListView(_) => {
300 value = as_list_view_array(&value)?.value(0);
301 res.push(Some(value.len() as u64));
302 }
303 DataType::LargeListView(_) => {
304 value = as_large_list_view_array(&value)?.value(0);
305 res.push(Some(value.len() as u64));
306 }
307 DataType::FixedSizeList(..) => {
308 value = as_fixed_size_list_array(&value)?.value(0);
309 res.push(Some(value.len() as u64));
310 }
311 _ => return Ok(Some(res)),
312 }
313 }
314}
315
316pub(crate) fn get_map_entry_field(data_type: &DataType) -> Result<&Fields> {
317 match data_type {
318 DataType::Map(field, _) => {
319 let field_data_type = field.data_type();
320 match field_data_type {
321 DataType::Struct(fields) => Ok(fields),
322 _ => {
323 internal_err!("Expected a Struct type, got {}", field_data_type)
324 }
325 }
326 }
327 _ => internal_err!("Expected a Map type, got {data_type}"),
328 }
329}
330
331pub(crate) fn coerce_array_math_arg_types(
340 name: &str,
341 arg_types: &[DataType],
342) -> Result<Vec<DataType>> {
343 use DataType::{FixedSizeList, LargeList, List, Null};
344 use datafusion_common::utils::{ListCoercion, coerced_type_with_base_type_only};
345
346 let coercion = Some(&ListCoercion::FixedSizedListToList);
347
348 for arg_type in arg_types {
349 if !matches!(arg_type, Null | List(_) | LargeList(_) | FixedSizeList(..)) {
350 return plan_err!("{name} does not support type {arg_type}");
351 }
352 }
353
354 let any_large_list = arg_types.iter().any(|t| matches!(t, LargeList(_)));
358
359 let coerced = arg_types
360 .iter()
361 .map(|arg_type| {
362 if matches!(arg_type, Null) {
363 let field = Arc::new(Field::new_list_field(DataType::Float64, true));
364 return if any_large_list {
365 LargeList(field)
366 } else {
367 List(field)
368 };
369 }
370 let coerced =
371 coerced_type_with_base_type_only(arg_type, &DataType::Float64, coercion);
372 match coerced {
373 List(field) if any_large_list => LargeList(field),
374 other => other,
375 }
376 })
377 .collect();
378
379 Ok(coerced)
380}
381
382pub(crate) fn array_math_binary_op<O, F>(
394 op_name: &str,
395 lhs: &ArrayRef,
396 rhs: &ArrayRef,
397 op: F,
398) -> Result<ArrayRef>
399where
400 O: OffsetSizeTrait,
401 F: Fn(f64, f64) -> f64,
402{
403 let lhs = as_generic_list_array::<O>(lhs)?;
404 let rhs = as_generic_list_array::<O>(rhs)?;
405
406 let lhs_values = as_float64_array(lhs.values())?;
407 let rhs_values = as_float64_array(rhs.values())?;
408 let lhs_offsets = lhs.value_offsets();
409 let rhs_offsets = rhs.value_offsets();
410
411 let row_nulls = NullBuffer::union(lhs.nulls(), rhs.nulls());
412
413 let mut out_values: Vec<f64> = Vec::with_capacity(lhs_values.len());
414 let mut out_inner_nulls = NullBufferBuilder::new(lhs_values.len());
415 let mut out_offsets = Vec::<O>::with_capacity(lhs.len() + 1);
416 out_offsets.push(O::zero());
417
418 for row in 0..lhs.len() {
419 if row_nulls.as_ref().is_some_and(|nb| nb.is_null(row)) {
420 out_offsets.push(out_offsets[row]);
421 continue;
422 }
423
424 let start1 = lhs_offsets[row].as_usize();
425 let len1 = lhs.value_length(row).as_usize();
426 let start2 = rhs_offsets[row].as_usize();
427 let len2 = rhs.value_length(row).as_usize();
428
429 if len1 != len2 {
430 return exec_err!(
431 "{op_name} requires both list inputs to have the same length per row, got {len1} and {len2} at row {row}"
432 );
433 }
434
435 let l_slice = lhs_values.slice(start1, len1);
436 let r_slice = rhs_values.slice(start2, len2);
437
438 let l_vals = l_slice.values();
439 let r_vals = r_slice.values();
440
441 for i in 0..len1 {
442 out_values.push(op(l_vals[i], r_vals[i]));
443 }
444
445 match NullBuffer::union(l_slice.nulls(), r_slice.nulls()) {
446 Some(nb) => out_inner_nulls.append_buffer(&nb),
447 None => out_inner_nulls.append_n_non_nulls(len1),
448 }
449
450 out_offsets.push(out_offsets[row] + O::usize_as(len1));
451 }
452
453 let values_array = Arc::new(Float64Array::new(
454 out_values.into(),
455 out_inner_nulls.finish(),
456 ));
457 let field = Arc::new(Field::new_list_field(DataType::Float64, true));
458
459 Ok(Arc::new(GenericListArray::<O>::try_new(
460 field,
461 OffsetBuffer::new(out_offsets.into()),
462 values_array,
463 row_nulls,
464 )?))
465}
466
467#[cfg(test)]
468mod tests {
469 use super::*;
470 use arrow::array::ListArray;
471 use arrow::datatypes::Int64Type;
472 use datafusion_common::utils::SingleRowListArrayBuilder;
473
474 #[test]
476 fn test_align_array_dimensions() {
477 let array1d_1: ArrayRef =
478 Arc::new(ListArray::from_iter_primitive::<Int64Type, _, _>(vec![
479 Some(vec![Some(1), Some(2), Some(3)]),
480 Some(vec![Some(4), Some(5)]),
481 ]));
482 let array1d_2: ArrayRef =
483 Arc::new(ListArray::from_iter_primitive::<Int64Type, _, _>(vec![
484 Some(vec![Some(6), Some(7), Some(8)]),
485 ]));
486
487 let array2d_1: ArrayRef = Arc::new(
488 SingleRowListArrayBuilder::new(Arc::clone(&array1d_1)).build_list_array(),
489 );
490 let array2d_2 = Arc::new(
491 SingleRowListArrayBuilder::new(Arc::clone(&array1d_2)).build_list_array(),
492 );
493
494 let res = align_array_dimensions::<i32>(vec![
495 array1d_1.to_owned(),
496 array2d_2.to_owned(),
497 ])
498 .unwrap();
499
500 let expected = as_list_array(&array2d_1).unwrap();
501 let expected_dim = datafusion_common::utils::list_ndims(array2d_1.data_type());
502 assert_ne!(as_list_array(&res[0]).unwrap(), expected);
503 assert_eq!(
504 datafusion_common::utils::list_ndims(res[0].data_type()),
505 expected_dim
506 );
507
508 let array3d_1: ArrayRef =
509 Arc::new(SingleRowListArrayBuilder::new(array2d_1).build_list_array());
510 let array3d_2: ArrayRef =
511 Arc::new(SingleRowListArrayBuilder::new(array2d_2).build_list_array());
512 let res = align_array_dimensions::<i32>(vec![array1d_1, array3d_2]).unwrap();
513
514 let expected = as_list_array(&array3d_1).unwrap();
515 let expected_dim = datafusion_common::utils::list_ndims(array3d_1.data_type());
516 assert_ne!(as_list_array(&res[0]).unwrap(), expected);
517 assert_eq!(
518 datafusion_common::utils::list_ndims(res[0].data_type()),
519 expected_dim
520 );
521 }
522}