diesel/pg/types/
ranges.rs

1use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt};
2use std::collections::Bound;
3use std::io::Write;
4
5use crate::deserialize::{self, FromSql, Queryable};
6use crate::expression::bound::Bound as SqlBound;
7use crate::expression::AsExpression;
8use crate::pg::{Pg, PgTypeMetadata, PgValue};
9use crate::query_builder::bind_collector::ByteWrapper;
10use crate::serialize::{self, IsNull, Output, ToSql};
11use crate::sql_types::*;
12
13// https://github.com/postgres/postgres/blob/113b0045e20d40f726a0a30e33214455e4f1385e/src/include/utils/rangetypes.h#L35-L43
14bitflags::bitflags! {
15    struct RangeFlags: u8 {
16        const EMPTY = 0x01;
17        const LB_INC = 0x02;
18        const UB_INC = 0x04;
19        const LB_INF = 0x08;
20        const UB_INF = 0x10;
21        const LB_NULL = 0x20;
22        const UB_NULL = 0x40;
23        const CONTAIN_EMPTY = 0x80;
24    }
25}
26
27#[cfg(feature = "postgres_backend")]
28impl<ST: 'static, T> AsExpression<Range<ST>> for (Bound<T>, Bound<T>) {
29    type Expression = SqlBound<Range<ST>, Self>;
30
31    fn as_expression(self) -> Self::Expression {
32        SqlBound::new(self)
33    }
34}
35
36#[cfg(feature = "postgres_backend")]
37impl<ST: 'static, T> AsExpression<Range<ST>> for &(Bound<T>, Bound<T>) {
38    type Expression = SqlBound<Range<ST>, Self>;
39
40    fn as_expression(self) -> Self::Expression {
41        SqlBound::new(self)
42    }
43}
44
45#[cfg(feature = "postgres_backend")]
46impl<ST: 'static, T> AsExpression<Nullable<Range<ST>>> for (Bound<T>, Bound<T>) {
47    type Expression = SqlBound<Nullable<Range<ST>>, Self>;
48
49    fn as_expression(self) -> Self::Expression {
50        SqlBound::new(self)
51    }
52}
53
54#[cfg(feature = "postgres_backend")]
55impl<ST: 'static, T> AsExpression<Nullable<Range<ST>>> for &(Bound<T>, Bound<T>) {
56    type Expression = SqlBound<Nullable<Range<ST>>, Self>;
57
58    fn as_expression(self) -> Self::Expression {
59        SqlBound::new(self)
60    }
61}
62
63#[cfg(feature = "postgres_backend")]
64impl<T, ST> FromSql<Range<ST>, Pg> for (Bound<T>, Bound<T>)
65where
66    T: FromSql<ST, Pg>,
67{
68    fn from_sql(value: PgValue<'_>) -> deserialize::Result<Self> {
69        let mut bytes = value.as_bytes();
70        let flags: RangeFlags = RangeFlags::from_bits_truncate(bytes.read_u8()?);
71        let mut lower_bound = Bound::Unbounded;
72        let mut upper_bound = Bound::Unbounded;
73
74        if !flags.contains(RangeFlags::LB_INF) {
75            let elem_size = bytes.read_i32::<NetworkEndian>()?;
76            let (elem_bytes, new_bytes) = bytes.split_at(elem_size.try_into()?);
77            bytes = new_bytes;
78            let value = T::from_sql(PgValue::new_internal(elem_bytes, &value))?;
79
80            lower_bound = if flags.contains(RangeFlags::LB_INC) {
81                Bound::Included(value)
82            } else {
83                Bound::Excluded(value)
84            };
85        }
86
87        if !flags.contains(RangeFlags::UB_INF) {
88            let _size = bytes.read_i32::<NetworkEndian>()?;
89            let value = T::from_sql(PgValue::new_internal(bytes, &value))?;
90
91            upper_bound = if flags.contains(RangeFlags::UB_INC) {
92                Bound::Included(value)
93            } else {
94                Bound::Excluded(value)
95            };
96        }
97
98        Ok((lower_bound, upper_bound))
99    }
100}
101
102#[cfg(feature = "postgres_backend")]
103impl<T, ST> Queryable<Range<ST>, Pg> for (Bound<T>, Bound<T>)
104where
105    T: FromSql<ST, Pg>,
106{
107    type Row = Self;
108
109    fn build(row: Self) -> deserialize::Result<Self> {
110        Ok(row)
111    }
112}
113
114#[cfg(feature = "postgres_backend")]
115impl<ST, T> ToSql<Range<ST>, Pg> for (Bound<T>, Bound<T>)
116where
117    T: ToSql<ST, Pg>,
118{
119    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
120        let mut flags = match self.0 {
121            Bound::Included(_) => RangeFlags::LB_INC,
122            Bound::Excluded(_) => RangeFlags::empty(),
123            Bound::Unbounded => RangeFlags::LB_INF,
124        };
125
126        flags |= match self.1 {
127            Bound::Included(_) => RangeFlags::UB_INC,
128            Bound::Excluded(_) => RangeFlags::empty(),
129            Bound::Unbounded => RangeFlags::UB_INF,
130        };
131
132        out.write_u8(flags.bits())?;
133
134        let mut buffer = Vec::new();
135
136        match self.0 {
137            Bound::Included(ref value) | Bound::Excluded(ref value) => {
138                {
139                    let mut inner_buffer =
140                        Output::new(ByteWrapper(&mut buffer), out.metadata_lookup());
141                    value.to_sql(&mut inner_buffer)?;
142                }
143                out.write_u32::<NetworkEndian>(buffer.len().try_into()?)?;
144                out.write_all(&buffer)?;
145                buffer.clear();
146            }
147            Bound::Unbounded => {}
148        }
149
150        match self.1 {
151            Bound::Included(ref value) | Bound::Excluded(ref value) => {
152                {
153                    let mut inner_buffer =
154                        Output::new(ByteWrapper(&mut buffer), out.metadata_lookup());
155                    value.to_sql(&mut inner_buffer)?;
156                }
157                out.write_u32::<NetworkEndian>(buffer.len().try_into()?)?;
158                out.write_all(&buffer)?;
159            }
160            Bound::Unbounded => {}
161        }
162
163        Ok(IsNull::No)
164    }
165}
166
167#[cfg(feature = "postgres_backend")]
168impl<ST, T> ToSql<Nullable<Range<ST>>, Pg> for (Bound<T>, Bound<T>)
169where
170    ST: 'static,
171    (Bound<T>, Bound<T>): ToSql<Range<ST>, Pg>,
172{
173    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
174        ToSql::<Range<ST>, Pg>::to_sql(self, out)
175    }
176}
177
178#[cfg(feature = "postgres_backend")]
179impl HasSqlType<Int4range> for Pg {
180    fn metadata(_: &mut Self::MetadataLookup) -> PgTypeMetadata {
181        PgTypeMetadata::new(3904, 3905)
182    }
183}
184
185#[cfg(feature = "postgres_backend")]
186impl HasSqlType<Numrange> for Pg {
187    fn metadata(_: &mut Self::MetadataLookup) -> PgTypeMetadata {
188        PgTypeMetadata::new(3906, 3907)
189    }
190}
191
192impl HasSqlType<Tsrange> for Pg {
193    fn metadata(_: &mut Self::MetadataLookup) -> PgTypeMetadata {
194        PgTypeMetadata::new(3908, 3909)
195    }
196}
197
198#[cfg(feature = "postgres_backend")]
199impl HasSqlType<Tstzrange> for Pg {
200    fn metadata(_: &mut Self::MetadataLookup) -> PgTypeMetadata {
201        PgTypeMetadata::new(3910, 3911)
202    }
203}
204
205#[cfg(feature = "postgres_backend")]
206impl HasSqlType<Daterange> for Pg {
207    fn metadata(_: &mut Self::MetadataLookup) -> PgTypeMetadata {
208        PgTypeMetadata::new(3912, 3913)
209    }
210}
211
212#[cfg(feature = "postgres_backend")]
213impl HasSqlType<Int8range> for Pg {
214    fn metadata(_: &mut Self::MetadataLookup) -> PgTypeMetadata {
215        PgTypeMetadata::new(3926, 3927)
216    }
217}