@@ -27,6 +27,7 @@ use arrow::array::MutableArrayData;
2727use arrow:: array:: OffsetSizeTrait ;
2828use arrow:: buffer:: OffsetBuffer ;
2929use arrow:: datatypes:: DataType ;
30+ use arrow_buffer:: NullBufferBuilder ;
3031use arrow_schema:: DataType :: { FixedSizeList , LargeList , List } ;
3132use arrow_schema:: Field ;
3233use datafusion_common:: cast:: as_int64_array;
@@ -35,12 +36,13 @@ use datafusion_common::cast::as_list_array;
3536use datafusion_common:: {
3637 exec_err, internal_datafusion_err, plan_err, DataFusionError , Result ,
3738} ;
38- use datafusion_expr:: Expr ;
39+ use datafusion_expr:: { ArrayFunctionSignature , Expr , TypeSignature } ;
3940use datafusion_expr:: {
40- ColumnarValue , Documentation , ScalarUDFImpl , Signature , Volatility ,
41+ ColumnarValue , Documentation , NullHandling , ScalarUDFImpl , Signature , Volatility ,
4142} ;
4243use datafusion_macros:: user_doc;
4344use std:: any:: Any ;
45+ use std:: num:: NonZeroUsize ;
4446use std:: sync:: Arc ;
4547
4648use crate :: utils:: make_scalar_function;
@@ -330,7 +332,26 @@ pub(super) struct ArraySlice {
330332impl ArraySlice {
331333 pub fn new ( ) -> Self {
332334 Self {
333- signature : Signature :: variadic_any ( Volatility :: Immutable ) ,
335+ signature : Signature :: one_of (
336+ vec ! [
337+ TypeSignature :: ArraySignature (
338+ ArrayFunctionSignature :: ArrayAndIndexes (
339+ NonZeroUsize :: new( 1 ) . expect( "1 is non-zero" ) ,
340+ ) ,
341+ ) ,
342+ TypeSignature :: ArraySignature (
343+ ArrayFunctionSignature :: ArrayAndIndexes (
344+ NonZeroUsize :: new( 2 ) . expect( "2 is non-zero" ) ,
345+ ) ,
346+ ) ,
347+ TypeSignature :: ArraySignature (
348+ ArrayFunctionSignature :: ArrayAndIndexes (
349+ NonZeroUsize :: new( 3 ) . expect( "3 is non-zero" ) ,
350+ ) ,
351+ ) ,
352+ ] ,
353+ Volatility :: Immutable ,
354+ ) ,
334355 aliases : vec ! [ String :: from( "list_slice" ) ] ,
335356 }
336357 }
@@ -374,6 +395,10 @@ impl ScalarUDFImpl for ArraySlice {
374395 Ok ( arg_types[ 0 ] . clone ( ) )
375396 }
376397
398+ fn null_handling ( & self ) -> NullHandling {
399+ NullHandling :: Propagate
400+ }
401+
377402 fn invoke_batch (
378403 & self ,
379404 args : & [ ColumnarValue ] ,
@@ -430,8 +455,6 @@ fn array_slice_inner(args: &[ArrayRef]) -> Result<ArrayRef> {
430455 }
431456 LargeList ( _) => {
432457 let array = as_large_list_array ( & args[ 0 ] ) ?;
433- let from_array = as_int64_array ( & args[ 1 ] ) ?;
434- let to_array = as_int64_array ( & args[ 2 ] ) ?;
435458 general_array_slice :: < i64 > ( array, from_array, to_array, stride)
436459 }
437460 _ => exec_err ! ( "array_slice does not support type: {:?}" , array_data_type) ,
@@ -451,9 +474,8 @@ where
451474 let original_data = values. to_data ( ) ;
452475 let capacity = Capacities :: Array ( original_data. len ( ) ) ;
453476
454- // use_nulls: false, we don't need nulls but empty array for array_slice, so we don't need explicit nulls but adjust offset to indicate nulls.
455477 let mut mutable =
456- MutableArrayData :: with_capacities ( vec ! [ & original_data] , false , capacity) ;
478+ MutableArrayData :: with_capacities ( vec ! [ & original_data] , true , capacity) ;
457479
458480 // We have the slice syntax compatible with DuckDB v0.8.1.
459481 // The rule `adjusted_from_index` and `adjusted_to_index` follows the rule of array_slice in duckdb.
@@ -516,30 +538,33 @@ where
516538 }
517539
518540 let mut offsets = vec ! [ O :: usize_as( 0 ) ] ;
541+ let mut null_builder = NullBufferBuilder :: new ( array. len ( ) ) ;
519542
520543 for ( row_index, offset_window) in array. offsets ( ) . windows ( 2 ) . enumerate ( ) {
521544 let start = offset_window[ 0 ] ;
522545 let end = offset_window[ 1 ] ;
523546 let len = end - start;
524547
525- // len 0 indicate array is null, return empty array in this row.
548+ // If any input is null, return null.
549+ if array. is_null ( row_index)
550+ || from_array. is_null ( row_index)
551+ || to_array. is_null ( row_index)
552+ {
553+ mutable. extend_nulls ( 1 ) ;
554+ offsets. push ( offsets[ row_index] + O :: usize_as ( 1 ) ) ;
555+ null_builder. append_null ( ) ;
556+ continue ;
557+ }
558+ null_builder. append_non_null ( ) ;
559+
560+ // Empty arrays always return an empty array.
526561 if len == O :: usize_as ( 0 ) {
527562 offsets. push ( offsets[ row_index] ) ;
528563 continue ;
529564 }
530565
531- // If index is null, we consider it as the minimum / maximum index of the array.
532- let from_index = if from_array. is_null ( row_index) {
533- Some ( O :: usize_as ( 0 ) )
534- } else {
535- adjusted_from_index :: < O > ( from_array. value ( row_index) , len) ?
536- } ;
537-
538- let to_index = if to_array. is_null ( row_index) {
539- Some ( len - O :: usize_as ( 1 ) )
540- } else {
541- adjusted_to_index :: < O > ( to_array. value ( row_index) , len) ?
542- } ;
566+ let from_index = adjusted_from_index :: < O > ( from_array. value ( row_index) , len) ?;
567+ let to_index = adjusted_to_index :: < O > ( to_array. value ( row_index) , len) ?;
543568
544569 if let ( Some ( from) , Some ( to) ) = ( from_index, to_index) {
545570 let stride = stride. map ( |s| s. value ( row_index) ) ;
@@ -613,7 +638,7 @@ where
613638 Arc :: new ( Field :: new_list_field ( array. value_type ( ) , true ) ) ,
614639 OffsetBuffer :: < O > :: new ( offsets. into ( ) ) ,
615640 arrow_array:: make_array ( data) ,
616- None ,
641+ null_builder . finish ( ) ,
617642 ) ?) )
618643}
619644
@@ -665,6 +690,10 @@ impl ScalarUDFImpl for ArrayPopFront {
665690 Ok ( arg_types[ 0 ] . clone ( ) )
666691 }
667692
693+ fn null_handling ( & self ) -> NullHandling {
694+ NullHandling :: Propagate
695+ }
696+
668697 fn invoke_batch (
669698 & self ,
670699 args : & [ ColumnarValue ] ,
@@ -765,6 +794,10 @@ impl ScalarUDFImpl for ArrayPopBack {
765794 Ok ( arg_types[ 0 ] . clone ( ) )
766795 }
767796
797+ fn null_handling ( & self ) -> NullHandling {
798+ NullHandling :: Propagate
799+ }
800+
768801 fn invoke_batch (
769802 & self ,
770803 args : & [ ColumnarValue ] ,
0 commit comments