1use arrow::array::{
21 Array, ArrayRef, AsArray, Capacities, GenericListArray, MutableArrayData,
22 NullBufferBuilder, OffsetSizeTrait, Scalar, new_null_array,
23};
24use arrow::buffer::OffsetBuffer;
25use arrow::datatypes::{DataType, Field, FieldRef};
26use datafusion_common::cast::as_int64_array;
27use datafusion_common::utils::ListCoercion;
28use datafusion_common::{
29 Result, ScalarValue, exec_err, internal_err, utils::take_function_args,
30};
31use datafusion_expr::{
32 ArrayFunctionArgument, ArrayFunctionSignature, ColumnarValue, Documentation,
33 ReturnFieldArgs, ScalarFunctionArgs, ScalarUDFImpl, Signature, TypeSignature,
34 Volatility,
35};
36use datafusion_macros::user_doc;
37
38use crate::utils::{compare_element_to_list, list_inner_field, list_type_with_element};
39
40use std::sync::Arc;
41
42make_udf_expr_and_func!(ArrayReplace,
44 array_replace,
45 array from to,
46 "replaces the first occurrence of the specified element with another specified element.",
47 array_replace_udf
48);
49make_udf_expr_and_func!(ArrayReplaceN,
50 array_replace_n,
51 array from to max,
52 "replaces the first `max` occurrences of the specified element with another specified element.",
53 array_replace_n_udf
54);
55make_udf_expr_and_func!(ArrayReplaceAll,
56 array_replace_all,
57 array from to,
58 "replaces all occurrences of the specified element with another specified element.",
59 array_replace_all_udf
60);
61
62#[user_doc(
63 doc_section(label = "Array Functions"),
64 description = "Replaces the first occurrence of the specified element with another specified element.",
65 syntax_example = "array_replace(array, from, to)",
66 sql_example = r#"```sql
67> select array_replace([1, 2, 2, 3, 2, 1, 4], 2, 5);
68+--------------------------------------------------------+
69| array_replace(List([1,2,2,3,2,1,4]),Int64(2),Int64(5)) |
70+--------------------------------------------------------+
71| [1, 5, 2, 3, 2, 1, 4] |
72+--------------------------------------------------------+
73```"#,
74 argument(
75 name = "array",
76 description = "Array expression. Can be a constant, column, or function, and any combination of array operators."
77 ),
78 argument(name = "from", description = "Initial element."),
79 argument(name = "to", description = "Final element.")
80)]
81#[derive(Debug, PartialEq, Eq, Hash)]
82pub struct ArrayReplace {
83 signature: Signature,
84 aliases: Vec<String>,
85}
86
87impl Default for ArrayReplace {
88 fn default() -> Self {
89 Self::new()
90 }
91}
92
93impl ArrayReplace {
94 pub fn new() -> Self {
95 Self {
96 signature: Signature {
97 type_signature: TypeSignature::ArraySignature(
98 ArrayFunctionSignature::Array {
99 arguments: vec![
100 ArrayFunctionArgument::Array,
101 ArrayFunctionArgument::Element,
102 ArrayFunctionArgument::Element,
103 ],
104 array_coercion: Some(ListCoercion::FixedSizedListToList),
105 },
106 ),
107 volatility: Volatility::Immutable,
108 parameter_names: None,
109 },
110 aliases: vec![String::from("list_replace")],
111 }
112 }
113}
114
115impl ScalarUDFImpl for ArrayReplace {
116 fn name(&self) -> &str {
117 "array_replace"
118 }
119
120 fn signature(&self) -> &Signature {
121 &self.signature
122 }
123
124 fn return_type(&self, _arg_types: &[DataType]) -> Result<DataType> {
125 internal_err!("return_field_from_args should be used instead")
126 }
127
128 fn return_field_from_args(&self, args: ReturnFieldArgs) -> Result<FieldRef> {
129 replace_return_field(self.name(), args.arg_fields)
130 }
131
132 fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
133 let return_type = args.return_field.data_type().clone();
134 let [list_arg, from_arg, to_arg] = take_function_args(self.name(), &args.args)?;
135 let num_rows = args.number_rows;
136 let list_array = list_arg.to_array(num_rows)?;
137 match (from_arg, to_arg) {
138 (ColumnarValue::Scalar(scalar_from), ColumnarValue::Scalar(scalar_to)) => {
139 let result = array_replace_with_scalar_args(
140 self.name(),
141 &list_array,
142 scalar_from,
143 scalar_to,
144 1i64,
145 &return_type,
146 )?;
147 Ok(ColumnarValue::Array(result))
148 }
149 (from_arg, to_arg) => {
150 let from_array = from_arg.to_array(num_rows)?;
151 let to_array = to_arg.to_array(num_rows)?;
152 let result = array_replace_internal(
153 self.name(),
154 &list_array,
155 &from_array,
156 &to_array,
157 &[Some(1)],
158 &return_type,
159 )?;
160 Ok(ColumnarValue::Array(result))
161 }
162 }
163 }
164
165 fn aliases(&self) -> &[String] {
166 &self.aliases
167 }
168
169 fn documentation(&self) -> Option<&Documentation> {
170 self.doc()
171 }
172}
173
174#[user_doc(
175 doc_section(label = "Array Functions"),
176 description = "Replaces the first `max` occurrences of the specified element with another specified element.",
177 syntax_example = "array_replace_n(array, from, to, max)",
178 sql_example = r#"```sql
179> select array_replace_n([1, 2, 2, 3, 2, 1, 4], 2, 5, 2);
180+-------------------------------------------------------------------+
181| array_replace_n(List([1,2,2,3,2,1,4]),Int64(2),Int64(5),Int64(2)) |
182+-------------------------------------------------------------------+
183| [1, 5, 5, 3, 2, 1, 4] |
184+-------------------------------------------------------------------+
185```"#,
186 argument(
187 name = "array",
188 description = "Array expression. Can be a constant, column, or function, and any combination of array operators."
189 ),
190 argument(name = "from", description = "Initial element."),
191 argument(name = "to", description = "Final element."),
192 argument(name = "max", description = "Number of first occurrences to replace.")
193)]
194#[derive(Debug, PartialEq, Eq, Hash)]
195pub(super) struct ArrayReplaceN {
196 signature: Signature,
197 aliases: Vec<String>,
198}
199
200impl ArrayReplaceN {
201 pub fn new() -> Self {
202 Self {
203 signature: Signature {
204 type_signature: TypeSignature::ArraySignature(
205 ArrayFunctionSignature::Array {
206 arguments: vec![
207 ArrayFunctionArgument::Array,
208 ArrayFunctionArgument::Element,
209 ArrayFunctionArgument::Element,
210 ArrayFunctionArgument::Index,
211 ],
212 array_coercion: Some(ListCoercion::FixedSizedListToList),
213 },
214 ),
215 volatility: Volatility::Immutable,
216 parameter_names: None,
217 },
218 aliases: vec![String::from("list_replace_n")],
219 }
220 }
221}
222
223impl ScalarUDFImpl for ArrayReplaceN {
224 fn name(&self) -> &str {
225 "array_replace_n"
226 }
227
228 fn signature(&self) -> &Signature {
229 &self.signature
230 }
231
232 fn return_type(&self, _arg_types: &[DataType]) -> Result<DataType> {
233 internal_err!("return_field_from_args should be used instead")
234 }
235
236 fn return_field_from_args(&self, args: ReturnFieldArgs) -> Result<FieldRef> {
237 replace_return_field(self.name(), args.arg_fields)
238 }
239
240 fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
241 let return_type = args.return_field.data_type().clone();
242 let [list_arg, from_arg, to_arg, max_arg] =
243 take_function_args(self.name(), &args.args)?;
244 let num_rows = args.number_rows;
245 let list_array = list_arg.to_array(num_rows)?;
246 match (from_arg, to_arg, max_arg) {
247 (
248 ColumnarValue::Scalar(scalar_from),
249 ColumnarValue::Scalar(scalar_to),
250 ColumnarValue::Scalar(scalar_max),
251 ) => {
252 let ScalarValue::Int64(Some(n)) = scalar_max else {
253 return Ok(ColumnarValue::Array(new_null_array(
254 &return_type,
255 num_rows,
256 )));
257 };
258 let result = array_replace_with_scalar_args(
259 self.name(),
260 &list_array,
261 scalar_from,
262 scalar_to,
263 *n,
264 &return_type,
265 )?;
266 Ok(ColumnarValue::Array(result))
267 }
268 (from_arg, to_arg, max_arg) => {
269 let from_array = from_arg.to_array(num_rows)?;
270 let to_array = to_arg.to_array(num_rows)?;
271 let max_array = max_arg.to_array(num_rows)?;
272 let result = array_replace_n_inner(
273 self.name(),
274 &list_array,
275 &from_array,
276 &to_array,
277 &max_array,
278 &return_type,
279 )?;
280 Ok(ColumnarValue::Array(result))
281 }
282 }
283 }
284
285 fn aliases(&self) -> &[String] {
286 &self.aliases
287 }
288
289 fn documentation(&self) -> Option<&Documentation> {
290 self.doc()
291 }
292}
293
294#[user_doc(
295 doc_section(label = "Array Functions"),
296 description = "Replaces all occurrences of the specified element with another specified element.",
297 syntax_example = "array_replace_all(array, from, to)",
298 sql_example = r#"```sql
299> select array_replace_all([1, 2, 2, 3, 2, 1, 4], 2, 5);
300+------------------------------------------------------------+
301| array_replace_all(List([1,2,2,3,2,1,4]),Int64(2),Int64(5)) |
302+------------------------------------------------------------+
303| [1, 5, 5, 3, 5, 1, 4] |
304+------------------------------------------------------------+
305```"#,
306 argument(
307 name = "array",
308 description = "Array expression. Can be a constant, column, or function, and any combination of array operators."
309 ),
310 argument(name = "from", description = "Initial element."),
311 argument(name = "to", description = "Final element.")
312)]
313#[derive(Debug, PartialEq, Eq, Hash)]
314pub(super) struct ArrayReplaceAll {
315 signature: Signature,
316 aliases: Vec<String>,
317}
318
319impl ArrayReplaceAll {
320 pub fn new() -> Self {
321 Self {
322 signature: Signature {
323 type_signature: TypeSignature::ArraySignature(
324 ArrayFunctionSignature::Array {
325 arguments: vec![
326 ArrayFunctionArgument::Array,
327 ArrayFunctionArgument::Element,
328 ArrayFunctionArgument::Element,
329 ],
330 array_coercion: Some(ListCoercion::FixedSizedListToList),
331 },
332 ),
333 volatility: Volatility::Immutable,
334 parameter_names: None,
335 },
336 aliases: vec![String::from("list_replace_all")],
337 }
338 }
339}
340
341impl ScalarUDFImpl for ArrayReplaceAll {
342 fn name(&self) -> &str {
343 "array_replace_all"
344 }
345
346 fn signature(&self) -> &Signature {
347 &self.signature
348 }
349
350 fn return_type(&self, _arg_types: &[DataType]) -> Result<DataType> {
351 internal_err!("return_field_from_args should be used instead")
352 }
353
354 fn return_field_from_args(&self, args: ReturnFieldArgs) -> Result<FieldRef> {
355 replace_return_field(self.name(), args.arg_fields)
356 }
357
358 fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
359 let return_type = args.return_field.data_type().clone();
360 let [list_arg, from_arg, to_arg] = take_function_args(self.name(), &args.args)?;
361 let num_rows = args.number_rows;
362 let list_array = list_arg.to_array(num_rows)?;
363 match (from_arg, to_arg) {
364 (ColumnarValue::Scalar(scalar_from), ColumnarValue::Scalar(scalar_to)) => {
365 let result = array_replace_with_scalar_args(
366 self.name(),
367 &list_array,
368 scalar_from,
369 scalar_to,
370 i64::MAX,
371 &return_type,
372 )?;
373 Ok(ColumnarValue::Array(result))
374 }
375 (from_arg, to_arg) => {
376 let from_array = from_arg.to_array(num_rows)?;
377 let to_array = to_arg.to_array(num_rows)?;
378 let result = array_replace_internal(
379 self.name(),
380 &list_array,
381 &from_array,
382 &to_array,
383 &[Some(i64::MAX)],
384 &return_type,
385 )?;
386 Ok(ColumnarValue::Array(result))
387 }
388 }
389 }
390
391 fn aliases(&self) -> &[String] {
392 &self.aliases
393 }
394
395 fn documentation(&self) -> Option<&Documentation> {
396 self.doc()
397 }
398}
399
400fn replace_return_field(name: &str, arg_fields: &[FieldRef]) -> Result<FieldRef> {
404 let [array_field, _from_field, to_field, ..] = arg_fields else {
408 return exec_err!(
409 "{name} expects at least 3 arguments, got {}",
410 arg_fields.len()
411 );
412 };
413 let data_type =
414 list_type_with_element(array_field.data_type(), to_field.is_nullable());
415 Ok(Arc::new(Field::new(name, data_type, true)))
416}
417
418fn general_replace<O: OffsetSizeTrait>(
436 list_array: &GenericListArray<O>,
437 from_array: &ArrayRef,
438 to_array: &ArrayRef,
439 arr_n: &[Option<i64>],
440 field: FieldRef,
441) -> Result<ArrayRef> {
442 let mut offsets: Vec<O> = Vec::with_capacity(list_array.len() + 1);
444 offsets.push(O::usize_as(0));
445 let values = list_array.values();
446 let original_data = values.to_data();
447 let to_data = to_array.to_data();
448 let capacity = Capacities::Array(original_data.len());
449
450 let mut mutable = MutableArrayData::with_capacities(
452 vec![&original_data, &to_data],
453 false,
454 capacity,
455 );
456
457 let mut valid = NullBufferBuilder::new(list_array.len());
458
459 for (row_index, offset_window) in list_array.offsets().windows(2).enumerate() {
460 if list_array.is_null(row_index) {
461 offsets.push(offsets[row_index]);
462 valid.append_null();
463 continue;
464 }
465
466 let n = if arr_n.len() == 1 {
467 arr_n[0]
468 } else {
469 arr_n[row_index]
470 };
471 let Some(n) = n else {
472 offsets.push(offsets[row_index]);
473 valid.append_null();
474 continue;
475 };
476
477 let start = offset_window[0];
478 let end = offset_window[1];
479
480 let list_array_row = list_array.value(row_index);
481
482 let eq_array =
485 compare_element_to_list(&list_array_row, &from_array, row_index, true)?;
486
487 let original_idx = O::usize_as(0);
488 let replace_idx = O::usize_as(1);
489 let mut counter = 0;
490
491 if n <= 0 || !eq_array.has_true() {
493 mutable.try_extend(
494 original_idx.to_usize().unwrap(),
495 start.to_usize().unwrap(),
496 end.to_usize().unwrap(),
497 )?;
498 offsets.push(offsets[row_index] + (end - start));
499 valid.append_non_null();
500 continue;
501 }
502
503 let mut pending_retain: Option<O> = None;
504 for (i, to_replace) in eq_array.iter().enumerate() {
505 let i = O::usize_as(i);
506 if to_replace == Some(true) && counter < n {
507 if let Some(rs) = pending_retain.take() {
509 mutable.try_extend(
510 original_idx.to_usize().unwrap(),
511 (start + rs).to_usize().unwrap(),
512 (start + i).to_usize().unwrap(),
513 )?;
514 }
515 mutable.try_extend(
516 replace_idx.to_usize().unwrap(),
517 row_index,
518 row_index + 1,
519 )?;
520 counter += 1;
521 if counter == n {
522 mutable.try_extend(
524 original_idx.to_usize().unwrap(),
525 (start + i).to_usize().unwrap() + 1,
526 end.to_usize().unwrap(),
527 )?;
528 break;
529 }
530 } else if pending_retain.is_none() {
531 pending_retain = Some(i);
532 }
533 }
534
535 if counter < n
538 && let Some(rs) = pending_retain
539 {
540 mutable.try_extend(
541 original_idx.to_usize().unwrap(),
542 (start + rs).to_usize().unwrap(),
543 end.to_usize().unwrap(),
544 )?;
545 }
546
547 offsets.push(offsets[row_index] + (end - start));
548 valid.append_non_null();
549 }
550
551 let data = mutable.freeze();
552
553 Ok(Arc::new(GenericListArray::<O>::try_new(
554 field,
555 OffsetBuffer::<O>::new(offsets.into()),
556 arrow::array::make_array(data),
557 valid.finish(),
558 )?))
559}
560
561fn general_replace_with_scalar<O: OffsetSizeTrait>(
568 list_array: &GenericListArray<O>,
569 needle: &Scalar<ArrayRef>,
570 scalar_to: &ScalarValue,
571 max_replacements: i64,
572 field: FieldRef,
573) -> Result<ArrayRef> {
574 if max_replacements <= 0 {
577 return Ok(Arc::new(GenericListArray::<O>::try_new(
578 field,
579 list_array.offsets().clone(),
580 Arc::clone(list_array.values()),
581 list_array.nulls().cloned(),
582 )?));
583 }
584
585 let first_offset = list_array.offsets()[0].to_usize().unwrap();
586 let last_offset = list_array.offsets()[list_array.len()].to_usize().unwrap();
587 let visible_values = list_array
588 .values()
589 .slice(first_offset, last_offset - first_offset);
590
591 let to_array = scalar_to.to_array_of_size(1)?;
592 let original_data = visible_values.to_data();
593 let to_data = to_array.to_data();
594 let capacity = Capacities::Array(original_data.len());
595
596 let mut mutable = MutableArrayData::with_capacities(
597 vec![&original_data, &to_data],
598 false,
599 capacity,
600 );
601
602 let mut offsets = Vec::<O>::with_capacity(list_array.len() + 1);
603 offsets.push(O::zero());
604
605 let match_bitmap = arrow_ord::cmp::not_distinct(&visible_values, needle)?;
607 let match_bits = match_bitmap.values();
608
609 for (row_index, offset_window) in list_array.offsets().windows(2).enumerate() {
610 let start = offset_window[0].to_usize().unwrap() - first_offset;
612 let end = offset_window[1].to_usize().unwrap() - first_offset;
613 let row_len = end - start;
614
615 if list_array.is_null(row_index) {
616 offsets.push(offsets[row_index]);
617 continue;
618 }
619
620 let row_bits = match_bits.slice(start, row_len);
622 let mut match_positions = row_bits
623 .set_indices()
624 .take(max_replacements as usize)
625 .peekable();
626 if match_positions.peek().is_none() {
627 mutable.try_extend(0, start, end)?;
628 offsets.push(offsets[row_index] + O::usize_as(row_len));
629 continue;
630 }
631
632 let mut prev_end = 0usize;
636 for match_pos in match_positions {
637 if match_pos > prev_end {
639 mutable.try_extend(0, start + prev_end, start + match_pos)?;
640 }
641 mutable.try_extend(1, 0, 1)?;
643 prev_end = match_pos + 1;
644 }
645
646 if prev_end < row_len {
648 mutable.try_extend(0, start + prev_end, end)?;
649 }
650
651 offsets.push(offsets[row_index] + O::usize_as(row_len));
652 }
653
654 let data = mutable.freeze();
655
656 Ok(Arc::new(GenericListArray::<O>::try_new(
657 field,
658 OffsetBuffer::new(offsets.into()),
659 arrow::array::make_array(data),
660 list_array.nulls().cloned(),
661 )?))
662}
663
664fn array_replace_with_scalar_args(
668 name: &str,
669 list_array: &ArrayRef,
670 scalar_from: &ScalarValue,
671 scalar_to: &ScalarValue,
672 max_replacements: i64,
673 return_type: &DataType,
674) -> Result<ArrayRef> {
675 if scalar_from.data_type().is_nested() {
677 let num_rows = list_array.len();
678 let from_array = scalar_from.to_array_of_size(num_rows)?;
679 let to_array = scalar_to.to_array_of_size(num_rows)?;
680 return array_replace_internal(
681 name,
682 list_array,
683 &from_array,
684 &to_array,
685 &vec![Some(max_replacements); num_rows],
686 return_type,
687 );
688 }
689
690 let needle = Scalar::new(scalar_from.to_array_of_size(1)?);
691 match list_array.data_type() {
692 DataType::List(_) => general_replace_with_scalar::<i32>(
693 list_array.as_list::<i32>(),
694 &needle,
695 scalar_to,
696 max_replacements,
697 list_inner_field(name, return_type)?,
698 ),
699 DataType::LargeList(_) => general_replace_with_scalar::<i64>(
700 list_array.as_list::<i64>(),
701 &needle,
702 scalar_to,
703 max_replacements,
704 list_inner_field(name, return_type)?,
705 ),
706 DataType::Null => Ok(new_null_array(return_type, list_array.len())),
707 array_type => exec_err!("{name} does not support type '{array_type}'."),
708 }
709}
710
711fn array_replace_internal(
712 name: &str,
713 array: &ArrayRef,
714 from: &ArrayRef,
715 to: &ArrayRef,
716 arr_n: &[Option<i64>],
717 return_type: &DataType,
718) -> Result<ArrayRef> {
719 match array.data_type() {
720 DataType::List(_) => general_replace::<i32>(
721 array.as_list::<i32>(),
722 from,
723 to,
724 arr_n,
725 list_inner_field(name, return_type)?,
726 ),
727 DataType::LargeList(_) => general_replace::<i64>(
728 array.as_list::<i64>(),
729 from,
730 to,
731 arr_n,
732 list_inner_field(name, return_type)?,
733 ),
734 DataType::Null => Ok(new_null_array(return_type, array.len())),
735 array_type => exec_err!("{name} does not support type '{array_type}'."),
736 }
737}
738
739fn array_replace_n_inner(
740 name: &str,
741 array: &ArrayRef,
742 from: &ArrayRef,
743 to: &ArrayRef,
744 max: &ArrayRef,
745 return_type: &DataType,
746) -> Result<ArrayRef> {
747 let arr_n = as_int64_array(max)?.iter().collect::<Vec<_>>();
748 array_replace_internal(name, array, from, to, &arr_n, return_type)
749}
750
751#[cfg(test)]
752mod tests {
753 use super::{ArrayReplaceN, array_replace_n_inner};
754 use arrow::array::{ArrayRef, AsArray, Int32Array, Int64Array, ListArray};
755 use arrow::buffer::{NullBuffer, ScalarBuffer};
756 use arrow::datatypes::{DataType, Field, Int32Type};
757 use datafusion_common::{Result, ScalarValue, config::ConfigOptions};
758 use datafusion_expr::{ColumnarValue, ScalarFunctionArgs, ScalarUDFImpl};
759 use std::sync::Arc;
760
761 #[test]
762 fn test_array_replace_n_null_max_returns_null() -> Result<()> {
763 let array: ArrayRef =
764 Arc::new(ListArray::from_iter_primitive::<Int32Type, _, _>(vec![
765 Some(vec![Some(1), Some(2), Some(3)]),
766 Some(vec![Some(4), Some(2)]),
767 ]));
768 let from: ArrayRef = Arc::new(Int32Array::from(vec![2, 2]));
769 let to: ArrayRef = Arc::new(Int32Array::from(vec![9, 9]));
770 let max: ArrayRef = Arc::new(Int64Array::new(
771 ScalarBuffer::from(vec![1, 1]),
772 Some(NullBuffer::from(vec![true, false])),
773 ));
774
775 let result = array_replace_n_inner(
776 "array_replace_n",
777 &array,
778 &from,
779 &to,
780 &max,
781 array.data_type(),
782 )?;
783 let expected = ListArray::from_iter_primitive::<Int32Type, _, _>(vec![
784 Some(vec![Some(1), Some(9), Some(3)]),
785 None,
786 ]);
787
788 assert_eq!(result.as_list::<i32>(), &expected);
789
790 Ok(())
791 }
792
793 #[test]
794 fn test_array_replace_n_scalar_null_max_returns_null() -> Result<()> {
795 let array: ArrayRef =
796 Arc::new(ListArray::from_iter_primitive::<Int32Type, _, _>(vec![
797 Some(vec![Some(1), Some(2), Some(3)]),
798 Some(vec![Some(4), Some(2)]),
799 ]));
800 let array_field = Arc::new(Field::new("array", array.data_type().clone(), true));
801
802 let result = ArrayReplaceN::new().invoke_with_args(ScalarFunctionArgs {
803 args: vec![
804 ColumnarValue::Array(Arc::clone(&array)),
805 ColumnarValue::Scalar(ScalarValue::Int32(Some(2))),
806 ColumnarValue::Scalar(ScalarValue::Int32(Some(9))),
807 ColumnarValue::Scalar(ScalarValue::Int64(None)),
808 ],
809 arg_fields: vec![
810 Arc::clone(&array_field),
811 Arc::new(Field::new("from", DataType::Int32, false)),
812 Arc::new(Field::new("to", DataType::Int32, false)),
813 Arc::new(Field::new("max", DataType::Int64, true)),
814 ],
815 number_rows: array.len(),
816 return_field: Arc::clone(&array_field),
817 config_options: Arc::new(ConfigOptions::default()),
818 })?;
819
820 let result = result.into_array(array.len())?;
821 let expected = ListArray::from_iter_primitive::<Int32Type, _, _>(vec![
822 Option::<Vec<Option<i32>>>::None,
823 Option::<Vec<Option<i32>>>::None,
824 ]);
825
826 assert_eq!(result.as_list::<i32>(), &expected);
827
828 Ok(())
829 }
830}