Skip to content

Commit 8602d94

Browse files
authored
Implement Decode, Encode and Type for Box, Arc, Cow and Rc (#3674)
* feat: implement Decode,Encode,Type for Box,Arc,Cow * feat implement Encode,Type for Rc * feat: implement Decode for Rc * chore: make tests more concise * chore: use macro's * chore: remove conflicting impls * chore: more macro's * Relax Sized bound for Decode, Encode * update unit tests * fixes after review * add comment in `Decode` impl * add comment about `ToOwned` trait bound * add comment explaining why decoding to `Cow::Owned` was chosen * Remove unnecessary Decode impls
1 parent 64e154a commit 8602d94

File tree

12 files changed

+269
-136
lines changed

12 files changed

+269
-136
lines changed

sqlx-core/src/decode.rs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
//! Provides [`Decode`] for decoding values from the database.
22
3+
use std::borrow::Cow;
4+
use std::rc::Rc;
5+
use std::sync::Arc;
6+
37
use crate::database::Database;
48
use crate::error::BoxDynError;
59

@@ -77,3 +81,60 @@ where
7781
}
7882
}
7983
}
84+
85+
macro_rules! impl_decode_for_smartpointer {
86+
($smart_pointer:tt) => {
87+
impl<'r, DB, T> Decode<'r, DB> for $smart_pointer<T>
88+
where
89+
DB: Database,
90+
T: Decode<'r, DB>,
91+
{
92+
fn decode(value: <DB as Database>::ValueRef<'r>) -> Result<Self, BoxDynError> {
93+
Ok(Self::new(T::decode(value)?))
94+
}
95+
}
96+
97+
impl<'r, DB> Decode<'r, DB> for $smart_pointer<str>
98+
where
99+
DB: Database,
100+
&'r str: Decode<'r, DB>,
101+
{
102+
fn decode(value: <DB as Database>::ValueRef<'r>) -> Result<Self, BoxDynError> {
103+
let ref_str = <&str as Decode<DB>>::decode(value)?;
104+
Ok(ref_str.into())
105+
}
106+
}
107+
108+
impl<'r, DB> Decode<'r, DB> for $smart_pointer<[u8]>
109+
where
110+
DB: Database,
111+
Vec<u8>: Decode<'r, DB>,
112+
{
113+
fn decode(value: <DB as Database>::ValueRef<'r>) -> Result<Self, BoxDynError> {
114+
// The `Postgres` implementation requires this to be decoded as an owned value because
115+
// bytes can be sent in text format.
116+
let bytes = <Vec<u8> as Decode<DB>>::decode(value)?;
117+
Ok(bytes.into())
118+
}
119+
}
120+
};
121+
}
122+
123+
impl_decode_for_smartpointer!(Arc);
124+
impl_decode_for_smartpointer!(Box);
125+
impl_decode_for_smartpointer!(Rc);
126+
127+
// implement `Decode` for Cow<T> for all SQL types
128+
impl<'r, 'a, DB, T> Decode<'r, DB> for Cow<'a, T>
129+
where
130+
DB: Database,
131+
// `ToOwned` is required here to satisfy `Cow`
132+
T: ToOwned + ?Sized,
133+
<T as ToOwned>::Owned: Decode<'r, DB>,
134+
{
135+
fn decode(value: <DB as Database>::ValueRef<'r>) -> Result<Self, BoxDynError> {
136+
// See https://github.com/launchbadge/sqlx/pull/3674#discussion_r2008611502 for more info
137+
// about why decoding to a `Cow::Owned` was chosen.
138+
<<T as ToOwned>::Owned as Decode<DB>>::decode(value).map(Cow::Owned)
139+
}
140+
}

sqlx-core/src/encode.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
//! Provides [`Encode`] for encoding values for the database.
22
3+
use std::borrow::Cow;
34
use std::mem;
5+
use std::rc::Rc;
6+
use std::sync::Arc;
47

