From 3a73ece68870c0cbf5c60ed97cc593f0e1efb520 Mon Sep 17 00:00:00 2001 From: Joey de Waal Date: Thu, 9 Jan 2025 21:12:39 +0100 Subject: [PATCH 1/3] feat: implement Decode,Encode,Type for Box,Arc,Cow --- sqlx-core/src/decode.rs | 37 ++++++++++++++++ sqlx-core/src/encode.rs | 87 ++++++++++++++++++++++++++++++++++++++ sqlx-core/src/types/mod.rs | 40 ++++++++++++++++++ tests/postgres/types.rs | 42 ++++++++++++++++++ 4 files changed, 206 insertions(+) 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..15f48a6086 100644 --- a/sqlx-core/src/encode.rs +++ b/sqlx-core/src/encode.rs @@ -1,6 +1,8 @@ //! Provides [`Encode`] for encoding values for the database. +use std::borrow::Cow; use std::mem; +use std::sync::Arc; use crate::database::Database; use crate::error::BoxDynError; @@ -129,3 +131,88 @@ 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() + } +} diff --git a/sqlx-core/src/types/mod.rs b/sqlx-core/src/types/mod.rs index 25837b1e77..1bc6aacd32 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, sync::Arc}; + use crate::database::Database; use crate::type_info::TypeInfo; @@ -238,3 +240,41 @@ 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) + } +} diff --git a/tests/postgres/types.rs b/tests/postgres/types.rs index 3f6c362043..ed169ee1da 100644 --- a/tests/postgres/types.rs +++ b/tests/postgres/types.rs @@ -1,7 +1,9 @@ extern crate time_ as time; +use std::borrow::Cow; use std::net::SocketAddr; use std::ops::Bound; +use std::sync::Arc; use sqlx::postgres::types::{Oid, PgCiText, PgInterval, PgMoney, PgRange}; use sqlx::postgres::Postgres; @@ -657,3 +659,43 @@ 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(()) +} From 676e11ef2aab259d9c1f113da293c7f950b17c8b Mon Sep 17 00:00:00 2001 From: Joey de Waal Date: Thu, 9 Jan 2025 22:19:09 +0100 Subject: [PATCH 2/3] feat implement Encode,Type for Rc --- sqlx-core/src/encode.rs | 29 +++++++++++++++++++++++++++++ sqlx-core/src/types/mod.rs | 12 +++++++++++- tests/postgres/types.rs | 20 +++++++++++++++++--- 3 files changed, 57 insertions(+), 4 deletions(-) diff --git a/sqlx-core/src/encode.rs b/sqlx-core/src/encode.rs index 15f48a6086..51a1becfc3 100644 --- a/sqlx-core/src/encode.rs +++ b/sqlx-core/src/encode.rs @@ -2,6 +2,7 @@ use std::borrow::Cow; use std::mem; +use std::rc::Rc; use std::sync::Arc; use crate::database::Database; @@ -216,3 +217,31 @@ where (**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 1bc6aacd32..fd08b51bc1 100644 --- a/sqlx-core/src/types/mod.rs +++ b/sqlx-core/src/types/mod.rs @@ -17,7 +17,7 @@ //! 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, sync::Arc}; +use std::{borrow::Cow, rc::Rc, sync::Arc}; use crate::database::Database; use crate::type_info::TypeInfo; @@ -278,3 +278,13 @@ impl, DB: Database> Type for Box { 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 ed169ee1da..7aa81617bb 100644 --- a/tests/postgres/types.rs +++ b/tests/postgres/types.rs @@ -3,6 +3,7 @@ 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}; @@ -664,7 +665,7 @@ CREATE TEMPORARY TABLE user_login ( async fn test_arc() -> anyhow::Result<()> { let mut conn = new::().await?; - let user_age: Arc = sqlx::query_scalar("select $1 as age ") + let user_age: Arc = sqlx::query_scalar("SELECT $1 AS age ") .bind(Arc::new(1i32)) .fetch_one(&mut conn) .await?; @@ -678,7 +679,7 @@ async fn test_cow() -> anyhow::Result<()> { let age: Cow<'_, i32> = Cow::Owned(1i32); - let user_age: Cow<'static, i32> = sqlx::query_scalar("select $1 as age ") + let user_age: Cow<'static, i32> = sqlx::query_scalar("SELECT $1 AS age ") .bind(age) .fetch_one(&mut conn) .await?; @@ -691,7 +692,7 @@ async fn test_cow() -> anyhow::Result<()> { async fn test_box() -> anyhow::Result<()> { let mut conn = new::().await?; - let user_age: Box = sqlx::query_scalar("select $1 as age ") + let user_age: Box = sqlx::query_scalar("SELECT $1 AS age ") .bind(Box::new(1)) .fetch_one(&mut conn) .await?; @@ -699,3 +700,16 @@ async fn test_box() -> anyhow::Result<()> { 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(()) +} From b5c5d68d9ed653484448a0132857d0b156854864 Mon Sep 17 00:00:00 2001 From: Joey de Waal Date: Sun, 12 Jan 2025 18:57:15 +0100 Subject: [PATCH 3/3] feat: implement Encode,Decode,Type for Arc and Arc<[u8]> --- sqlx-mysql/src/types/bytes.rs | 14 ++++++++++ sqlx-mysql/src/types/str.rs | 13 +++++++++ sqlx-postgres/src/types/array.rs | 21 ++++++++++++++ sqlx-postgres/src/types/bytes.rs | 23 +++++++++++++++ sqlx-postgres/src/types/str.rs | 23 +++++++++++++++ sqlx-sqlite/src/types/bytes.rs | 24 ++++++++++++++++ sqlx-sqlite/src/types/str.rs | 24 ++++++++++++++++ tests/mysql/types.rs | 31 +++++++++++++++++++++ tests/postgres/types.rs | 48 ++++++++++++++++++++++++++++++++ tests/sqlite/types.rs | 31 +++++++++++++++++++++ 10 files changed, 252 insertions(+) diff --git a/sqlx-mysql/src/types/bytes.rs b/sqlx-mysql/src/types/bytes.rs index ade079ad4e..990c3a4c08 100644 --- a/sqlx-mysql/src/types/bytes.rs +++ b/sqlx-mysql/src/types/bytes.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use crate::decode::Decode; use crate::encode::{Encode, IsNull}; use crate::error::BoxDynError; @@ -83,3 +85,15 @@ impl Decode<'_, MySql> for Vec { <&[u8] as Decode>::decode(value).map(ToOwned::to_owned) } } + +impl Encode<'_, MySql> for Arc<[u8]> { + fn encode_by_ref(&self, buf: &mut Vec) -> Result { + <&[u8] as Encode>::encode(&**self, buf) + } +} + +impl Decode<'_, MySql> for Arc<[u8]> { + fn decode(value: MySqlValueRef<'_>) -> Result { + <&[u8] as Decode>::decode(value).map(Into::into) + } +} diff --git a/sqlx-mysql/src/types/str.rs b/sqlx-mysql/src/types/str.rs index 8233e90893..594800a704 100644 --- a/sqlx-mysql/src/types/str.rs +++ b/sqlx-mysql/src/types/str.rs @@ -6,6 +6,7 @@ use crate::protocol::text::{ColumnFlags, ColumnType}; use crate::types::Type; use crate::{MySql, MySqlTypeInfo, MySqlValueRef}; use std::borrow::Cow; +use std::sync::Arc; impl Type for str { fn type_info() -> MySqlTypeInfo { @@ -114,3 +115,15 @@ impl<'r> Decode<'r, MySql> for Cow<'r, str> { value.as_str().map(Cow::Borrowed) } } + +impl Encode<'_, MySql> for Arc { + fn encode_by_ref(&self, buf: &mut Vec) -> Result { + <&str as Encode>::encode(&**self, buf) + } +} + +impl Decode<'_, MySql> for Arc { + fn decode(value: MySqlValueRef<'_>) -> Result { + <&str as Decode>::decode(value).map(Into::into) + } +} diff --git a/sqlx-postgres/src/types/array.rs b/sqlx-postgres/src/types/array.rs index 9b8be63412..dd3023ea1d 100644 --- a/sqlx-postgres/src/types/array.rs +++ b/sqlx-postgres/src/types/array.rs @@ -1,6 +1,7 @@ use sqlx_core::bytes::Buf; use sqlx_core::types::Text; use std::borrow::Cow; +use std::sync::Arc; use crate::decode::Decode; use crate::encode::{Encode, IsNull}; @@ -192,6 +193,17 @@ where } } +impl<'q, T> Encode<'q, Postgres> for Arc<[T]> +where + for<'a> &'a [T]: Encode<'q, Postgres>, + T: Encode<'q, Postgres>, +{ + #[inline] + fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result { + <&[T] as Encode>::encode_by_ref(&self.as_ref(), buf) + } +} + impl<'r, T, const N: usize> Decode<'r, Postgres> for [T; N] where T: for<'a> Decode<'a, Postgres> + Type, @@ -354,3 +366,12 @@ where } } } + +impl<'r, T> Decode<'r, Postgres> for Arc<[T]> +where + T: for<'a> Decode<'a, Postgres> + Type, +{ + fn decode(value: PgValueRef<'r>) -> Result { + as Decode>::decode(value).map(Into::into) + } +} diff --git a/sqlx-postgres/src/types/bytes.rs b/sqlx-postgres/src/types/bytes.rs index 45968837af..7ce0eb1692 100644 --- a/sqlx-postgres/src/types/bytes.rs +++ b/sqlx-postgres/src/types/bytes.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use crate::decode::Decode; use crate::encode::{Encode, IsNull}; use crate::error::BoxDynError; @@ -28,6 +30,12 @@ impl PgHasArrayType for Vec { } } +impl PgHasArrayType for Arc<[u8]> { + fn array_type_info() -> PgTypeInfo { + <[&[u8]] as Type>::type_info() + } +} + impl PgHasArrayType for [u8; N] { fn array_type_info() -> PgTypeInfo { <[&[u8]] as Type>::type_info() @@ -60,6 +68,12 @@ impl Encode<'_, Postgres> for [u8; N] { } } +impl Encode<'_, Postgres> for Arc<[u8]> { + fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result { + <&[u8] as Encode>::encode(self, buf) + } +} + impl<'r> Decode<'r, Postgres> for &'r [u8] { fn decode(value: PgValueRef<'r>) -> Result { match value.format() { @@ -110,3 +124,12 @@ impl Decode<'_, Postgres> for [u8; N] { Ok(bytes) } } + +impl Decode<'_, Postgres> for Arc<[u8]> { + fn decode(value: PgValueRef<'_>) -> Result { + Ok(match value.format() { + PgValueFormat::Binary => value.as_bytes()?.into(), + PgValueFormat::Text => hex::decode(text_hex_decode_input(value)?)?.into(), + }) + } +} diff --git a/sqlx-postgres/src/types/str.rs b/sqlx-postgres/src/types/str.rs index ca7e20a558..f6ba9d82e3 100644 --- a/sqlx-postgres/src/types/str.rs +++ b/sqlx-postgres/src/types/str.rs @@ -5,6 +5,7 @@ use crate::types::array_compatible; use crate::types::Type; use crate::{PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueRef, Postgres}; use std::borrow::Cow; +use std::sync::Arc; impl Type for str { fn type_info() -> PgTypeInfo { @@ -94,6 +95,16 @@ impl PgHasArrayType for String { } } +impl PgHasArrayType for Arc { + fn array_type_info() -> PgTypeInfo { + <&str as PgHasArrayType>::array_type_info() + } + + fn array_compatible(ty: &PgTypeInfo) -> bool { + <&str as PgHasArrayType>::array_compatible(ty) + } +} + impl Encode<'_, Postgres> for &'_ str { fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result { buf.extend(self.as_bytes()); @@ -123,6 +134,12 @@ impl Encode<'_, Postgres> for String { } } +impl Encode<'_, Postgres> for Arc { + 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() @@ -146,3 +163,9 @@ impl Decode<'_, Postgres> for String { Ok(value.as_str()?.to_owned()) } } + +impl Decode<'_, Postgres> for Arc { + fn decode(value: PgValueRef<'_>) -> Result { + Ok(value.as_str()?.into()) + } +} diff --git a/sqlx-sqlite/src/types/bytes.rs b/sqlx-sqlite/src/types/bytes.rs index f854b911c5..d609b0a798 100644 --- a/sqlx-sqlite/src/types/bytes.rs +++ b/sqlx-sqlite/src/types/bytes.rs @@ -1,4 +1,5 @@ use std::borrow::Cow; +use std::sync::Arc; use crate::decode::Decode; use crate::encode::{Encode, IsNull}; @@ -101,3 +102,26 @@ impl<'r> Decode<'r, Sqlite> for Vec { Ok(value.blob().to_owned()) } } + +impl<'q> Encode<'q, Sqlite> for Arc<[u8]> { + fn encode(self, args: &mut Vec>) -> Result { + args.push(SqliteArgumentValue::Blob(Cow::Owned(self.to_vec()))); + + Ok(IsNull::No) + } + + fn encode_by_ref( + &self, + args: &mut Vec>, + ) -> Result { + args.push(SqliteArgumentValue::Blob(Cow::Owned(self.to_vec()))); + + Ok(IsNull::No) + } +} + +impl<'r> Decode<'r, Sqlite> for Arc<[u8]> { + fn decode(value: SqliteValueRef<'r>) -> Result { + Ok(value.blob().into()) + } +} diff --git a/sqlx-sqlite/src/types/str.rs b/sqlx-sqlite/src/types/str.rs index bfaffae78e..102d2f286e 100644 --- a/sqlx-sqlite/src/types/str.rs +++ b/sqlx-sqlite/src/types/str.rs @@ -1,4 +1,5 @@ use std::borrow::Cow; +use std::sync::Arc; use crate::decode::Decode; use crate::encode::{Encode, IsNull}; @@ -122,3 +123,26 @@ impl<'r> Decode<'r, Sqlite> for Cow<'r, str> { value.text().map(Cow::Borrowed) } } + +impl<'q> Encode<'q, Sqlite> for Arc { + fn encode(self, args: &mut Vec>) -> Result { + args.push(SqliteArgumentValue::Text(Cow::Owned(self.to_string()))); + + Ok(IsNull::No) + } + + fn encode_by_ref( + &self, + args: &mut Vec>, + ) -> Result { + args.push(SqliteArgumentValue::Text(Cow::Owned(self.to_string()))); + + Ok(IsNull::No) + } +} + +impl<'r> Decode<'r, Sqlite> for Arc { + fn decode(value: SqliteValueRef<'r>) -> Result { + value.text().map(Into::into) + } +} diff --git a/tests/mysql/types.rs b/tests/mysql/types.rs index e837a53f75..006237a301 100644 --- a/tests/mysql/types.rs +++ b/tests/mysql/types.rs @@ -3,6 +3,7 @@ extern crate time_ as time; use std::net::SocketAddr; #[cfg(feature = "rust_decimal")] use std::str::FromStr; +use std::sync::Arc; use sqlx::mysql::MySql; use sqlx::{Executor, Row}; @@ -384,3 +385,33 @@ CREATE TEMPORARY TABLE user_login ( Ok(()) } + +#[sqlx_macros::test] +async fn test_arc_str() -> anyhow::Result<()> { + let mut conn = new::().await?; + + let name: Arc = "Harold".into(); + + let username: Arc = sqlx::query_scalar("SELECT ? AS username") + .bind(&name) + .fetch_one(&mut conn) + .await?; + + assert!(username == name); + Ok(()) +} + +#[sqlx_macros::test] +async fn test_arc_slice() -> anyhow::Result<()> { + let mut conn = new::().await?; + + let name: Arc<[u8]> = [5, 0].into(); + + let username: Arc<[u8]> = sqlx::query_scalar("SELECT ?") + .bind(&name) + .fetch_one(&mut conn) + .await?; + + assert!(username == name); + Ok(()) +} diff --git a/tests/postgres/types.rs b/tests/postgres/types.rs index 7aa81617bb..fa6135576d 100644 --- a/tests/postgres/types.rs +++ b/tests/postgres/types.rs @@ -8,6 +8,7 @@ use std::sync::Arc; use sqlx::postgres::types::{Oid, PgCiText, PgInterval, PgMoney, PgRange}; use sqlx::postgres::Postgres; +use sqlx_macros::FromRow; use sqlx_test::{new, test_decode_type, test_prepared_type, test_type}; use sqlx_core::executor::Executor; @@ -673,6 +674,21 @@ async fn test_arc() -> anyhow::Result<()> { Ok(()) } +#[sqlx_macros::test] +async fn test_arc_str() -> anyhow::Result<()> { + let mut conn = new::().await?; + + let name: Arc = "Harold".into(); + + let username: Arc = sqlx::query_scalar("SELECT $1 AS username") + .bind(&name) + .fetch_one(&mut conn) + .await?; + + assert!(username == name); + Ok(()) +} + #[sqlx_macros::test] async fn test_cow() -> anyhow::Result<()> { let mut conn = new::().await?; @@ -688,6 +704,21 @@ async fn test_cow() -> anyhow::Result<()> { Ok(()) } +#[sqlx_macros::test] +async fn test_arc_slice() -> anyhow::Result<()> { + let mut conn = new::().await?; + + let name: Arc<[u8]> = [5, 0].into(); + + let username: Arc<[u8]> = sqlx::query_scalar("SELECT $1") + .bind(&name) + .fetch_one(&mut conn) + .await?; + + assert!(username == name); + Ok(()) +} + #[sqlx_macros::test] async fn test_box() -> anyhow::Result<()> { let mut conn = new::().await?; @@ -713,3 +744,20 @@ async fn test_rc() -> anyhow::Result<()> { assert!(user_age == 1); Ok(()) } + +#[sqlx_macros::test] +async fn test_arc_slice_2() -> anyhow::Result<()> { + let mut conn = new::().await?; + + #[derive(FromRow)] + struct Nested { + inner: Arc<[i32]>, + } + + let username: Nested = sqlx::query_as("SELECT ARRAY[1, 2, 3]::INT4[] as inner") + .fetch_one(&mut conn) + .await?; + + assert!(username.inner.as_ref() == &[1, 2, 3]); + Ok(()) +} diff --git a/tests/sqlite/types.rs b/tests/sqlite/types.rs index 2497e406cc..b99963b035 100644 --- a/tests/sqlite/types.rs +++ b/tests/sqlite/types.rs @@ -7,6 +7,7 @@ use sqlx_core::types::Text; use sqlx_test::new; use sqlx_test::test_type; use std::net::SocketAddr; +use std::sync::Arc; test_type!(null>(Sqlite, "NULL" == None:: @@ -250,3 +251,33 @@ CREATE TEMPORARY TABLE user_login ( Ok(()) } + +#[sqlx_macros::test] +async fn test_arc_str() -> anyhow::Result<()> { + let mut conn = new::().await?; + + let name: Arc = "Harold".into(); + + let username: Arc = sqlx::query_scalar("SELECT $1 AS username") + .bind(&name) + .fetch_one(&mut conn) + .await?; + + assert!(username == name); + Ok(()) +} + +#[sqlx_macros::test] +async fn test_arc_slice() -> anyhow::Result<()> { + let mut conn = new::().await?; + + let name: Arc<[u8]> = [5, 0].into(); + + let username: Arc<[u8]> = sqlx::query_scalar("SELECT $1") + .bind(&name) + .fetch_one(&mut conn) + .await?; + + assert!(username == name); + Ok(()) +}