mirror of
https://github.com/launchbadge/sqlx.git
synced 2025-09-28 21:42:06 +00:00
clean up Time impl, impl for text modes
This commit is contained in:
parent
4a98a51a19
commit
db543f8391
131
Cargo.lock
generated
131
Cargo.lock
generated
@ -162,6 +162,12 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base-x"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b20b618342cf9891c292c4f5ac2cde7287cc5c87e87e9c769d617793607dec1"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.10.1"
|
||||
@ -246,6 +252,12 @@ dependencies = [
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f359dc14ff8911330a51ef78022d376f25ed00248912803b58f00cb1c27f742"
|
||||
|
||||
[[package]]
|
||||
name = "byte-tools"
|
||||
version = "0.3.1"
|
||||
@ -436,6 +448,12 @@ dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "discard"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0"
|
||||
|
||||
[[package]]
|
||||
name = "dotenv"
|
||||
version = "0.15.0"
|
||||
@ -1586,6 +1604,12 @@ dependencies = [
|
||||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d"
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.8.1"
|
||||
@ -1672,6 +1696,7 @@ dependencies = [
|
||||
"sqlx-core 0.3.0-alpha.1",
|
||||
"sqlx-macros 0.3.0-alpha.1",
|
||||
"sqlx-test",
|
||||
"time 0.2.9",
|
||||
"tokio 0.2.13",
|
||||
"trybuild",
|
||||
]
|
||||
@ -1821,6 +1846,55 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4edf667ea8f60afc06d6aeec079d20d5800351109addec1faea678a8663da4e1"
|
||||
|
||||
[[package]]
|
||||
name = "stdweb"
|
||||
version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5"
|
||||
dependencies = [
|
||||
"discard",
|
||||
"rustc_version",
|
||||
"stdweb-derive",
|
||||
"stdweb-internal-macros",
|
||||
"stdweb-internal-runtime",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stdweb-derive"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stdweb-internal-macros"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11"
|
||||
dependencies = [
|
||||
"base-x",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"sha1",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stdweb-internal-runtime"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0"
|
||||
|
||||
[[package]]
|
||||
name = "string"
|
||||
version = "0.2.1"
|
||||
@ -1987,9 +2061,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6329a7835505d46f5f3a9a2c237f8d6bf5ca6f0015decb3698ba57fcdbb609ba"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"rustversion",
|
||||
"standback",
|
||||
"stdweb",
|
||||
"time-macros",
|
||||
"winapi 0.3.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2322,6 +2399,60 @@ version = "0.9.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.59"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3557c397ab5a8e347d434782bcd31fc1483d927a6826804cec05cc792ee2519d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.59"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0da9c9a19850d3af6df1cb9574970b566d617ecfaf36eb0b706b6f3ef9bd2f8"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.59"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f6fde1d36e75a714b5fe0cffbb78978f222ea6baebb726af13c78869fdb4205"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.59"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25bda4168030a6412ea8a047e27238cadf56f0e53516e1e83fec0a8b7c786f6d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.59"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc9f36ad51f25b0219a3d4d13b90eb44cd075dff8b6280cca015775d7acaddd8"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.2.8"
|
||||
|
@ -59,6 +59,7 @@ sqlx-macros = { version = "0.3.0-alpha.1", path = "sqlx-macros", default-feature
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = "1.0.26"
|
||||
time_ = { version = "0.2.9", package = "time" }
|
||||
futures = "0.3.4"
|
||||
env_logger = "0.7.1"
|
||||
async-std = { version = "1.5.0", features = [ "attributes" ] }
|
||||
|
@ -1,17 +1,19 @@
|
||||
use std::borrow::Cow;
|
||||
use std::convert::TryFrom;
|
||||
use std::convert::TryInto;
|
||||
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use time::{Date, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset};
|
||||
|
||||
use crate::decode::{Decode, DecodeError};
|
||||
use crate::decode::Decode;
|
||||
use crate::encode::Encode;
|
||||
use crate::io::{Buf, BufMut};
|
||||
use crate::mysql::protocol::TypeId;
|
||||
use crate::mysql::types::MySqlTypeInfo;
|
||||
use crate::mysql::MySql;
|
||||
use crate::types::HasSqlType;
|
||||
use crate::mysql::{MySql, MySqlValue};
|
||||
use crate::types::Type;
|
||||
|
||||
impl HasSqlType<OffsetDateTime> for MySql {
|
||||
impl Type<MySql> for OffsetDateTime {
|
||||
fn type_info() -> MySqlTypeInfo {
|
||||
MySqlTypeInfo::new(TypeId::TIMESTAMP)
|
||||
}
|
||||
@ -26,15 +28,15 @@ impl Encode<MySql> for OffsetDateTime {
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode<MySql> for OffsetDateTime {
|
||||
fn decode(buf: &[u8]) -> Result<Self, DecodeError> {
|
||||
let primitive: PrimitiveDateTime = Decode::<MySql>::decode(buf)?;
|
||||
impl<'de> Decode<'de, MySql> for OffsetDateTime {
|
||||
fn decode(value: Option<MySqlValue<'de>>) -> crate::Result<MySql, Self> {
|
||||
let primitive: PrimitiveDateTime = Decode::<MySql>::decode(value)?;
|
||||
|
||||
Ok(primitive.assume_utc())
|
||||
}
|
||||
}
|
||||
|
||||
impl HasSqlType<Time> for MySql {
|
||||
impl Type<MySql> for Time {
|
||||
fn type_info() -> MySqlTypeInfo {
|
||||
MySqlTypeInfo::new(TypeId::TIME)
|
||||
}
|
||||
@ -66,24 +68,44 @@ impl Encode<MySql> for Time {
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode<MySql> for Time {
|
||||
fn decode(mut buf: &[u8]) -> Result<Self, DecodeError> {
|
||||
// data length, expecting 8 or 12 (fractional seconds)
|
||||
let len = buf.get_u8()?;
|
||||
impl<'de> Decode<'de, MySql> for Time {
|
||||
fn decode(value: Option<MySqlValue<'de>>) -> crate::Result<MySql, Self> {
|
||||
match value.try_into()? {
|
||||
MySqlValue::Binary(mut buf) => {
|
||||
// data length, expecting 8 or 12 (fractional seconds)
|
||||
let len = buf.get_u8()?;
|
||||
|
||||
// is negative : int<1>
|
||||
let is_negative = buf.get_u8()?;
|
||||
assert_eq!(is_negative, 0, "Negative dates/times are not supported");
|
||||
// is negative : int<1>
|
||||
let is_negative = buf.get_u8()?;
|
||||
assert_eq!(is_negative, 0, "Negative dates/times are not supported");
|
||||
|
||||
// "date on 4 bytes little-endian format" (?)
|
||||
// https://mariadb.com/kb/en/resultset-row/#timestamp-binary-encoding
|
||||
buf.advance(4);
|
||||
// "date on 4 bytes little-endian format" (?)
|
||||
// https://mariadb.com/kb/en/resultset-row/#timestamp-binary-encoding
|
||||
buf.advance(4);
|
||||
|
||||
decode_time(len - 5, buf)
|
||||
decode_time(len - 5, buf)
|
||||
}
|
||||
|
||||
MySqlValue::Text(buf) => {
|
||||
let s = from_utf8(buf).map_err(crate::Error::decode)?;
|
||||
|
||||
// If there are less than 9 digits after the decimal point
|
||||
// We need to zero-pad
|
||||
// TODO: Ask [time] to add a parse % for less-than-fixed-9 nanos
|
||||
|
||||
let s = if s.len() < 20 {
|
||||
Cow::Owned(format!("{:0<19}", s))
|
||||
} else {
|
||||
Cow::Borrowed(s)
|
||||
};
|
||||
|
||||
Time::parse(&*s, "%H:%M:%S.%N").map_err(crate::Error::decode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HasSqlType<Date> for MySql {
|
||||
impl Type<MySql> for Date {
|
||||
fn type_info() -> MySqlTypeInfo {
|
||||
MySqlTypeInfo::new(TypeId::DATE)
|
||||
}
|
||||
@ -101,13 +123,19 @@ impl Encode<MySql> for Date {
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode<MySql> for Date {
|
||||
fn decode(buf: &[u8]) -> Result<Self, DecodeError> {
|
||||
decode_date(&buf[1..])
|
||||
impl<'de> Decode<'de, MySql> for Date {
|
||||
fn decode(value: Option<MySqlValue<'de>>) -> crate::Result<MySql, Self> {
|
||||
match value.try_into()? {
|
||||
MySqlValue::Binary(buf) => decode_date(&buf[1..]),
|
||||
MySqlValue::Text(buf) => {
|
||||
let s = from_utf8(buf).map_err(crate::Error::decode)?;
|
||||
Date::parse(s, "%Y-%m-%d").map_err(crate::Error::decode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HasSqlType<PrimitiveDateTime> for MySql {
|
||||
impl Type<MySql> for PrimitiveDateTime {
|
||||
fn type_info() -> MySqlTypeInfo {
|
||||
MySqlTypeInfo::new(TypeId::DATETIME)
|
||||
}
|
||||
@ -142,18 +170,42 @@ impl Encode<MySql> for PrimitiveDateTime {
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode<MySql> for PrimitiveDateTime {
|
||||
fn decode(buf: &[u8]) -> Result<Self, DecodeError> {
|
||||
let len = buf[0];
|
||||
let date = decode_date(&buf[1..])?;
|
||||
impl<'de> Decode<'de, MySql> for PrimitiveDateTime {
|
||||
fn decode(value: Option<MySqlValue<'de>>) -> crate::Result<MySql, Self> {
|
||||
match value.try_into()? {
|
||||
MySqlValue::Binary(buf) => {
|
||||
let len = buf[0];
|
||||
let date = decode_date(&buf[1..])?;
|
||||
|
||||
let dt = if len > 4 {
|
||||
date.with_time(decode_time(len - 4, &buf[5..])?)
|
||||
} else {
|
||||
date.midnight()
|
||||
};
|
||||
let dt = if len > 4 {
|
||||
date.with_time(decode_time(len - 4, &buf[5..])?)
|
||||
} else {
|
||||
date.midnight()
|
||||
};
|
||||
|
||||
Ok(dt)
|
||||
Ok(dt)
|
||||
}
|
||||
|
||||
MySqlValue::Text(buf) => {
|
||||
let s = from_utf8(buf).map_err(crate::Error::decode)?;
|
||||
|
||||
// If there are less than 9 digits after the decimal point
|
||||
// We need to zero-pad
|
||||
// TODO: Ask [time] to add a parse % for less-than-fixed-9 nanos
|
||||
|
||||
let s = if s.len() < 31 {
|
||||
if s.contains('.') {
|
||||
Cow::Owned(format!("{:0<30}", s))
|
||||
} else {
|
||||
Cow::Owned(format!("{}.000000000", s))
|
||||
}
|
||||
} else {
|
||||
Cow::Borrowed(s)
|
||||
};
|
||||
|
||||
PrimitiveDateTime::parse(&*s, "%Y-%m-%d %H:%M:%S.%N").map_err(crate::Error::decode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,13 +219,13 @@ fn encode_date(date: &Date, buf: &mut Vec<u8>) {
|
||||
buf.push(date.day());
|
||||
}
|
||||
|
||||
fn decode_date(buf: &[u8]) -> Result<Date, DecodeError> {
|
||||
fn decode_date(buf: &[u8]) -> crate::Result<MySql, Date> {
|
||||
Date::try_from_ymd(
|
||||
LittleEndian::read_u16(buf) as i32,
|
||||
buf[2] as u8,
|
||||
buf[3] as u8,
|
||||
)
|
||||
.map_err(|e| DecodeError::Message(Box::new(format!("Error while decoding Date: {}", e))))
|
||||
.map_err(|e| decode_err!("Error while decoding Date: {}", e))
|
||||
}
|
||||
|
||||
fn encode_time(time: &Time, include_micros: bool, buf: &mut Vec<u8>) {
|
||||
@ -186,7 +238,7 @@ fn encode_time(time: &Time, include_micros: bool, buf: &mut Vec<u8>) {
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_time(len: u8, mut buf: &[u8]) -> Result<Time, DecodeError> {
|
||||
fn decode_time(len: u8, mut buf: &[u8]) -> crate::Result<MySql, Time> {
|
||||
let hour = buf.get_u8()?;
|
||||
let minute = buf.get_u8()?;
|
||||
let seconds = buf.get_u8()?;
|
||||
@ -199,9 +251,10 @@ fn decode_time(len: u8, mut buf: &[u8]) -> Result<Time, DecodeError> {
|
||||
};
|
||||
|
||||
Time::try_from_hms_micro(hour, minute, seconds, micros as u32)
|
||||
.map_err(|e| DecodeError::Message(Box::new(format!("Time out of range for MySQL: {}", e))))
|
||||
.map_err(|e| decode_err!("Time out of range for MySQL: {}", e))
|
||||
}
|
||||
|
||||
use std::str::from_utf8;
|
||||
#[cfg(test)]
|
||||
use time::{date, time};
|
||||
|
||||
|
@ -48,7 +48,7 @@ impl TryFrom<u8> for Message {
|
||||
b'I' => Message::EmptyQueryResponse,
|
||||
|
||||
id => {
|
||||
return Err(protocol_err!("unknown message: {:?}", id).into());
|
||||
return Err(protocol_err!("unknown message: {:?}", id as char).into());
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -1,62 +1,89 @@
|
||||
use std::borrow::Cow;
|
||||
use std::convert::TryInto;
|
||||
use std::mem;
|
||||
|
||||
use byteorder::BigEndian;
|
||||
use time::{date, offset, Date, NumericalDuration, OffsetDateTime, PrimitiveDateTime, Time};
|
||||
|
||||
use crate::decode::{Decode, DecodeError};
|
||||
use crate::decode::Decode;
|
||||
use crate::encode::Encode;
|
||||
use crate::io::Buf;
|
||||
use crate::postgres::protocol::TypeId;
|
||||
use crate::postgres::types::PgTypeInfo;
|
||||
use crate::postgres::Postgres;
|
||||
use crate::types::HasSqlType;
|
||||
use crate::postgres::{PgValue, Postgres};
|
||||
use crate::types::Type;
|
||||
|
||||
const POSTGRES_EPOCH: PrimitiveDateTime = date!(2000 - 1 - 1).midnight();
|
||||
|
||||
impl HasSqlType<Time> for Postgres {
|
||||
impl Type<Postgres> for Time {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
PgTypeInfo::new(TypeId::TIME)
|
||||
PgTypeInfo::new(TypeId::TIME, "TIME")
|
||||
}
|
||||
}
|
||||
|
||||
impl HasSqlType<Date> for Postgres {
|
||||
impl Type<Postgres> for Date {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
PgTypeInfo::new(TypeId::DATE)
|
||||
PgTypeInfo::new(TypeId::DATE, "DATE")
|
||||
}
|
||||
}
|
||||
|
||||
impl HasSqlType<PrimitiveDateTime> for Postgres {
|
||||
impl Type<Postgres> for PrimitiveDateTime {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
PgTypeInfo::new(TypeId::TIMESTAMP)
|
||||
PgTypeInfo::new(TypeId::TIMESTAMP, "TIMESTAMP")
|
||||
}
|
||||
}
|
||||
|
||||
impl HasSqlType<OffsetDateTime> for Postgres {
|
||||
impl Type<Postgres> for OffsetDateTime {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
PgTypeInfo::new(TypeId::TIMESTAMPTZ)
|
||||
PgTypeInfo::new(TypeId::TIMESTAMPTZ, "TIMESTAMPTZ")
|
||||
}
|
||||
}
|
||||
|
||||
impl HasSqlType<[Time]> for Postgres {
|
||||
impl Type<Postgres> for [Time] {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
PgTypeInfo::new(TypeId::ARRAY_TIME)
|
||||
PgTypeInfo::new(TypeId::ARRAY_TIME, "TIME[]")
|
||||
}
|
||||
}
|
||||
|
||||
impl HasSqlType<[Date]> for Postgres {
|
||||
impl Type<Postgres> for [Date] {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
PgTypeInfo::new(TypeId::ARRAY_DATE)
|
||||
PgTypeInfo::new(TypeId::ARRAY_DATE, "DATE[]")
|
||||
}
|
||||
}
|
||||
|
||||
impl HasSqlType<[PrimitiveDateTime]> for Postgres {
|
||||
impl Type<Postgres> for [PrimitiveDateTime] {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
PgTypeInfo::new(TypeId::ARRAY_TIMESTAMP)
|
||||
PgTypeInfo::new(TypeId::ARRAY_TIMESTAMP, "TIMESTAMP[]")
|
||||
}
|
||||
}
|
||||
|
||||
impl HasSqlType<[OffsetDateTime]> for Postgres {
|
||||
impl Type<Postgres> for [OffsetDateTime] {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
PgTypeInfo::new(TypeId::ARRAY_TIMESTAMPTZ)
|
||||
PgTypeInfo::new(TypeId::ARRAY_TIMESTAMPTZ, "TIMESTAMPTZ[]")
|
||||
}
|
||||
}
|
||||
|
||||
impl Type<Postgres> for Vec<Time> {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
PgTypeInfo::new(TypeId::ARRAY_TIME, "TIME[]")
|
||||
}
|
||||
}
|
||||
|
||||
impl Type<Postgres> for Vec<Date> {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
PgTypeInfo::new(TypeId::ARRAY_DATE, "DATE[]")
|
||||
}
|
||||
}
|
||||
|
||||
impl Type<Postgres> for Vec<PrimitiveDateTime> {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
PgTypeInfo::new(TypeId::ARRAY_TIMESTAMP, "TIMESTAMP[]")
|
||||
}
|
||||
}
|
||||
|
||||
impl Type<Postgres> for Vec<OffsetDateTime> {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
PgTypeInfo::new(TypeId::ARRAY_TIMESTAMPTZ, "TIMESTAMPTZ[]")
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,7 +94,7 @@ fn microseconds_since_midnight(time: Time) -> i64 {
|
||||
+ time.microsecond() as i64
|
||||
}
|
||||
|
||||
fn from_microseconds_since_midnight(mut microsecond: u64) -> Result<Time, DecodeError> {
|
||||
fn from_microseconds_since_midnight(mut microsecond: u64) -> crate::Result<Postgres, Time> {
|
||||
#![allow(clippy::cast_possible_truncation)]
|
||||
|
||||
microsecond %= 86_400 * 1_000_000;
|
||||
@ -78,14 +105,32 @@ fn from_microseconds_since_midnight(mut microsecond: u64) -> Result<Time, Decode
|
||||
(microsecond / 1_000_000 % 60) as u8,
|
||||
(microsecond % 1_000_000) as u32,
|
||||
)
|
||||
.map_err(|e| DecodeError::Message(Box::new(format!("Time out of range for Postgres: {}", e))))
|
||||
.map_err(|e| decode_err!("Time out of range for Postgres: {}", e))
|
||||
}
|
||||
|
||||
impl Decode<Postgres> for Time {
|
||||
fn decode(raw: &[u8]) -> Result<Self, DecodeError> {
|
||||
let micros: i64 = Decode::<Postgres>::decode(raw)?;
|
||||
impl<'de> Decode<'de, Postgres> for Time {
|
||||
fn decode(value: Option<PgValue<'de>>) -> crate::Result<Postgres, Self> {
|
||||
match value.try_into()? {
|
||||
PgValue::Binary(mut buf) => {
|
||||
let micros: i64 = buf.get_i64::<BigEndian>()?;
|
||||
|
||||
from_microseconds_since_midnight(micros as u64)
|
||||
from_microseconds_since_midnight(micros as u64)
|
||||
}
|
||||
|
||||
PgValue::Text(s) => {
|
||||
// If there are less than 9 digits after the decimal point
|
||||
// We need to zero-pad
|
||||
// TODO: Ask [time] to add a parse % for less-than-fixed-9 nanos
|
||||
|
||||
let s = if s.len() < 20 {
|
||||
Cow::Owned(format!("{:0<19}", s))
|
||||
} else {
|
||||
Cow::Borrowed(s)
|
||||
};
|
||||
|
||||
Time::parse(&*s, "%H:%M:%S.%N").map_err(crate::Error::decode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,11 +146,17 @@ impl Encode<Postgres> for Time {
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode<Postgres> for Date {
|
||||
fn decode(raw: &[u8]) -> Result<Self, DecodeError> {
|
||||
let n: i32 = Decode::<Postgres>::decode(raw)?;
|
||||
impl<'de> Decode<'de, Postgres> for Date {
|
||||
fn decode(value: Option<PgValue<'de>>) -> crate::Result<Postgres, Self> {
|
||||
match value.try_into()? {
|
||||
PgValue::Binary(mut buf) => {
|
||||
let n: i32 = buf.get_i32::<BigEndian>()?;
|
||||
|
||||
Ok(date!(2000 - 1 - 1) + n.days())
|
||||
Ok(date!(2000 - 1 - 1) + n.days())
|
||||
}
|
||||
|
||||
PgValue::Text(s) => Date::parse(s, "%Y-%m-%d").map_err(crate::Error::decode),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,11 +176,44 @@ impl Encode<Postgres> for Date {
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode<Postgres> for PrimitiveDateTime {
|
||||
fn decode(raw: &[u8]) -> Result<Self, DecodeError> {
|
||||
let n: i64 = Decode::<Postgres>::decode(raw)?;
|
||||
impl<'de> Decode<'de, Postgres> for PrimitiveDateTime {
|
||||
fn decode(value: Option<PgValue<'de>>) -> crate::Result<Postgres, Self> {
|
||||
match value.try_into()? {
|
||||
PgValue::Binary(mut buf) => {
|
||||
let n: i64 = buf.get_i64::<BigEndian>()?;
|
||||
|
||||
Ok(POSTGRES_EPOCH + n.microseconds())
|
||||
Ok(POSTGRES_EPOCH + n.microseconds())
|
||||
}
|
||||
|
||||
// TODO: Try and fix duplication between here and MySQL
|
||||
PgValue::Text(s) => {
|
||||
// If there are less than 9 digits after the decimal point
|
||||
// We need to zero-pad
|
||||
// TODO: Ask [time] to add a parse % for less-than-fixed-9 nanos
|
||||
|
||||
let s = if let Some(plus) = s.rfind('+') {
|
||||
let mut big = String::from(&s[..plus]);
|
||||
|
||||
while big.len() < 31 {
|
||||
big.push('0');
|
||||
}
|
||||
|
||||
big.push_str(&s[plus..]);
|
||||
|
||||
Cow::Owned(big)
|
||||
} else if s.len() < 31 {
|
||||
if s.contains('.') {
|
||||
Cow::Owned(format!("{:0<30}", s))
|
||||
} else {
|
||||
Cow::Owned(format!("{}.000000000", s))
|
||||
}
|
||||
} else {
|
||||
Cow::Borrowed(s)
|
||||
};
|
||||
|
||||
PrimitiveDateTime::parse(&*s, "%Y-%m-%d %H:%M:%S.%N").map_err(crate::Error::decode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,10 +232,11 @@ impl Encode<Postgres> for PrimitiveDateTime {
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode<Postgres> for OffsetDateTime {
|
||||
fn decode(raw: &[u8]) -> Result<Self, DecodeError> {
|
||||
let date_time: PrimitiveDateTime = Decode::<Postgres>::decode(raw)?;
|
||||
Ok(date_time.assume_utc())
|
||||
impl<'de> Decode<'de, Postgres> for OffsetDateTime {
|
||||
fn decode(value: Option<PgValue<'de>>) -> crate::Result<Postgres, Self> {
|
||||
let primitive: PrimitiveDateTime = Decode::<Postgres>::decode(value)?;
|
||||
|
||||
Ok(primitive.assume_utc())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,88 +0,0 @@
|
||||
use sqlx::types::chrono::{DateTime, NaiveDate, NaiveTime, Utc};
|
||||
use sqlx::{mysql::MySqlConnection, Connection, Row};
|
||||
|
||||
async fn connect() -> anyhow::Result<MySqlConnection> {
|
||||
Ok(MySqlConnection::open(dotenv::var("DATABASE_URL")?).await?)
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "chrono", not(feature = "time")))]
|
||||
#[cfg_attr(feature = "runtime-async-std", async_std::test)]
|
||||
#[cfg_attr(feature = "runtime-tokio", tokio::test)]
|
||||
async fn mysql_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' = ?) as _1, CAST(? AS DATE) as _2",
|
||||
value,
|
||||
value
|
||||
)
|
||||
.fetch_one(&mut conn)
|
||||
.await?;
|
||||
|
||||
assert!(row._1 != 0);
|
||||
assert_eq!(value, row._2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "runtime-async-std", async_std::test)]
|
||||
#[cfg_attr(feature = "runtime-tokio", tokio::test)]
|
||||
async fn mysql_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' = ?, ?")
|
||||
.bind(&value)
|
||||
.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 mysql_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' = ?, 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 mysql_chrono_timestamp() -> 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 TIMESTAMP '2019-01-02 05:10:20.115100' = ?, TIMESTAMP '2019-01-02 05:10:20.115100'",
|
||||
)
|
||||
.bind(&value)
|
||||
.fetch_one(&mut conn)
|
||||
.await?;
|
||||
|
||||
assert!(row.get::<bool, _>(0));
|
||||
assert_eq!(value, row.get(1));
|
||||
|
||||
Ok(())
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
use sqlx::types::time::{Date, OffsetDateTime, Time, UtcOffset};
|
||||
use sqlx::{mysql::MySqlConnection, Connection, Row};
|
||||
|
||||
async fn connect() -> anyhow::Result<MySqlConnection> {
|
||||
Ok(MySqlConnection::open(dotenv::var("DATABASE_URL")?).await?)
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "runtime-async-std", async_std::test)]
|
||||
#[cfg_attr(feature = "runtime-tokio", tokio::test)]
|
||||
async fn mysql_timers_date() -> anyhow::Result<()> {
|
||||
let mut conn = connect().await?;
|
||||
|
||||
// TODO: maybe use macro here? but is it OK to include `time` as test dependency?
|
||||
let value = Date::try_from_ymd(2019, 1, 2).unwrap();
|
||||
|
||||
let row = sqlx::query!(
|
||||
"SELECT (DATE '2019-01-02' = ?) as _1, CAST(? AS DATE) as _2",
|
||||
value,
|
||||
value
|
||||
)
|
||||
.fetch_one(&mut conn)
|
||||
.await?;
|
||||
|
||||
assert!(row._1 != 0);
|
||||
assert_eq!(value, row._2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "runtime-async-std", async_std::test)]
|
||||
#[cfg_attr(feature = "runtime-tokio", tokio::test)]
|
||||
async fn mysql_timers_date_time() -> anyhow::Result<()> {
|
||||
let mut conn = connect().await?;
|
||||
|
||||
let value = Date::try_from_ymd(2019, 1, 2)
|
||||
.unwrap()
|
||||
.try_with_hms(5, 10, 20)
|
||||
.unwrap();
|
||||
|
||||
let row = sqlx::query("SELECT '2019-01-02 05:10:20' = ?, ?")
|
||||
.bind(&value)
|
||||
.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 mysql_timers_time() -> anyhow::Result<()> {
|
||||
let mut conn = connect().await?;
|
||||
|
||||
let value = Time::try_from_hms_micro(5, 10, 20, 115100).unwrap();
|
||||
|
||||
let row = sqlx::query("SELECT TIME '05:10:20.115100' = ?, 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 mysql_timers_timestamp() -> anyhow::Result<()> {
|
||||
let mut conn = connect().await?;
|
||||
|
||||
let value = Date::try_from_ymd(2019, 1, 2)
|
||||
.unwrap()
|
||||
.try_with_hms_micro(5, 10, 20, 115100)
|
||||
.unwrap()
|
||||
.assume_utc();
|
||||
|
||||
let row = sqlx::query(
|
||||
"SELECT TIMESTAMP '2019-01-02 05:10:20.115100' = ?, TIMESTAMP '2019-01-02 05:10:20.115100'",
|
||||
)
|
||||
.bind(&value)
|
||||
.fetch_one(&mut conn)
|
||||
.await?;
|
||||
|
||||
assert!(row.get::<bool, _>(0));
|
||||
assert_eq!(value, row.get(1));
|
||||
|
||||
Ok(())
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
extern crate time_ as time;
|
||||
|
||||
use sqlx::MySql;
|
||||
use sqlx_test::test_type;
|
||||
|
||||
@ -74,7 +76,7 @@ mod chrono {
|
||||
"'2019-01-02 05:10:20'" == NaiveDate::from_ymd(2019, 1, 2).and_hms(5, 10, 20)
|
||||
));
|
||||
|
||||
test_type!(chrono_date_time_tz(
|
||||
test_type!(chrono_timestamp(
|
||||
MySql,
|
||||
DateTime::<Utc>,
|
||||
"TIMESTAMP '2019-01-02 05:10:20.115100'"
|
||||
@ -84,3 +86,39 @@ mod chrono {
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
#[cfg(feature = "time")]
|
||||
mod time_tests {
|
||||
use super::*;
|
||||
use sqlx::types::time::{Date, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset};
|
||||
use time::{date, time};
|
||||
|
||||
test_type!(time_date(
|
||||
MySql,
|
||||
Date,
|
||||
"DATE '2001-01-05'" == date!(2001 - 1 - 5),
|
||||
"DATE '2050-11-23'" == date!(2050 - 11 - 23)
|
||||
));
|
||||
|
||||
test_type!(time_time(
|
||||
MySql,
|
||||
Time,
|
||||
"TIME '05:10:20.115100'" == time!(5:10:20.115100)
|
||||
));
|
||||
|
||||
test_type!(time_date_time(
|
||||
MySql,
|
||||
PrimitiveDateTime,
|
||||
"'2019-01-02 05:10:20'" == date!(2019 - 1 - 2).with_time(time!(5:10:20)),
|
||||
"'2019-01-02 05:10:20.115100'" == date!(2019 - 1 - 2).with_time(time!(5:10:20.115100))
|
||||
));
|
||||
|
||||
test_type!(time_timestamp(
|
||||
MySql,
|
||||
OffsetDateTime,
|
||||
"TIMESTAMP '2019-01-02 05:10:20.115100'"
|
||||
== date!(2019 - 1 - 2)
|
||||
.with_time(time!(5:10:20.115100))
|
||||
.assume_utc()
|
||||
));
|
||||
}
|
||||
|
@ -1,91 +0,0 @@
|
||||
use sqlx::types::chrono::{DateTime, NaiveDate, Utc};
|
||||
use sqlx::{Connection, PgConnection, Row};
|
||||
|
||||
#[cfg(all(feature = "chrono", not(feature = "time")))]
|
||||
use sqlx::types::chrono::NaiveTime;
|
||||
|
||||
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 mysql_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(all(feature = "chrono", not(feature = "time")))]
|
||||
#[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 AS equality, TIME '05:10:20.115100' AS time",
|
||||
value,
|
||||
)
|
||||
.fetch_one(&mut conn)
|
||||
.await?;
|
||||
|
||||
assert!(row.equality);
|
||||
assert_eq!(value, row.time);
|
||||
|
||||
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(())
|
||||
}
|
@ -1,109 +0,0 @@
|
||||
use sqlx::types::time::{Date, OffsetDateTime, Time, UtcOffset};
|
||||
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_timers_date() -> anyhow::Result<()> {
|
||||
let mut conn = connect().await?;
|
||||
|
||||
let value = Date::try_from_ymd(2019, 1, 2).unwrap();
|
||||
|
||||
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 mysql_timers_date_time() -> anyhow::Result<()> {
|
||||
let mut conn = connect().await?;
|
||||
|
||||
let value = Date::try_from_ymd(2019, 1, 2)
|
||||
.unwrap()
|
||||
.try_with_hms(5, 10, 20)
|
||||
.unwrap();
|
||||
|
||||
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_timers_time() -> anyhow::Result<()> {
|
||||
let mut conn = connect().await?;
|
||||
|
||||
let value = Time::try_from_hms_micro(5, 10, 20, 115100).unwrap();
|
||||
|
||||
let row = sqlx::query!(
|
||||
"SELECT TIME '05:10:20.115100' = $1 AS equality, TIME '05:10:20.115100' AS time",
|
||||
value
|
||||
)
|
||||
.fetch_one(&mut conn)
|
||||
.await?;
|
||||
|
||||
assert!(row.equality);
|
||||
assert_eq!(value, row.time);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "runtime-async-std", async_std::test)]
|
||||
#[cfg_attr(feature = "runtime-tokio", tokio::test)]
|
||||
async fn postgres_timers_timestamp_tz() -> anyhow::Result<()> {
|
||||
let mut conn = connect().await?;
|
||||
|
||||
let value = Date::try_from_ymd(2019, 1, 2)
|
||||
.unwrap()
|
||||
.try_with_hms_micro(5, 10, 20, 115100)
|
||||
.unwrap()
|
||||
.assume_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: OffsetDateTime = row.get(1);
|
||||
assert_eq!(value, out);
|
||||
|
||||
let value = Date::try_from_ymd(2019, 1, 2)
|
||||
.unwrap()
|
||||
.try_with_hms_micro(5, 10, 20, 115100)
|
||||
.unwrap()
|
||||
.assume_offset(UtcOffset::east_hours(3));
|
||||
|
||||
let row = sqlx::query(
|
||||
"SELECT TIMESTAMPTZ '2019-01-02 02:10:20.115100' = $1, TIMESTAMPTZ '2019-01-02 02:10:20.115100'",
|
||||
)
|
||||
.bind(&value)
|
||||
.fetch_one(&mut conn)
|
||||
.await?;
|
||||
|
||||
assert!(row.get::<bool, _>(0));
|
||||
|
||||
let out: OffsetDateTime = row.get(1);
|
||||
assert_eq!(value, out);
|
||||
|
||||
Ok(())
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
extern crate time_ as time;
|
||||
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
use sqlx::decode::Decode;
|
||||
@ -200,6 +202,43 @@ mod chrono {
|
||||
));
|
||||
}
|
||||
|
||||
#[cfg(feature = "time")]
|
||||
mod time_tests {
|
||||
use super::*;
|
||||
use sqlx::types::time::{Date, OffsetDateTime, PrimitiveDateTime, Time};
|
||||
use time::{date, time};
|
||||
|
||||
test_type!(time_date(
|
||||
Postgres,
|
||||
Date,
|
||||
"DATE '2001-01-05'" == date!(2001 - 1 - 5),
|
||||
"DATE '2050-11-23'" == date!(2050 - 11 - 23)
|
||||
));
|
||||
|
||||
test_type!(time_time(
|
||||
Postgres,
|
||||
Time,
|
||||
"TIME '05:10:20.115100'" == time!(5:10:20.115100)
|
||||
));
|
||||
|
||||
test_type!(time_date_time(
|
||||
Postgres,
|
||||
PrimitiveDateTime,
|
||||
"TIMESTAMP '2019-01-02 05:10:20'" == date!(2019 - 1 - 2).with_time(time!(5:10:20)),
|
||||
"TIMESTAMP '2019-01-02 05:10:20.115100'"
|
||||
== date!(2019 - 1 - 2).with_time(time!(5:10:20.115100))
|
||||
));
|
||||
|
||||
test_type!(time_timestamp(
|
||||
Postgres,
|
||||
OffsetDateTime,
|
||||
"TIMESTAMPTZ '2019-01-02 05:10:20.115100'"
|
||||
== date!(2019 - 1 - 2)
|
||||
.with_time(time!(5:10:20.115100))
|
||||
.assume_utc()
|
||||
));
|
||||
}
|
||||
|
||||
// This is trying to break my complete lack of understanding of null bitmaps for array/record
|
||||
// decoding. The docs in pg are either wrong or I'm reading the wrong docs.
|
||||
test_type!(lots_of_nulls_vec(Postgres, Vec<Option<bool>>,
|
||||
|
Loading…
x
Reference in New Issue
Block a user