58
use crate::database::Database;
69
use crate::error::BoxDynError;
@@ -129,3 +132,71 @@ macro_rules! impl_encode_for_option {
129132
}
130133
};
131134
}
135+
136+
macro_rules! impl_encode_for_smartpointer {
137+
($smart_pointer:ty) => {
138+
impl<'q, T, DB: Database> Encode<'q, DB> for $smart_pointer
139+
where
140+
T: Encode<'q, DB>,
141+
{
142+
#[inline]
143+
fn encode(
144+
self,
145+
buf: &mut <DB as Database>::ArgumentBuffer<'q>,
146+
) -> Result<IsNull, BoxDynError> {
147+
<T as Encode<DB>>::encode_by_ref(self.as_ref(), buf)
148+
}
149+
150+
#[inline]
151+
fn encode_by_ref(
152+
&self,
153+
buf: &mut <DB as Database>::ArgumentBuffer<'q>,
154+
) -> Result<IsNull, BoxDynError> {
155+
<&T as Encode<DB>>::encode(self, buf)
156+
}
157+
158+
#[inline]
159+
fn produces(&self) -> Option<DB::TypeInfo> {
160+
(**self).produces()
161+
}
162+
163+
#[inline]
164+
fn size_hint(&self) -> usize {
165+
(**self).size_hint()
166+
}
167+
}
168+
};
169+
}
170+
171+
impl_encode_for_smartpointer!(Arc<T>);
172+
impl_encode_for_smartpointer!(Box<T>);
173+
impl_encode_for_smartpointer!(Rc<T>);
174+
175+
impl<'q, T, DB: Database> Encode<'q, DB> for Cow<'q, T>
176+
where
177+
T: Encode<'q, DB>,
178+
T: ToOwned,
179+
{
180+
#[inline]
181+
fn encode(self, buf: &mut <DB as Database>::ArgumentBuffer<'q>) -> Result<IsNull, BoxDynError> {
182+
<&T as Encode<DB>>::encode_by_ref(&self.as_ref(), buf)
183+
}
184+
185+
#[inline]
186+
fn encode_by_ref(
187+
&self,
188+
buf: &mut <DB as Database>::ArgumentBuffer<'q>,
189+
) -> Result<IsNull, BoxDynError> {
190+
<&T as Encode<DB>>::encode_by_ref(&self.as_ref(), buf)
191+
}
192+
193+
#[inline]
194+
fn produces(&self) -> Option<DB::TypeInfo> {
195+
<&T as Encode<DB>>::produces(&self.as_ref())
196+
}
197+
198+
#[inline]
199+
fn size_hint(&self) -> usize {
200+
<&T as Encode<DB>>::size_hint(&self.as_ref())
201+
}
202+
}

sqlx-core/src/types/mod.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
//! To represent nullable SQL types, `Option<T>` is supported where `T` implements `Type`.
1818
//! An `Option<T>` represents a potentially `NULL` value from SQL.
1919
20+
use std::{borrow::Cow, rc::Rc, sync::Arc};
21+
2022
use crate::database::Database;
2123
use crate::type_info::TypeInfo;
2224

@@ -251,3 +253,38 @@ impl<T: Type<DB>, DB: Database> Type<DB> for Option<T> {
251253
ty.is_null() || <T as Type<DB>>::compatible(ty)
252254
}
253255
}
256+
257+
macro_rules! impl_type_for_smartpointer {
258+
($smart_pointer:ty) => {
259+
impl<T, DB: Database> Type<DB> for $smart_pointer
260+
where
261+
T: Type<DB> + ?Sized,
262+
{
263+
fn type_info() -> DB::TypeInfo {
264+
<T as Type<DB>>::type_info()
265+
}
266+
267+
fn compatible(ty: &DB::TypeInfo) -> bool {
268+
<T as Type<DB>>::compatible(ty)
269+
}
270+
}
271+
};
272+
}
273+
274+
impl_type_for_smartpointer!(Arc<T>);
275+
impl_type_for_smartpointer!(Box<T>);
276+
impl_type_for_smartpointer!(Rc<T>);
277+
278+
impl<T, DB: Database> Type<DB> for Cow<'_, T>
279+
where
280+
// `ToOwned` is required here to satisfy `Cow`
281+
T: Type<DB> + ToOwned + ?Sized,
282+
{
283+
fn type_info() -> DB::TypeInfo {
284+
<T as Type<DB>>::type_info()
285+
}
286+
287+
fn compatible(ty: &DB::TypeInfo) -> bool {
288+
<T as Type<DB>>::compatible(ty)
289+
}
290+
}

