mz_pgrepr/value/
interval.rs

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.
9
10use std::error::Error;
11use std::fmt;
12
13use 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};
17
18/// 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);
23
24impl fmt::Display for Interval {
25    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
26        self.0.fmt(f)
27    }
28}
29
30impl ToSql for Interval {
31    fn 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
42        out.put_i64(self.0.micros);
43        out.put_i32(self.0.days);
44        out.put_i32(self.0.months);
45        Ok(IsNull::No)
46    }
47
48    fn accepts(ty: &Type) -> bool {
49        matches!(*ty, Type::INTERVAL)
50    }
51
52    to_sql_checked!();
53}
54
55impl<'a> FromSql<'a> for Interval {
56    fn from_sql(_: &Type, mut raw: &'a [u8]) -> Result<Interval, Box<dyn Error + Sync + Send>> {
57        let micros = raw.read_i64::<NetworkEndian>()?;
58        let days = raw.read_i32::<NetworkEndian>()?;
59        let months = raw.read_i32::<NetworkEndian>()?;
60        Ok(Interval(ReprInterval::new(months, days, micros)))
61    }
62
63    fn accepts(ty: &Type) -> bool {
64        matches!(*ty, Type::INTERVAL)
65    }
66}