This is an automated email from the ASF dual-hosted git repository.
Jefffrey pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-rs.git
The following commit(s) were added to refs/heads/main by this push:
new 4dc1469336 fix(arrow-json): validate ListView child nullability
(#10486)
4dc1469336 is described below
commit 4dc146933636be970316ebfa536fdd56c2845a9b
Author: Joshua Nwachinemere <[email protected]>
AuthorDate: Thu Jul 30 03:43:09 2026 +0100
fix(arrow-json): validate ListView child nullability (#10486)
# Which issue does this PR close?
- Closes #10476.
# Rationale for this change
The JSON reader constructs `ListView` arrays without validating child
nullability. As a result, JSON containing `null` can be accepted for a
non-nullable child field.
# What changes are included in this PR?
- Replace unchecked `ListView` construction with
`GenericListViewArray::try_new`, returning an `ArrowError` when child
nullability is invalid.
- Add regression coverage for both `ListView` and `LargeListView`.
# Are these changes tested?
Yes:
- `cargo test -p arrow-json
reader::tests::test_read_list_view_rejects_null_non_nullable_child --
--exact`
- `cargo test -p arrow-json --all-features`
- `cargo clippy --workspace --all-targets --all-features -- -D warnings`
- `cargo fmt --all -- --check`
- `git diff --check`
# Are there any user-facing changes?
Yes. Invalid JSON containing a null inside a non-nullable `ListView` or
`LargeListView` child now returns an error. There is no public API
change.
---
arrow-json/src/reader/list_array.rs | 17 +++++++----------
arrow-json/src/reader/mod.rs | 28 ++++++++++++++++++++++++++++
2 files changed, 35 insertions(+), 10 deletions(-)
diff --git a/arrow-json/src/reader/list_array.rs
b/arrow-json/src/reader/list_array.rs
index 63644964c3..fc6af59079 100644
--- a/arrow-json/src/reader/list_array.rs
+++ b/arrow-json/src/reader/list_array.rs
@@ -112,16 +112,13 @@ impl<O: OffsetSizeTrait, const IS_VIEW: bool>
ArrayDecoder for ListLikeArrayDeco
sizes.push(offsets[i] - offsets[i - 1]);
}
offsets.pop();
- // SAFETY: offsets and sizes are constructed correctly from the
tape
- let array = unsafe {
- GenericListViewArray::<O>::new_unchecked(
- self.field.clone(),
- ScalarBuffer::from(offsets),
- ScalarBuffer::from(sizes),
- values,
- nulls,
- )
- };
+ let array = GenericListViewArray::<O>::try_new(
+ self.field.clone(),
+ ScalarBuffer::from(offsets),
+ ScalarBuffer::from(sizes),
+ values,
+ nulls,
+ )?;
Ok(Arc::new(array))
} else {
// SAFETY: offsets are built monotonically starting from 0
diff --git a/arrow-json/src/reader/mod.rs b/arrow-json/src/reader/mod.rs
index bc95f9562b..ee8d0a542a 100644
--- a/arrow-json/src/reader/mod.rs
+++ b/arrow-json/src/reader/mod.rs
@@ -2341,6 +2341,34 @@ mod tests {
assert_read_list_view::<i64>();
}
+ #[test]
+ fn test_read_list_view_rejects_null_non_nullable_child() {
+ let field = Arc::new(Field::new("item", DataType::Int32, false));
+ for (data_type, array_type) in [
+ (DataType::ListView(field.clone()), "ListViewArray"),
+ (DataType::LargeListView(field.clone()), "LargeListViewArray"),
+ ] {
+ let schema = Arc::new(Schema::new(vec![Field::new("lv", data_type,
true)]));
+ let buf = r#"
+ {"lv": [1, 2, 3]}
+ {"lv": [4, null]}
+ "#;
+
+ let error = ReaderBuilder::new(schema)
+ .build(Cursor::new(buf.as_bytes()))
+ .unwrap()
+ .collect::<Result<Vec<_>, _>>()
+ .unwrap_err();
+
+ assert_eq!(
+ error.to_string(),
+ format!(
+ "Invalid argument error: Non-nullable field of
{array_type} \"item\" cannot contain nulls"
+ )
+ );
+ }
+ }
+
#[test]
fn test_fixed_size_list() {
let buf = r#"