Uploaded image for project: 'Apache Avro'
  1. Apache Avro
  2. AVRO-3787

[Rust] Deserialization fails to use default if an enum in a record in a union is given an unknown symbol

    XMLWordPrintableJSON

Details

    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

          Activity

            People

              mgrigorov Martin Tzvetanov Grigorov
              evanmb27 Evan Blackwell
              Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:

                Time Tracking

                  Estimated:
                  Original Estimate - Not Specified
                  Not Specified
                  Remaining:
                  Remaining Estimate - 0h
                  0h
                  Logged:
                  Time Spent - 20m
                  20m