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 1d4437018c fix(arrow-json): validate map value nullability (#10475)
1d4437018c is described below

commit 1d4437018c145a8595210609e89815d13dde8d9b
Author: subotac <[email protected]>
AuthorDate: Thu Jul 30 05:42:45 2026 +0300

    fix(arrow-json): validate map value nullability (#10475)
    
    > AI disclosure: Codex assisted with issue analysis and running local
    test
    
    # Which issue does this PR close?
    
    - Closes #6391.
    
    # Rationale for this change
    
    The JSON map decoder constructed map entries with an unchecked
    `StructArray` constructor. This bypassed child field nullability
    validation and allowed a null value even when the map value field was
    non-nullable.
    
    # What changes are included in this PR?
    
    - Construct map entries with `StructArray::try_new_with_length` so
    existing schema validation rejects unmasked nulls.
    - Add a regression test for a null JSON map value with a non-nullable
    value field.
    
    # Are these changes tested?
    
    Yes:
    
    - `cargo +stable fmt --all -- --check`
    - `cargo test -p arrow-json --all-features`
    - `cargo clippy --workspace --all-targets --all-features -- -D warnings`
    - `git diff --check`
    
    # Are there any user-facing changes?
    
    Yes. Reading a null JSON map value against a non-nullable value field
    now returns an error instead of producing an array that violates its
    schema. There are no public API changes.
---
 arrow-json/src/reader/map_array.rs | 15 ++++++---------
 arrow-json/src/reader/mod.rs       | 25 +++++++++++++++++++++++++
 2 files changed, 31 insertions(+), 9 deletions(-)

diff --git a/arrow-json/src/reader/map_array.rs 
b/arrow-json/src/reader/map_array.rs
index 396b60b162..abb035193c 100644
--- a/arrow-json/src/reader/map_array.rs
+++ b/arrow-json/src/reader/map_array.rs
@@ -130,15 +130,12 @@ impl ArrayDecoder for MapArrayDecoder {
         let key_array = self.keys.decode(tape, &key_pos)?;
         let value_array = self.values.decode(tape, &value_pos)?;
 
-        // SAFETY: fields/arrays match the schema, lengths are equal, no nulls
-        let entries = unsafe {
-            StructArray::new_unchecked_with_length(
-                self.key_value_fields.clone(),
-                vec![key_array, value_array],
-                None,
-                key_pos.len(),
-            )
-        };
+        let entries = StructArray::try_new_with_length(
+            self.key_value_fields.clone(),
+            vec![key_array, value_array],
+            None,
+            key_pos.len(),
+        )?;
 
         let nulls = nulls.as_mut().and_then(|x| x.finish());
         // 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 ea4cbe2664..bc95f9562b 100644
--- a/arrow-json/src/reader/mod.rs
+++ b/arrow-json/src/reader/mod.rs
@@ -1325,6 +1325,31 @@ mod tests {
         assert_eq!(formatter.value(2).to_string(), "{c: null, a: [baz]}");
     }
 
+    #[test]
+    fn test_map_non_nullable_value() {
+        let map = Field::new_map(
+            "map",
+            Field::MAP_ENTRIES_FIELD_DEFAULT_NAME,
+            Field::new(Field::MAP_KEY_FIELD_DEFAULT_NAME, DataType::Utf8, 
false),
+            Field::new(Field::MAP_VALUE_FIELD_DEFAULT_NAME, DataType::Utf8, 
false),
+            false,
+            false,
+        );
+        let schema = Arc::new(Schema::new(vec![map]));
+        let buf = r#"{"map": {"key": null}}"#;
+
+        let err = ReaderBuilder::new(schema)
+            .build(Cursor::new(buf.as_bytes()))
+            .unwrap()
+            .read()
+            .unwrap_err();
+
+        assert_eq!(
+            err.to_string(),
+            "Invalid argument error: Found unmasked nulls for non-nullable 
StructArray field \"value\""
+        );
+    }
+
     #[test]
     fn test_not_coercing_primitive_into_string_without_flag() {
         let schema = Arc::new(Schema::new(vec![Field::new("a", DataType::Utf8, 
true)]));

Reply via email to