1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
910use chrono::NaiveTime;
11use mz_ore::cast::CastLossy;
12use mz_repr::adt::interval::{Interval, USECS_PER_DAY};
13use mz_repr::strconv;
14use num::traits::CheckedNeg;
1516use crate::EvalError;
1718sqlfunc!(
19#[sqlname = "interval_to_text"]
20 #[preserves_uniqueness = true]
21 #[inverse = to_unary!(super::CastStringToInterval)]
22fn cast_interval_to_string(a: Interval) -> String {
23let mut buf = String::new();
24 strconv::format_interval(&mut buf, a);
25 buf
26 }
27);
2829sqlfunc!(
30#[sqlname = "interval_to_time"]
31 #[preserves_uniqueness = false]
32 #[inverse = to_unary!(super::CastTimeToInterval)]
33fn cast_interval_to_time(i: Interval) -> NaiveTime {
34// Modeled after the PostgreSQL implementation:
35 // https://github.com/postgres/postgres/blob/6a1ea02c491d16474a6214603dce40b5b122d4d1/src/backend/utils/adt/date.c#L2003-L2027
36let mut result = i.micros % *USECS_PER_DAY;
37if result < 0 {
38 result += *USECS_PER_DAY;
39 }
4041let i = Interval::new(0, 0, result);
4243let hours: u32 = i
44 .hours()
45 .try_into()
46 .expect("interval is positive and hours() returns a value in the range [-24, 24]");
47let minutes: u32 = i
48 .minutes()
49 .try_into()
50 .expect("interval is positive and minutes() returns a value in the range [-60, 60]");
51let seconds: u32 = i64::cast_lossy(i.seconds::<f64>()).try_into().expect(
52"interval is positive and seconds() returns a value in the range [-60.0, 60.0]",
53 );
54let nanoseconds: u32 =
55 i.nanoseconds().try_into().expect(
56"interval is positive and nanoseconds() returns a value in the range [-1_000_000_000, 1_000_000_000]",
57 );
5859 NaiveTime::from_hms_nano_opt(hours, minutes, seconds, nanoseconds).unwrap()
60 }
61);
6263sqlfunc!(
64#[sqlname = "-"]
65 #[preserves_uniqueness = true]
66 #[inverse = to_unary!(super::NegInterval)]
67fn neg_interval(i: Interval) -> Result<Interval, EvalError> {
68 i.checked_neg()
69 .ok_or_else(|| EvalError::IntervalOutOfRange(i.to_string().into()))
70 }
71);
7273sqlfunc!(
74#[sqlname = "justify_days"]
75fn justify_days(i: Interval) -> Result<Interval, EvalError> {
76 i.justify_days()
77 .map_err(|_| EvalError::IntervalOutOfRange(i.to_string().into()))
78 }
79);
8081sqlfunc!(
82#[sqlname = "justify_hours"]
83fn justify_hours(i: Interval) -> Result<Interval, EvalError> {
84 i.justify_hours()
85 .map_err(|_| EvalError::IntervalOutOfRange(i.to_string().into()))
86 }
87);
8889sqlfunc!(
90#[sqlname = "justify_interval"]
91fn justify_interval(i: Interval) -> Result<Interval, EvalError> {
92 i.justify_interval()
93 .map_err(|_| EvalError::IntervalOutOfRange(i.to_string().into()))
94 }
95);