diff --git a/arrow-json/src/reader/mod.rs b/arrow-json/src/reader/mod.rs index ea4cbe2664f6..ececc671d3ba 100644 --- a/arrow-json/src/reader/mod.rs +++ b/arrow-json/src/reader/mod.rs @@ -845,9 +845,9 @@ fn make_decoder( DataType::BinaryView => Ok(Box::new(BinaryViewDecoder::default())), DataType::Map(_, _) => Ok(Box::new(MapArrayDecoder::new(ctx, data_type, is_nullable)?)), DataType::RunEndEncoded(ref r, _) => match r.data_type() { - DataType::Int16 => Ok(Box::new(RunEndEncodedArrayDecoder::::new(ctx, data_type, is_nullable)?)), - DataType::Int32 => Ok(Box::new(RunEndEncodedArrayDecoder::::new(ctx, data_type, is_nullable)?)), - DataType::Int64 => Ok(Box::new(RunEndEncodedArrayDecoder::::new(ctx, data_type, is_nullable)?)), + DataType::Int16 => Ok(Box::new(RunEndEncodedArrayDecoder::::new(ctx, data_type)?)), + DataType::Int32 => Ok(Box::new(RunEndEncodedArrayDecoder::::new(ctx, data_type)?)), + DataType::Int64 => Ok(Box::new(RunEndEncodedArrayDecoder::::new(ctx, data_type)?)), d => unreachable!("unsupported run end index type: {d}"), }, _ => Err(ArrowError::NotYetImplemented(format!("Support for {data_type} in JSON reader"))) @@ -3584,6 +3584,23 @@ mod tests { assert_eq!(values.value(1), "y"); } + #[test] + fn test_read_run_end_encoded_nulls_in_non_nullable_values() { + let ree_type = DataType::RunEndEncoded( + Arc::new(Field::new("run_ends", DataType::Int32, false)), + Arc::new(Field::new("values", DataType::Utf8, false)), + ); + let schema = Arc::new(Schema::new(vec![Field::new("a", ree_type, true)])); + + for buf in [r#"{"a": null}"#, r#"{}"#] { + let mut decoder = ReaderBuilder::new(schema.clone()).build_decoder().unwrap(); + decoder.decode(buf.as_bytes()).unwrap(); + decoder + .flush() + .expect_err("nullable REE parent must not make values nullable"); + } + } + #[test] fn test_read_run_end_encoded_consecutive_nulls() { let buf = r#" diff --git a/arrow-json/src/reader/run_end_array.rs b/arrow-json/src/reader/run_end_array.rs index 26ade9c8ed71..8beb4d9bd695 100644 --- a/arrow-json/src/reader/run_end_array.rs +++ b/arrow-json/src/reader/run_end_array.rs @@ -33,27 +33,22 @@ use crate::reader::{ArrayDecoder, DecoderContext}; pub struct RunEndEncodedArrayDecoder { data_type: DataType, decoder: Box, + values_nullable: bool, phantom: PhantomData, } impl RunEndEncodedArrayDecoder { - pub fn new( - ctx: &DecoderContext, - data_type: &DataType, - is_nullable: bool, - ) -> Result { + pub fn new(ctx: &DecoderContext, data_type: &DataType) -> Result { let values_field = match data_type { DataType::RunEndEncoded(_, v) => v, _ => unreachable!(), }; - let decoder = ctx.make_decoder( - values_field.data_type(), - values_field.is_nullable() || is_nullable, - )?; + let decoder = ctx.make_decoder(values_field.data_type(), values_field.is_nullable())?; Ok(Self { data_type: data_type.clone(), decoder, + values_nullable: values_field.is_nullable(), phantom: Default::default(), }) } @@ -67,6 +62,11 @@ impl ArrayDecoder for RunEndEncodedArrayDecoder { } let flat_array = self.decoder.decode(tape, pos)?; + if !self.values_nullable && flat_array.null_count() != 0 { + return Err(ArrowError::JsonError( + "Encountered nulls in non-nullable RunEndEncoded values field".to_string(), + )); + } let partitions = partition(from_ref(&flat_array))?; let size = partitions.len();