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 std::error::Error;
11use std::fmt;
1213use byteorder::{NetworkEndian, ReadBytesExt};
14use bytes::{BufMut, BytesMut};
15use mz_repr::adt::interval::Interval as ReprInterval;
16use postgres_types::{FromSql, IsNull, ToSql, Type, to_sql_checked};
1718/// A wrapper for the `repr` crate's [`Interval`](mz_repr::adt::interval::Interval)
19/// type that can be serialized to and deserialized from the PostgreSQL binary
20/// format.
21#[derive(Debug, Clone)]
22pub struct Interval(pub ReprInterval);
2324impl fmt::Display for Interval {
25fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
26self.0.fmt(f)
27 }
28}
2930impl ToSql for Interval {
31fn to_sql(
32&self,
33_: &Type,
34 out: &mut BytesMut,
35 ) -> Result<IsNull, Box<dyn Error + 'static + Send + Sync>> {
36// Postgres represents intervals as an i64 representing the whole number
37 // of microseconds, followed by an i32 representing the whole number of
38 // days, followed by an i32 representing the whole number of months.
39 //
40 // Postgres implementation: https://github.com/postgres/postgres/blob/517bf2d91/src/backend/utils/adt/timestamp.c#L1008
41 // Diesel implementation: https://github.com/diesel-rs/diesel/blob/a8b52bd05/diesel/src/pg/types/date_and_time/mod.rs#L39
42out.put_i64(self.0.micros);
43 out.put_i32(self.0.days);
44 out.put_i32(self.0.months);
45Ok(IsNull::No)
46 }
4748fn accepts(ty: &Type) -> bool {
49matches!(*ty, Type::INTERVAL)
50 }
5152to_sql_checked!();
53}
5455impl<'a> FromSql<'a> for Interval {
56fn from_sql(_: &Type, mut raw: &'a [u8]) -> Result<Interval, Box<dyn Error + Sync + Send>> {
57let micros = raw.read_i64::<NetworkEndian>()?;
58let days = raw.read_i32::<NetworkEndian>()?;
59let months = raw.read_i32::<NetworkEndian>()?;
60Ok(Interval(ReprInterval::new(months, days, micros)))
61 }
6263fn accepts(ty: &Type) -> bool {
64matches!(*ty, Type::INTERVAL)
65 }
66}