From 60f67dbc39d59d2c9682f974e702256311e95167 Mon Sep 17 00:00:00 2001 From: Joey de Waal <99046430+joeydewaal@users.noreply.github.com> Date: Sat, 5 Jul 2025 02:58:33 +0200 Subject: [PATCH] feat: implement Encode, Decode, Type for `Arc` and `Arc<[u8]>` (and `Rc` equivalents) (#3675) * implement Encode, Decode, Type for Arc and Arc<[u8]> (and Rc equivalents) * sqlx-sqlite: Remove clone in Encode impl * sqlx-sqlite: Remove unnecessary impls --- sqlx-core/src/encode.rs | 14 ++++++++++++++ sqlx-mysql/src/types/bytes.rs | 17 ++++++----------- sqlx-mysql/src/types/str.rs | 27 +++++++-------------------- sqlx-postgres/src/types/bytes.rs | 17 ++++++----------- sqlx-postgres/src/types/str.rs | 29 ++++++++--------------------- sqlx-sqlite/src/types/bytes.rs | 20 ++++++++++++++++++++ sqlx-sqlite/src/types/str.rs | 20 ++++++++++++++++++++ tests/mysql/types.rs | 4 ++++ tests/postgres/types.rs | 4 ++++ tests/sqlite/types.rs | 4 ++++ 10 files changed, 93 insertions(+), 63 deletions(-) diff --git a/sqlx-core/src/encode.rs b/sqlx-core/src/encode.rs index 1a149b6e8..ba9a1d40c 100644 --- a/sqlx-core/src/encode.rs +++ b/sqlx-core/src/encode.rs @@ -200,3 +200,17 @@ where <&T as Encode>::size_hint(&self.as_ref()) } } + +#[macro_export] +macro_rules! forward_encode_impl { + ($for_type:ty, $forward_to:ty, $db:ident) => { + impl<'q> Encode<'q, $db> for $for_type { + fn encode_by_ref( + &self, + buf: &mut <$db as sqlx_core::database::Database>::ArgumentBuffer<'q>, + ) -> Result { + <$forward_to as Encode<$db>>::encode(self.as_ref(), buf) + } + } + }; +} diff --git a/sqlx-mysql/src/types/bytes.rs b/sqlx-mysql/src/types/bytes.rs index 023a8ee87..649fb9dc0 100644 --- a/sqlx-mysql/src/types/bytes.rs +++ b/sqlx-mysql/src/types/bytes.rs @@ -1,4 +1,6 @@ use std::borrow::Cow; +use std::rc::Rc; +use std::sync::Arc; use crate::decode::Decode; use crate::encode::{Encode, IsNull}; @@ -42,12 +44,6 @@ impl<'r> Decode<'r, MySql> for &'r [u8] { } } -impl Encode<'_, MySql> for Box<[u8]> { - fn encode_by_ref(&self, buf: &mut Vec) -> Result { - <&[u8] as Encode>::encode(self.as_ref(), buf) - } -} - impl Type for Vec { fn type_info() -> MySqlTypeInfo { <[u8] as Type>::type_info() @@ -70,8 +66,7 @@ impl Decode<'_, MySql> for Vec { } } -impl Encode<'_, MySql> for Cow<'_, [u8]> { - fn encode_by_ref(&self, buf: &mut Vec) -> Result { - <&[u8] as Encode>::encode(self.as_ref(), buf) - } -} +forward_encode_impl!(Arc<[u8]>, &[u8], MySql); +forward_encode_impl!(Rc<[u8]>, &[u8], MySql); +forward_encode_impl!(Box<[u8]>, &[u8], MySql); +forward_encode_impl!(Cow<'_, [u8]>, &[u8], MySql); diff --git a/sqlx-mysql/src/types/str.rs b/sqlx-mysql/src/types/str.rs index 4e2730577..3a306fec4 100644 --- a/sqlx-mysql/src/types/str.rs +++ b/sqlx-mysql/src/types/str.rs @@ -1,4 +1,6 @@ use std::borrow::Cow; +use std::rc::Rc; +use std::sync::Arc; use crate::decode::Decode; use crate::encode::{Encode, IsNull}; @@ -47,12 +49,6 @@ impl<'r> Decode<'r, MySql> for &'r str { } } -impl Encode<'_, MySql> for Box { - fn encode_by_ref(&self, buf: &mut Vec) -> Result { - <&str as Encode>::encode(&**self, buf) - } -} - impl Type for String { fn type_info() -> MySqlTypeInfo { >::type_info() @@ -63,23 +59,14 @@ impl Type for String { } } -impl Encode<'_, MySql> for String { - fn encode_by_ref(&self, buf: &mut Vec) -> Result { - <&str as Encode>::encode(&**self, buf) - } -} - impl Decode<'_, MySql> for String { fn decode(value: MySqlValueRef<'_>) -> Result { <&str as Decode>::decode(value).map(ToOwned::to_owned) } } -impl Encode<'_, MySql> for Cow<'_, str> { - fn encode_by_ref(&self, buf: &mut Vec) -> Result { - match self { - Cow::Borrowed(str) => <&str as Encode>::encode(*str, buf), - Cow::Owned(str) => <&str as Encode>::encode(&**str, buf), - } - } -} +forward_encode_impl!(Arc, &str, MySql); +forward_encode_impl!(Rc, &str, MySql); +forward_encode_impl!(Cow<'_, str>, &str, MySql); +forward_encode_impl!(Box, &str, MySql); +forward_encode_impl!(String, &str, MySql); diff --git a/sqlx-postgres/src/types/bytes.rs b/sqlx-postgres/src/types/bytes.rs index 17b7ce9a3..16cbc14a0 100644 --- a/sqlx-postgres/src/types/bytes.rs +++ b/sqlx-postgres/src/types/bytes.rs @@ -1,4 +1,6 @@ use std::borrow::Cow; +use std::rc::Rc; +use std::sync::Arc; use crate::decode::Decode; use crate::encode::{Encode, IsNull}; @@ -44,12 +46,6 @@ impl Encode<'_, Postgres> for &'_ [u8] { } } -impl Encode<'_, Postgres> for Box<[u8]> { - fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result { - <&[u8] as Encode>::encode(self.as_ref(), buf) - } -} - impl Encode<'_, Postgres> for Vec { fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result { <&[u8] as Encode>::encode(self, buf) @@ -104,8 +100,7 @@ impl Decode<'_, Postgres> for [u8; N] { } } -impl Encode<'_, Postgres> for Cow<'_, [u8]> { - fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result { - <&[u8] as Encode>::encode(self.as_ref(), buf) - } -} +forward_encode_impl!(Arc<[u8]>, &[u8], Postgres); +forward_encode_impl!(Rc<[u8]>, &[u8], Postgres); +forward_encode_impl!(Box<[u8]>, &[u8], Postgres); +forward_encode_impl!(Cow<'_, [u8]>, &[u8], Postgres); diff --git a/sqlx-postgres/src/types/str.rs b/sqlx-postgres/src/types/str.rs index 8b9c33ef4..8ab0a37af 100644 --- a/sqlx-postgres/src/types/str.rs +++ b/sqlx-postgres/src/types/str.rs @@ -5,6 +5,8 @@ use crate::types::array_compatible; use crate::types::Type; use crate::{PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueRef, Postgres}; use std::borrow::Cow; +use std::rc::Rc; +use std::sync::Arc; impl Type for str { fn type_info() -> PgTypeInfo { @@ -82,27 +84,6 @@ impl Encode<'_, Postgres> for &'_ str { } } -impl Encode<'_, Postgres> for Cow<'_, str> { - fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result { - match self { - Cow::Borrowed(str) => <&str as Encode>::encode(*str, buf), - Cow::Owned(str) => <&str as Encode>::encode(&**str, buf), - } - } -} - -impl Encode<'_, Postgres> for Box { - fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result { - <&str as Encode>::encode(&**self, buf) - } -} - -impl Encode<'_, Postgres> for String { - fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result { - <&str as Encode>::encode(&**self, buf) - } -} - impl<'r> Decode<'r, Postgres> for &'r str { fn decode(value: PgValueRef<'r>) -> Result { value.as_str() @@ -114,3 +95,9 @@ impl Decode<'_, Postgres> for String { Ok(value.as_str()?.to_owned()) } } + +forward_encode_impl!(Arc, &str, Postgres); +forward_encode_impl!(Rc, &str, Postgres); +forward_encode_impl!(Cow<'_, str>, &str, Postgres); +forward_encode_impl!(Box, &str, Postgres); +forward_encode_impl!(String, &str, Postgres); diff --git a/sqlx-sqlite/src/types/bytes.rs b/sqlx-sqlite/src/types/bytes.rs index 48dffe0ae..af625579e 100644 --- a/sqlx-sqlite/src/types/bytes.rs +++ b/sqlx-sqlite/src/types/bytes.rs @@ -1,4 +1,6 @@ use std::borrow::Cow; +use std::rc::Rc; +use std::sync::Arc; use crate::decode::Decode; use crate::encode::{Encode, IsNull}; @@ -102,3 +104,21 @@ impl<'q> Encode<'q, Sqlite> for Cow<'q, [u8]> { Ok(IsNull::No) } } + +impl<'q> Encode<'q, Sqlite> for Arc<[u8]> { + fn encode_by_ref( + &self, + args: &mut Vec>, + ) -> Result { + as Encode<'_, Sqlite>>::encode(self.to_vec(), args) + } +} + +impl<'q> Encode<'q, Sqlite> for Rc<[u8]> { + fn encode_by_ref( + &self, + args: &mut Vec>, + ) -> Result { + as Encode<'_, Sqlite>>::encode(self.to_vec(), args) + } +} diff --git a/sqlx-sqlite/src/types/str.rs b/sqlx-sqlite/src/types/str.rs index 6c51fa1af..8acd0d879 100644 --- a/sqlx-sqlite/src/types/str.rs +++ b/sqlx-sqlite/src/types/str.rs @@ -1,4 +1,6 @@ use std::borrow::Cow; +use std::rc::Rc; +use std::sync::Arc; use crate::decode::Decode; use crate::encode::{Encode, IsNull}; @@ -94,3 +96,21 @@ impl<'q> Encode<'q, Sqlite> for Cow<'q, str> { Ok(IsNull::No) } } + +impl<'q> Encode<'q, Sqlite> for Arc { + fn encode_by_ref( + &self, + args: &mut Vec>, + ) -> Result { + >::encode(self.to_string(), args) + } +} + +impl<'q> Encode<'q, Sqlite> for Rc { + fn encode_by_ref( + &self, + args: &mut Vec>, + ) -> Result { + >::encode(self.to_string(), args) + } +} diff --git a/tests/mysql/types.rs b/tests/mysql/types.rs index df46cdfd8..9686d4172 100644 --- a/tests/mysql/types.rs +++ b/tests/mysql/types.rs @@ -310,9 +310,13 @@ test_type!(test_rc>(MySql, "1" == Rc::new(1i32))); test_type!(test_box_str>(MySql, "'John'" == Box::::from("John"))); test_type!(test_cow_str>(MySql, "'Phil'" == Cow::<'static, str>::from("Phil"))); +test_type!(test_arc_str>(MySql, "'1234'" == Arc::::from("1234"))); +test_type!(test_rc_str>(MySql, "'5678'" == Rc::::from("5678"))); test_prepared_type!(test_box_slice>(MySql, "X'01020304'" == Box::<[u8]>::from([1,2,3,4]))); test_prepared_type!(test_cow_slice>(MySql, "X'01020304'" == Cow::<'static, [u8]>::from(&[1,2,3,4]))); +test_prepared_type!(test_arc_slice>(MySql, "X'01020304'" == Arc::<[u8]>::from([1,2,3,4]))); +test_prepared_type!(test_rc_slice>(MySql, "X'01020304'" == Rc::<[u8]>::from([1,2,3,4]))); #[sqlx_macros::test] async fn test_bits() -> anyhow::Result<()> { diff --git a/tests/postgres/types.rs b/tests/postgres/types.rs index 569fe585e..8a9288ba7 100644 --- a/tests/postgres/types.rs +++ b/tests/postgres/types.rs @@ -704,9 +704,13 @@ test_type!(test_rc>(Postgres, "1::INT4" == Rc::new(1i32))); test_type!(test_box_str>(Postgres, "'John'::TEXT" == Box::::from("John"))); test_type!(test_cow_str>(Postgres, "'Phil'::TEXT" == Cow::<'static, str>::from("Phil"))); +test_type!(test_arc_str>(Postgres, "'1234'::TEXT" == Arc::::from("1234"))); +test_type!(test_rc_str>(Postgres, "'5678'::TEXT" == Rc::::from("5678"))); test_prepared_type!(test_box_slice>(Postgres, "'\\x01020304'::BYTEA" == Box::<[u8]>::from([1,2,3,4]))); test_prepared_type!(test_cow_slice>(Postgres, "'\\x01020304'::BYTEA" == Cow::<'static, [u8]>::from(&[1,2,3,4]))); +test_prepared_type!(test_arc_slice>(Postgres, "'\\x01020304'::BYTEA" == Arc::<[u8]>::from([1,2,3,4]))); +test_prepared_type!(test_rc_slice>(Postgres, "'\\x01020304'::BYTEA" == Rc::<[u8]>::from([1,2,3,4]))); #[sqlx_macros::test] async fn test_text_adapter() -> anyhow::Result<()> { diff --git a/tests/sqlite/types.rs b/tests/sqlite/types.rs index de949d525..302ad54a6 100644 --- a/tests/sqlite/types.rs +++ b/tests/sqlite/types.rs @@ -219,9 +219,13 @@ test_type!(test_rc>(Sqlite, "1" == Rc::new(1i32))); test_type!(test_box_str>(Sqlite, "'John'" == Box::::from("John"))); test_type!(test_cow_str>(Sqlite, "'Phil'" == Cow::<'static, str>::from("Phil"))); +test_type!(test_arc_str>(Sqlite, "'1234'" == Arc::::from("1234"))); +test_type!(test_rc_str>(Sqlite, "'5678'" == Rc::::from("5678"))); test_type!(test_box_slice>(Sqlite, "X'01020304'" == Box::<[u8]>::from([1,2,3,4]))); test_type!(test_cow_slice>(Sqlite, "X'01020304'" == Cow::<'static, [u8]>::from(&[1,2,3,4]))); +test_type!(test_arc_slice>(Sqlite, "X'01020304'" == Arc::<[u8]>::from([1,2,3,4]))); +test_type!(test_rc_slice>(Sqlite, "X'01020304'" == Rc::<[u8]>::from([1,2,3,4]))); #[sqlx_macros::test] async fn test_text_adapter() -> anyhow::Result<()> {