Skip to content
3 changes: 1 addition & 2 deletions avro/src/bigdecimal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,7 @@ pub(crate) fn serialize_big_decimal(decimal: &BigDecimal) -> AvroResult<Vec<u8>>
Ok(final_buffer)
}

pub(crate) fn deserialize_big_decimal(bytes: &Vec<u8>) -> AvroResult<BigDecimal> {
let mut bytes: &[u8] = bytes.as_slice();
pub(crate) fn deserialize_big_decimal(mut bytes: &[u8]) -> AvroResult<BigDecimal> {
let mut big_decimal_buffer = match decode_len(&mut bytes) {
Ok(size) => vec![0u8; size],
Err(err) => return Err(Details::BigDecimalLen(Box::new(err)).into()),
Expand Down
8 changes: 4 additions & 4 deletions avro/src/schema/name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,12 +202,12 @@ impl Alias {
Name::new(name).map(Self)
}

pub fn name(&self) -> String {
self.0.name.clone()
pub fn name(&self) -> &str {
&self.0.name
}

pub fn namespace(&self) -> Namespace {
self.0.namespace.clone()
pub fn namespace(&self) -> &Namespace {
&self.0.namespace
}

pub fn fullname(&self, default_namespace: Namespace) -> String {
Expand Down
9 changes: 9 additions & 0 deletions avro/src/serde/derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,7 @@ impl_schema!(f64, Schema::Double);
impl_schema!(String, Schema::String);
impl_schema!(str, Schema::String);
impl_schema!(char, Schema::String);
impl_schema!((), Schema::Null);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that () maps to Schema::Null, Option<()> will try to build a union [Null, Null] in Option<T>::get_schema_in_ctxt, which UnionSchema::new rejects (and the code expects), causing a panic during schema generation.

Severity: medium

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

value:useful; category:bug; feedback: The Augment AI reviewer is correct! Trying to derive an AvroSchema for Option<()> will lead to a runtime error due to the duplicate Null variant. It would be better to fail the build with an useful error instead of failing at runtime.


macro_rules! impl_passthrough_schema (
($type:ty where T: AvroSchemaComponent + ?Sized $(+ $bound:tt)*) => (
Expand Down Expand Up @@ -943,4 +944,12 @@ mod tests {

Ok(())
}

#[test]
fn avro_rs_486_unit() -> TestResult {
let schema = <()>::get_schema();
assert_eq!(schema, Schema::Null);

Ok(())
}
}
2 changes: 1 addition & 1 deletion avro/src/serde/ser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ impl ser::Serializer for Serializer {

fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error> {
match SER_BYTES_TYPE.get() {
BytesType::Bytes => Ok(Value::Bytes(v.to_owned())),
BytesType::Unset | BytesType::Bytes => Ok(Value::Bytes(v.to_owned())),
BytesType::Fixed => Ok(Value::Fixed(v.len(), v.to_owned())),
}
}
Expand Down
File renamed without changes.
17 changes: 16 additions & 1 deletion avro/src/serde/with.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ thread_local! {
/// [`Value::Bytes`] or [`Value::Fixed`].
///
/// Relies on the fact that serde's serialization process is single-threaded.
pub(crate) static SER_BYTES_TYPE: Cell<BytesType> = const { Cell::new(BytesType::Bytes) };
pub(crate) static SER_BYTES_TYPE: Cell<BytesType> = const { Cell::new(BytesType::Unset) };

/// A thread local that is used to decide if a [`Value::Bytes`] needs to be deserialized to
/// a [`Vec`] or slice.
Expand All @@ -33,6 +33,7 @@ thread_local! {

#[derive(Debug, Clone, Copy)]
pub(crate) enum BytesType {
Unset,
Bytes,
Fixed,
}
Expand Down Expand Up @@ -92,6 +93,7 @@ impl Drop for BorrowedGuard {
///
/// [`apache_avro::serde::bytes_opt`]: bytes_opt
pub mod bytes {
use super::BytesType;
use std::collections::HashSet;

use serde::{Deserializer, Serializer};
Expand Down Expand Up @@ -119,13 +121,15 @@ pub mod bytes {
where
S: Serializer,
{
let _guard = super::BytesTypeGuard::set(BytesType::Bytes);
serde_bytes::serialize(bytes, serializer)
}

pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
where
D: Deserializer<'de>,
{
let _guard = super::BytesTypeGuard::set(BytesType::Bytes);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The BytesTypeGuard sets the SER_BYTES_TYPE thread-local, which is documented as being for serialization. It seems incorrect to set this during deserialization, and it could lead to unexpected behavior if serialization is performed on the same thread. This guard should likely be removed.

This concern also applies to the deserialize functions in the bytes_opt, fixed, fixed_opt, slice, and slice_opt modules within this file.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

value:useful; category:bug; feedback: The Gemini AI reviewer is correct! The thread local is used only during serialization, so there is no need to set it for deserialization. The change does not cause any harm but it should be reverted.

serde_bytes::deserialize(deserializer)
}
}
Expand Down Expand Up @@ -155,6 +159,7 @@ pub mod bytes {
///
/// [`apache_avro::serde::bytes`]: bytes
pub mod bytes_opt {
use super::BytesType;
use serde::{Deserializer, Serializer};
use std::{borrow::Borrow, collections::HashSet};

Expand Down Expand Up @@ -184,13 +189,15 @@ pub mod bytes_opt {
S: Serializer,
B: Borrow<[u8]> + serde_bytes::Serialize,
{
let _guard = super::BytesTypeGuard::set(BytesType::Bytes);
serde_bytes::serialize(bytes, serializer)
}

pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Vec<u8>>, D::Error>
where
D: Deserializer<'de>,
{
let _guard = super::BytesTypeGuard::set(BytesType::Bytes);
serde_bytes::deserialize(deserializer)
}
}
Expand Down Expand Up @@ -268,6 +275,7 @@ pub mod fixed {
where
D: Deserializer<'de>,
{
let _guard = super::BytesTypeGuard::set(BytesType::Fixed);
serde_bytes::deserialize(deserializer)
}
}
Expand Down Expand Up @@ -342,6 +350,7 @@ pub mod fixed_opt {
where
D: Deserializer<'de>,
{
let _guard = super::BytesTypeGuard::set(BytesType::Fixed);
serde_bytes::deserialize(deserializer)
}
}
Expand Down Expand Up @@ -373,6 +382,7 @@ pub mod fixed_opt {
/// [`Value::Fixed`]: crate::types::Value::Fixed
/// [`apache_avro::serde::slice_opt`]: slice_opt
pub mod slice {
use super::BytesType;
use std::collections::HashSet;

use serde::{Deserializer, Serializer};
Expand Down Expand Up @@ -400,13 +410,15 @@ pub mod slice {
where
S: Serializer,
{
let _guard = super::BytesTypeGuard::set(BytesType::Bytes);
serde_bytes::serialize(bytes, serializer)
}

pub fn deserialize<'de, D>(deserializer: D) -> Result<&'de [u8], D::Error>
where
D: Deserializer<'de>,
{
let _bytes_guard = super::BytesTypeGuard::set(BytesType::Bytes);
let _guard = super::BorrowedGuard::set(true);
serde_bytes::deserialize(deserializer)
}
Expand Down Expand Up @@ -439,6 +451,7 @@ pub mod slice {
/// [`Value::Fixed`]: crate::types::Value::Fixed
/// [`apache_avro::serde::slice`]: mod@slice
pub mod slice_opt {
use super::BytesType;
use serde::{Deserializer, Serializer};
use std::{borrow::Borrow, collections::HashSet};

Expand Down Expand Up @@ -468,13 +481,15 @@ pub mod slice_opt {
S: Serializer,
B: Borrow<[u8]> + serde_bytes::Serialize,
{
let _guard = super::BytesTypeGuard::set(BytesType::Bytes);
serde_bytes::serialize(&bytes, serializer)
}

pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<&'de [u8]>, D::Error>
where
D: Deserializer<'de>,
{
let _bytes_guard = super::BytesTypeGuard::set(BytesType::Bytes);
let _guard = super::BorrowedGuard::set(true);
serde_bytes::deserialize(deserializer)
}
Expand Down
5 changes: 3 additions & 2 deletions avro/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,9 @@ fn encode_variable<W: Write>(mut zigzagged: u64, mut writer: W) -> AvroResult<us
}
}
writer
.write(&buffer[..i])
.map_err(|e| Details::WriteBytes(e).into())
.write_all(&buffer[..i])
.map_err(Details::WriteBytes)?;
Ok(i)
}

/// Read a varint from the reader.
Expand Down
2 changes: 2 additions & 0 deletions avro_test_helper/src/logger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ pub fn clear_log_messages() {
});
}

#[track_caller]
pub fn assert_not_logged(unexpected_message: &str) {
LOG_MESSAGES.with(|msgs| match msgs.borrow().last() {
Some(last_log) if last_log == unexpected_message => {
Expand All @@ -69,6 +70,7 @@ pub fn assert_not_logged(unexpected_message: &str) {
});
}

#[track_caller]
pub fn assert_logged(expected_message: &str) {
let mut deleted = false;
LOG_MESSAGES.with(|msgs| {
Expand Down