You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@avro.apache.org by mg...@apache.org on 2023/07/10 12:56:16 UTC
[avro] branch branch-1.11 updated: AVRO-3787: [Rust] Deserialization fails to use default if an enum in a record in a union is given an unknown symbol (#2333)
This is an automated email from the ASF dual-hosted git repository.
mgrigorov pushed a commit to branch branch-1.11
in repository https://gitbox.apache.org/repos/asf/avro.git
The following commit(s) were added to refs/heads/branch-1.11 by this push:
new 3915d9ad8 AVRO-3787: [Rust] Deserialization fails to use default if an enum in a record in a union is given an unknown symbol (#2333)
3915d9ad8 is described below
commit 3915d9ad898060815b4be4aa4e0f74130c5bb5ee
Author: Martin Grigorov <ma...@users.noreply.github.com>
AuthorDate: Mon Jul 10 15:54:57 2023 +0300
AVRO-3787: [Rust] Deserialization fails to use default if an enum in a record in a union is given an unknown symbol (#2333)
Do not fail the validation if the provided enum symbol is not in the
list of available ones if there schema defines a default symbol
Signed-off-by: Martin Tzvetanov Grigorov <mg...@apache.org>
(cherry picked from commit 5e592a36586384df0024039fa02ed44460b8f4ef)
---
lang/rust/avro/src/types.rs | 12 +-
lang/rust/avro/tests/avro-3787.rs | 279 ++++++++++++++++++++++++++++++++++++++
2 files changed, 289 insertions(+), 2 deletions(-)
diff --git a/lang/rust/avro/src/types.rs b/lang/rust/avro/src/types.rs
index 1b87f25e6..fc1e9130f 100644
--- a/lang/rust/avro/src/types.rs
+++ b/lang/rust/avro/src/types.rs
@@ -460,7 +460,12 @@ impl Value {
None
}
}
- (&Value::Enum(i, ref s), Schema::Enum(EnumSchema { symbols, .. })) => symbols
+ (
+ &Value::Enum(i, ref s),
+ Schema::Enum(EnumSchema {
+ symbols, default, ..
+ }),
+ ) => symbols
.get(i as usize)
.map(|ref symbol| {
if symbol != &s {
@@ -469,7 +474,10 @@ impl Value {
None
}
})
- .unwrap_or_else(|| Some(format!("No symbol at position '{i}'"))),
+ .unwrap_or_else(|| match default {
+ Some(_) => None,
+ None => Some(format!("No symbol at position '{i}'")),
+ }),
// (&Value::Union(None), &Schema::Union(_)) => None,
(&Value::Union(i, ref value), Schema::Union(inner)) => inner
.variants()
diff --git a/lang/rust/avro/tests/avro-3787.rs b/lang/rust/avro/tests/avro-3787.rs
new file mode 100644
index 000000000..c08c3c6cc
--- /dev/null
+++ b/lang/rust/avro/tests/avro-3787.rs
@@ -0,0 +1,279 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use apache_avro::{from_avro_datum, to_avro_datum, to_value, types, Schema};
+use apache_avro_test_helper::TestResult;
+
+#[test]
+fn avro_3787_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 foo1 = Foo {
+ bar_init: Bar::Bar1,
+ bar_use_parent: Some(BarUseParent { bar_use: Bar::Bar2 }),
+ };
+ let avro_value = to_value(foo1)?;
+ assert!(
+ avro_value.validate(&writer_schema),
+ "value is valid for schema",
+ );
+ let datum = to_avro_datum(&writer_schema, avro_value)?;
+ let mut x = &datum[..];
+ let reader_schema = Schema::parse_str(reader_schema)?;
+ let deser_value = 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]
+fn avro_3787_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 foo2 = Foo {
+ bar_parent: Some(BarParent { bar: Bar::Bar2 }),
+ };
+ let avro_value = to_value(foo2)?;
+ assert!(
+ avro_value.validate(&writer_schema),
+ "value is valid for schema",
+ );
+ let datum = to_avro_datum(&writer_schema, avro_value)?;
+ let mut x = &datum[..];
+ let reader_schema = Schema::parse_str(reader_schema)?;
+ let deser_value = 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(())
+}