1818//! [`ScalarUDFImpl`] definitions for array_remove, array_remove_n, array_remove_all functions.
1919
2020use crate :: utils;
21- use crate :: utils:: make_scalar_function;
2221use arrow:: array:: {
23- Array , ArrayRef , Capacities , GenericListArray , MutableArrayData , OffsetSizeTrait ,
24- cast:: AsArray , make_array,
22+ Array , ArrayRef , Capacities , GenericListArray , MutableArrayData , OffsetBufferBuilder ,
23+ OffsetSizeTrait , Scalar , cast:: AsArray , make_array,
2524} ;
2625use arrow:: buffer:: { NullBuffer , OffsetBuffer } ;
2726use arrow:: datatypes:: { DataType , FieldRef } ;
2827use datafusion_common:: cast:: as_int64_array;
2928use datafusion_common:: utils:: ListCoercion ;
30- use datafusion_common:: { Result , exec_err, internal_err, utils:: take_function_args} ;
29+ use datafusion_common:: {
30+ Result , ScalarValue , exec_err, internal_err, utils:: take_function_args,
31+ } ;
3132use datafusion_expr:: {
3233 ArrayFunctionArgument , ArrayFunctionSignature , ColumnarValue , Documentation ,
3334 ScalarFunctionArgs , ScalarUDFImpl , Signature , TypeSignature , Volatility ,
@@ -113,7 +114,24 @@ impl ScalarUDFImpl for ArrayRemove {
113114 }
114115
115116 fn invoke_with_args ( & self , args : ScalarFunctionArgs ) -> Result < ColumnarValue > {
116- make_scalar_function ( array_remove_inner) ( & args. args )
117+ let [ list_arg, element_arg] = take_function_args ( self . name ( ) , & args. args ) ?;
118+ let num_rows = args. number_rows ;
119+ let list_array = list_arg. to_array ( num_rows) ?;
120+ match element_arg {
121+ ColumnarValue :: Scalar ( scalar_element)
122+ if !scalar_element. is_null ( )
123+ && !scalar_element. data_type ( ) . is_nested ( ) =>
124+ {
125+ let result =
126+ array_remove_with_scalar_args ( & list_array, scalar_element, 1i64 ) ?;
127+ Ok ( ColumnarValue :: Array ( result) )
128+ }
129+ element_arg => {
130+ let element_array = element_arg. to_array ( num_rows) ?;
131+ let result = array_remove_internal ( & list_array, & element_array, & [ 1 ] ) ?;
132+ Ok ( ColumnarValue :: Array ( result) )
133+ }
134+ }
117135 }
118136
119137 fn aliases ( & self ) -> & [ String ] {
@@ -214,7 +232,40 @@ impl ScalarUDFImpl for ArrayRemoveN {
214232 }
215233
216234 fn invoke_with_args ( & self , args : ScalarFunctionArgs ) -> Result < ColumnarValue > {
217- make_scalar_function ( array_remove_n_inner) ( & args. args )
235+ let [ list_arg, element_arg, max_arg] =
236+ take_function_args ( self . name ( ) , & args. args ) ?;
237+ let num_rows = args. number_rows ;
238+ let list_array = list_arg. to_array ( num_rows) ?;
239+ match ( element_arg, max_arg) {
240+ (
241+ ColumnarValue :: Scalar ( scalar_element) ,
242+ ColumnarValue :: Scalar ( scalar_max) ,
243+ ) if !scalar_element. is_null ( ) && !scalar_element. data_type ( ) . is_nested ( ) => {
244+ let ScalarValue :: Int64 ( Some ( n) ) = scalar_max else {
245+ // null max means no remove
246+ return Ok ( ColumnarValue :: Array ( list_array) ) ;
247+ } ;
248+ let result =
249+ array_remove_with_scalar_args ( & list_array, scalar_element, * n) ?;
250+ Ok ( ColumnarValue :: Array ( result) )
251+ }
252+ ( element_arg, max_arg) => {
253+ let element_array = element_arg. to_array ( num_rows) ?;
254+ let max_array = max_arg. to_array ( num_rows) ?;
255+ let max_array = as_int64_array ( & max_array) ?;
256+ let arr_n = ( 0 ..max_array. len ( ) )
257+ . map ( |i| {
258+ if max_array. is_null ( i) {
259+ 0
260+ } else {
261+ max_array. value ( i)
262+ }
263+ } )
264+ . collect :: < Vec < _ > > ( ) ;
265+ let result = array_remove_internal ( & list_array, & element_array, & arr_n) ?;
266+ Ok ( ColumnarValue :: Array ( result) )
267+ }
268+ }
218269 }
219270
220271 fn aliases ( & self ) -> & [ String ] {
@@ -304,7 +355,25 @@ impl ScalarUDFImpl for ArrayRemoveAll {
304355 }
305356
306357 fn invoke_with_args ( & self , args : ScalarFunctionArgs ) -> Result < ColumnarValue > {
307- make_scalar_function ( array_remove_all_inner) ( & args. args )
358+ let [ list_arg, element_arg] = take_function_args ( self . name ( ) , & args. args ) ?;
359+ let num_rows = args. number_rows ;
360+ let list_array = list_arg. to_array ( num_rows) ?;
361+ match element_arg {
362+ ColumnarValue :: Scalar ( scalar_element)
363+ if !scalar_element. is_null ( )
364+ && !scalar_element. data_type ( ) . is_nested ( ) =>
365+ {
366+ let result =
367+ array_remove_with_scalar_args ( & list_array, scalar_element, i64:: MAX ) ?;
368+ Ok ( ColumnarValue :: Array ( result) )
369+ }
370+ element_arg => {
371+ let element_array = element_arg. to_array ( num_rows) ?;
372+ let result =
373+ array_remove_internal ( & list_array, & element_array, & [ i64:: MAX ] ) ?;
374+ Ok ( ColumnarValue :: Array ( result) )
375+ }
376+ }
308377 }
309378
310379 fn aliases ( & self ) -> & [ String ] {
@@ -316,27 +385,6 @@ impl ScalarUDFImpl for ArrayRemoveAll {
316385 }
317386}
318387
319- fn array_remove_inner ( args : & [ ArrayRef ] ) -> Result < ArrayRef > {
320- let [ array, element] = take_function_args ( "array_remove" , args) ?;
321-
322- let arr_n = vec ! [ 1 ; array. len( ) ] ;
323- array_remove_internal ( array, element, & arr_n)
324- }
325-
326- fn array_remove_n_inner ( args : & [ ArrayRef ] ) -> Result < ArrayRef > {
327- let [ array, element, max] = take_function_args ( "array_remove_n" , args) ?;
328-
329- let arr_n = as_int64_array ( max) ?. values ( ) . to_vec ( ) ;
330- array_remove_internal ( array, element, & arr_n)
331- }
332-
333- fn array_remove_all_inner ( args : & [ ArrayRef ] ) -> Result < ArrayRef > {
334- let [ array, element] = take_function_args ( "array_remove_all" , args) ?;
335-
336- let arr_n = vec ! [ i64 :: MAX ; array. len( ) ] ;
337- array_remove_internal ( array, element, & arr_n)
338- }
339-
340388fn array_remove_internal (
341389 array : & ArrayRef ,
342390 element_array : & ArrayRef ,
@@ -357,6 +405,28 @@ fn array_remove_internal(
357405 }
358406}
359407
408+ /// Fast path for `array_remove` when the needle is a non-null, non-nested scalar.
409+ /// Dispatches to the bulk `not_distinct` comparison kernel.
410+ fn array_remove_with_scalar_args (
411+ array : & ArrayRef ,
412+ scalar_needle : & ScalarValue ,
413+ max_removals : i64 ,
414+ ) -> Result < ArrayRef > {
415+ match array. data_type ( ) {
416+ DataType :: List ( _) => {
417+ let list_array = array. as_list :: < i32 > ( ) ;
418+ general_remove_with_scalar :: < i32 > ( list_array, scalar_needle, max_removals)
419+ }
420+ DataType :: LargeList ( _) => {
421+ let list_array = array. as_list :: < i64 > ( ) ;
422+ general_remove_with_scalar :: < i64 > ( list_array, scalar_needle, max_removals)
423+ }
424+ array_type => exec_err ! (
425+ "array_remove/array_remove_n/array_remove_all does not support type '{array_type}'."
426+ ) ,
427+ }
428+ }
429+
360430/// For each element of `list_array[i]`, removed up to `arr_n[i]` occurrences
361431/// of `element_array[i]`.
362432///
@@ -411,7 +481,11 @@ fn general_remove<OffsetSize: OffsetSizeTrait>(
411481 let start = offset_window[ 0 ] . to_usize ( ) . unwrap ( ) ;
412482 let end = offset_window[ 1 ] . to_usize ( ) . unwrap ( ) ;
413483 // n is the number of elements to remove in this row
414- let n = arr_n[ row_index] ;
484+ let n = if arr_n. len ( ) == 1 {
485+ arr_n[ 0 ]
486+ } else {
487+ arr_n[ row_index]
488+ } ;
415489
416490 // compare each element in the list, `false` means the element matches and should be removed
417491 let eq_array = utils:: compare_element_to_list (
@@ -468,6 +542,105 @@ fn general_remove<OffsetSize: OffsetSizeTrait>(
468542 ) ?) )
469543}
470544
545+ /// For each element of `list_array[i]`, removes up to `max_removals` occurrences
546+ /// of the scalar needle.
547+ ///
548+ /// This is a specialized version of `general_remove` for scalar elements that
549+ /// uses bulk comparison for better performance.
550+ fn general_remove_with_scalar < OffsetSize : OffsetSizeTrait > (
551+ list_array : & GenericListArray < OffsetSize > ,
552+ scalar_needle : & ScalarValue ,
553+ max_removals : i64 ,
554+ ) -> Result < ArrayRef > {
555+ if max_removals <= 0 {
556+ return Ok ( Arc :: new ( list_array. clone ( ) ) ) ;
557+ }
558+
559+ let list_field = match list_array. data_type ( ) {
560+ DataType :: List ( field) | DataType :: LargeList ( field) => field,
561+ _ => {
562+ return exec_err ! (
563+ "Expected List or LargeList data type, got {:?}" ,
564+ list_array. data_type( )
565+ ) ;
566+ }
567+ } ;
568+
569+ let list_offsets = list_array. offsets ( ) ;
570+ let first_offset = list_offsets[ 0 ] . to_usize ( ) . unwrap ( ) ;
571+ let last_offset = list_offsets[ list_offsets. len ( ) - 1 ] . to_usize ( ) . unwrap ( ) ;
572+ let values_range_len = last_offset - first_offset;
573+ let values_slice = list_array. values ( ) . slice ( first_offset, values_range_len) ;
574+ let original_data = values_slice. to_data ( ) ;
575+ let mut offsets = OffsetBufferBuilder :: < OffsetSize > :: new ( list_array. len ( ) ) ;
576+
577+ let mut mutable = MutableArrayData :: with_capacities (
578+ vec ! [ & original_data] ,
579+ false ,
580+ Capacities :: Array ( original_data. len ( ) ) ,
581+ ) ;
582+ let nulls = list_array. nulls ( ) . cloned ( ) ;
583+ let needle = scalar_needle. to_array_of_size ( 1 ) ?;
584+ let remove_mask = arrow_ord:: cmp:: not_distinct ( & values_slice, & Scalar :: new ( needle) ) ?;
585+ let remove_bits = remove_mask. values ( ) ;
586+
587+ for ( row_index, offset_window) in list_offsets. windows ( 2 ) . enumerate ( ) {
588+ if nulls. as_ref ( ) . is_some_and ( |nulls| nulls. is_null ( row_index) ) {
589+ offsets. push_length ( 0 ) ;
590+ continue ;
591+ }
592+
593+ let start = offset_window[ 0 ] . to_usize ( ) . unwrap ( ) - first_offset;
594+ let end = offset_window[ 1 ] . to_usize ( ) . unwrap ( ) - first_offset;
595+ let row_len = end - start;
596+
597+ let row_remove_bits = remove_bits. slice ( start, row_len) ;
598+ let num_to_remove = row_remove_bits. count_set_bits ( ) ;
599+
600+ if num_to_remove == 0 {
601+ mutable. extend ( 0 , start, end) ;
602+ offsets. push_length ( row_len) ;
603+ continue ;
604+ }
605+
606+ let removals_to_apply = max_removals. min ( num_to_remove as i64 ) as usize ;
607+
608+ // Iterate only over the removal positions via set_indices. This is
609+ // efficient when the number of removals is small relative to the row
610+ // length (common case), since it skips over retained elements.
611+ let mut removed = 0usize ;
612+ let mut copied = 0usize ;
613+ let mut prev_end = start;
614+ for remove_pos in row_remove_bits. set_indices ( ) {
615+ let abs_pos = start + remove_pos;
616+ if abs_pos > prev_end {
617+ mutable. extend ( 0 , prev_end, abs_pos) ;
618+ copied += abs_pos - prev_end;
619+ }
620+ prev_end = abs_pos + 1 ;
621+ removed += 1 ;
622+ if removed == removals_to_apply {
623+ break ;
624+ }
625+ }
626+ // Copy the remaining tail after the last removal
627+ if prev_end < end {
628+ mutable. extend ( 0 , prev_end, end) ;
629+ copied += end - prev_end;
630+ }
631+
632+ offsets. push_length ( copied) ;
633+ }
634+
635+ let new_values = make_array ( mutable. freeze ( ) ) ;
636+ Ok ( Arc :: new ( GenericListArray :: < OffsetSize > :: try_new (
637+ Arc :: clone ( list_field) ,
638+ offsets. finish ( ) ,
639+ new_values,
640+ nulls,
641+ ) ?) )
642+ }
643+
471644#[ cfg( test) ]
472645mod tests {
473646 use crate :: remove:: { ArrayRemove , ArrayRemoveAll , ArrayRemoveN } ;
0 commit comments