This is an automated email from the ASF dual-hosted git repository. mgrigorov pushed a commit to branch branch-1.11 in repository https://gitbox.apache.org/repos/asf/avro.git
commit 9f3dfbbc7251dc7c038c012a1b808b71d29d2177 Author: Christophe Le Saec <[email protected]> AuthorDate: Tue Oct 25 15:33:35 2022 +0200 AVRO-3649: default for union inside union (cherry picked from commit c28dbe9f7d485f5e5d1df41df1bb2c12c838b5db) --- lang/rust/avro/src/error.rs | 5 +++- lang/rust/avro/src/schema.rs | 62 +++++++++++++++++++++++++++++++++--------- lang/rust/avro/tests/schema.rs | 8 +++--- 3 files changed, 57 insertions(+), 18 deletions(-) diff --git a/lang/rust/avro/src/error.rs b/lang/rust/avro/src/error.rs index c07d34e77..5ec21cecd 100644 --- a/lang/rust/avro/src/error.rs +++ b/lang/rust/avro/src/error.rs @@ -199,6 +199,9 @@ pub enum Error { #[error("Could not find matching type in union")] FindUnionVariant, + #[error("Union type should not be empty")] + EmptyUnion, + #[error("Array({expected:?}) expected, got {other:?}")] GetArray { expected: SchemaKind, @@ -229,7 +232,7 @@ pub enum Error { #[error("Unions cannot contain duplicate types")] GetUnionDuplicate, - #[error("Union's first type {0:?} must match the `default`'s value type {1:?}")] + #[error("One union type {0:?} must match the `default`'s value type {1:?}")] GetDefaultUnion(SchemaKind, ValueKind), #[error("JSON value {0} claims to be u64 but cannot be converted")] diff --git a/lang/rust/avro/src/schema.rs b/lang/rust/avro/src/schema.rs index 848c35b95..a27cfe8c2 100644 --- a/lang/rust/avro/src/schema.rs +++ b/lang/rust/avro/src/schema.rs @@ -1610,19 +1610,19 @@ impl Parser { .and_then(|schemas| { if let Some(default_value) = default.cloned() { let avro_value = types::Value::from(default_value); - let first_schema = schemas.first(); - if let Some(schema) = first_schema { - // Try to resolve the schema - let resolved_value = avro_value.to_owned().resolve(schema); - match resolved_value { - Ok(_) => {} - Err(_) => { - return Err(Error::GetDefaultUnion( - SchemaKind::from(schema), - types::ValueKind::from(avro_value), - )); - } - } + let resolved = schemas + .iter() + .any(|schema| avro_value.to_owned().resolve(schema).is_ok()); + + if !resolved { + let schema: Option<&Schema> = schemas.get(0); + return match schema { + Some(first_schema) => Err(Error::GetDefaultUnion( + SchemaKind::from(first_schema), + types::ValueKind::from(avro_value), + )), + None => Err(Error::EmptyUnion), + }; } } Ok(schemas) @@ -5423,4 +5423,40 @@ mod tests { Ok(()) } + + #[test] + fn avro_3649_default_notintfirst() { + let schema_str = String::from( + r#" + { + "type": "record", + "name": "union_schema_test", + "fields": [ + {"name": "a", "type": ["string", "int"], "default": 123} + ] + } + "#, + ); + + let schema = Schema::parse_str(&schema_str).unwrap(); + + match schema { + Schema::Record { name, fields, .. } => { + assert_eq!(name, Name::new("union_schema_test").unwrap()); + assert_eq!(fields.len(), 1); + let field = &fields[0]; + assert_eq!(&field.name, "a"); + assert_eq!(&field.default, &Some(json!(123))); + match &field.schema { + Schema::Union(union) => { + assert_eq!(union.variants().len(), 2); + assert_eq!(union.variants()[0], Schema::String); + assert_eq!(union.variants()[1], Schema::Int); + } + _ => panic!("Expected Schema::Union"), + } + } + _ => panic!("Expected Schema::Record"), + } + } } diff --git a/lang/rust/avro/tests/schema.rs b/lang/rust/avro/tests/schema.rs index 1508035ab..223305876 100644 --- a/lang/rust/avro/tests/schema.rs +++ b/lang/rust/avro/tests/schema.rs @@ -151,19 +151,19 @@ const UNION_EXAMPLES: &[(&str, bool)] = &[ ), ( r#"{"name": "foo", "type": ["string", "long"], "default": 1}"#, - false, + true, ), ( r#"{"name": "foo", "type": ["string", "null"], "default": null}"#, - false, + true, ), ( r#"{"name": "foo", "type": ["null", "string"], "default": "null"}"#, - false, + true, ), ( r#"{"name": "foo", "type": ["long", "string"], "default": "str"}"#, - false, + true, ), ];
