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 63f861d90 AVRO-3925: Fix decimal serialization format (#2673)
63f861d90 is described below
commit 63f861d908fad90ca66e51687d2e7361ade38155
Author: Renjie Liu <[email protected]>
AuthorDate: Thu Jan 4 19:02:09 2024 +0800
AVRO-3925: Fix decimal serialization format (#2673)
(cherry picked from commit 4295bc2289d5e16525beaae622d929067716dbd9)
---
lang/rust/avro/src/schema.rs | 142 ++++++++++++++++++++++++++++++++++---------
1 file changed, 113 insertions(+), 29 deletions(-)
diff --git a/lang/rust/avro/src/schema.rs b/lang/rust/avro/src/schema.rs
index 3ab7a1356..90872a768 100644
--- a/lang/rust/avro/src/schema.rs
+++ b/lang/rust/avro/src/schema.rs
@@ -49,7 +49,7 @@ fn schema_name_r() -> &'static Regex {
Regex::new(
r"^((?P<namespace>([A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*)?)\.)?(?P<name>[A-Za-z_][A-Za-z0-9_]*)$",
)
- .unwrap()
+ .unwrap()
})
}
@@ -836,6 +836,33 @@ pub struct FixedSchema {
pub attributes: BTreeMap<String, Value>,
}
+impl FixedSchema {
+ fn serialize_to_map<S>(&self, mut map: S::SerializeMap) ->
Result<S::SerializeMap, S::Error>
+ where
+ S: Serializer,
+ {
+ map.serialize_entry("type", "fixed")?;
+ if let Some(ref n) = self.name.namespace {
+ map.serialize_entry("namespace", n)?;
+ }
+ map.serialize_entry("name", &self.name.name)?;
+ if let Some(ref docstr) = self.doc {
+ map.serialize_entry("doc", docstr)?;
+ }
+ map.serialize_entry("size", &self.size)?;
+
+ if let Some(ref aliases) = self.aliases {
+ map.serialize_entry("aliases", aliases)?;
+ }
+
+ for attr in &self.attributes {
+ map.serialize_entry(attr.0, attr.1)?;
+ }
+
+ Ok(map)
+ }
+}
+
/// A description of a Union schema.
///
/// `scale` defaults to 0 and is an integer greater than or equal to 0 and
`precision` is an
@@ -1883,31 +1910,9 @@ impl Serialize for Schema {
}
map.end()
}
- Schema::Fixed(FixedSchema {
- ref name,
- ref doc,
- ref size,
- ref aliases,
- ref attributes,
- }) => {
+ Schema::Fixed(ref fixed_schema) => {
let mut map = serializer.serialize_map(None)?;
- map.serialize_entry("type", "fixed")?;
- if let Some(ref n) = name.namespace {
- map.serialize_entry("namespace", n)?;
- }
- map.serialize_entry("name", &name.name)?;
- if let Some(ref docstr) = doc {
- map.serialize_entry("doc", docstr)?;
- }
- map.serialize_entry("size", size)?;
-
- if let Some(ref aliases) = aliases {
- map.serialize_entry("aliases", aliases)?;
- }
-
- for attr in attributes {
- map.serialize_entry(attr.0, attr.1)?;
- }
+ map = fixed_schema.serialize_to_map::<S>(map)?;
map.end()
}
Schema::Decimal(DecimalSchema {
@@ -1916,12 +1921,26 @@ impl Serialize for Schema {
ref inner,
}) => {
let mut map = serializer.serialize_map(None)?;
- map.serialize_entry("type", &*inner.clone())?;
+ match inner.as_ref() {
+ Schema::Fixed(fixed_schema) => {
+ map = fixed_schema.serialize_to_map::<S>(map)?;
+ }
+ Schema::Bytes => {
+ map.serialize_entry("type", "bytes")?;
+ }
+ others => {
+ return Err(serde::ser::Error::custom(format!(
+ "DecimalSchema inner type must be Fixed or Bytes,
got {:?}",
+ SchemaKind::from(others)
+ )));
+ }
+ }
map.serialize_entry("logicalType", "decimal")?;
map.serialize_entry("scale", scale)?;
map.serialize_entry("precision", precision)?;
map.end()
}
+
Schema::BigDecimal => {
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("type", "bytes")?;
@@ -2210,7 +2229,7 @@ pub mod derive {
}
}
- macro_rules! impl_schema(
+ macro_rules! impl_schema (
($type:ty, $variant_constructor:expr) => (
impl AvroSchemaComponent for $type {
fn get_schema_in_ctxt(_: &mut Names, _: &Namespace) -> Schema {
@@ -3341,7 +3360,7 @@ mod tests {
schema,
Schema::Union(UnionSchema::new(vec![
Schema::Null,
- Schema::TimestampMicros
+ Schema::TimestampMicros,
])?)
);
@@ -5233,7 +5252,7 @@ mod tests {
match Schema::parse_str(schema_str) {
Err(Error::FieldNameDuplicate(_)) => (),
other => {
- return Err(format!("Expected Error::FieldNameDuplicate, got
{other:?}").into())
+ return Err(format!("Expected Error::FieldNameDuplicate, got
{other:?}").into());
}
};
@@ -6394,4 +6413,69 @@ mod tests {
Ok(())
}
+
+ #[test]
+ fn test_avro_3925_serialize_decimal_inner_fixed() -> TestResult {
+ let schema = Schema::Decimal(DecimalSchema {
+ precision: 36,
+ scale: 10,
+ inner: Box::new(Schema::Fixed(FixedSchema {
+ name: Name::new("decimal_36_10").unwrap(),
+ aliases: None,
+ doc: None,
+ size: 16,
+ attributes: Default::default(),
+ })),
+ });
+
+ let serialized_json = serde_json::to_string_pretty(&schema)?;
+
+ let expected_json = r#"{
+ "type": "fixed",
+ "name": "decimal_36_10",
+ "size": 16,
+ "logicalType": "decimal",
+ "scale": 10,
+ "precision": 36
+}"#;
+
+ assert_eq!(serialized_json, expected_json);
+ Ok(())
+ }
+
+ #[test]
+ fn test_avro_3925_serialize_decimal_inner_bytes() -> TestResult {
+ let schema = Schema::Decimal(DecimalSchema {
+ precision: 36,
+ scale: 10,
+ inner: Box::new(Schema::Bytes),
+ });
+
+ let serialized_json = serde_json::to_string_pretty(&schema)?;
+
+ let expected_json = r#"{
+ "type": "bytes",
+ "logicalType": "decimal",
+ "scale": 10,
+ "precision": 36
+}"#;
+
+ assert_eq!(serialized_json, expected_json);
+ Ok(())
+ }
+
+ #[test]
+ fn test_avro_3925_serialize_decimal_inner_invalid() -> TestResult {
+ let schema = Schema::Decimal(DecimalSchema {
+ precision: 36,
+ scale: 10,
+ inner: Box::new(Schema::String),
+ });
+
+ let serialized_json = serde_json::to_string_pretty(&schema);
+
+ assert!(serialized_json.is_err());
+
+ Ok(())
+ }
}