mirror of
https://github.com/launchbadge/sqlx
synced 2024-11-10 14:34:19 +00:00
feat(postgres) Create bindings for PgInterval
This commit is contained in:
parent
72c4e040bc
commit
71cb68b2f4
5 changed files with 194 additions and 7 deletions
|
@ -83,6 +83,8 @@ pub enum PgType {
|
|||
TimeArray,
|
||||
Timestamptz,
|
||||
TimestamptzArray,
|
||||
Interval,
|
||||
IntervalArray,
|
||||
NumericArray,
|
||||
Timetz,
|
||||
TimetzArray,
|
||||
|
@ -92,7 +94,6 @@ pub enum PgType {
|
|||
VarbitArray,
|
||||
Numeric,
|
||||
Record,
|
||||
Interval,
|
||||
RecordArray,
|
||||
Uuid,
|
||||
UuidArray,
|
||||
|
@ -285,6 +286,8 @@ impl PgType {
|
|||
1183 => PgType::TimeArray,
|
||||
1184 => PgType::Timestamptz,
|
||||
1185 => PgType::TimestamptzArray,
|
||||
1186 => PgType::Interval,
|
||||
1187 => PgType::IntervalArray,
|
||||
1231 => PgType::NumericArray,
|
||||
1266 => PgType::Timetz,
|
||||
1270 => PgType::TimetzArray,
|
||||
|
@ -294,7 +297,6 @@ impl PgType {
|
|||
1563 => PgType::VarbitArray,
|
||||
1700 => PgType::Numeric,
|
||||
2249 => PgType::Record,
|
||||
2281 => PgType::Interval,
|
||||
2287 => PgType::RecordArray,
|
||||
2950 => PgType::Uuid,
|
||||
2951 => PgType::UuidArray,
|
||||
|
@ -389,6 +391,8 @@ impl PgType {
|
|||
PgType::TimeArray => 1183,
|
||||
PgType::Timestamptz => 1184,
|
||||
PgType::TimestamptzArray => 1185,
|
||||
PgType::Interval => 1186,
|
||||
PgType::IntervalArray => 1187,
|
||||
PgType::NumericArray => 1231,
|
||||
PgType::Timetz => 1266,
|
||||
PgType::TimetzArray => 1270,
|
||||
|
@ -398,7 +402,6 @@ impl PgType {
|
|||
PgType::VarbitArray => 1563,
|
||||
PgType::Numeric => 1700,
|
||||
PgType::Record => 2249,
|
||||
PgType::Interval => 2281,
|
||||
PgType::RecordArray => 2287,
|
||||
PgType::Uuid => 2950,
|
||||
PgType::UuidArray => 2951,
|
||||
|
@ -488,6 +491,8 @@ impl PgType {
|
|||
PgType::TimeArray => "TIME[]",
|
||||
PgType::Timestamptz => "TIMESTAMPTZ",
|
||||
PgType::TimestamptzArray => "TIMESTAMPTZ[]",
|
||||
PgType::Interval => "INTERVAL",
|
||||
PgType::IntervalArray => "INTERVAL[]",
|
||||
PgType::NumericArray => "NUMERIC[]",
|
||||
PgType::Timetz => "TIMETZ",
|
||||
PgType::TimetzArray => "TIMETZ[]",
|
||||
|
@ -497,7 +502,6 @@ impl PgType {
|
|||
PgType::VarbitArray => "VARBIT[]",
|
||||
PgType::Numeric => "NUMERIC",
|
||||
PgType::Record => "RECORD",
|
||||
PgType::Interval => "INTERVAL",
|
||||
PgType::RecordArray => "RECORD[]",
|
||||
PgType::Uuid => "UUID",
|
||||
PgType::UuidArray => "UUID[]",
|
||||
|
@ -584,6 +588,8 @@ impl PgType {
|
|||
PgType::TimeArray => "_time",
|
||||
PgType::Timestamptz => "timestamptz",
|
||||
PgType::TimestamptzArray => "_timestamptz",
|
||||
PgType::Interval => "interval",
|
||||
PgType::IntervalArray => "_interval",
|
||||
PgType::NumericArray => "_numeric",
|
||||
PgType::Timetz => "timetz",
|
||||
PgType::TimetzArray => "_timetz",
|
||||
|
@ -593,7 +599,6 @@ impl PgType {
|
|||
PgType::VarbitArray => "_varbit",
|
||||
PgType::Numeric => "numeric",
|
||||
PgType::Record => "record",
|
||||
PgType::Interval => "interval",
|
||||
PgType::RecordArray => "_record",
|
||||
PgType::Uuid => "uuid",
|
||||
PgType::UuidArray => "_uuid",
|
||||
|
@ -680,6 +685,8 @@ impl PgType {
|
|||
PgType::TimeArray => &PgTypeKind::Array(PgTypeInfo(PgType::Time)),
|
||||
PgType::Timestamptz => &PgTypeKind::Simple,
|
||||
PgType::TimestamptzArray => &PgTypeKind::Array(PgTypeInfo(PgType::Timestamptz)),
|
||||
PgType::Interval => &PgTypeKind::Simple,
|
||||
PgType::IntervalArray => &PgTypeKind::Array(PgTypeInfo(PgType::Interval)),
|
||||
PgType::NumericArray => &PgTypeKind::Array(PgTypeInfo(PgType::Numeric)),
|
||||
PgType::Timetz => &PgTypeKind::Simple,
|
||||
PgType::TimetzArray => &PgTypeKind::Array(PgTypeInfo(PgType::Timetz)),
|
||||
|
@ -689,7 +696,6 @@ impl PgType {
|
|||
PgType::VarbitArray => &PgTypeKind::Array(PgTypeInfo(PgType::Varbit)),
|
||||
PgType::Numeric => &PgTypeKind::Simple,
|
||||
PgType::Record => &PgTypeKind::Simple,
|
||||
PgType::Interval => &PgTypeKind::Simple,
|
||||
PgType::RecordArray => &PgTypeKind::Array(PgTypeInfo(PgType::Record)),
|
||||
PgType::Uuid => &PgTypeKind::Simple,
|
||||
PgType::UuidArray => &PgTypeKind::Array(PgTypeInfo(PgType::Uuid)),
|
||||
|
@ -866,6 +872,7 @@ impl PgTypeInfo {
|
|||
|
||||
// time interval
|
||||
pub(crate) const INTERVAL: Self = Self(PgType::Interval);
|
||||
pub(crate) const INTERVAL_ARRAY: Self = Self(PgType::IntervalArray);
|
||||
|
||||
//
|
||||
// geometric types
|
||||
|
|
147
sqlx-core/src/postgres/types/interval.rs
Normal file
147
sqlx-core/src/postgres/types/interval.rs
Normal file
|
@ -0,0 +1,147 @@
|
|||
use std::mem;
|
||||
|
||||
use byteorder::{NetworkEndian, ReadBytesExt};
|
||||
|
||||
use crate::decode::Decode;
|
||||
use crate::encode::{Encode, IsNull};
|
||||
use crate::error::BoxDynError;
|
||||
use crate::postgres::{PgArgumentBuffer, PgTypeInfo, PgValueFormat, PgValueRef, Postgres};
|
||||
use crate::types::Type;
|
||||
|
||||
/// PostgreSQL INTERVAL type binding
|
||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||
pub struct PgInterval {
|
||||
pub months: i32,
|
||||
pub days: i32,
|
||||
pub microseconds: i64,
|
||||
}
|
||||
|
||||
impl Type<Postgres> for PgInterval {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
PgTypeInfo::INTERVAL
|
||||
}
|
||||
}
|
||||
|
||||
impl Type<Postgres> for [PgInterval] {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
PgTypeInfo::INTERVAL_ARRAY
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Decode<'de, Postgres> for PgInterval {
|
||||
fn decode(value: PgValueRef<'de>) -> Result<Self, BoxDynError> {
|
||||
match value.format() {
|
||||
PgValueFormat::Binary => {
|
||||
let mut buf = value.as_bytes()?;
|
||||
let microseconds = buf.read_i64::<NetworkEndian>()?;
|
||||
let days = buf.read_i32::<NetworkEndian>()?;
|
||||
let months = buf.read_i32::<NetworkEndian>()?;
|
||||
Ok(PgInterval {
|
||||
months,
|
||||
days,
|
||||
microseconds,
|
||||
})
|
||||
}
|
||||
PgValueFormat::Text => Err("INTERVAL Text format unsuported".into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode<'_, Postgres> for PgInterval {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
if let IsNull::Yes = Encode::<Postgres>::encode(&self.microseconds, buf) {
|
||||
return IsNull::Yes;
|
||||
}
|
||||
if let IsNull::Yes = Encode::<Postgres>::encode(&self.days, buf) {
|
||||
return IsNull::Yes;
|
||||
}
|
||||
if let IsNull::Yes = Encode::<Postgres>::encode(&self.months, buf) {
|
||||
return IsNull::Yes;
|
||||
}
|
||||
IsNull::No
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> usize {
|
||||
2 * mem::size_of::<i64>()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encode_interval() {
|
||||
let mut buf = PgArgumentBuffer::default();
|
||||
|
||||
let interval = PgInterval {
|
||||
months: 0,
|
||||
days: 0,
|
||||
microseconds: 0,
|
||||
};
|
||||
assert!(matches!(
|
||||
Encode::<Postgres>::encode(&interval, &mut buf),
|
||||
IsNull::No
|
||||
));
|
||||
assert_eq!(&**buf, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
buf.clear();
|
||||
|
||||
let interval = PgInterval {
|
||||
months: 0,
|
||||
days: 0,
|
||||
microseconds: 1_000,
|
||||
};
|
||||
assert!(matches!(
|
||||
Encode::<Postgres>::encode(&interval, &mut buf),
|
||||
IsNull::No
|
||||
));
|
||||
assert_eq!(&**buf, [0, 0, 0, 0, 0, 0, 3, 232, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
buf.clear();
|
||||
|
||||
let interval = PgInterval {
|
||||
months: 0,
|
||||
days: 0,
|
||||
microseconds: 1_000_000,
|
||||
};
|
||||
assert!(matches!(
|
||||
Encode::<Postgres>::encode(&interval, &mut buf),
|
||||
IsNull::No
|
||||
));
|
||||
assert_eq!(&**buf, [0, 0, 0, 0, 0, 15, 66, 64, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
buf.clear();
|
||||
|
||||
let interval = PgInterval {
|
||||
months: 0,
|
||||
days: 0,
|
||||
microseconds: 3_600_000_000,
|
||||
};
|
||||
assert!(matches!(
|
||||
Encode::<Postgres>::encode(&interval, &mut buf),
|
||||
IsNull::No
|
||||
));
|
||||
assert_eq!(
|
||||
&**buf,
|
||||
[0, 0, 0, 0, 214, 147, 164, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||
);
|
||||
buf.clear();
|
||||
|
||||
let interval = PgInterval {
|
||||
months: 0,
|
||||
days: 1,
|
||||
microseconds: 0,
|
||||
};
|
||||
assert!(matches!(
|
||||
Encode::<Postgres>::encode(&interval, &mut buf),
|
||||
IsNull::No
|
||||
));
|
||||
assert_eq!(&**buf, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]);
|
||||
buf.clear();
|
||||
|
||||
let interval = PgInterval {
|
||||
months: 1,
|
||||
days: 0,
|
||||
microseconds: 0,
|
||||
};
|
||||
assert!(matches!(
|
||||
Encode::<Postgres>::encode(&interval, &mut buf),
|
||||
IsNull::No
|
||||
));
|
||||
assert_eq!(&**buf, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
|
||||
buf.clear();
|
||||
}
|
|
@ -12,6 +12,7 @@
|
|||
//! | `f64` | DOUBLE PRECISION, FLOAT8 |
|
||||
//! | `&str`, `String` | VARCHAR, CHAR(N), TEXT, NAME |
|
||||
//! | `&[u8]`, `Vec<u8>` | BYTEA |
|
||||
//! | `sqlx::postgres::types::PgInterval` | INTERVAL |
|
||||
//!
|
||||
//! ### [`chrono`](https://crates.io/crates/chrono)
|
||||
//!
|
||||
|
@ -137,6 +138,7 @@ mod bool;
|
|||
mod bytes;
|
||||
mod float;
|
||||
mod int;
|
||||
mod interval;
|
||||
mod range;
|
||||
mod record;
|
||||
mod str;
|
||||
|
@ -163,6 +165,7 @@ mod json;
|
|||
#[cfg(feature = "ipnetwork")]
|
||||
mod ipnetwork;
|
||||
|
||||
pub use interval::PgInterval;
|
||||
pub use range::PgRange;
|
||||
|
||||
// used in derive(Type) for `struct`
|
||||
|
|
|
@ -41,6 +41,8 @@ impl_database_ext! {
|
|||
#[cfg(feature = "time")]
|
||||
sqlx::types::time::OffsetDateTime,
|
||||
|
||||
sqlx::postgres::types::PgInterval,
|
||||
|
||||
#[cfg(feature = "bigdecimal")]
|
||||
sqlx::types::BigDecimal,
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ extern crate time_ as time;
|
|||
|
||||
use std::ops::Bound;
|
||||
|
||||
use sqlx::postgres::types::PgRange;
|
||||
use sqlx::postgres::types::{PgInterval, PgRange};
|
||||
use sqlx::postgres::Postgres;
|
||||
use sqlx_test::{test_decode_type, test_prepared_type, test_type};
|
||||
|
||||
|
@ -360,3 +360,31 @@ test_type!(int4range<PgRange<i32>>(Postgres,
|
|||
"'[1,2)'::int4range" == PgRange::from((INC1, EXC2)),
|
||||
"'[1,2]'::int4range" == PgRange::from((INC1, EXC3)),
|
||||
));
|
||||
|
||||
test_prepared_type!(interval<PgInterval>(
|
||||
Postgres,
|
||||
"INTERVAL '1h'"
|
||||
== PgInterval {
|
||||
months: 0,
|
||||
days: 0,
|
||||
microseconds: 3_600_000_000
|
||||
},
|
||||
"INTERVAL '-1 hours'"
|
||||
== PgInterval {
|
||||
months: 0,
|
||||
days: 0,
|
||||
microseconds: -3_600_000_000
|
||||
},
|
||||
"INTERVAL '3 months 12 days 1h 15 minutes 10 second '"
|
||||
== PgInterval {
|
||||
months: 3,
|
||||
days: 12,
|
||||
microseconds: (3_600 + 15 * 60 + 10) * 1_000_000
|
||||
},
|
||||
"INTERVAL '03:10:20.116100'"
|
||||
== PgInterval {
|
||||
months: 0,
|
||||
days: 0,
|
||||
microseconds: (3 * 3_600 + 10 * 60 + 20) * 1_000_000 + 116100
|
||||
},
|
||||
));
|
||||
|
|
Loading…
Reference in a new issue