This is an automated email from the ASF dual-hosted git repository.
mgrigorov pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/avro.git
The following commit(s) were added to refs/heads/master by this push:
new efe9aa7fb AVRO-3688: Fix UnionSchema resolution if a UnionSchema
contains a reference (#2011)
efe9aa7fb is described below
commit efe9aa7fbe457805dc894a9186e1267603d82dd2
Author: Rik Heijdens <[email protected]>
AuthorDate: Tue Dec 13 14:14:15 2022 +0100
AVRO-3688: Fix UnionSchema resolution if a UnionSchema contains a reference
(#2011)
* AVRO-3688: Add test-case to reproduce
* AVRO-3688: Implement fix for resolving Union schemas
A UnionSchema might contain a schema reference in the form of a
`Schema::Ref`, in order to properly support resolution of UnionSchemas
we must ensure we first resolve the `Schema::Ref` into the resolved
schema prior to resolving the union fields itself.
* AVRO-3688: Fix clippy lint
---
lang/rust/avro/src/types.rs | 108 ++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 104 insertions(+), 4 deletions(-)
diff --git a/lang/rust/avro/src/types.rs b/lang/rust/avro/src/types.rs
index 9ee6b7b2d..940ca17aa 100644
--- a/lang/rust/avro/src/types.rs
+++ b/lang/rust/avro/src/types.rs
@@ -372,7 +372,6 @@ impl Value {
match (self, schema) {
(_, &Schema::Ref { ref name }) => {
let name = name.fully_qualified_name(enclosing_namespace);
-
names.get(&name).map_or_else(
|| {
Some(format!(
@@ -870,9 +869,29 @@ impl Value {
v => v,
};
- // Find the first match in the reader schema.
- // FIXME: this might be wrong when the union consists of multiple same
records that have different names
- let (i, inner) =
schema.find_schema(&v).ok_or(Error::FindUnionVariant)?;
+ // A union might contain references to another schema in the form of a
Schema::Ref,
+ // resolve these prior to finding the schema.
+ let resolved_schemas: Vec<Schema> = schema
+ .schemas
+ .iter()
+ .cloned()
+ .map(|schema| match schema {
+ Schema::Ref { name } => {
+ let name = name.fully_qualified_name(enclosing_namespace);
+ names
+ .get(&name)
+ .map(|s| (**s).clone())
+ .ok_or_else(||
Error::SchemaResolutionError(name.clone()))
+ }
+ schema => Ok(schema),
+ })
+ .collect::<Result<Vec<Schema>, Error>>()?;
+
+ let resolved_union_schema =
UnionSchema::new(resolved_schemas).unwrap();
+ let (i, inner) = resolved_union_schema
+ .find_schema(&v)
+ .ok_or(Error::FindUnionVariant)?;
+
Ok(Value::Union(
i as u32,
Box::new(v.resolve_internal(inner, names, enclosing_namespace)?),
@@ -2540,4 +2559,85 @@ Field with name '"b"' is not a member of the map items"#,
fn test_avro_3674_validate_with_namespace_resolution() {
avro_3674_with_or_without_namespace(true);
}
+
+ fn avro_3688_schema_resolution_panic(set_field_b: bool) {
+ use crate::ser::Serializer;
+ use serde::{Deserialize, Serialize};
+
+ let schema_str = r#"{
+ "type": "record",
+ "name": "Message",
+ "fields": [
+ {
+ "name": "field_a",
+ "type": [
+ "null",
+ {
+ "name": "Inner",
+ "type": "record",
+ "fields": [
+ {
+ "name": "inner_a",
+ "type": "string"
+ }
+ ]
+ }
+ ],
+ "default": null
+ },
+ {
+ "name": "field_b",
+ "type": [
+ "null",
+ "Inner"
+ ],
+ "default": null
+ }
+ ]
+ }"#;
+
+ #[derive(Serialize, Deserialize)]
+ struct Inner {
+ inner_a: String,
+ }
+
+ #[derive(Serialize, Deserialize)]
+ struct Message {
+ field_a: Option<Inner>,
+ field_b: Option<Inner>,
+ }
+
+ let schema = Schema::parse_str(schema_str).unwrap();
+
+ let msg = Message {
+ field_a: Some(Inner {
+ inner_a: "foo".to_string(),
+ }),
+ field_b: if set_field_b {
+ Some(Inner {
+ inner_a: "bar".to_string(),
+ })
+ } else {
+ None
+ },
+ };
+
+ let mut ser = Serializer::default();
+ let test_value: Value = msg.serialize(&mut ser).unwrap();
+ assert!(test_value.validate(&schema), "test_value should validate");
+ assert!(
+ test_value.resolve(&schema).is_ok(),
+ "test_value should resolve"
+ );
+ }
+
+ #[test]
+ fn test_avro_3688_field_b_not_set() {
+ avro_3688_schema_resolution_panic(false);
+ }
+
+ #[test]
+ fn test_avro_3688_field_b_set() {
+ avro_3688_schema_resolution_panic(true);
+ }
}