diff --git a/arrow-avro/src/codec.rs b/arrow-avro/src/codec.rs index d54c6602dad6..2864677f20bc 100644 --- a/arrow-avro/src/codec.rs +++ b/arrow-avro/src/codec.rs @@ -307,18 +307,18 @@ impl AvroDataType { } } - // Handle JSON nulls per-spec: allowed only for `null` type or unions with null FIRST + // Handle JSON nulls per-spec: allowed only for `null` type or unions with null if default_json.is_null() { return match self.codec() { Codec::Null => Ok(AvroLiteral::Null), - Codec::Union(encodings, _, _) if !encodings.is_empty() - && matches!(encodings[0].codec(), Codec::Null) => + Codec::Union(encodings, _, _) + if encodings.iter().any(|enc| matches!(enc.codec(), Codec::Null)) => { Ok(AvroLiteral::Null) } - _ if self.nullability() == Some(Nullability::NullFirst) => Ok(AvroLiteral::Null), + _ if self.nullability().is_some() => Ok(AvroLiteral::Null), _ => Err(ArrowError::SchemaError( - "JSON null default is only valid for `null` type or for a union whose first branch is `null`" + "JSON null default is only valid for `null` type or for a union with a `null` branch" .to_string(), )), }; @@ -2946,12 +2946,9 @@ mod tests { assert_default_stored(&dt_int_nf, &Value::Null); let mut dt_int_ns = AvroDataType::new(Codec::Int32, HashMap::new(), Some(Nullability::NullSecond)); - let err2 = dt_int_ns.parse_and_store_default(&Value::Null).unwrap_err(); - assert!( - err2.to_string() - .contains("JSON null default is only valid for `null` type"), - "unexpected error: {err2}" - ); + let lit3 = dt_int_ns.parse_and_store_default(&Value::Null).unwrap(); + assert_eq!(lit3, AvroLiteral::Null); + assert_default_stored(&dt_int_ns, &Value::Null); } #[test] diff --git a/arrow-avro/src/reader/mod.rs b/arrow-avro/src/reader/mod.rs index aa01f272bfeb..6b2568b42eb6 100644 --- a/arrow-avro/src/reader/mod.rs +++ b/arrow-avro/src/reader/mod.rs @@ -2936,6 +2936,7 @@ mod test { },"default":{"x":7}}), serde_json::json!({"name":"d_nullable_null","type":["null","int"],"default":null}), serde_json::json!({"name":"d_nullable_value","type":["int","null"],"default":123}), + serde_json::json!({"name":"d_nullable_null_second","type":["int","null"],"default":null}), ], ); let actual = read_alltypes_with_reader_schema(path, reader_schema); @@ -2943,7 +2944,7 @@ mod test { assert!(num_rows > 0, "skippable_types.avro should contain rows"); assert_eq!( actual.num_columns(), - 22, + 23, "expected exactly our defaulted fields" ); let mut arrays: Vec> = Vec::with_capacity(22); @@ -3070,6 +3071,10 @@ mod test { arrays.push(Arc::new(Int32Array::from_iter_values(std::iter::repeat_n( 123, num_rows, )))); + arrays.push(Arc::new(Int32Array::from_iter(std::iter::repeat_n( + None::, + num_rows, + )))); let expected = RecordBatch::try_new(actual.schema(), arrays).unwrap(); assert_eq!( actual, expected,