sqlx-mysql/src/types/bytes.rs

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::borrow::Cow;
2+
13
use crate::decode::Decode;
24
use crate::encode::{Encode, IsNull};
35
use crate::error::BoxDynError;
@@ -40,28 +42,12 @@ impl<'r> Decode<'r, MySql> for &'r [u8] {
4042
}
4143
}
4244

43-
impl Type<MySql> for Box<[u8]> {
44-
fn type_info() -> MySqlTypeInfo {
45-
<&[u8] as Type<MySql>>::type_info()
46-
}
47-
48-
fn compatible(ty: &MySqlTypeInfo) -> bool {
49-
<&[u8] as Type<MySql>>::compatible(ty)
50-
}
51-
}
52-
5345
impl Encode<'_, MySql> for Box<[u8]> {
5446
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
5547
<&[u8] as Encode<MySql>>::encode(self.as_ref(), buf)
5648
}
5749
}
5850

59-
impl<'r> Decode<'r, MySql> for Box<[u8]> {
60-
fn decode(value: MySqlValueRef<'r>) -> Result<Self, BoxDynError> {
61-
<&[u8] as Decode<MySql>>::decode(value).map(Box::from)
62-
}
63-
}
64-
6551
impl Type<MySql> for Vec<u8> {
6652
fn type_info() -> MySqlTypeInfo {
6753
<[u8] as Type<MySql>>::type_info()
@@ -83,3 +69,9 @@ impl Decode<'_, MySql> for Vec<u8> {
8369
<&[u8] as Decode<MySql>>::decode(value).map(ToOwned::to_owned)
8470
}
8571
}
72+
73+
impl Encode<'_, MySql> for Cow<'_, [u8]> {
74+
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
75+
<&[u8] as Encode<MySql>>::encode(self.as_ref(), buf)
76+
}
77+
}

sqlx-mysql/src/types/str.rs

Lines changed: 2 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1+
use std::borrow::Cow;
2+
13
use crate::decode::Decode;
24
use crate::encode::{Encode, IsNull};
35
use crate::error::BoxDynError;
46
use crate::io::MySqlBufMutExt;
57
use crate::protocol::text::{ColumnFlags, ColumnType};
68
use crate::types::Type;
79
use crate::{MySql, MySqlTypeInfo, MySqlValueRef};
8-
use std::borrow::Cow;
910

1011
impl Type<MySql> for str {
1112
fn type_info() -> MySqlTypeInfo {
@@ -46,28 +47,12 @@ impl<'r> Decode<'r, MySql> for &'r str {
4647
}
4748
}
4849

49-
impl Type<MySql> for Box<str> {
50-
fn type_info() -> MySqlTypeInfo {
51-
<&str as Type<MySql>>::type_info()
52-
}
53-
54-
fn compatible(ty: &MySqlTypeInfo) -> bool {
55-
<&str as Type<MySql>>::compatible(ty)
56-
}
57-
}
58-
5950
impl Encode<'_, MySql> for Box<str> {
6051
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
6152
<&str as Encode<MySql>>::encode(&**self, buf)
6253
}
6354
}
6455

65-
impl<'r> Decode<'r, MySql> for Box<str> {
66-
fn decode(value: MySqlValueRef<'r>) -> Result<Self, BoxDynError> {
67-
<&str as Decode<MySql>>::decode(value).map(Box::from)
68-
}
69-
}
70-
7156
impl Type<MySql> for String {
7257
fn type_info() -> MySqlTypeInfo {
7358
<str as Type<MySql>>::type_info()
@@ -90,16 +75,6 @@ impl Decode<'_, MySql> for String {
9075
}
9176
}
9277

