Evan Blackwell created AVRO-3787:
------------------------------------

             Summary: [Rust] Deserialization fails to use default if an enum in 
a record in a union is given an unknown symbol 
                 Key: AVRO-3787
                 URL: https://issues.apache.org/jira/browse/AVRO-3787
             Project: Apache Avro
          Issue Type: Bug
          Components: rust
            Reporter: Evan Blackwell


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

 
{code:java}
#[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(())
    } {code}
 

 

Test that defines the enum in the record and fails

 
{code:java}
#[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(())
    } {code}
 

Both tests fail with the same error: 
{code:java}
apache_avro::error::Error: Could not find matching type in union {code}
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.

 



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to