This is an automated email from the ASF dual-hosted git repository.
mgrigorov pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/avro.git
The following commit(s) were added to refs/heads/main by this push:
new 0661bfe71 AVRO-3965: [Rust] Default values for fixed can be longer
than size (#2907)
0661bfe71 is described below
commit 0661bfe71836be253626e1c85d7c6c12a48fe667
Author: Martin Grigorov <[email protected]>
AuthorDate: Wed May 15 10:38:37 2024 +0300
AVRO-3965: [Rust] Default values for fixed can be longer than size (#2907)
Return an error if the default value's length is not the same as the
specified size of a Fixed schema
Signed-off-by: Martin Tzvetanov Grigorov <[email protected]>
---
lang/rust/avro/src/decode.rs | 4 +++
lang/rust/avro/src/encode.rs | 1 +
lang/rust/avro/src/error.rs | 3 ++
lang/rust/avro/src/schema.rs | 51 ++++++++++++++++++++++++++++--
lang/rust/avro/src/schema_compatibility.rs | 2 ++
lang/rust/avro/src/schema_equality.rs | 2 ++
lang/rust/avro/src/types.rs | 5 +++
lang/rust/avro/src/writer.rs | 2 ++
8 files changed, 67 insertions(+), 3 deletions(-)
diff --git a/lang/rust/avro/src/decode.rs b/lang/rust/avro/src/decode.rs
index 41c3f24ab..9c685e9a8 100644
--- a/lang/rust/avro/src/decode.rs
+++ b/lang/rust/avro/src/decode.rs
@@ -149,6 +149,7 @@ pub(crate) fn decode_internal<R: Read, S: Borrow<Schema>>(
name: "uuid".into(),
aliases: None,
doc: None,
+ default: None,
attributes: Default::default(),
}),
names,
@@ -419,6 +420,7 @@ mod tests {
doc: None,
name: Name::new("decimal")?,
aliases: None,
+ default: None,
attributes: Default::default(),
}));
let schema = Schema::Decimal(DecimalSchema {
@@ -448,6 +450,7 @@ mod tests {
name: Name::new("decimal")?,
aliases: None,
doc: None,
+ default: None,
attributes: Default::default(),
}));
let schema = Schema::Decimal(DecimalSchema {
@@ -893,6 +896,7 @@ mod tests {
name: "uuid".into(),
aliases: None,
doc: None,
+ default: None,
attributes: Default::default(),
});
let value =
Value::Uuid(Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000")?);
diff --git a/lang/rust/avro/src/encode.rs b/lang/rust/avro/src/encode.rs
index a7d34a4f5..214673ec9 100644
--- a/lang/rust/avro/src/encode.rs
+++ b/lang/rust/avro/src/encode.rs
@@ -901,6 +901,7 @@ pub(crate) mod tests {
name: "uuid".into(),
aliases: None,
doc: None,
+ default: None,
attributes: Default::default(),
});
let value =
Value::Uuid(Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000")?);
diff --git a/lang/rust/avro/src/error.rs b/lang/rust/avro/src/error.rs
index e46a9975c..b436f403f 100644
--- a/lang/rust/avro/src/error.rs
+++ b/lang/rust/avro/src/error.rs
@@ -370,6 +370,9 @@ pub enum Error {
#[error("Fixed schema has no `size`")]
GetFixedSizeField,
+ #[error("Fixed schema's default value length ({0}) does not match its size
({1})")]
+ FixedDefaultLenSizeMismatch(usize, u64),
+
#[error("Failed to compress with flate")]
DeflateCompress(#[source] std::io::Error),
diff --git a/lang/rust/avro/src/schema.rs b/lang/rust/avro/src/schema.rs
index e7f6d5e4b..a2e83ad6a 100644
--- a/lang/rust/avro/src/schema.rs
+++ b/lang/rust/avro/src/schema.rs
@@ -817,6 +817,8 @@ pub struct FixedSchema {
pub doc: Documentation,
/// The size of the fixed schema
pub size: usize,
+ /// An optional default symbol used for compatibility
+ pub default: Option<String>,
/// The custom attributes of the schema
pub attributes: BTreeMap<String, Value>,
}
@@ -1832,6 +1834,18 @@ impl Parser {
None => Err(Error::GetFixedSizeField),
}?;
+ let default = complex.get("default").and_then(|v| match &v {
+ Value::String(ref default) => Some(default.clone()),
+ _ => None,
+ });
+
+ if default.is_some() {
+ let len = default.clone().unwrap().len();
+ if len != size as usize {
+ return Err(Error::FixedDefaultLenSizeMismatch(len, size));
+ }
+ }
+
let fully_qualified_name = Name::parse(complex, enclosing_namespace)?;
let aliases = fix_aliases_namespace(complex.aliases(),
&fully_qualified_name.namespace);
@@ -1840,6 +1854,7 @@ impl Parser {
aliases: aliases.clone(),
doc,
size: size as usize,
+ default,
attributes: self.get_custom_attributes(complex, vec!["size"]),
});
@@ -2080,6 +2095,7 @@ impl Serialize for Schema {
aliases: None,
doc: None,
size: 12,
+ default: None,
attributes: Default::default(),
});
map.serialize_entry("type", &inner)?;
@@ -3192,6 +3208,7 @@ mod tests {
aliases: None,
doc: None,
size: 456,
+ default: None,
attributes: Default::default(),
}),
order: RecordFieldOrder::Ascending,
@@ -3211,6 +3228,7 @@ mod tests {
aliases: None,
doc: None,
size: 456,
+ default: None,
attributes: Default::default(),
}),
order: RecordFieldOrder::Ascending,
@@ -3285,7 +3303,8 @@ mod tests {
name: Name::new("test")?,
aliases: None,
doc: None,
- size: 16usize,
+ size: 16_usize,
+ default: None,
attributes: Default::default(),
});
@@ -3304,7 +3323,8 @@ mod tests {
name: Name::new("test")?,
aliases: None,
doc: Some(String::from("FixedSchema documentation")),
- size: 16usize,
+ size: 16_usize,
+ default: None,
attributes: Default::default(),
});
@@ -6256,6 +6276,7 @@ mod tests {
aliases: None,
doc: None,
size: 1,
+ default: None,
attributes: attributes.clone(),
});
let serialized = serde_json::to_string(&schema)?;
@@ -6380,11 +6401,12 @@ mod tests {
aliases: None,
doc: None,
size: 6,
+ default: None,
attributes: BTreeMap::from([("logicalType".to_string(),
"uuid".into())]),
})
);
assert_logged(
- r#"Ignoring uuid logical type for a Fixed schema because its size
(6) is not 16! Schema: Fixed(FixedSchema { name: Name { name: "FixedUUID",
namespace: None }, aliases: None, doc: None, size: 6, attributes:
{"logicalType": String("uuid")} })"#,
+ r#"Ignoring uuid logical type for a Fixed schema because its size
(6) is not 16! Schema: Fixed(FixedSchema { name: Name { name: "FixedUUID",
namespace: None }, aliases: None, doc: None, size: 6, default: None,
attributes: {"logicalType": String("uuid")} })"#,
);
Ok(())
@@ -6524,6 +6546,7 @@ mod tests {
aliases: None,
doc: None,
size: 16,
+ default: None,
attributes: Default::default(),
})),
});
@@ -6708,4 +6731,26 @@ mod tests {
Ok(())
}
+
+ #[test]
+ fn avro_3965_fixed_schema_with_default_bigger_than_size() -> TestResult {
+ match Schema::parse_str(
+ r#"{
+ "type": "fixed",
+ "name": "test",
+ "size": 1,
+ "default": "123456789"
+ }"#,
+ ) {
+ Ok(_schema) => panic!("Must fail!"),
+ Err(err) => {
+ assert_eq!(
+ err.to_string(),
+ "Fixed schema's default value length (9) does not match
its size (1)"
+ );
+ }
+ }
+
+ Ok(())
+ }
}
diff --git a/lang/rust/avro/src/schema_compatibility.rs
b/lang/rust/avro/src/schema_compatibility.rs
index 1c7fa8194..5df7c5201 100644
--- a/lang/rust/avro/src/schema_compatibility.rs
+++ b/lang/rust/avro/src/schema_compatibility.rs
@@ -393,6 +393,7 @@ impl SchemaCompatibility {
aliases: _,
doc: _w_doc,
size: w_size,
+ default: _w_default,
attributes: _,
}) = writers_schema
{
@@ -401,6 +402,7 @@ impl SchemaCompatibility {
aliases: _,
doc: _r_doc,
size: r_size,
+ default: _r_default,
attributes: _,
}) = readers_schema
{
diff --git a/lang/rust/avro/src/schema_equality.rs
b/lang/rust/avro/src/schema_equality.rs
index ae90c3f3f..c9eaa2a11 100644
--- a/lang/rust/avro/src/schema_equality.rs
+++ b/lang/rust/avro/src/schema_equality.rs
@@ -424,6 +424,7 @@ mod tests {
name: Name::from("fixed"),
doc: None,
size: 10,
+ default: None,
aliases: None,
attributes: BTreeMap::new(),
});
@@ -434,6 +435,7 @@ mod tests {
name: Name::from("fixed"),
doc: None,
size: 10,
+ default: None,
aliases: None,
attributes: BTreeMap::new(),
});
diff --git a/lang/rust/avro/src/types.rs b/lang/rust/avro/src/types.rs
index 8bac843bb..47daab503 100644
--- a/lang/rust/avro/src/types.rs
+++ b/lang/rust/avro/src/types.rs
@@ -1367,6 +1367,7 @@ mod tests {
name: Name::new("some_fixed").unwrap(),
aliases: None,
doc: None,
+ default: None,
attributes: Default::default(),
});
@@ -1722,6 +1723,7 @@ Field with name '"b"' is not a member of the map items"#,
aliases: None,
size: 20,
doc: None,
+ default: None,
attributes: Default::default(),
}))
}))
@@ -3036,6 +3038,7 @@ Field with name '"b"' is not a member of the map items"#,
aliases: None,
doc: None,
size: 3,
+ default: None,
attributes: Default::default()
}))?,
Value::Fixed(3, vec![97, 98, 99])
@@ -3048,6 +3051,7 @@ Field with name '"b"' is not a member of the map items"#,
aliases: None,
doc: None,
size: 3,
+ default: None,
attributes: Default::default()
}))
.is_err(),);
@@ -3059,6 +3063,7 @@ Field with name '"b"' is not a member of the map items"#,
aliases: None,
doc: None,
size: 3,
+ default: None,
attributes: Default::default()
}))
.is_err(),);
diff --git a/lang/rust/avro/src/writer.rs b/lang/rust/avro/src/writer.rs
index dc6fd55e8..90bb2da4c 100644
--- a/lang/rust/avro/src/writer.rs
+++ b/lang/rust/avro/src/writer.rs
@@ -790,6 +790,7 @@ mod tests {
aliases: None,
doc: None,
size,
+ default: None,
attributes: Default::default(),
});
let value = vec![0u8; size];
@@ -830,6 +831,7 @@ mod tests {
aliases: None,
doc: None,
size: 12,
+ default: None,
attributes: Default::default(),
});
let value = Value::Duration(Duration::new(