diesel/pg/types/
ranges.rs

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