MCPcopy Create free account
hub / github.com/apache/datafusion / array_position_scalar

Function array_position_scalar

datafusion/functions-nested/src/position.rs:226–300  ·  view source on GitHub ↗

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
)

Source from the content-addressed store, hash-verified

224/// entire flat values buffer, then walks the result bitmap using offsets to
225/// find per-row first-match positions.
226fn 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 }

Callers

nothing calls this directly

Calls 12

check_datatypesFunction · 0.85
newFunction · 0.85
offsetsMethod · 0.80
sliceMethod · 0.80
peekMethod · 0.80
valuesMethod · 0.45
lenMethod · 0.45
nullsMethod · 0.45
as_usizeMethod · 0.45
is_nullMethod · 0.45
nextMethod · 0.45
pushMethod · 0.45

Tested by

no test coverage detected

Used in the wild real call sites across dependent graphs

searching dependent graphs…