Skip to content

Commit 862c36e

Browse files
fix: guard array_position start_from underflow
1 parent 7e90f52 commit 862c36e

2 files changed

Lines changed: 33 additions & 6 deletions

File tree

datafusion/functions-nested/src/position.rs

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -207,14 +207,23 @@ fn resolve_start_from(
207207
match third_arg {
208208
None => Ok(vec![0i64; num_rows]),
209209
Some(ColumnarValue::Scalar(ScalarValue::Int64(Some(v)))) => {
210-
Ok(vec![v - 1; num_rows])
210+
Ok(vec![normalize_start_from(*v)?; num_rows])
211211
}
212212
Some(ColumnarValue::Scalar(s)) => {
213213
exec_err!("array_position expected Int64 for start_from, got {s}")
214214
}
215-
Some(ColumnarValue::Array(a)) => {
216-
Ok(as_int64_array(a)?.values().iter().map(|&x| x - 1).collect())
217-
}
215+
Some(ColumnarValue::Array(a)) => as_int64_array(a)?
216+
.values()
217+
.iter()
218+
.map(|&x| normalize_start_from(x))
219+
.collect(),
220+
}
221+
}
222+
223+
fn normalize_start_from(start_from: i64) -> Result<i64> {
224+
match start_from.checked_sub(1) {
225+
Some(start_from) => Ok(start_from),
226+
None => exec_err!("start_from out of bounds: {start_from}"),
218227
}
219228
}
220229

@@ -309,8 +318,8 @@ fn general_position_dispatch<O: OffsetSizeTrait>(args: &[ArrayRef]) -> Result<Ar
309318
as_int64_array(&args[2])?
310319
.values()
311320
.iter()
312-
.map(|&x| x - 1)
313-
.collect::<Vec<_>>()
321+
.map(|&x| normalize_start_from(x))
322+
.collect::<Result<Vec<_>>>()?
314323
} else {
315324
vec![0; haystack.len()]
316325
};
@@ -592,9 +601,24 @@ fn array_positions_scalar<O: OffsetSizeTrait>(
592601
mod tests {
593602
use super::*;
594603
use arrow::array::AsArray;
604+
use arrow::array::Int64Array;
595605
use arrow::datatypes::Int32Type;
596606
use datafusion_common::config::ConfigOptions;
597607

608+
#[test]
609+
fn test_array_position_start_from_min_value() {
610+
let scalar_arg = ColumnarValue::Scalar(ScalarValue::Int64(Some(i64::MIN)));
611+
let array_arg = ColumnarValue::Array(Arc::new(Int64Array::from(vec![i64::MIN])));
612+
613+
for arg in [scalar_arg, array_arg] {
614+
let err = resolve_start_from(Some(&arg), 1).unwrap_err().to_string();
615+
assert!(
616+
err.contains("start_from out of bounds: -9223372036854775808"),
617+
"unexpected error: {err}"
618+
);
619+
}
620+
}
621+
598622
#[test]
599623
fn test_array_position_sliced_list() -> Result<()> {
600624
// [[10, 20], [30, 40], [50, 60], [70, 80]] → slice(1,2) → [[30, 40], [50, 60]]

datafusion/sqllogictest/test_files/array/array_position.slt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,9 @@ select array_position([1, 2, 3], 3, 4), array_position([1], 1, 2);
282282
----
283283
NULL NULL
284284

285+
query error DataFusion error: Execution error: start_from out of bounds: -9223372036854775808
286+
SELECT array_position([1], 1, -9223372036854775808);
287+
285288
# array_position with empty array in various contexts
286289
query II
287290
select array_position(arrow_cast(make_array(), 'List(Int64)'), 1), array_position(arrow_cast(make_array(), 'LargeList(Int64)'), 1);

0 commit comments

Comments
 (0)