Skip to content

Commit

Permalink
Rewrite Postgres array type handling (#1385)
Browse files Browse the repository at this point in the history
… to reduce boilerplate and allow custom types.
  • Loading branch information
jplatte authored Nov 23, 2021
1 parent 5aef7d7 commit 12d9f54
Show file tree
Hide file tree
Showing 25 changed files with 202 additions and 407 deletions.
1 change: 1 addition & 0 deletions sqlx-core/src/postgres/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ pub use row::PgRow;
pub use statement::PgStatement;
pub use transaction::PgTransactionManager;
pub use type_info::{PgTypeInfo, PgTypeKind};
pub use types::PgHasArrayType;
pub use value::{PgValue, PgValueFormat, PgValueRef};

/// An alias for [`Pool`][crate::pool::Pool], specialized for Postgres.
Expand Down
36 changes: 28 additions & 8 deletions sqlx-core/src/postgres/types/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,49 @@ use crate::postgres::type_info::PgType;
use crate::postgres::{PgArgumentBuffer, PgTypeInfo, PgValueFormat, PgValueRef, Postgres};
use crate::types::Type;

impl<T> Type<Postgres> for [Option<T>]
pub trait PgHasArrayType {
fn array_type_info() -> PgTypeInfo;
fn array_compatible(ty: &PgTypeInfo) -> bool {
*ty == Self::array_type_info()
}
}

impl<T> PgHasArrayType for Option<T>
where
T: PgHasArrayType,
{
fn array_type_info() -> PgTypeInfo {
T::array_type_info()
}

fn array_compatible(ty: &PgTypeInfo) -> bool {
T::array_compatible(ty)
}
}

impl<T> Type<Postgres> for [T]
where
[T]: Type<Postgres>,
T: PgHasArrayType,
{
fn type_info() -> PgTypeInfo {
<[T] as Type<Postgres>>::type_info()
T::array_type_info()
}

fn compatible(ty: &PgTypeInfo) -> bool {
<[T] as Type<Postgres>>::compatible(ty)
T::array_compatible(ty)
}
}

impl<T> Type<Postgres> for Vec<Option<T>>
impl<T> Type<Postgres> for Vec<T>
where
Vec<T>: Type<Postgres>,
T: PgHasArrayType,
{
fn type_info() -> PgTypeInfo {
<Vec<T> as Type<Postgres>>::type_info()
T::array_type_info()
}

fn compatible(ty: &PgTypeInfo) -> bool {
<Vec<T> as Type<Postgres>>::compatible(ty)
T::array_compatible(ty)
}
}

Expand Down
14 changes: 5 additions & 9 deletions sqlx-core/src/postgres/types/bigdecimal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ use crate::decode::Decode;
use crate::encode::{Encode, IsNull};
use crate::error::BoxDynError;
use crate::postgres::types::numeric::{PgNumeric, PgNumericSign};
use crate::postgres::{PgArgumentBuffer, PgTypeInfo, PgValueFormat, PgValueRef, Postgres};
use crate::postgres::{
PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueFormat, PgValueRef, Postgres,
};
use crate::types::Type;

impl Type<Postgres> for BigDecimal {
Expand All @@ -17,18 +19,12 @@ impl Type<Postgres> for BigDecimal {
}
}

impl Type<Postgres> for [BigDecimal] {
fn type_info() -> PgTypeInfo {
impl PgHasArrayType for BigDecimal {
fn array_type_info() -> PgTypeInfo {
PgTypeInfo::NUMERIC_ARRAY
}
}

impl Type<Postgres> for Vec<BigDecimal> {
fn type_info() -> PgTypeInfo {
<[BigDecimal] as Type<Postgres>>::type_info()
}
}

impl TryFrom<PgNumeric> for BigDecimal {
type Error = BoxDynError;

Expand Down
18 changes: 4 additions & 14 deletions sqlx-core/src/postgres/types/bit_vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::{
decode::Decode,
encode::{Encode, IsNull},
error::BoxDynError,
postgres::{PgArgumentBuffer, PgTypeInfo, PgValueFormat, PgValueRef, Postgres},
postgres::{PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueFormat, PgValueRef, Postgres},
types::Type,
};
use bit_vec::BitVec;
Expand All @@ -19,26 +19,16 @@ impl Type<Postgres> for BitVec {
}
}

impl Type<Postgres> for [BitVec] {
fn type_info() -> PgTypeInfo {
impl PgHasArrayType for BitVec {
fn array_type_info() -> PgTypeInfo {
PgTypeInfo::VARBIT_ARRAY
}

fn compatible(ty: &PgTypeInfo) -> bool {
fn array_compatible(ty: &PgTypeInfo) -> bool {
*ty == PgTypeInfo::BIT_ARRAY || *ty == PgTypeInfo::VARBIT_ARRAY
}
}

impl Type<Postgres> for Vec<BitVec> {
fn type_info() -> PgTypeInfo {
<[BitVec] as Type<Postgres>>::type_info()
}

fn compatible(ty: &PgTypeInfo) -> bool {
<[BitVec] as Type<Postgres>>::compatible(ty)
}
}

impl Encode<'_, Postgres> for BitVec {
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
buf.extend(&(self.len() as i32).to_be_bytes());
Expand Down
14 changes: 5 additions & 9 deletions sqlx-core/src/postgres/types/bool.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use crate::decode::Decode;
use crate::encode::{Encode, IsNull};
use crate::error::BoxDynError;
use crate::postgres::{PgArgumentBuffer, PgTypeInfo, PgValueFormat, PgValueRef, Postgres};
use crate::postgres::{
PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueFormat, PgValueRef, Postgres,
};
use crate::types::Type;

impl Type<Postgres> for bool {
Expand All @@ -10,18 +12,12 @@ impl Type<Postgres> for bool {
}
}

impl Type<Postgres> for [bool] {
fn type_info() -> PgTypeInfo {
impl PgHasArrayType for bool {
fn array_type_info() -> PgTypeInfo {
PgTypeInfo::BOOL_ARRAY
}
}

impl Type<Postgres> for Vec<bool> {
fn type_info() -> PgTypeInfo {
<[bool] as Type<Postgres>>::type_info()
}
}

impl Encode<'_, Postgres> for bool {
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
buf.push(*self as u8);
Expand Down
34 changes: 9 additions & 25 deletions sqlx-core/src/postgres/types/bytes.rs
Original file line number Diff line number Diff line change
@@ -1,41 +1,25 @@
use crate::decode::Decode;
use crate::encode::{Encode, IsNull};
use crate::error::BoxDynError;
use crate::postgres::{PgArgumentBuffer, PgTypeInfo, PgValueFormat, PgValueRef, Postgres};
use crate::postgres::{
PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueFormat, PgValueRef, Postgres,
};
use crate::types::Type;

impl Type<Postgres> for [u8] {
fn type_info() -> PgTypeInfo {
impl PgHasArrayType for u8 {
fn array_type_info() -> PgTypeInfo {
PgTypeInfo::BYTEA
}
}

impl Type<Postgres> for Vec<u8> {
fn type_info() -> PgTypeInfo {
<[u8] as Type<Postgres>>::type_info()
}
}

impl Type<Postgres> for [&'_ [u8]] {
fn type_info() -> PgTypeInfo {
impl PgHasArrayType for &'_ [u8] {
fn array_type_info() -> PgTypeInfo {
PgTypeInfo::BYTEA_ARRAY
}
}

impl Type<Postgres> for [Vec<u8>] {
fn type_info() -> PgTypeInfo {
<[&[u8]] as Type<Postgres>>::type_info()
}
}

impl Type<Postgres> for Vec<&'_ [u8]> {
fn type_info() -> PgTypeInfo {
<[&[u8]] as Type<Postgres>>::type_info()
}
}

impl Type<Postgres> for Vec<Vec<u8>> {
fn type_info() -> PgTypeInfo {
impl PgHasArrayType for Vec<u8> {
fn array_type_info() -> PgTypeInfo {
<[&[u8]] as Type<Postgres>>::type_info()
}
}
Expand Down
14 changes: 5 additions & 9 deletions sqlx-core/src/postgres/types/chrono/date.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use crate::decode::Decode;
use crate::encode::{Encode, IsNull};
use crate::error::BoxDynError;
use crate::postgres::{PgArgumentBuffer, PgTypeInfo, PgValueFormat, PgValueRef, Postgres};
use crate::postgres::{
PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueFormat, PgValueRef, Postgres,
};
use crate::types::Type;
use chrono::{Duration, NaiveDate};
use std::mem;
Expand All @@ -12,18 +14,12 @@ impl Type<Postgres> for NaiveDate {
}
}

impl Type<Postgres> for [NaiveDate] {
fn type_info() -> PgTypeInfo {
impl PgHasArrayType for NaiveDate {
fn array_type_info() -> PgTypeInfo {
PgTypeInfo::DATE_ARRAY
}
}

impl Type<Postgres> for Vec<NaiveDate> {
fn type_info() -> PgTypeInfo {
<[NaiveDate] as Type<Postgres>>::type_info()
}
}

impl Encode<'_, Postgres> for NaiveDate {
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
// DATE is encoded as the days since epoch
Expand Down
24 changes: 7 additions & 17 deletions sqlx-core/src/postgres/types/chrono/datetime.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use crate::decode::Decode;
use crate::encode::{Encode, IsNull};
use crate::error::BoxDynError;
use crate::postgres::{PgArgumentBuffer, PgTypeInfo, PgValueFormat, PgValueRef, Postgres};
use crate::postgres::{
PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueFormat, PgValueRef, Postgres,
};
use crate::types::Type;
use chrono::{
DateTime, Duration, FixedOffset, Local, NaiveDate, NaiveDateTime, Offset, TimeZone, Utc,
Expand All @@ -20,30 +22,18 @@ impl<Tz: TimeZone> Type<Postgres> for DateTime<Tz> {
}
}

impl Type<Postgres> for [NaiveDateTime] {
fn type_info() -> PgTypeInfo {
impl PgHasArrayType for NaiveDateTime {
fn array_type_info() -> PgTypeInfo {
PgTypeInfo::TIMESTAMP_ARRAY
}
}

impl<Tz: TimeZone> Type<Postgres> for [DateTime<Tz>] {
fn type_info() -> PgTypeInfo {
impl<Tz: TimeZone> PgHasArrayType for DateTime<Tz> {
fn array_type_info() -> PgTypeInfo {
PgTypeInfo::TIMESTAMPTZ_ARRAY
}
}

impl Type<Postgres> for Vec<NaiveDateTime> {
fn type_info() -> PgTypeInfo {
<[NaiveDateTime] as Type<Postgres>>::type_info()
}
}

impl<Tz: TimeZone> Type<Postgres> for Vec<DateTime<Tz>> {
fn type_info() -> PgTypeInfo {
<[DateTime<Tz>] as Type<Postgres>>::type_info()
}
}

impl Encode<'_, Postgres> for NaiveDateTime {
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
// FIXME: We should *really* be returning an error, Encode needs to be fallible
Expand Down
14 changes: 5 additions & 9 deletions sqlx-core/src/postgres/types/chrono/time.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use crate::decode::Decode;
use crate::encode::{Encode, IsNull};
use crate::error::BoxDynError;
use crate::postgres::{PgArgumentBuffer, PgTypeInfo, PgValueFormat, PgValueRef, Postgres};
use crate::postgres::{
PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueFormat, PgValueRef, Postgres,
};
use crate::types::Type;
use chrono::{Duration, NaiveTime};
use std::mem;
Expand All @@ -12,18 +14,12 @@ impl Type<Postgres> for NaiveTime {
}
}

impl Type<Postgres> for [NaiveTime] {
fn type_info() -> PgTypeInfo {
impl PgHasArrayType for NaiveTime {
fn array_type_info() -> PgTypeInfo {
PgTypeInfo::TIME_ARRAY
}
}

impl Type<Postgres> for Vec<NaiveTime> {
fn type_info() -> PgTypeInfo {
<[NaiveTime] as Type<Postgres>>::type_info()
}
}

impl Encode<'_, Postgres> for NaiveTime {
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
// TIME is encoded as the microseconds since midnight
Expand Down
14 changes: 5 additions & 9 deletions sqlx-core/src/postgres/types/decimal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ use crate::decode::Decode;
use crate::encode::{Encode, IsNull};
use crate::error::BoxDynError;
use crate::postgres::types::numeric::{PgNumeric, PgNumericSign};
use crate::postgres::{PgArgumentBuffer, PgTypeInfo, PgValueFormat, PgValueRef, Postgres};
use crate::postgres::{
PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueFormat, PgValueRef, Postgres,
};
use crate::types::Type;

impl Type<Postgres> for Decimal {
Expand All @@ -18,18 +20,12 @@ impl Type<Postgres> for Decimal {
}
}

impl Type<Postgres> for [Decimal] {
fn type_info() -> PgTypeInfo {
impl PgHasArrayType for Decimal {
fn array_type_info() -> PgTypeInfo {
PgTypeInfo::NUMERIC_ARRAY
}
}

impl Type<Postgres> for Vec<Decimal> {
fn type_info() -> PgTypeInfo {
<[Decimal] as Type<Postgres>>::type_info()
}
}

impl TryFrom<PgNumeric> for Decimal {
type Error = BoxDynError;

Expand Down
Loading

0 comments on commit 12d9f54

Please sign in to comment.