You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datafusion.apache.org by gi...@apache.org on 2026/09/20 00:46:34 UTC
(datafusion) 01/01: fix: reject LargeList offsets that exceed i32 (#25516)
This is an automated email from the ASF dual-hosted git repository.
github-merge-queue[bot] pushed a commit to branch gh-readonly-queue/main/pr-25516-925d7f8ffdcb5584e26f2410facbcfc5b4234d20
in repository https://gitbox.apache.org/repos/asf/datafusion.git
commit 38a1865b7e18889f4645c5b081a44b414af3545c
Author: Liang-Chi Hsieh <vi...@gmail.com>
AuthorDate: Sun Sep 20 00:46:12 2026 +0000
fix: reject LargeList offsets that exceed i32 (#25516)
## Which issue does this PR close?
N/A
## Rationale for this change
Spark map functions accept `LargeList` inputs whose offsets are
represented as
`i64`. The map construction path converts these offsets to `i32`, but
the
existing unchecked cast silently wraps oversized values.
For example, an offset of `2147483648` becomes `-2147483648`. Downstream
code
can then interpret the wrapped value as an invalid child-array index
instead of
reporting the unsupported input.
## What changes are included in this PR?
- Replace the unchecked `i64` to `i32` conversion with a checked
conversion.
- Return an actionable execution error when an offset cannot be
represented as
`i32`.
- Add a regression test using a sliced `LargeList` whose first offset
exceeds
`i32::MAX`.
## What is the testing strategy for this PR?
Added `large_list_offsets_do_not_truncate`.
Ablation testing confirmed that the test fails with the old
implementation,
which returns the truncated offsets `[-2147483648, -2147483647]`, and
passes
with the checked conversion.
Also verified:
- `cargo fmt --all -- --check`
- `git diff --check`
The full test suite is covered by CI.
## Are there any user-facing changes?
Yes. Oversized `LargeList` offsets now produce a clear execution error
instead
of being silently truncated. There are no API changes.
---
datafusion/spark/src/function/map/utils.rs | 32 ++++++++++++++++++++++++++----
1 file changed, 28 insertions(+), 4 deletions(-)
diff --git a/datafusion/spark/src/function/map/utils.rs b/datafusion/spark/src/function/map/utils.rs
index cd8fa9ccdd..6f6fec5b3b 100644
--- a/datafusion/spark/src/function/map/utils.rs
+++ b/datafusion/spark/src/function/map/utils.rs
@@ -25,7 +25,7 @@ use arrow::array::{
use arrow::buffer::{NullBuffer, OffsetBuffer};
use arrow::compute::{filter, take};
use arrow::datatypes::{DataType, Field, Fields};
-use datafusion_common::{Result, ScalarValue, exec_err};
+use datafusion_common::{Result, ScalarValue, exec_datafusion_err, exec_err};
/// Helper function to get element [`DataType`]
/// from [`List`](DataType::List)/[`LargeList`](DataType::LargeList)/[`FixedSizeList`](DataType::FixedSizeList)<br>
@@ -70,8 +70,14 @@ pub fn get_list_offsets(array: &ArrayRef) -> Result<Cow<'_, [i32]>> {
.as_list::<i64>()
.offsets()
.iter()
- .map(|i| *i as i32)
- .collect::<Vec<_>>(),
+ .map(|offset| {
+ i32::try_from(*offset).map_err(|_| {
+ exec_datafusion_err!(
+ "get_list_offsets: LargeList offset {offset} cannot be represented as i32"
+ )
+ })
+ })
+ .collect::<Result<Vec<_>>>()?,
)),
DataType::FixedSizeList(_, size) => Ok(Cow::Owned(
(0..=array.len() as i32).map(|i| size * i).collect(),
@@ -259,7 +265,7 @@ fn map_deduplicate_keys(
#[cfg(test)]
mod tests {
use super::*;
- use arrow::array::{Int32Array, StringArray};
+ use arrow::array::{Int32Array, LargeListArray, NullArray, StringArray};
use arrow::datatypes::Int32Type;
fn int32_utf8_inputs(
@@ -271,6 +277,24 @@ mod tests {
(keys, values)
}
+ #[test]
+ fn large_list_offsets_do_not_truncate() {
+ let start = i64::from(i32::MAX) + 1;
+ let values: ArrayRef = Arc::new(NullArray::new((start + 1) as usize));
+ let array: ArrayRef = Arc::new(
+ LargeListArray::new(
+ Arc::new(Field::new_list_field(DataType::Null, true)),
+ OffsetBuffer::new(vec![0, start, start + 1].into()),
+ values,
+ None,
+ )
+ .slice(1, 1),
+ );
+
+ let err = get_list_offsets(&array).unwrap_err().to_string();
+ assert!(err.contains("cannot be represented as i32"), "{err}");
+ }
+
#[test]
fn happy_path_two_rows_no_duplicates() {
let (keys, values) =
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datafusion.apache.org
For additional commands, e-mail: commits-help@datafusion.apache.org