@@ -352,8 +352,6 @@ fn array_has_dispatch_for_scalar(
352352 haystack : ArrayWrapper < ' _ > ,
353353 needle : & dyn Datum ,
354354) -> Result < ArrayRef > {
355- let values = haystack. values ( ) ;
356- let is_nested = values. data_type ( ) . is_nested ( ) ;
357355 // If first argument is empty list (second argument is non-null), return false
358356 // i.e. array_has([], non-null element) -> false
359357 if haystack. len ( ) == 0 {
@@ -362,7 +360,17 @@ fn array_has_dispatch_for_scalar(
362360 None ,
363361 ) ) ) ;
364362 }
365- let eq_array = compare_with_eq ( values, needle, is_nested) ?;
363+
364+ // For sliced ListArrays, values() returns the full underlying array but
365+ // only elements between the first and last offset are visible.
366+ let offsets: Vec < usize > = haystack. offsets ( ) . collect ( ) ;
367+ let first_offset = offsets[ 0 ] ;
368+ let visible_values = haystack
369+ . values ( )
370+ . slice ( first_offset, offsets[ offsets. len ( ) - 1 ] - first_offset) ;
371+
372+ let is_nested = visible_values. data_type ( ) . is_nested ( ) ;
373+ let eq_array = compare_with_eq ( & visible_values, needle, is_nested) ?;
366374
367375 // When a haystack element is null, `eq()` returns null (not false).
368376 // In Arrow, a null BooleanArray entry has validity=0 but an
@@ -382,10 +390,14 @@ fn array_has_dispatch_for_scalar(
382390 ArrayWrapper :: LargeList ( arr) => arr. nulls ( ) ,
383391 } ;
384392 let mut matches = eq_bits. set_indices ( ) . peekable ( ) ;
385- let mut values = BooleanBufferBuilder :: new ( haystack. len ( ) ) ;
386- values. append_n ( haystack. len ( ) , false ) ;
393+ let mut result = BooleanBufferBuilder :: new ( haystack. len ( ) ) ;
394+ result. append_n ( haystack. len ( ) , false ) ;
395+
396+ // Match positions are relative to visible_values (0-based), so
397+ // subtract first_offset from each offset when comparing.
398+ for ( i, window) in offsets. windows ( 2 ) . enumerate ( ) {
399+ let end = window[ 1 ] - first_offset;
387400
388- for ( i, ( _start, end) ) in haystack. offsets ( ) . tuple_windows ( ) . enumerate ( ) {
389401 let has_match = matches. peek ( ) . is_some_and ( |& p| p < end) ;
390402
391403 // Advance past all match positions in this row's range.
@@ -394,14 +406,14 @@ fn array_has_dispatch_for_scalar(
394406 }
395407
396408 if has_match && validity. is_none_or ( |v| v. is_valid ( i) ) {
397- values . set_bit ( i, true ) ;
409+ result . set_bit ( i, true ) ;
398410 }
399411 }
400412
401413 // A null haystack row always produces a null output, so we can
402414 // reuse the haystack's null buffer directly.
403415 Ok ( Arc :: new ( BooleanArray :: new (
404- values . finish ( ) ,
416+ result . finish ( ) ,
405417 validity. cloned ( ) ,
406418 ) ) )
407419}
@@ -1066,6 +1078,52 @@ mod tests {
10661078 Ok ( ( ) )
10671079 }
10681080
1081+ #[ test]
1082+ fn test_array_has_sliced_list ( ) -> Result < ( ) , DataFusionError > {
1083+ // [[10, 20], [30, 40], [50, 60], [70, 80]] → slice(1,2) → [[30, 40], [50, 60]]
1084+ let list = ListArray :: from_iter_primitive :: < Int32Type , _ , _ > ( vec ! [
1085+ Some ( vec![ Some ( 10 ) , Some ( 20 ) ] ) ,
1086+ Some ( vec![ Some ( 30 ) , Some ( 40 ) ] ) ,
1087+ Some ( vec![ Some ( 50 ) , Some ( 60 ) ] ) ,
1088+ Some ( vec![ Some ( 70 ) , Some ( 80 ) ] ) ,
1089+ ] ) ;
1090+ let sliced = list. slice ( 1 , 2 ) ;
1091+ let haystack_field =
1092+ Arc :: new ( Field :: new ( "haystack" , sliced. data_type ( ) . clone ( ) , true ) ) ;
1093+ let needle_field = Arc :: new ( Field :: new ( "needle" , DataType :: Int32 , true ) ) ;
1094+ let return_field = Arc :: new ( Field :: new ( "return" , DataType :: Boolean , true ) ) ;
1095+
1096+ // Search for elements that exist only in sliced-away rows:
1097+ // 10 is in the prefix row, 70 is in the suffix row.
1098+ let invoke = |needle : i32 | -> Result < ArrayRef , DataFusionError > {
1099+ ArrayHas :: new ( )
1100+ . invoke_with_args ( ScalarFunctionArgs {
1101+ args : vec ! [
1102+ ColumnarValue :: Array ( Arc :: new( sliced. clone( ) ) ) ,
1103+ ColumnarValue :: Scalar ( ScalarValue :: Int32 ( Some ( needle) ) ) ,
1104+ ] ,
1105+ arg_fields : vec ! [
1106+ Arc :: clone( & haystack_field) ,
1107+ Arc :: clone( & needle_field) ,
1108+ ] ,
1109+ number_rows : 2 ,
1110+ return_field : Arc :: clone ( & return_field) ,
1111+ config_options : Arc :: new ( ConfigOptions :: default ( ) ) ,
1112+ } ) ?
1113+ . into_array ( 2 )
1114+ } ;
1115+
1116+ let output = invoke ( 10 ) ?. as_boolean ( ) . clone ( ) ;
1117+ assert ! ( !output. value( 0 ) ) ;
1118+ assert ! ( !output. value( 1 ) ) ;
1119+
1120+ let output = invoke ( 70 ) ?. as_boolean ( ) . clone ( ) ;
1121+ assert ! ( !output. value( 0 ) ) ;
1122+ assert ! ( !output. value( 1 ) ) ;
1123+
1124+ Ok ( ( ) )
1125+ }
1126+
10691127 #[ test]
10701128 fn test_array_has_list_null_haystack ( ) -> Result < ( ) , DataFusionError > {
10711129 let haystack_field = Arc :: new ( Field :: new ( "haystack" , DataType :: Null , true ) ) ;
0 commit comments