mirror of
https://github.com/launchbadge/sqlx
synced 2024-11-10 06:24:16 +00:00
postgres: implement text mode for chrono and clean up type tests
This commit is contained in:
parent
7fbc26de05
commit
f337f1c602
7 changed files with 136 additions and 125 deletions
|
@ -86,10 +86,6 @@ required-features = [ "postgres" ]
|
|||
name = "postgres-types"
|
||||
required-features = [ "postgres" ]
|
||||
|
||||
[[test]]
|
||||
name = "postgres-types-chrono"
|
||||
required-features = [ "postgres", "chrono" ]
|
||||
|
||||
[[test]]
|
||||
name = "mysql-types"
|
||||
required-features = [ "mysql" ]
|
||||
|
|
|
@ -21,7 +21,7 @@ fn test_ssl_request() {
|
|||
use crate::io::Buf;
|
||||
|
||||
let mut buf = Vec::new();
|
||||
SslRequest::encode(&mut buf);
|
||||
SslRequest.encode(&mut buf);
|
||||
|
||||
assert_eq!(&buf, b"\x00\x00\x00\x08\x04\xd2\x16/");
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use std::convert::TryInto;
|
||||
use std::mem;
|
||||
|
||||
use byteorder::{NetworkEndian, ReadBytesExt};
|
||||
use chrono::{DateTime, Duration, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc};
|
||||
|
||||
use crate::decode::Decode;
|
||||
|
@ -10,6 +11,7 @@ use crate::postgres::row::PgValue;
|
|||
use crate::postgres::types::PgTypeInfo;
|
||||
use crate::postgres::Postgres;
|
||||
use crate::types::Type;
|
||||
use crate::Error;
|
||||
|
||||
impl Type<Postgres> for NaiveTime {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
|
@ -67,9 +69,15 @@ where
|
|||
|
||||
impl<'de> Decode<'de, Postgres> for NaiveTime {
|
||||
fn decode(value: Option<PgValue<'de>>) -> crate::Result<Self> {
|
||||
let micros: i64 = Decode::<Postgres>::decode(value)?;
|
||||
match value.try_into()? {
|
||||
PgValue::Binary(mut buf) => {
|
||||
let micros = buf.read_i64::<NetworkEndian>().map_err(Error::decode)?;
|
||||
|
||||
Ok(NaiveTime::from_hms(0, 0, 0) + Duration::microseconds(micros))
|
||||
Ok(NaiveTime::from_hms(0, 0, 0) + Duration::microseconds(micros))
|
||||
}
|
||||
|
||||
PgValue::Text(s) => NaiveTime::parse_from_str(s, "%H:%M:%S%.f").map_err(Error::decode),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,9 +97,15 @@ impl Encode<Postgres> for NaiveTime {
|
|||
|
||||
impl<'de> Decode<'de, Postgres> for NaiveDate {
|
||||
fn decode(value: Option<PgValue<'de>>) -> crate::Result<Self> {
|
||||
let days: i32 = Decode::<Postgres>::decode(value)?;
|
||||
match value.try_into()? {
|
||||
PgValue::Binary(mut buf) => {
|
||||
let days: i32 = buf.read_i32::<NetworkEndian>().map_err(Error::decode)?;
|
||||
|
||||
Ok(NaiveDate::from_ymd(2000, 1, 1) + Duration::days(days as i64))
|
||||
Ok(NaiveDate::from_ymd(2000, 1, 1) + Duration::days(days as i64))
|
||||
}
|
||||
|
||||
PgValue::Text(s) => NaiveDate::parse_from_str(s, "%Y-%m-%d").map_err(Error::decode),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -114,20 +128,39 @@ impl Encode<Postgres> for NaiveDate {
|
|||
|
||||
impl<'de> Decode<'de, Postgres> for NaiveDateTime {
|
||||
fn decode(value: Option<PgValue<'de>>) -> crate::Result<Self> {
|
||||
let micros: i64 = Decode::<Postgres>::decode(value)?;
|
||||
match value.try_into()? {
|
||||
PgValue::Binary(mut buf) => {
|
||||
let micros = buf.read_i64::<NetworkEndian>().map_err(Error::decode)?;
|
||||
|
||||
postgres_epoch()
|
||||
.naive_utc()
|
||||
.checked_add_signed(Duration::microseconds(micros))
|
||||
.ok_or_else(|| {
|
||||
crate::Error::Decode(
|
||||
format!(
|
||||
"Postgres timestamp out of range for NaiveDateTime: {:?}",
|
||||
micros
|
||||
)
|
||||
.into(),
|
||||
postgres_epoch()
|
||||
.naive_utc()
|
||||
.checked_add_signed(Duration::microseconds(micros))
|
||||
.ok_or_else(|| {
|
||||
crate::Error::Decode(
|
||||
format!(
|
||||
"Postgres timestamp out of range for NaiveDateTime: {:?}",
|
||||
micros
|
||||
)
|
||||
.into(),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
PgValue::Text(s) => {
|
||||
NaiveDateTime::parse_from_str(
|
||||
s,
|
||||
if s.contains('+') {
|
||||
// Contains a time-zone specifier
|
||||
// This is given for timestamptz for some reason
|
||||
// Postgres already guarantees this to always be UTC
|
||||
"%Y-%m-%d %H:%M:%S%.f%#z"
|
||||
} else {
|
||||
"%Y-%m-%d %H:%M:%S%.f"
|
||||
},
|
||||
)
|
||||
})
|
||||
.map_err(Error::decode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -205,15 +238,15 @@ fn test_encode_datetime() {
|
|||
#[test]
|
||||
fn test_decode_datetime() {
|
||||
let buf = [0u8; 8];
|
||||
let date: NaiveDateTime = Decode::<Postgres>::decode(&buf).unwrap();
|
||||
let date: NaiveDateTime = Decode::<Postgres>::decode(Some(PgValue::Binary(&buf))).unwrap();
|
||||
assert_eq!(date.to_string(), "2000-01-01 00:00:00");
|
||||
|
||||
let buf = 3_600_000_000i64.to_be_bytes();
|
||||
let date: NaiveDateTime = Decode::<Postgres>::decode(&buf).unwrap();
|
||||
let date: NaiveDateTime = Decode::<Postgres>::decode(Some(PgValue::Binary(&buf))).unwrap();
|
||||
assert_eq!(date.to_string(), "2000-01-01 01:00:00");
|
||||
|
||||
let buf = 629_377_265_000_000i64.to_be_bytes();
|
||||
let date: NaiveDateTime = Decode::<Postgres>::decode(&buf).unwrap();
|
||||
let date: NaiveDateTime = Decode::<Postgres>::decode(Some(PgValue::Binary(&buf))).unwrap();
|
||||
assert_eq!(date.to_string(), "2019-12-11 11:01:05");
|
||||
}
|
||||
|
||||
|
@ -241,14 +274,14 @@ fn test_encode_date() {
|
|||
#[test]
|
||||
fn test_decode_date() {
|
||||
let buf = [0; 4];
|
||||
let date: NaiveDate = Decode::<Postgres>::decode(&buf).unwrap();
|
||||
let date: NaiveDate = Decode::<Postgres>::decode(Some(PgValue::Binary(&buf))).unwrap();
|
||||
assert_eq!(date.to_string(), "2000-01-01");
|
||||
|
||||
let buf = 366i32.to_be_bytes();
|
||||
let date: NaiveDate = Decode::<Postgres>::decode(&buf).unwrap();
|
||||
let date: NaiveDate = Decode::<Postgres>::decode(Some(PgValue::Binary(&buf))).unwrap();
|
||||
assert_eq!(date.to_string(), "2001-01-01");
|
||||
|
||||
let buf = 7284i32.to_be_bytes();
|
||||
let date: NaiveDate = Decode::<Postgres>::decode(&buf).unwrap();
|
||||
let date: NaiveDate = Decode::<Postgres>::decode(Some(PgValue::Binary(&buf))).unwrap();
|
||||
assert_eq!(date.to_string(), "2019-12-11");
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ where
|
|||
macro_rules! test_type {
|
||||
($name:ident($db:ident, $ty:ty, $($text:literal == $value:expr),+)) => {
|
||||
$crate::test_prepared_type!($name($db, $ty, $($text == $value),+));
|
||||
// $crate::test_unprepared_type!($name($db, $ty, $($text == $value),+));
|
||||
$crate::test_unprepared_type!($name($db, $ty, $($text == $value),+));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,8 @@ macro_rules! test_unprepared_type {
|
|||
#[cfg_attr(feature = "runtime-async-std", async_std::test)]
|
||||
#[cfg_attr(feature = "runtime-tokio", tokio::test)]
|
||||
async fn [< test_unprepared_type_ $name >] () -> anyhow::Result<()> {
|
||||
use sqlx::prelude::*;
|
||||
|
||||
let mut conn = sqlx_test::new::<$db>().await?;
|
||||
|
||||
$(
|
||||
|
|
|
@ -59,6 +59,13 @@ pub mod decode {
|
|||
}
|
||||
|
||||
pub mod prelude {
|
||||
pub use super::Connect as _;
|
||||
pub use super::Connection as _;
|
||||
pub use super::Cursor as _;
|
||||
pub use super::Executor as _;
|
||||
pub use super::FromRow as _;
|
||||
pub use super::Row as _;
|
||||
|
||||
#[cfg(feature = "postgres")]
|
||||
pub use super::postgres::PgQueryAs as _;
|
||||
}
|
||||
|
|
|
@ -1,85 +0,0 @@
|
|||
use sqlx::types::chrono::{DateTime, NaiveDate, NaiveTime, Utc};
|
||||
use sqlx::{Connection, PgConnection, Row};
|
||||
|
||||
async fn connect() -> anyhow::Result<PgConnection> {
|
||||
Ok(PgConnection::open(dotenv::var("DATABASE_URL")?).await?)
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "runtime-async-std", async_std::test)]
|
||||
#[cfg_attr(feature = "runtime-tokio", tokio::test)]
|
||||
async fn postgres_chrono_date() -> anyhow::Result<()> {
|
||||
let mut conn = connect().await?;
|
||||
|
||||
let value = NaiveDate::from_ymd(2019, 1, 2);
|
||||
|
||||
let row = sqlx::query("SELECT DATE '2019-01-02' = $1, $1")
|
||||
.bind(&value)
|
||||
.fetch_one(&mut conn)
|
||||
.await?;
|
||||
|
||||
assert!(row.get::<bool, _>(0));
|
||||
assert_eq!(value, row.get(1));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "runtime-async-std", async_std::test)]
|
||||
#[cfg_attr(feature = "runtime-tokio", tokio::test)]
|
||||
async fn postgres_chrono_date_time() -> anyhow::Result<()> {
|
||||
let mut conn = connect().await?;
|
||||
|
||||
let value = NaiveDate::from_ymd(2019, 1, 2).and_hms(5, 10, 20);
|
||||
|
||||
let row = sqlx::query("SELECT '2019-01-02 05:10:20' = $1, $1")
|
||||
.bind(&value)
|
||||
.fetch_one(&mut conn)
|
||||
.await?;
|
||||
|
||||
assert!(row.get::<bool, _>(0));
|
||||
assert_eq!(value, row.get(1));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "runtime-async-std", async_std::test)]
|
||||
#[cfg_attr(feature = "runtime-tokio", tokio::test)]
|
||||
async fn postgres_chrono_time() -> anyhow::Result<()> {
|
||||
let mut conn = connect().await?;
|
||||
|
||||
let value = NaiveTime::from_hms_micro(5, 10, 20, 115100);
|
||||
|
||||
let row = sqlx::query("SELECT TIME '05:10:20.115100' = $1, TIME '05:10:20.115100'")
|
||||
.bind(&value)
|
||||
.fetch_one(&mut conn)
|
||||
.await?;
|
||||
|
||||
assert!(row.get::<bool, _>(0));
|
||||
assert_eq!(value, row.get(1));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "runtime-async-std", async_std::test)]
|
||||
#[cfg_attr(feature = "runtime-tokio", tokio::test)]
|
||||
async fn postgres_chrono_timestamp_tz() -> anyhow::Result<()> {
|
||||
let mut conn = connect().await?;
|
||||
|
||||
let value = DateTime::<Utc>::from_utc(
|
||||
NaiveDate::from_ymd(2019, 1, 2).and_hms_micro(5, 10, 20, 115100),
|
||||
Utc,
|
||||
);
|
||||
|
||||
let row = sqlx::query(
|
||||
"SELECT TIMESTAMPTZ '2019-01-02 05:10:20.115100' = $1, TIMESTAMPTZ '2019-01-02 05:10:20.115100'",
|
||||
)
|
||||
.bind(&value)
|
||||
.fetch_one(&mut conn)
|
||||
.await?;
|
||||
|
||||
assert!(row.get::<bool, _>(0));
|
||||
|
||||
let out: DateTime<Utc> = row.get(1);
|
||||
assert_eq!(value, out);
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -26,25 +26,83 @@ test_type!(string(
|
|||
"''" == ""
|
||||
));
|
||||
|
||||
// TODO: BYTEA
|
||||
// TODO: UUID
|
||||
// TODO: CHRONO
|
||||
test_type!(bytea(
|
||||
Postgres,
|
||||
Vec<u8>,
|
||||
"E'\\\\xDEADBEEF'::bytea"
|
||||
== vec![0xDE_u8, 0xAD, 0xBE, 0xEF],
|
||||
"E'\\\\x'::bytea"
|
||||
== Vec::<u8>::new(),
|
||||
"E'\\\\x0000000052'::bytea"
|
||||
== vec![0_u8, 0, 0, 0, 0x52]
|
||||
));
|
||||
|
||||
#[cfg(feature = "uuid")]
|
||||
test_type!(uuid(
|
||||
Postgres,
|
||||
sqlx::types::Uuid,
|
||||
"'b731678f-636f-4135-bc6f-19440c13bd19'::uuid"
|
||||
== sqlx::types::Uuid::parse_str("b731678f-636f-4135-bc6f-19440c13bd19").unwrap(),
|
||||
"'00000000-0000-0000-0000-000000000000'::uuid"
|
||||
== sqlx::types::Uuid::parse_str("00000000-0000-0000-0000-000000000000").unwrap()
|
||||
));
|
||||
|
||||
#[cfg(feature = "chrono")]
|
||||
mod chrono {
|
||||
use super::*;
|
||||
use sqlx::types::chrono::{DateTime, NaiveDate, NaiveDateTime, NaiveTime, Utc};
|
||||
|
||||
test_type!(chrono_date(
|
||||
Postgres,
|
||||
NaiveDate,
|
||||
"DATE '2001-01-05'" == NaiveDate::from_ymd(2001, 1, 5),
|
||||
"DATE '2050-11-23'" == NaiveDate::from_ymd(2050, 11, 23)
|
||||
));
|
||||
|
||||
test_type!(chrono_time(
|
||||
Postgres,
|
||||
NaiveTime,
|
||||
"TIME '05:10:20.115100'" == NaiveTime::from_hms_micro(5, 10, 20, 115100)
|
||||
));
|
||||
|
||||
test_type!(chrono_date_time(
|
||||
Postgres,
|
||||
NaiveDateTime,
|
||||
"'2019-01-02 05:10:20'" == NaiveDate::from_ymd(2019, 1, 2).and_hms(5, 10, 20)
|
||||
));
|
||||
|
||||
test_type!(chrono_date_time_tz(
|
||||
Postgres,
|
||||
DateTime::<Utc>,
|
||||
"TIMESTAMPTZ '2019-01-02 05:10:20.115100'"
|
||||
== DateTime::<Utc>::from_utc(
|
||||
NaiveDate::from_ymd(2019, 1, 2).and_hms_micro(5, 10, 20, 115100),
|
||||
Utc,
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
// #[cfg_attr(feature = "runtime-async-std", async_std::test)]
|
||||
// #[cfg_attr(feature = "runtime-tokio", tokio::test)]
|
||||
// async fn postgres_bytes() -> anyhow::Result<()> {
|
||||
// async fn postgres_chrono_timestamp_tz() -> anyhow::Result<()> {
|
||||
// let mut conn = connect().await?;
|
||||
//
|
||||
// let value = b"Hello, World";
|
||||
// let value = DateTime::<Utc>::from_utc(
|
||||
// NaiveDate::from_ymd(2019, 1, 2).and_hms_micro(5, 10, 20, 115100),
|
||||
// Utc,
|
||||
// );
|
||||
//
|
||||
// let rec: (bool, Vec<u8>) = sqlx::query("SELECT E'\\\\x48656c6c6f2c20576f726c64' = $1, $1")
|
||||
// .bind(&value[..])
|
||||
// .map(|row: PgRow| Ok((row.get(0)?, row.get(1)?)))
|
||||
// .fetch_one(&mut conn)
|
||||
// .await?;
|
||||
// let row = sqlx::query(
|
||||
// "SELECT TIMESTAMPTZ '2019-01-02 05:10:20.115100' = $1, TIMESTAMPTZ '2019-01-02 05:10:20.115100'",
|
||||
// )
|
||||
// .bind(&value)
|
||||
// .fetch_one(&mut conn)
|
||||
// .await?;
|
||||
//
|
||||
// assert!(rec.0);
|
||||
// assert_eq!(&value[..], &*rec.1);
|
||||
// assert!(row.get::<bool, _>(0));
|
||||
//
|
||||
// let out: DateTime<Utc> = row.get(1);
|
||||
// assert_eq!(value, out);
|
||||
//
|
||||
// Ok(())
|
||||
// }
|
||||
|
|
Loading…
Reference in a new issue