mirror of
https://github.com/launchbadge/sqlx
synced 2024-11-10 14:34:19 +00:00
fix: replace use of deprecated chrono
APIs
This commit is contained in:
parent
0f6c377c12
commit
ec60b1d32d
5 changed files with 65 additions and 34 deletions
|
@ -106,7 +106,8 @@ impl<'r> Decode<'r, MySql> for NaiveTime {
|
||||||
// are 0 then the length is 0 and no further data is send
|
// are 0 then the length is 0 and no further data is send
|
||||||
// https://dev.mysql.com/doc/internals/en/binary-protocol-value.html
|
// https://dev.mysql.com/doc/internals/en/binary-protocol-value.html
|
||||||
if len == 0 {
|
if len == 0 {
|
||||||
return Ok(NaiveTime::from_hms_micro(0, 0, 0, 0));
|
return Ok(NaiveTime::from_hms_micro_opt(0, 0, 0, 0)
|
||||||
|
.expect("expected NaiveTime to construct from all zeroes"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// is negative : int<1>
|
// is negative : int<1>
|
||||||
|
@ -117,7 +118,7 @@ impl<'r> Decode<'r, MySql> for NaiveTime {
|
||||||
// https://mariadb.com/kb/en/resultset-row/#timestamp-binary-encoding
|
// https://mariadb.com/kb/en/resultset-row/#timestamp-binary-encoding
|
||||||
buf.advance(4);
|
buf.advance(4);
|
||||||
|
|
||||||
Ok(decode_time(len - 5, buf))
|
decode_time(len - 5, buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
MySqlValueFormat::Text => {
|
MySqlValueFormat::Text => {
|
||||||
|
@ -152,7 +153,7 @@ impl<'r> Decode<'r, MySql> for NaiveDate {
|
||||||
fn decode(value: MySqlValueRef<'r>) -> Result<Self, BoxDynError> {
|
fn decode(value: MySqlValueRef<'r>) -> Result<Self, BoxDynError> {
|
||||||
match value.format() {
|
match value.format() {
|
||||||
MySqlValueFormat::Binary => {
|
MySqlValueFormat::Binary => {
|
||||||
decode_date(&value.as_bytes()?[1..]).ok_or_else(|| UnexpectedNullError.into())
|
decode_date(&value.as_bytes()?[1..])?.ok_or_else(|| UnexpectedNullError.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
MySqlValueFormat::Text => {
|
MySqlValueFormat::Text => {
|
||||||
|
@ -212,12 +213,13 @@ impl<'r> Decode<'r, MySql> for NaiveDateTime {
|
||||||
let buf = value.as_bytes()?;
|
let buf = value.as_bytes()?;
|
||||||
|
|
||||||
let len = buf[0];
|
let len = buf[0];
|
||||||
let date = decode_date(&buf[1..]).ok_or(UnexpectedNullError)?;
|
let date = decode_date(&buf[1..])?.ok_or(UnexpectedNullError)?;
|
||||||
|
|
||||||
let dt = if len > 4 {
|
let dt = if len > 4 {
|
||||||
date.and_time(decode_time(len - 4, &buf[5..]))
|
date.and_time(decode_time(len - 4, &buf[5..])?)
|
||||||
} else {
|
} else {
|
||||||
date.and_hms(0, 0, 0)
|
date.and_hms_opt(0, 0, 0)
|
||||||
|
.expect("expected `NaiveDate::and_hms_opt(0, 0, 0)` to be valid")
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(dt)
|
Ok(dt)
|
||||||
|
@ -241,17 +243,21 @@ fn encode_date(date: &NaiveDate, buf: &mut Vec<u8>) {
|
||||||
buf.push(date.day() as u8);
|
buf.push(date.day() as u8);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_date(mut buf: &[u8]) -> Option<NaiveDate> {
|
fn decode_date(mut buf: &[u8]) -> Result<Option<NaiveDate>, BoxDynError> {
|
||||||
if buf.len() == 0 {
|
match buf.len() {
|
||||||
// MySQL specifies that if there are no bytes, this is all zeros
|
// MySQL specifies that if there are no bytes, this is all zeros
|
||||||
None
|
0 => Ok(None),
|
||||||
} else {
|
4.. => {
|
||||||
let year = buf.get_u16_le();
|
let year = buf.get_u16_le() as i32;
|
||||||
Some(NaiveDate::from_ymd(
|
let month = buf[0] as u32;
|
||||||
year as i32,
|
let day = buf[1] as u32;
|
||||||
buf[0] as u32,
|
|
||||||
buf[1] as u32,
|
let date = NaiveDate::from_ymd_opt(year, month, day)
|
||||||
))
|
.ok_or_else(|| format!("server returned invalid date: {year}/{month}/{day}"))?;
|
||||||
|
|
||||||
|
Ok(Some(date))
|
||||||
|
}
|
||||||
|
len => Err(format!("expected at least 4 bytes for date, got {len}").into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,7 +271,7 @@ fn encode_time(time: &NaiveTime, include_micros: bool, buf: &mut Vec<u8>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_time(len: u8, mut buf: &[u8]) -> NaiveTime {
|
fn decode_time(len: u8, mut buf: &[u8]) -> Result<NaiveTime, BoxDynError> {
|
||||||
let hour = buf.get_u8();
|
let hour = buf.get_u8();
|
||||||
let minute = buf.get_u8();
|
let minute = buf.get_u8();
|
||||||
let seconds = buf.get_u8();
|
let seconds = buf.get_u8();
|
||||||
|
@ -277,5 +283,6 @@ fn decode_time(len: u8, mut buf: &[u8]) -> NaiveTime {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
NaiveTime::from_hms_micro(hour as u32, minute as u32, seconds as u32, micros as u32)
|
NaiveTime::from_hms_micro_opt(hour as u32, minute as u32, seconds as u32, micros as u32)
|
||||||
|
.ok_or_else(|| format!("server returned invalid time: {hour:02}:{minute:02}:{seconds:02}; micros: {micros}").into())
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ impl PgHasArrayType for NaiveDate {
|
||||||
impl Encode<'_, Postgres> for NaiveDate {
|
impl Encode<'_, Postgres> for NaiveDate {
|
||||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||||
// DATE is encoded as the days since epoch
|
// DATE is encoded as the days since epoch
|
||||||
let days = (*self - NaiveDate::from_ymd(2000, 1, 1)).num_days() as i32;
|
let days = (*self - postgres_epoch_date()).num_days() as i32;
|
||||||
Encode::<Postgres>::encode(&days, buf)
|
Encode::<Postgres>::encode(&days, buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,10 +36,15 @@ impl<'r> Decode<'r, Postgres> for NaiveDate {
|
||||||
PgValueFormat::Binary => {
|
PgValueFormat::Binary => {
|
||||||
// DATE is encoded as the days since epoch
|
// DATE is encoded as the days since epoch
|
||||||
let days: i32 = Decode::<Postgres>::decode(value)?;
|
let days: i32 = Decode::<Postgres>::decode(value)?;
|
||||||
NaiveDate::from_ymd(2000, 1, 1) + Duration::days(days.into())
|
postgres_epoch_date() + Duration::days(days.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
PgValueFormat::Text => NaiveDate::parse_from_str(value.as_str()?, "%Y-%m-%d")?,
|
PgValueFormat::Text => NaiveDate::parse_from_str(value.as_str()?, "%Y-%m-%d")?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn postgres_epoch_date() -> NaiveDate {
|
||||||
|
NaiveDate::from_ymd_opt(2000, 1, 1).expect("expected 2000-01-01 to be a valid NaiveDate")
|
||||||
|
}
|
||||||
|
|
|
@ -36,8 +36,7 @@ impl Encode<'_, Postgres> for NaiveDateTime {
|
||||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||||
// FIXME: We should *really* be returning an error, Encode needs to be fallible
|
// FIXME: We should *really* be returning an error, Encode needs to be fallible
|
||||||
// TIMESTAMP is encoded as the microseconds since the epoch
|
// TIMESTAMP is encoded as the microseconds since the epoch
|
||||||
let epoch = NaiveDate::from_ymd(2000, 1, 1).and_hms(0, 0, 0);
|
let us = (*self - postgres_epoch_datetime())
|
||||||
let us = (*self - epoch)
|
|
||||||
.num_microseconds()
|
.num_microseconds()
|
||||||
.unwrap_or_else(|| panic!("NaiveDateTime out of range for Postgres: {:?}", self));
|
.unwrap_or_else(|| panic!("NaiveDateTime out of range for Postgres: {:?}", self));
|
||||||
|
|
||||||
|
@ -54,9 +53,8 @@ impl<'r> Decode<'r, Postgres> for NaiveDateTime {
|
||||||
Ok(match value.format() {
|
Ok(match value.format() {
|
||||||
PgValueFormat::Binary => {
|
PgValueFormat::Binary => {
|
||||||
// TIMESTAMP is encoded as the microseconds since the epoch
|
// TIMESTAMP is encoded as the microseconds since the epoch
|
||||||
let epoch = NaiveDate::from_ymd(2000, 1, 1).and_hms(0, 0, 0);
|
|
||||||
let us = Decode::<Postgres>::decode(value)?;
|
let us = Decode::<Postgres>::decode(value)?;
|
||||||
epoch + Duration::microseconds(us)
|
postgres_epoch_datetime() + Duration::microseconds(us)
|
||||||
}
|
}
|
||||||
|
|
||||||
PgValueFormat::Text => {
|
PgValueFormat::Text => {
|
||||||
|
@ -107,3 +105,11 @@ impl<'r> Decode<'r, Postgres> for DateTime<FixedOffset> {
|
||||||
Ok(Utc.fix().from_utc_datetime(&naive))
|
Ok(Utc.fix().from_utc_datetime(&naive))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn postgres_epoch_datetime() -> NaiveDateTime {
|
||||||
|
NaiveDate::from_ymd_opt(2000, 1, 1)
|
||||||
|
.expect("expected 2000-01-01 to be a valid NaiveDate")
|
||||||
|
.and_hms_opt(0, 0, 0)
|
||||||
|
.expect("expected 2000-01-01T00:00:00 to be a valid NaiveDateTime")
|
||||||
|
}
|
||||||
|
|
|
@ -22,9 +22,7 @@ impl Encode<'_, Postgres> for NaiveTime {
|
||||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||||
// TIME is encoded as the microseconds since midnight
|
// TIME is encoded as the microseconds since midnight
|
||||||
// NOTE: panic! is on overflow and 1 day does not have enough micros to overflow
|
// NOTE: panic! is on overflow and 1 day does not have enough micros to overflow
|
||||||
let us = (*self - NaiveTime::from_hms(0, 0, 0))
|
let us = (*self - NaiveTime::default()).num_microseconds().unwrap();
|
||||||
.num_microseconds()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Encode::<Postgres>::encode(&us, buf)
|
Encode::<Postgres>::encode(&us, buf)
|
||||||
}
|
}
|
||||||
|
@ -40,10 +38,20 @@ impl<'r> Decode<'r, Postgres> for NaiveTime {
|
||||||
PgValueFormat::Binary => {
|
PgValueFormat::Binary => {
|
||||||
// TIME is encoded as the microseconds since midnight
|
// TIME is encoded as the microseconds since midnight
|
||||||
let us: i64 = Decode::<Postgres>::decode(value)?;
|
let us: i64 = Decode::<Postgres>::decode(value)?;
|
||||||
NaiveTime::from_hms(0, 0, 0) + Duration::microseconds(us)
|
NaiveTime::default() + Duration::microseconds(us)
|
||||||
}
|
}
|
||||||
|
|
||||||
PgValueFormat::Text => NaiveTime::parse_from_str(value.as_str()?, "%H:%M:%S%.f")?,
|
PgValueFormat::Text => NaiveTime::parse_from_str(value.as_str()?, "%H:%M:%S%.f")?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_naive_time_default_is_midnight() {
|
||||||
|
// Just a canary in case this changes.
|
||||||
|
assert_eq!(
|
||||||
|
NaiveTime::from_hms_opt(0, 0, 0),
|
||||||
|
Some(NaiveTime::default()),
|
||||||
|
"implementation assumes `NaiveTime::default()` equals midnight"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -71,15 +71,20 @@ mod chrono {
|
||||||
|
|
||||||
// TIME is encoded as the microseconds since midnight
|
// TIME is encoded as the microseconds since midnight
|
||||||
let us = buf.read_i64::<BigEndian>()?;
|
let us = buf.read_i64::<BigEndian>()?;
|
||||||
let time = NaiveTime::from_hms(0, 0, 0) + Duration::microseconds(us);
|
// default is midnight, there is a canary test for this
|
||||||
|
// in `sqlx-postgres/src/types/chrono/time.rs`
|
||||||
|
let time = NaiveTime::default() + Duration::microseconds(us);
|
||||||
|
|
||||||
// OFFSET is encoded as seconds from UTC
|
// OFFSET is encoded as seconds from UTC
|
||||||
let seconds = buf.read_i32::<BigEndian>()?;
|
let offset_seconds = buf.read_i32::<BigEndian>()?;
|
||||||
|
|
||||||
Ok(PgTimeTz {
|
let offset = FixedOffset::west_opt(offset_seconds).ok_or_else(|| {
|
||||||
time,
|
format!(
|
||||||
offset: FixedOffset::west(seconds),
|
"server returned out-of-range offset for `TIMETZ`: {offset_seconds} seconds"
|
||||||
})
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(PgTimeTz { time, offset })
|
||||||
}
|
}
|
||||||
|
|
||||||
PgValueFormat::Text => {
|
PgValueFormat::Text => {
|
||||||
|
|
Loading…
Reference in a new issue