Details
-
Bug
-
Status: Resolved
-
Major
-
Resolution: Fixed
-
None
Description
When providing from_avro_datum with a reader_schema, there is a FindUnionVariant error if * There is a union with one of the types being a record that has an enum type field
- The enum type has a default
- The value in the message is a symbol in the writer schema but not the reader schema
- The enum can be defined earlier in the schema or inline in the record
Test that has a reference type and fails
#[test] fn deserialize_union_with_unknown_symbol() -> TestResult { #[derive(Debug, PartialEq, Eq, Clone, serde::Deserialize, serde::Serialize)] pub struct BarUseParent { #[serde(rename = "barUse")] pub bar_use: Bar, } #[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Clone, serde::Deserialize, serde::Serialize)] pub enum Bar { #[serde(rename = "bar0")] Bar0, #[serde(rename = "bar1")] Bar1, #[serde(rename = "bar2")] Bar2, } #[derive(Debug, PartialEq, Eq, Clone, serde::Deserialize, serde::Serialize)] pub struct Foo { #[serde(rename = "barInit")] pub bar_init: Bar, #[serde(rename = "barUseParent")] pub bar_use_parent: Option<BarUseParent>, } let writer_schema = r#"{ "type": "record", "name": "Foo", "fields": [ { "name": "barInit", "type": { "type": "enum", "name": "Bar", "symbols": [ "bar0", "bar1", "bar2" ], "default": "bar0" } }, { "name": "barUseParent", "type": [ "null", { "type": "record", "name": "BarUseParent", "fields": [ { "name": "barUse", "type": "Bar" } ] } ] } ] }"#; let reader_schema = r#"{ "type": "record", "name": "Foo", "fields": [ { "name": "barInit", "type": { "type": "enum", "name": "Bar", "symbols": [ "bar0", "bar1" ], "default": "bar0" } }, { "name": "barUseParent", "type": [ "null", { "type": "record", "name": "BarUseParent", "fields": [ { "name": "barUse", "type": "Bar" } ] } ] } ] }"#; let writer_schema = Schema::parse_str(writer_schema)?; let foo = Foo { bar_init: Bar::Bar1, bar_use_parent: Some(BarUseParent { bar_use: Bar::Bar2} ), }; let avro_value = crate::to_value(foo)?; assert!( avro_value.validate(&writer_schema), "value is valid for schema", ); let datum = crate::to_avro_datum(&writer_schema, avro_value)?; let mut x = &datum[..]; let reader_schema = Schema::parse_str(reader_schema)?; let deser_value = crate::from_avro_datum(&writer_schema, &mut x, Some(&reader_schema))?; match deser_value { types::Value::Record(fields) => { assert_eq!(fields.len(), 2); assert_eq!(fields[0].0, "barInit"); assert_eq!(fields[0].1, types::Value::Enum(1, "bar1".to_string())); assert_eq!(fields[1].0, "barUseParent"); // TODO: test value } _ => panic!("Expected Value::Record"), } Ok(()) }
Test that defines the enum in the record and fails
#[test] fn deserialize_union_with_unknown_symbol_no_ref() -> TestResult { #[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Clone, serde::Deserialize, serde::Serialize)] pub enum Bar { #[serde(rename = "bar0")] Bar0, #[serde(rename = "bar1")] Bar1, #[serde(rename = "bar2")] Bar2, } #[derive(Debug, PartialEq, Eq, Clone, serde::Deserialize, serde::Serialize)] #[serde(default)] pub struct BarParent { #[serde(rename = "Bar")] pub bar: Bar, } #[inline(always)] fn default_barparent_bar() -> Bar { Bar::Bar0 } impl Default for BarParent { fn default() -> BarParent { BarParent { bar: default_barparent_bar(), } } } #[derive(Debug, PartialEq, Eq, Clone, serde::Deserialize, serde::Serialize)] pub struct Foo { #[serde(rename = "barParent")] pub bar_parent: Option<BarParent>, } let writer_schema = r#"{ "type": "record", "name": "Foo", "fields": [ { "name": "barParent", "type": [ "null", { "type": "record", "name": "BarParent", "fields": [ { "type": "enum", "name": "Bar", "symbols": [ "bar0", "bar1", "bar2" ], "default": "bar0" } ] } ] } ] }"#; let reader_schema = r#"{ "type": "record", "name": "Foo", "fields": [ { "name": "barParent", "type": [ "null", { "type": "record", "name": "BarParent", "fields": [ { "type": "enum", "name": "Bar", "symbols": [ "bar0", "bar1" ], "default": "bar0" } ] } ] } ] }"#; let writer_schema = Schema::parse_str(writer_schema)?; let foo = Foo { bar_parent: Some(BarParent { bar: Bar::Bar2} ), }; let avro_value = crate::to_value(foo)?; assert!( avro_value.validate(&writer_schema), "value is valid for schema", ); let datum = crate::to_avro_datum(&writer_schema, avro_value)?; let mut x = &datum[..]; let reader_schema = Schema::parse_str(reader_schema)?; let deser_value = crate::from_avro_datum(&writer_schema, &mut x, Some(&reader_schema))?; match deser_value { types::Value::Record(fields) => { assert_eq!(fields.len(), 1); // assert_eq!(fields[0].0, "barInit"); // assert_eq!(fields[0].1, types::Value::Enum(0, "bar0".to_string())); assert_eq!(fields[0].0, "barParent"); // assert_eq!(fields[1].1, types::Value::Enum(1, "bar1".to_string())); } _ => panic!("Expected Value::Record"), } Ok(()) }
Both tests fail with the same error:
apache_avro::error::Error: Could not find matching type in union
If I write the same tests but give the value Bar1, then they pass, so it seems to only be if it's an unknown symbol.
Attachments
Issue Links
- links to