93-
impl Type<MySql> for Cow<'_, str> {
94-
fn type_info() -> MySqlTypeInfo {
95-
<&str as Type<MySql>>::type_info()
96-
}
97-
98-
fn compatible(ty: &MySqlTypeInfo) -> bool {
99-
<&str as Type<MySql>>::compatible(ty)
100-
}
101-
}
102-
10378
impl Encode<'_, MySql> for Cow<'_, str> {
10479
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
10580
match self {
@@ -108,9 +83,3 @@ impl Encode<'_, MySql> for Cow<'_, str> {
10883
}
10984
}
11085
}
111-
112-
impl<'r> Decode<'r, MySql> for Cow<'r, str> {
113-
fn decode(value: MySqlValueRef<'r>) -> Result<Self, BoxDynError> {
114-
value.as_str().map(Cow::Borrowed)
115-
}
116-
}

sqlx-postgres/src/types/bytes.rs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::borrow::Cow;
2+
13
use crate::decode::Decode;
24
use crate::encode::{Encode, IsNull};
35
use crate::error::BoxDynError;
@@ -80,15 +82,6 @@ fn text_hex_decode_input(value: PgValueRef<'_>) -> Result<&[u8], BoxDynError> {
8082
.map_err(Into::into)
8183
}
8284

83-
impl Decode<'_, Postgres> for Box<[u8]> {
84-
fn decode(value: PgValueRef<'_>) -> Result<Self, BoxDynError> {
85-
Ok(match value.format() {
86-
PgValueFormat::Binary => Box::from(value.as_bytes()?),
87-
PgValueFormat::Text => Box::from(hex::decode(text_hex_decode_input(value)?)?),
88-
})
89-
}
90-
}
91-
9285
impl Decode<'_, Postgres> for Vec<u8> {
9386
fn decode(value: PgValueRef<'_>) -> Result<Self, BoxDynError> {
9487
Ok(match value.format() {
@@ -110,3 +103,9 @@ impl<const N: usize> Decode<'_, Postgres> for [u8; N] {
110103
Ok(bytes)
111104
}
112105
}
106+
107+
impl Encode<'_, Postgres> for Cow<'_, [u8]> {
108+
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
109+
<&[u8] as Encode<Postgres>>::encode(self.as_ref(), buf)
110+
}
111+
}

sqlx-postgres/src/types/str.rs

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -24,26 +24,6 @@ impl Type<Postgres> for str {
2424
}
2525
}
2626

27-
impl Type<Postgres> for Cow<'_, str> {
28-
fn type_info() -> PgTypeInfo {
29-
<&str as Type<Postgres>>::type_info()
30-
}
31-
32-
fn compatible(ty: &PgTypeInfo) -> bool {
33-
<&str as Type<Postgres>>::compatible(ty)
34-
}
35-
}
36-
37-
impl Type<Postgres> for Box<str> {
38-
fn type_info() -> PgTypeInfo {
39-
<&str as Type<Postgres>>::type_info()
40-
}
41-
42-
fn compatible(ty: &PgTypeInfo) -> bool {
43-
<&str as Type<Postgres>>::compatible(ty)
44-
}
45-
}
46-
4727
impl Type<Postgres> for String {
4828
fn type_info() -> PgTypeInfo {
4929
<&str as Type<Postgres>>::type_info()
@@ -129,18 +109,6 @@ impl<'r> Decode<'r, Postgres> for &'r str {
129109
}
130110
}
131111

132-
impl<'r> Decode<'r, Postgres> for Cow<'r, str> {
133-
fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
134-
Ok(Cow::Borrowed(value.as_str()?))
135-
}
136-
}
137-
138-
impl<'r> Decode<'r, Postgres> for Box<str> {
139-
fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
140-
Ok(Box::from(value.as_str()?))
141-
}
142-
}
143-
144112
impl Decode<'_, Postgres> for String {
145113
fn decode(value: PgValueRef<'_>) -> Result<Self, BoxDynError> {
146114
Ok(value.as_str()?.to_owned())

0 commit comments

Comments
 (0)