Fast path for `array_position` when the needle is scalar. Performs a single bulk `not_distinct` comparison of the needle against the entire flat values buffer, then walks the result bitmap using offsets to find per-row first-match positions.
(
haystack: &GenericListArray<O>,
needle: &ArrayRef,
arr_from: &[i64], // 0-indexed
)
| 224 | /// entire flat values buffer, then walks the result bitmap using offsets to |
| 225 | /// find per-row first-match positions. |
| 226 | fn array_position_scalar<O: OffsetSizeTrait>( |
| 227 | haystack: &GenericListArray<O>, |
| 228 | needle: &ArrayRef, |
| 229 | arr_from: &[i64], // 0-indexed |
| 230 | ) -> Result<ArrayRef> { |
| 231 | crate::utils::check_datatypes("array_position", &[haystack.values(), needle])?; |
| 232 | |
| 233 | if haystack.len() == 0 { |
| 234 | return Ok(Arc::new(UInt64Array::new_null(0))); |
| 235 | } |
| 236 | |
| 237 | let needle_datum = Scalar::new(Arc::clone(needle)); |
| 238 | let validity = haystack.nulls(); |
| 239 | |
| 240 | // Only convert the visible portion of the values array. For sliced |
| 241 | // ListArrays, values() returns the full underlying array but only |
| 242 | // elements between the first and last offset are referenced. |
| 243 | let offsets = haystack.offsets(); |
| 244 | let first_offset = offsets[0].as_usize(); |
| 245 | let last_offset = offsets[haystack.len()].as_usize(); |
| 246 | let visible_values = haystack |
| 247 | .values() |
| 248 | .slice(first_offset, last_offset - first_offset); |
| 249 | |
| 250 | // `not_distinct` treats NULL=NULL as true, matching the semantics of |
| 251 | // `array_position`. |
| 252 | let eq_array = arrow_ord::cmp::not_distinct(&visible_values, &needle_datum)?; |
| 253 | let eq_bits = eq_array.values(); |
| 254 | |
| 255 | let mut result: Vec<Option<u64>> = Vec::with_capacity(haystack.len()); |
| 256 | let mut matches = eq_bits.set_indices().peekable(); |
| 257 | |
| 258 | // Match positions are relative to visible_values (0-based), so |
| 259 | // subtract first_offset from each offset when comparing. |
| 260 | for i in 0..haystack.len() { |
| 261 | let start = offsets[i].as_usize() - first_offset; |
| 262 | let end = offsets[i + 1].as_usize() - first_offset; |
| 263 | |
| 264 | if validity.is_some_and(|v| v.is_null(i)) { |
| 265 | // Null row -> null output; advance past matches in range |
| 266 | while matches.peek().is_some_and(|&p| p < end) { |
| 267 | matches.next(); |
| 268 | } |
| 269 | result.push(None); |
| 270 | continue; |
| 271 | } |
| 272 | |
| 273 | let from = arr_from[i]; |
| 274 | let row_len = end - start; |
| 275 | if !(from >= 0 && (from as usize) <= row_len) { |
| 276 | return exec_err!("start_from out of bounds: {}", from + 1); |
| 277 | } |
| 278 | let search_start = start + from as usize; |
| 279 | |
| 280 | // Advance past matches before search_start |
| 281 | while matches.peek().is_some_and(|&p| p < search_start) { |
| 282 | matches.next(); |
| 283 | } |
nothing calls this directly
no test coverage detected
searching dependent graphs…