Skip to content

Commit

Permalink
guarded try_from_months_days_usecs
Browse files Browse the repository at this point in the history
  • Loading branch information
mhov committed Aug 26, 2022
1 parent e6315c0 commit 408e610
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 10 deletions.
24 changes: 20 additions & 4 deletions pgx-tests/src/tests/datetime_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -402,12 +402,28 @@ mod tests {

#[pg_test]
fn test_interval_serialization() {
let interval = Interval::from_months_days_usecs(3, 4, 5_000_000);
let interval = Interval::try_from_months_days_usecs(3, 4, 5_000_000).unwrap();
let json = json!({ "interval test": interval });

assert_eq!(json!({"interval test":"3 mons 4 days 00:00:05"}), json);
}

#[pg_test]
fn test_try_from_months_days_usecs_err() {
let result = Interval::try_from_months_days_usecs(3, 30, 5_000_000);
match result {
Err(IntervalConversionError::FromDaysOutOfBounds) => (),
_ => panic!("invalid try_from_months_days_usecs conversion succeeded"),
};

let result =
Interval::try_from_months_days_usecs(3, 4, pg_sys::SECS_PER_DAY as i64 * 1_000_000i64);
match result {
Err(IntervalConversionError::FromUSecOutOfBounds) => (),
_ => panic!("invalid try_from_months_days_usecs conversion succeeded"),
};
}

#[pg_test]
fn test_duration_to_interval_err() {
// normal limit of i32::MAX months
Expand All @@ -416,7 +432,7 @@ mod tests {
let result = TryInto::<Interval>::try_into(duration);
match result {
Ok(_) => (),
Err(_) => panic!("failed duration -> interval conversion"),
_ => panic!("failed duration -> interval conversion"),
};

// one month too many, expect error
Expand All @@ -425,8 +441,8 @@ mod tests {

let result = TryInto::<Interval>::try_into(duration);
match result {
Ok(_) => panic!("invalid duration -> interval conversion succeeded"),
Err(_) => (),
Err(IntervalConversionError::DurationMonthsOutOfBounds) => (),
_ => panic!("invalid duration -> interval conversion succeeded"),
};
}
}
32 changes: 26 additions & 6 deletions pgx/src/datum/interval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Use of this source code is governed by the MIT license that can be found in the

use std::ops::{Mul, Sub};

use crate::{direct_function_call, pg_sys, FromDatum, IntoDatum, USECS_PER_SEC};
use crate::{direct_function_call, pg_sys, FromDatum, IntoDatum, USECS_PER_DAY, USECS_PER_SEC};
use pg_sys::{DAYS_PER_MONTH, SECS_PER_DAY};
use time::Duration;

Expand All @@ -20,12 +20,22 @@ const MONTH_DURATION: Duration = Duration::days(DAYS_PER_MONTH as i64);
pub struct Interval(pg_sys::Interval);

impl Interval {
pub fn from_months_days_usecs(months: i32, days: i32, usecs: i64) -> Self {
Interval(pg_sys::Interval {
pub fn try_from_months_days_usecs(
months: i32,
days: i32,
usecs: i64,
) -> Result<Self, IntervalConversionError> {
if days.abs() >= pg_sys::DAYS_PER_MONTH as i32 {
return Err(IntervalConversionError::FromDaysOutOfBounds);
}
if usecs.abs() >= USECS_PER_DAY {
return Err(IntervalConversionError::FromUSecOutOfBounds);
}
Ok(Interval(pg_sys::Interval {
day: days,
month: months,
time: usecs,
})
}))
}

pub fn months(&self) -> i32 {
Expand Down Expand Up @@ -72,7 +82,7 @@ impl FromDatum for Interval {
}

impl TryFrom<Duration> for Interval {
type Error = &'static str;
type Error = IntervalConversionError;
fn try_from(duration: Duration) -> Result<Interval, Self::Error> {
let total_months = duration.whole_days() / (pg_sys::DAYS_PER_MONTH as i64);

Expand All @@ -95,7 +105,7 @@ impl TryFrom<Duration> for Interval {

Ok(Interval(pg_sys::Interval { day, month, time }))
} else {
Err("duration's total month count outside of valid i32::MIN..=i32::MAX range")
Err(IntervalConversionError::DurationMonthsOutOfBounds)
}
}
}
Expand Down Expand Up @@ -140,3 +150,13 @@ impl serde::Serialize for Interval {
}
}
}

#[derive(thiserror::Error, Debug, Clone, Copy)]
pub enum IntervalConversionError {
#[error("duration's total month count outside of valid i32::MIN..=i32::MAX range")]
DurationMonthsOutOfBounds,
#[error("try_from_months_days_usecs's days abs count must be < DAYS_PER_MONTH (30)")]
FromDaysOutOfBounds,
#[error("try_from_months_days_usecs's usec abs count must be < USECS_PER_DAY")]
FromUSecOutOfBounds,
}
1 change: 1 addition & 0 deletions pgx/src/datum/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use time::format_description::FormatItem;
pub(crate) const USECS_PER_HOUR: i64 = 3_600_000_000;
pub(crate) const USECS_PER_MINUTE: i64 = 60_000_000;
pub(crate) const USECS_PER_SEC: i64 = 1_000_000;
pub(crate) const USECS_PER_DAY: i64 = pg_sys::SECS_PER_DAY as i64 * USECS_PER_SEC;
pub(crate) const MINS_PER_HOUR: i64 = 60;
pub(crate) const SEC_PER_MIN: i64 = 60;

Expand Down

0 comments on commit 408e610

Please sign in to comment.