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)