Skip to content

Commit 12d9f54

Browse files
authored
Rewrite Postgres array type handling (#1385)
… to reduce boilerplate and allow custom types.
1 parent 5aef7d7 commit 12d9f54

25 files changed

+202
-407
lines changed

sqlx-core/src/postgres/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ pub use row::PgRow;
3737
pub use statement::PgStatement;
3838
pub use transaction::PgTransactionManager;
3939
pub use type_info::{PgTypeInfo, PgTypeKind};
40+
pub use types::PgHasArrayType;
4041
pub use value::{PgValue, PgValueFormat, PgValueRef};
4142

4243
/// An alias for [`Pool`][crate::pool::Pool], specialized for Postgres.

sqlx-core/src/postgres/types/array.rs

+28-8
Original file line numberDiff line numberDiff line change
@@ -7,29 +7,49 @@ use crate::postgres::type_info::PgType;
77
use crate::postgres::{PgArgumentBuffer, PgTypeInfo, PgValueFormat, PgValueRef, Postgres};
88
use crate::types::Type;
99

10-
impl<T> Type<Postgres> for [Option<T>]
10+
pub trait PgHasArrayType {
11+
fn array_type_info() -> PgTypeInfo;
12+
fn array_compatible(ty: &PgTypeInfo) -> bool {
13+
*ty == Self::array_type_info()
14+
}
15+
}
16+
17+
impl<T> PgHasArrayType for Option<T>
18+
where
19+
T: PgHasArrayType,
20+
{
21+
fn array_type_info() -> PgTypeInfo {
22+
T::array_type_info()
23+
}
24+
25+
fn array_compatible(ty: &PgTypeInfo) -> bool {
26+
T::array_compatible(ty)
27+
}
28+
}
29+
30+
impl<T> Type<Postgres> for [T]
1131
where
12-
[T]: Type<Postgres>,
32+
T: PgHasArrayType,
1333
{
1434
fn type_info() -> PgTypeInfo {
15-
<[T] as Type<Postgres>>::type_info()
35+
T::array_type_info()
1636
}
1737

1838
fn compatible(ty: &PgTypeInfo) -> bool {
19-
<[T] as Type<Postgres>>::compatible(ty)
39+
T::array_compatible(ty)
2040
}
2141
}
2242

23-
impl<T> Type<Postgres> for Vec<Option<T>>
43+
impl<T> Type<Postgres> for Vec<T>
2444
where
25-
Vec<T>: Type<Postgres>,
45+
T: PgHasArrayType,
2646
{
2747
fn type_info() -> PgTypeInfo {
28-
<Vec<T> as Type<Postgres>>::type_info()
48+
T::array_type_info()
2949
}
3050

3151
fn compatible(ty: &PgTypeInfo) -> bool {
32-
<Vec<T> as Type<Postgres>>::compatible(ty)
52+
T::array_compatible(ty)
3353
}
3454
}
3555

sqlx-core/src/postgres/types/bigdecimal.rs

+5-9
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ use crate::decode::Decode;
88
use crate::encode::{Encode, IsNull};
99
use crate::error::BoxDynError;
1010
use crate::postgres::types::numeric::{PgNumeric, PgNumericSign};
11-
use crate::postgres::{PgArgumentBuffer, PgTypeInfo, PgValueFormat, PgValueRef, Postgres};
11+
use crate::postgres::{
12+
PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueFormat, PgValueRef, Postgres,
13+
};
1214
use crate::types::Type;
1315

1416
impl Type<Postgres> for BigDecimal {
@@ -17,18 +19,12 @@ impl Type<Postgres> for BigDecimal {
1719
}
1820
}
1921

20-
impl Type<Postgres> for [BigDecimal] {
21-
fn type_info() -> PgTypeInfo {
22+
impl PgHasArrayType for BigDecimal {
23+
fn array_type_info() -> PgTypeInfo {
2224
PgTypeInfo::NUMERIC_ARRAY
2325
}
2426
}
2527

26-
impl Type<Postgres> for Vec<BigDecimal> {
27-
fn type_info() -> PgTypeInfo {
28-
<[BigDecimal] as Type<Postgres>>::type_info()
29-
}
30-
}
31-
3228
impl TryFrom<PgNumeric> for BigDecimal {
3329
type Error = BoxDynError;
3430

sqlx-core/src/postgres/types/bit_vec.rs

+4-14
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::{
22
decode::Decode,
33
encode::{Encode, IsNull},
44
error::BoxDynError,
5-
postgres::{PgArgumentBuffer, PgTypeInfo, PgValueFormat, PgValueRef, Postgres},
5+
postgres::{PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueFormat, PgValueRef, Postgres},
66
types::Type,
77
};
88
use bit_vec::BitVec;
@@ -19,26 +19,16 @@ impl Type<Postgres> for BitVec {
1919
}
2020
}
2121

22-
impl Type<Postgres> for [BitVec] {
23-
fn type_info() -> PgTypeInfo {
22+
impl PgHasArrayType for BitVec {
23+
fn array_type_info() -> PgTypeInfo {
2424
PgTypeInfo::VARBIT_ARRAY
2525
}
2626

27-
fn compatible(ty: &PgTypeInfo) -> bool {
27+
fn array_compatible(ty: &PgTypeInfo) -> bool {
2828
*ty == PgTypeInfo::BIT_ARRAY || *ty == PgTypeInfo::VARBIT_ARRAY
2929
}
3030
}
3131

32-
impl Type<Postgres> for Vec<BitVec> {
33-
fn type_info() -> PgTypeInfo {
34-
<[BitVec] as Type<Postgres>>::type_info()
35-
}
36-
37-
fn compatible(ty: &PgTypeInfo) -> bool {
38-
<[BitVec] as Type<Postgres>>::compatible(ty)
39-
}
40-
}
41-
4232
impl Encode<'_, Postgres> for BitVec {
4333
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
4434
buf.extend(&(self.len() as i32).to_be_bytes());

sqlx-core/src/postgres/types/bool.rs

+5-9
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
use crate::decode::Decode;
22
use crate::encode::{Encode, IsNull};
33
use crate::error::BoxDynError;
4-
use crate::postgres::{PgArgumentBuffer, PgTypeInfo, PgValueFormat, PgValueRef, Postgres};
4+
use crate::postgres::{
5+
PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueFormat, PgValueRef, Postgres,
6+
};
57
use crate::types::Type;
68

79
impl Type<Postgres> for bool {
@@ -10,18 +12,12 @@ impl Type<Postgres> for bool {
1012
}
1113
}
1214

13-
impl Type<Postgres> for [bool] {
14-
fn type_info() -> PgTypeInfo {
15+
impl PgHasArrayType for bool {
16+
fn array_type_info() -> PgTypeInfo {
1517
PgTypeInfo::BOOL_ARRAY
1618
}
1719
}
1820

19-
impl Type<Postgres> for Vec<bool> {
20-
fn type_info() -> PgTypeInfo {
21-
<[bool] as Type<Postgres>>::type_info()
22-
}
23-
}
24-
2521
impl Encode<'_, Postgres> for bool {
2622
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
2723
buf.push(*self as u8);

sqlx-core/src/postgres/types/bytes.rs

+9-25
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,25 @@
11
use crate::decode::Decode;
22
use crate::encode::{Encode, IsNull};
33
use crate::error::BoxDynError;
4-
use crate::postgres::{PgArgumentBuffer, PgTypeInfo, PgValueFormat, PgValueRef, Postgres};
4+
use crate::postgres::{
5+
PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueFormat, PgValueRef, Postgres,
6+
};
57
use crate::types::Type;
68

7-
impl Type<Postgres> for [u8] {
8-
fn type_info() -> PgTypeInfo {
9+
impl PgHasArrayType for u8 {
10+
fn array_type_info() -> PgTypeInfo {
911
PgTypeInfo::BYTEA
1012
}
1113
}
1214

13-
impl Type<Postgres> for Vec<u8> {
14-
fn type_info() -> PgTypeInfo {
15-
<[u8] as Type<Postgres>>::type_info()
16-
}
17-
}
18-
19-
impl Type<Postgres> for [&'_ [u8]] {
20-
fn type_info() -> PgTypeInfo {
15+
impl PgHasArrayType for &'_ [u8] {
16+
fn array_type_info() -> PgTypeInfo {
2117
PgTypeInfo::BYTEA_ARRAY
2218
}
2319
}
2420

25-
impl Type<Postgres> for [Vec<u8>] {
26-
fn type_info() -> PgTypeInfo {
27-
<[&[u8]] as Type<Postgres>>::type_info()
28-
}
29-
}
30-
31-
impl Type<Postgres> for Vec<&'_ [u8]> {
32-
fn type_info() -> PgTypeInfo {
33-
<[&[u8]] as Type<Postgres>>::type_info()
34-
}
35-
}
36-
37-
impl Type<Postgres> for Vec<Vec<u8>> {
38-
fn type_info() -> PgTypeInfo {
21+
impl PgHasArrayType for Vec<u8> {
22+
fn array_type_info() -> PgTypeInfo {
3923
<[&[u8]] as Type<Postgres>>::type_info()
4024
}
4125
}

sqlx-core/src/postgres/types/chrono/date.rs

+5-9
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
use crate::decode::Decode;
22
use crate::encode::{Encode, IsNull};
33
use crate::error::BoxDynError;
4-
use crate::postgres::{PgArgumentBuffer, PgTypeInfo, PgValueFormat, PgValueRef, Postgres};
4+
use crate::postgres::{
5+
PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueFormat, PgValueRef, Postgres,
6+
};
57
use crate::types::Type;
68
use chrono::{Duration, NaiveDate};
79
use std::mem;
@@ -12,18 +14,12 @@ impl Type<Postgres> for NaiveDate {
1214
}
1315
}
1416

15-
impl Type<Postgres> for [NaiveDate] {
16-
fn type_info() -> PgTypeInfo {
17+
impl PgHasArrayType for NaiveDate {
18+
fn array_type_info() -> PgTypeInfo {
1719
PgTypeInfo::DATE_ARRAY
1820
}
1921
}
2022

21-
impl Type<Postgres> for Vec<NaiveDate> {
22-
fn type_info() -> PgTypeInfo {
23-
<[NaiveDate] as Type<Postgres>>::type_info()
24-
}
25-
}
26-
2723
impl Encode<'_, Postgres> for NaiveDate {
2824
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
2925
// DATE is encoded as the days since epoch

sqlx-core/src/postgres/types/chrono/datetime.rs

+7-17
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
use crate::decode::Decode;
22
use crate::encode::{Encode, IsNull};
33
use crate::error::BoxDynError;
4-
use crate::postgres::{PgArgumentBuffer, PgTypeInfo, PgValueFormat, PgValueRef, Postgres};
4+
use crate::postgres::{
5+
PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueFormat, PgValueRef, Postgres,
6+
};
57
use crate::types::Type;
68
use chrono::{
79
DateTime, Duration, FixedOffset, Local, NaiveDate, NaiveDateTime, Offset, TimeZone, Utc,
@@ -20,30 +22,18 @@ impl<Tz: TimeZone> Type<Postgres> for DateTime<Tz> {
2022
}
2123
}
2224

23-
impl Type<Postgres> for [NaiveDateTime] {
24-
fn type_info() -> PgTypeInfo {
25+
impl PgHasArrayType for NaiveDateTime {
26+
fn array_type_info() -> PgTypeInfo {
2527
PgTypeInfo::TIMESTAMP_ARRAY
2628
}
2729
}
2830

29-
impl<Tz: TimeZone> Type<Postgres> for [DateTime<Tz>] {
30-
fn type_info() -> PgTypeInfo {
31+
impl<Tz: TimeZone> PgHasArrayType for DateTime<Tz> {
32+
fn array_type_info() -> PgTypeInfo {
3133
PgTypeInfo::TIMESTAMPTZ_ARRAY
3234
}
3335
}
3436

35-
impl Type<Postgres> for Vec<NaiveDateTime> {
36-
fn type_info() -> PgTypeInfo {
37-
<[NaiveDateTime] as Type<Postgres>>::type_info()
38-
}
39-
}
40-
41-
impl<Tz: TimeZone> Type<Postgres> for Vec<DateTime<Tz>> {
42-
fn type_info() -> PgTypeInfo {
43-
<[DateTime<Tz>] as Type<Postgres>>::type_info()
44-
}
45-
}
46-
4737
impl Encode<'_, Postgres> for NaiveDateTime {
4838
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
4939
// FIXME: We should *really* be returning an error, Encode needs to be fallible

sqlx-core/src/postgres/types/chrono/time.rs

+5-9
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
use crate::decode::Decode;
22
use crate::encode::{Encode, IsNull};
33
use crate::error::BoxDynError;
4-
use crate::postgres::{PgArgumentBuffer, PgTypeInfo, PgValueFormat, PgValueRef, Postgres};
4+
use crate::postgres::{
5+
PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueFormat, PgValueRef, Postgres,
6+
};
57
use crate::types::Type;
68
use chrono::{Duration, NaiveTime};
79
use std::mem;
@@ -12,18 +14,12 @@ impl Type<Postgres> for NaiveTime {
1214
}
1315
}
1416

15-
impl Type<Postgres> for [NaiveTime] {
16-
fn type_info() -> PgTypeInfo {
17+
impl PgHasArrayType for NaiveTime {
18+
fn array_type_info() -> PgTypeInfo {
1719
PgTypeInfo::TIME_ARRAY
1820
}
1921
}
2022

21-
impl Type<Postgres> for Vec<NaiveTime> {
22-
fn type_info() -> PgTypeInfo {
23-
<[NaiveTime] as Type<Postgres>>::type_info()
24-
}
25-
}
26-
2723
impl Encode<'_, Postgres> for NaiveTime {
2824
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
2925
// TIME is encoded as the microseconds since midnight

sqlx-core/src/postgres/types/decimal.rs

+5-9
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ use crate::decode::Decode;
99
use crate::encode::{Encode, IsNull};
1010
use crate::error::BoxDynError;
1111
use crate::postgres::types::numeric::{PgNumeric, PgNumericSign};
12-
use crate::postgres::{PgArgumentBuffer, PgTypeInfo, PgValueFormat, PgValueRef, Postgres};
12+
use crate::postgres::{
13+
PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueFormat, PgValueRef, Postgres,
14+
};
1315
use crate::types::Type;
1416

1517
impl Type<Postgres> for Decimal {
@@ -18,18 +20,12 @@ impl Type<Postgres> for Decimal {
1820
}
1921
}
2022

21-
impl Type<Postgres> for [Decimal] {
22-
fn type_info() -> PgTypeInfo {
23+
impl PgHasArrayType for Decimal {
24+
fn array_type_info() -> PgTypeInfo {
2325
PgTypeInfo::NUMERIC_ARRAY
2426
}
2527
}
2628

27-
impl Type<Postgres> for Vec<Decimal> {
28-
fn type_info() -> PgTypeInfo {
29-
<[Decimal] as Type<Postgres>>::type_info()
30-
}
31-
}
32-
3329
impl TryFrom<PgNumeric> for Decimal {
3430
type Error = BoxDynError;
3531

0 commit comments

Comments
 (0)