diff --git a/sqlx-core/src/decode.rs b/sqlx-core/src/decode.rs index 3249c349cc..7d36e24785 100644 --- a/sqlx-core/src/decode.rs +++ b/sqlx-core/src/decode.rs @@ -1,5 +1,8 @@ //! Provides [`Decode`] for decoding values from the database. +use std::borrow::Cow; +use std::sync::Arc; + use crate::database::Database; use crate::error::BoxDynError; @@ -77,3 +80,37 @@ where } } } + +// implement `Decode` for Arc for all SQL types +impl<'r, DB, T> Decode<'r, DB> for Arc +where + DB: Database, + T: Decode<'r, DB>, +{ + fn decode(value: ::ValueRef<'r>) -> Result { + Ok(Arc::new(T::decode(value)?)) + } +} + +// implement `Decode` for Cow for all SQL types +impl<'r, DB, T> Decode<'r, DB> for Cow<'_, T> +where + DB: Database, + T: Decode<'r, DB>, + T: ToOwned, +{ + fn decode(value: ::ValueRef<'r>) -> Result { + Ok(Cow::Owned(T::decode(value)?)) + } +} + +// implement `Decode` for Box for all SQL types +impl<'r, DB, T> Decode<'r, DB> for Box +where + DB: Database, + T: Decode<'r, DB>, +{ + fn decode(value: ::ValueRef<'r>) -> Result { + Ok(Box::new(T::decode(value)?)) + } +} diff --git a/sqlx-core/src/encode.rs b/sqlx-core/src/encode.rs index 2d28641f94..51a1becfc3 100644 --- a/sqlx-core/src/encode.rs +++ b/sqlx-core/src/encode.rs @@ -1,6 +1,9 @@ //! Provides [`Encode`] for encoding values for the database. +use std::borrow::Cow; use std::mem; +use std::rc::Rc; +use std::sync::Arc; use crate::database::Database; use crate::error::BoxDynError; @@ -129,3 +132,116 @@ macro_rules! impl_encode_for_option { } }; } + +impl<'q, T, DB: Database> Encode<'q, DB> for Arc +where + T: Encode<'q, DB>, +{ + #[inline] + fn encode(self, buf: &mut ::ArgumentBuffer<'q>) -> Result { + >::encode_by_ref(self.as_ref(), buf) + } + + #[inline] + fn encode_by_ref( + &self, + buf: &mut ::ArgumentBuffer<'q>, + ) -> Result { + <&T as Encode>::encode(self, buf) + } + + #[inline] + fn produces(&self) -> Option { + (**self).produces() + } + + #[inline] + fn size_hint(&self) -> usize { + (**self).size_hint() + } +} + +impl<'q, T, DB: Database> Encode<'q, DB> for Cow<'_, T> +where + T: Encode<'q, DB>, + T: ToOwned, +{ + #[inline] + fn encode(self, buf: &mut ::ArgumentBuffer<'q>) -> Result { + >::encode_by_ref(self.as_ref(), buf) + } + + #[inline] + fn encode_by_ref( + &self, + buf: &mut ::ArgumentBuffer<'q>, + ) -> Result { + <&T as Encode>::encode(self, buf) + } + + #[inline] + fn produces(&self) -> Option { + (**self).produces() + } + + #[inline] + fn size_hint(&self) -> usize { + (**self).size_hint() + } +} + +impl<'q, T, DB: Database> Encode<'q, DB> for Box +where + T: Encode<'q, DB>, +{ + #[inline] + fn encode(self, buf: &mut ::ArgumentBuffer<'q>) -> Result { + >::encode_by_ref(self.as_ref(), buf) + } + + #[inline] + fn encode_by_ref( + &self, + buf: &mut ::ArgumentBuffer<'q>, + ) -> Result { + <&T as Encode>::encode(self, buf) + } + + #[inline] + fn produces(&self) -> Option { + (**self).produces() + } + + #[inline] + fn size_hint(&self) -> usize { + (**self).size_hint() + } +} + +impl<'q, T, DB: Database> Encode<'q, DB> for Rc +where + T: Encode<'q, DB>, +{ + #[inline] + fn encode(self, buf: &mut ::ArgumentBuffer<'q>) -> Result { + >::encode_by_ref(self.as_ref(), buf) + } + + #[inline] + fn encode_by_ref( + &self, + buf: &mut ::ArgumentBuffer<'q>, + ) -> Result { + <&T as Encode>::encode(self, buf) + } + + #[inline] + fn produces(&self) -> Option { + (**self).produces() + } + + #[inline] + fn size_hint(&self) -> usize { + (**self).size_hint() + } +} diff --git a/sqlx-core/src/types/mod.rs b/sqlx-core/src/types/mod.rs index 25837b1e77..fd08b51bc1 100644 --- a/sqlx-core/src/types/mod.rs +++ b/sqlx-core/src/types/mod.rs @@ -17,6 +17,8 @@ //! To represent nullable SQL types, `Option` is supported where `T` implements `Type`. //! An `Option` represents a potentially `NULL` value from SQL. +use std::{borrow::Cow, rc::Rc, sync::Arc}; + use crate::database::Database; use crate::type_info::TypeInfo; @@ -238,3 +240,51 @@ impl, DB: Database> Type for Option { ty.is_null() || >::compatible(ty) } } + +impl Type for Arc +where + T: Type, + T: ?Sized, +{ + fn type_info() -> DB::TypeInfo { + >::type_info() + } + + fn compatible(ty: &DB::TypeInfo) -> bool { + ty.is_null() || >::compatible(ty) + } +} + +impl Type for Cow<'_, T> +where + T: Type, + T: ToOwned, +{ + fn type_info() -> DB::TypeInfo { + >::type_info() + } + + fn compatible(ty: &DB::TypeInfo) -> bool { + ty.is_null() || >::compatible(ty) + } +} + +impl, DB: Database> Type for Box { + fn type_info() -> DB::TypeInfo { + >::type_info() + } + + fn compatible(ty: &DB::TypeInfo) -> bool { + ty.is_null() || >::compatible(ty) + } +} + +impl, DB: Database> Type for Rc { + fn type_info() -> DB::TypeInfo { + >::type_info() + } + + fn compatible(ty: &DB::TypeInfo) -> bool { + ty.is_null() || >::compatible(ty) + } +} diff --git a/tests/postgres/types.rs b/tests/postgres/types.rs index 3f6c362043..7aa81617bb 100644 --- a/tests/postgres/types.rs +++ b/tests/postgres/types.rs @@ -1,7 +1,10 @@ extern crate time_ as time; +use std::borrow::Cow; use std::net::SocketAddr; use std::ops::Bound; +use std::rc::Rc; +use std::sync::Arc; use sqlx::postgres::types::{Oid, PgCiText, PgInterval, PgMoney, PgRange}; use sqlx::postgres::Postgres; @@ -657,3 +660,56 @@ CREATE TEMPORARY TABLE user_login ( Ok(()) } + +#[sqlx_macros::test] +async fn test_arc() -> anyhow::Result<()> { + let mut conn = new::().await?; + + let user_age: Arc = sqlx::query_scalar("SELECT $1 AS age ") + .bind(Arc::new(1i32)) + .fetch_one(&mut conn) + .await?; + assert!(user_age.as_ref() == &1); + Ok(()) +} + +#[sqlx_macros::test] +async fn test_cow() -> anyhow::Result<()> { + let mut conn = new::().await?; + + let age: Cow<'_, i32> = Cow::Owned(1i32); + + let user_age: Cow<'static, i32> = sqlx::query_scalar("SELECT $1 AS age ") + .bind(age) + .fetch_one(&mut conn) + .await?; + + assert!(user_age.as_ref() == &1); + Ok(()) +} + +#[sqlx_macros::test] +async fn test_box() -> anyhow::Result<()> { + let mut conn = new::().await?; + + let user_age: Box = sqlx::query_scalar("SELECT $1 AS age ") + .bind(Box::new(1)) + .fetch_one(&mut conn) + .await?; + + assert!(user_age.as_ref() == &1); + Ok(()) +} + +#[sqlx_macros::test] +async fn test_rc() -> anyhow::Result<()> { + let mut conn = new::().await?; + + let user_age: i32 = sqlx::query_scalar("SELECT $1 AS age") + .bind(Rc::new(1i32)) + .fetch_one(&mut conn) + .await?; + + assert!(user_age == 1); + Ok(()) +}