Skip to main content

diesel/pg/types/
multirange.rs

1use crate::deserialize::{self, Defaultable, FromSql};
2use crate::expression::AsExpression;
3use crate::expression::bound::Bound as SqlBound;
4use crate::pg::{Pg, PgTypeMetadata, PgValue};
5use crate::query_builder::bind_collector::ByteWrapper;
6use crate::serialize::{self, IsNull, Output, ToSql};
7use crate::sql_types::*;
8use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt};
9use core::ops::Bound;
10use core::ops::RangeBounds;
11use std::io::Write;
12
13// from `SELECT oid, typname FROM pg_catalog.pg_type where typname LIKE '%multirange'`;
14macro_rules! multirange_has_sql_type {
15    ($ty:ty, $oid:expr_2021, $array_oid:expr_2021) => {
16        #[cfg(feature = "postgres_backend")]
17        impl HasSqlType<$ty> for Pg {
18            fn metadata(_: &mut Self::MetadataLookup) -> PgTypeMetadata {
19                PgTypeMetadata::new($oid, $array_oid)
20            }
21        }
22    };
23}
24impl HasSqlType<Datemultirange> for Pg {
    fn metadata(_: &mut Self::MetadataLookup) -> PgTypeMetadata {
        PgTypeMetadata::new(4535, 6155)
    }
}multirange_has_sql_type!(Datemultirange, 4535, 6155);
25impl HasSqlType<Int4multirange> for Pg {
    fn metadata(_: &mut Self::MetadataLookup) -> PgTypeMetadata {
        PgTypeMetadata::new(4451, 6150)
    }
}multirange_has_sql_type!(Int4multirange, 4451, 6150);
26impl HasSqlType<Int8multirange> for Pg {
    fn metadata(_: &mut Self::MetadataLookup) -> PgTypeMetadata {
        PgTypeMetadata::new(4536, 6157)
    }
}multirange_has_sql_type!(Int8multirange, 4536, 6157);
27impl HasSqlType<Nummultirange> for Pg {
    fn metadata(_: &mut Self::MetadataLookup) -> PgTypeMetadata {
        PgTypeMetadata::new(4532, 6151)
    }
}multirange_has_sql_type!(Nummultirange, 4532, 6151);
28impl HasSqlType<Tsmultirange> for Pg {
    fn metadata(_: &mut Self::MetadataLookup) -> PgTypeMetadata {
        PgTypeMetadata::new(4533, 6152)
    }
}multirange_has_sql_type!(Tsmultirange, 4533, 6152);
29impl HasSqlType<Tstzmultirange> for Pg {
    fn metadata(_: &mut Self::MetadataLookup) -> PgTypeMetadata {
        PgTypeMetadata::new(4534, 6153)
    }
}multirange_has_sql_type!(Tstzmultirange, 4534, 6153);
30
31macro_rules! multirange_as_expression {
32    ($ty:ty, $sql_type:ty) => {
33        #[cfg(feature = "postgres_backend")]
34        // this simplifies the macro implementation
35        // as some macro calls use this lifetime
36        #[allow(clippy::extra_unused_lifetimes)]
37        impl<'a, 'b, ST: 'static, T> AsExpression<$sql_type> for $ty {
38            type Expression = SqlBound<$sql_type, Self>;
39            fn as_expression(self) -> Self::Expression {
40                SqlBound::new(self)
41            }
42        }
43    };
44}
45
46macro_rules! multirange_as_expressions {
47    ($ty:ty) => {
48        multirange_as_expression!(&'a [$ty], Multirange<ST>);
49        multirange_as_expression!(&'a [$ty], Nullable<Multirange<ST>>);
50        multirange_as_expression!(&'a &'b [$ty], Multirange<ST>);
51        multirange_as_expression!(&'a &'b [$ty], Nullable<Multirange<ST>>);
52        multirange_as_expression!(Vec<$ty>, Multirange<ST>);
53        multirange_as_expression!(Vec<$ty>, Nullable<Multirange<ST>>);
54        multirange_as_expression!(&'a Vec<$ty>, Multirange<ST>);
55        multirange_as_expression!(&'a Vec<$ty>, Nullable<Multirange<ST>>);
56        multirange_as_expression!(&'a &'b Vec<$ty>, Multirange<ST>);
57        multirange_as_expression!(&'a &'b Vec<$ty>, Nullable<Multirange<ST>>);
58    };
59}
60
61#[allow(clippy :: extra_unused_lifetimes)]
impl<'a, 'b, ST: 'static, T> AsExpression<Nullable<Multirange<ST>>> for
    &'a &'b Vec<(Bound<T>, Bound<T>)> {
    type Expression = SqlBound<Nullable<Multirange<ST>>, Self>;
    fn as_expression(self) -> Self::Expression { SqlBound::new(self) }
}multirange_as_expressions!((Bound<T>, Bound<T>));
62#[allow(clippy :: extra_unused_lifetimes)]
impl<'a, 'b, ST: 'static, T> AsExpression<Nullable<Multirange<ST>>> for
    &'a &'b Vec<core::ops::Range<T>> {
    type Expression = SqlBound<Nullable<Multirange<ST>>, Self>;
    fn as_expression(self) -> Self::Expression { SqlBound::new(self) }
}multirange_as_expressions!(core::ops::Range<T>);
63#[allow(clippy :: extra_unused_lifetimes)]
impl<'a, 'b, ST: 'static, T> AsExpression<Nullable<Multirange<ST>>> for
    &'a &'b Vec<core::ops::RangeInclusive<T>> {
    type Expression = SqlBound<Nullable<Multirange<ST>>, Self>;
    fn as_expression(self) -> Self::Expression { SqlBound::new(self) }
}multirange_as_expressions!(core::ops::RangeInclusive<T>);
64#[allow(clippy :: extra_unused_lifetimes)]
impl<'a, 'b, ST: 'static, T> AsExpression<Nullable<Multirange<ST>>> for
    &'a &'b Vec<core::ops::RangeToInclusive<T>> {
    type Expression = SqlBound<Nullable<Multirange<ST>>, Self>;
    fn as_expression(self) -> Self::Expression { SqlBound::new(self) }
}multirange_as_expressions!(core::ops::RangeToInclusive<T>);
65#[allow(clippy :: extra_unused_lifetimes)]
impl<'a, 'b, ST: 'static, T> AsExpression<Nullable<Multirange<ST>>> for
    &'a &'b Vec<core::ops::RangeFrom<T>> {
    type Expression = SqlBound<Nullable<Multirange<ST>>, Self>;
    fn as_expression(self) -> Self::Expression { SqlBound::new(self) }
}multirange_as_expressions!(core::ops::RangeFrom<T>);
66#[allow(clippy :: extra_unused_lifetimes)]
impl<'a, 'b, ST: 'static, T> AsExpression<Nullable<Multirange<ST>>> for
    &'a &'b Vec<core::ops::RangeTo<T>> {
    type Expression = SqlBound<Nullable<Multirange<ST>>, Self>;
    fn as_expression(self) -> Self::Expression { SqlBound::new(self) }
}multirange_as_expressions!(core::ops::RangeTo<T>);
67
68#[cfg(feature = "postgres_backend")]
69impl<T, ST> FromSql<Multirange<ST>, Pg> for Vec<(Bound<T>, Bound<T>)>
70where
71    T: FromSql<ST, Pg> + Defaultable,
72{
73    fn from_sql(value: PgValue<'_>) -> deserialize::Result<Self> {
74        let mut bytes = value.as_bytes();
75        let len = bytes.read_u32::<NetworkEndian>()?;
76
77        (0..len)
78            .map(|_| {
79                let range_size: usize = bytes.read_i32::<NetworkEndian>()?.try_into()?;
80                let (range_bytes, new_bytes) =
81                    bytes.split_at_checked(range_size).ok_or_else(|| {
82                        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("Invalid element byte count: Expected at least {1} bytes, but only {0} bytes were received",
                bytes.len(), range_size))
    })format!(
83                            "Invalid element byte count: Expected at least {range_size} bytes, but only {} bytes were received",
84                            bytes.len()
85                        )
86                    })?;
87                bytes = new_bytes;
88                FromSql::from_sql(PgValue::new_internal(range_bytes, &value))
89            })
90            .collect()
91    }
92}
93
94fn to_sql<'c, ST, T, I>(iter: I, out: &mut Output<'_, '_, Pg>) -> serialize::Result
95where
96    ST: 'static,
97    T: ToSql<ST, Pg> + 'c,
98    I: Iterator<Item = (Bound<&'c T>, Bound<&'c T>)> + ExactSizeIterator,
99{
100    out.write_u32::<NetworkEndian>(iter.len().try_into()?)?;
101
102    let mut buffer = Vec::new();
103    for value in iter {
104        {
105            let mut inner_buffer = Output::new(ByteWrapper(&mut buffer), out.metadata_lookup());
106            ToSql::<Range<ST>, Pg>::to_sql(&value, &mut inner_buffer)?;
107        }
108        let buffer_len: i32 = buffer.len().try_into()?;
109        out.write_i32::<NetworkEndian>(buffer_len)?;
110        out.write_all(&buffer)?;
111        buffer.clear();
112    }
113
114    Ok(IsNull::No)
115}
116
117#[cfg(feature = "postgres_backend")]
118impl<T, ST> ToSql<Multirange<ST>, Pg> for [(Bound<T>, Bound<T>)]
119where
120    T: ToSql<ST, Pg>,
121{
122    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
123        to_sql(self.iter().map(|r| (r.0.as_ref(), r.1.as_ref())), out)
124    }
125}
126
127#[cfg(feature = "postgres_backend")]
128impl<T, ST> ToSql<Multirange<ST>, Pg> for Vec<(Bound<T>, Bound<T>)>
129where
130    T: ToSql<ST, Pg>,
131    [(Bound<T>, Bound<T>)]: ToSql<Multirange<ST>, Pg>,
132{
133    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
134        ToSql::<Multirange<ST>, Pg>::to_sql(self.as_slice(), out)
135    }
136}
137
138macro_rules! multirange_std_to_sql {
139    ($ty:ty) => {
140        #[cfg(feature = "postgres_backend")]
141        impl<ST, T> ToSql<Multirange<ST>, Pg> for [$ty]
142        where
143            ST: 'static,
144            T: ToSql<ST, Pg>,
145        {
146            fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
147                to_sql(
148                    self.into_iter().map(|r| (r.start_bound(), r.end_bound())),
149                    out,
150                )
151            }
152        }
153
154        #[cfg(feature = "postgres_backend")]
155        impl<T, ST> ToSql<Multirange<ST>, Pg> for Vec<$ty>
156        where
157            T: ToSql<ST, Pg>,
158            [$ty]: ToSql<Multirange<ST>, Pg>,
159        {
160            fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
161                ToSql::<Multirange<ST>, Pg>::to_sql(self.as_slice(), out)
162            }
163        }
164    };
165}
166
167impl<ST, T> ToSql<Multirange<ST>, Pg> for [core::ops::Range<T>] where
    ST: 'static, T: ToSql<ST, Pg> {
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>)
        -> serialize::Result {
        to_sql(self.into_iter().map(|r| (r.start_bound(), r.end_bound())),
            out)
    }
}
impl<T, ST> ToSql<Multirange<ST>, Pg> for Vec<core::ops::Range<T>> where
    T: ToSql<ST, Pg>, [core::ops::Range<T>]: ToSql<Multirange<ST>, Pg> {
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>)
        -> serialize::Result {
        ToSql::<Multirange<ST>, Pg>::to_sql(self.as_slice(), out)
    }
}multirange_std_to_sql!(core::ops::Range<T>);
168impl<ST, T> ToSql<Multirange<ST>, Pg> for [core::ops::RangeInclusive<T>] where
    ST: 'static, T: ToSql<ST, Pg> {
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>)
        -> serialize::Result {
        to_sql(self.into_iter().map(|r| (r.start_bound(), r.end_bound())),
            out)
    }
}
impl<T, ST> ToSql<Multirange<ST>, Pg> for Vec<core::ops::RangeInclusive<T>>
    where T: ToSql<ST, Pg>,
    [core::ops::RangeInclusive<T>]: ToSql<Multirange<ST>, Pg> {
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>)
        -> serialize::Result {
        ToSql::<Multirange<ST>, Pg>::to_sql(self.as_slice(), out)
    }
}multirange_std_to_sql!(core::ops::RangeInclusive<T>);
169impl<ST, T> ToSql<Multirange<ST>, Pg> for [core::ops::RangeFrom<T>] where
    ST: 'static, T: ToSql<ST, Pg> {
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>)
        -> serialize::Result {
        to_sql(self.into_iter().map(|r| (r.start_bound(), r.end_bound())),
            out)
    }
}
impl<T, ST> ToSql<Multirange<ST>, Pg> for Vec<core::ops::RangeFrom<T>> where
    T: ToSql<ST, Pg>, [core::ops::RangeFrom<T>]: ToSql<Multirange<ST>, Pg> {
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>)
        -> serialize::Result {
        ToSql::<Multirange<ST>, Pg>::to_sql(self.as_slice(), out)
    }
}multirange_std_to_sql!(core::ops::RangeFrom<T>);
170impl<ST, T> ToSql<Multirange<ST>, Pg> for [core::ops::RangeTo<T>] where
    ST: 'static, T: ToSql<ST, Pg> {
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>)
        -> serialize::Result {
        to_sql(self.into_iter().map(|r| (r.start_bound(), r.end_bound())),
            out)
    }
}
impl<T, ST> ToSql<Multirange<ST>, Pg> for Vec<core::ops::RangeTo<T>> where
    T: ToSql<ST, Pg>, [core::ops::RangeTo<T>]: ToSql<Multirange<ST>, Pg> {
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>)
        -> serialize::Result {
        ToSql::<Multirange<ST>, Pg>::to_sql(self.as_slice(), out)
    }
}multirange_std_to_sql!(core::ops::RangeTo<T>);
171impl<ST, T> ToSql<Multirange<ST>, Pg> for [core::ops::RangeToInclusive<T>]
    where ST: 'static, T: ToSql<ST, Pg> {
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>)
        -> serialize::Result {
        to_sql(self.into_iter().map(|r| (r.start_bound(), r.end_bound())),
            out)
    }
}
impl<T, ST> ToSql<Multirange<ST>, Pg> for Vec<core::ops::RangeToInclusive<T>>
    where T: ToSql<ST, Pg>,
    [core::ops::RangeToInclusive<T>]: ToSql<Multirange<ST>, Pg> {
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>)
        -> serialize::Result {
        ToSql::<Multirange<ST>, Pg>::to_sql(self.as_slice(), out)
    }
}multirange_std_to_sql!(core::ops::RangeToInclusive<T>);
172
173macro_rules! multirange_to_sql_nullable {
174    ($ty:ty) => {
175        impl<T, ST> ToSql<Nullable<Multirange<ST>>, Pg> for [$ty]
176        where
177            ST: 'static,
178            [$ty]: ToSql<ST, Pg>,
179            T: ToSql<ST, Pg>,
180        {
181            fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
182                ToSql::<Multirange<ST>, Pg>::to_sql(self, out)
183            }
184        }
185
186        impl<T, ST> ToSql<Nullable<Multirange<ST>>, Pg> for Vec<$ty>
187        where
188            ST: 'static,
189            Vec<$ty>: ToSql<ST, Pg>,
190            T: ToSql<ST, Pg>,
191        {
192            fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
193                ToSql::<Multirange<ST>, Pg>::to_sql(self, out)
194            }
195        }
196    };
197}
198
199impl<T, ST> ToSql<Nullable<Multirange<ST>>, Pg> for [(Bound<T>, Bound<T>)]
    where ST: 'static, [(Bound<T>, Bound<T>)]: ToSql<ST, Pg>, T: ToSql<ST, Pg>
    {
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>)
        -> serialize::Result {
        ToSql::<Multirange<ST>, Pg>::to_sql(self, out)
    }
}
impl<T, ST> ToSql<Nullable<Multirange<ST>>, Pg> for Vec<(Bound<T>, Bound<T>)>
    where ST: 'static, Vec<(Bound<T>, Bound<T>)>: ToSql<ST, Pg>,
    T: ToSql<ST, Pg> {
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>)
        -> serialize::Result {
        ToSql::<Multirange<ST>, Pg>::to_sql(self, out)
    }
}multirange_to_sql_nullable!((Bound<T>, Bound<T>));
200impl<T, ST> ToSql<Nullable<Multirange<ST>>, Pg> for [core::ops::Range<T>]
    where ST: 'static, [core::ops::Range<T>]: ToSql<ST, Pg>, T: ToSql<ST, Pg>
    {
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>)
        -> serialize::Result {
        ToSql::<Multirange<ST>, Pg>::to_sql(self, out)
    }
}
impl<T, ST> ToSql<Nullable<Multirange<ST>>, Pg> for Vec<core::ops::Range<T>>
    where ST: 'static, Vec<core::ops::Range<T>>: ToSql<ST, Pg>,
    T: ToSql<ST, Pg> {
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>)
        -> serialize::Result {
        ToSql::<Multirange<ST>, Pg>::to_sql(self, out)
    }
}multirange_to_sql_nullable!(core::ops::Range<T>);
201impl<T, ST> ToSql<Nullable<Multirange<ST>>, Pg> for
    [core::ops::RangeInclusive<T>] where ST: 'static,
    [core::ops::RangeInclusive<T>]: ToSql<ST, Pg>, T: ToSql<ST, Pg> {
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>)
        -> serialize::Result {
        ToSql::<Multirange<ST>, Pg>::to_sql(self, out)
    }
}
impl<T, ST> ToSql<Nullable<Multirange<ST>>, Pg> for
    Vec<core::ops::RangeInclusive<T>> where ST: 'static,
    Vec<core::ops::RangeInclusive<T>>: ToSql<ST, Pg>, T: ToSql<ST, Pg> {
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>)
        -> serialize::Result {
        ToSql::<Multirange<ST>, Pg>::to_sql(self, out)
    }
}multirange_to_sql_nullable!(core::ops::RangeInclusive<T>);
202impl<T, ST> ToSql<Nullable<Multirange<ST>>, Pg> for [core::ops::RangeFrom<T>]
    where ST: 'static, [core::ops::RangeFrom<T>]: ToSql<ST, Pg>,
    T: ToSql<ST, Pg> {
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>)
        -> serialize::Result {
        ToSql::<Multirange<ST>, Pg>::to_sql(self, out)
    }
}
impl<T, ST> ToSql<Nullable<Multirange<ST>>, Pg> for
    Vec<core::ops::RangeFrom<T>> where ST: 'static,
    Vec<core::ops::RangeFrom<T>>: ToSql<ST, Pg>, T: ToSql<ST, Pg> {
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>)
        -> serialize::Result {
        ToSql::<Multirange<ST>, Pg>::to_sql(self, out)
    }
}multirange_to_sql_nullable!(core::ops::RangeFrom<T>);
203impl<T, ST> ToSql<Nullable<Multirange<ST>>, Pg> for [core::ops::RangeTo<T>]
    where ST: 'static, [core::ops::RangeTo<T>]: ToSql<ST, Pg>,
    T: ToSql<ST, Pg> {
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>)
        -> serialize::Result {
        ToSql::<Multirange<ST>, Pg>::to_sql(self, out)
    }
}
impl<T, ST> ToSql<Nullable<Multirange<ST>>, Pg> for Vec<core::ops::RangeTo<T>>
    where ST: 'static, Vec<core::ops::RangeTo<T>>: ToSql<ST, Pg>,
    T: ToSql<ST, Pg> {
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>)
        -> serialize::Result {
        ToSql::<Multirange<ST>, Pg>::to_sql(self, out)
    }
}multirange_to_sql_nullable!(core::ops::RangeTo<T>);
204impl<T, ST> ToSql<Nullable<Multirange<ST>>, Pg> for
    [core::ops::RangeToInclusive<T>] where ST: 'static,
    [core::ops::RangeToInclusive<T>]: ToSql<ST, Pg>, T: ToSql<ST, Pg> {
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>)
        -> serialize::Result {
        ToSql::<Multirange<ST>, Pg>::to_sql(self, out)
    }
}
impl<T, ST> ToSql<Nullable<Multirange<ST>>, Pg> for
    Vec<core::ops::RangeToInclusive<T>> where ST: 'static,
    Vec<core::ops::RangeToInclusive<T>>: ToSql<ST, Pg>, T: ToSql<ST, Pg> {
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>)
        -> serialize::Result {
        ToSql::<Multirange<ST>, Pg>::to_sql(self, out)
    }
}multirange_to_sql_nullable!(core::ops::RangeToInclusive<T>);
205
206#[cfg(test)]
207mod tests {
208    use crate::deserialize::FromSql;
209    use crate::pg::Pg;
210    use crate::pg::PgValue;
211    use crate::sql_types::{Integer, Multirange};
212    use byteorder::{NetworkEndian, WriteBytesExt};
213    use std::ops::Bound;
214
215    #[test]
216    fn check_invalid_element_size_for_multirange() {
217        // check for the wrong element size
218        let mut value = Vec::<u8>::new();
219        // size
220        value.write_u32::<NetworkEndian>(1).unwrap();
221        // range size
222        value.write_i32::<NetworkEndian>(6).unwrap();
223        // just too less bytes
224        value.write_i32::<NetworkEndian>(42).unwrap();
225
226        let value = PgValue::for_test(&value);
227        let res =
228            <Vec<(Bound<i32>, Bound<i32>)> as FromSql<Multirange<Integer>, Pg>>::from_sql(value);
229        assert!(res.is_err());
230        assert_eq!(
231            format!("{}", res.unwrap_err()),
232            "Invalid element byte count: Expected at least 6 bytes, but only 4 bytes were received",
233        );
234        // invalid size
235        let mut value = Vec::<u8>::new();
236        // size
237        value.write_u32::<NetworkEndian>(1).unwrap();
238        let value = PgValue::for_test(&value);
239        // we don't need anything else, it should already fail here
240        let res =
241            <Vec<(Bound<i32>, Bound<i32>)> as FromSql<Multirange<Integer>, Pg>>::from_sql(value);
242        assert!(res.is_err());
243        assert_eq!(
244            format!("{}", res.unwrap_err()),
245            "failed to fill whole buffer"
246        );
247    }
248}