diesel/pg/types/
array.rs

1use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt};
2use std::fmt;
3use std::io::Write;
4
5use deserialize::{self, FromSql};
6use pg::{Pg, PgMetadataLookup, PgTypeMetadata};
7use serialize::{self, IsNull, Output, ToSql};
8use sql_types::{Array, HasSqlType, Nullable};
9
10impl<T> HasSqlType<Array<T>> for Pg
11where
12    Pg: HasSqlType<T>,
13{
14    fn metadata(lookup: &PgMetadataLookup) -> PgTypeMetadata {
15        PgTypeMetadata {
16            oid: <Pg as HasSqlType<T>>::metadata(lookup).array_oid,
17            array_oid: 0,
18        }
19    }
20}
21
22impl<T, ST> FromSql<Array<ST>, Pg> for Vec<T>
23where
24    T: FromSql<ST, Pg>,
25{
26    fn from_sql(bytes: Option<&[u8]>) -> deserialize::Result<Self> {
27        let mut bytes = not_none!(bytes);
28        let num_dimensions = bytes.read_i32::<NetworkEndian>()?;
29        let has_null = bytes.read_i32::<NetworkEndian>()? != 0;
30        let _oid = bytes.read_i32::<NetworkEndian>()?;
31
32        if num_dimensions == 0 {
33            return Ok(Vec::new());
34        }
35
36        let num_elements = bytes.read_i32::<NetworkEndian>()?;
37        let _lower_bound = bytes.read_i32::<NetworkEndian>()?;
38
39        if num_dimensions != 1 {
40            return Err("multi-dimensional arrays are not supported".into());
41        }
42
43        (0..num_elements)
44            .map(|_| {
45                let elem_size = bytes.read_i32::<NetworkEndian>()?;
46                if has_null && elem_size == -1 {
47                    T::from_sql(None)
48                } else {
49                    let (elem_bytes, new_bytes) = bytes.split_at(elem_size as usize);
50                    bytes = new_bytes;
51                    T::from_sql(Some(elem_bytes))
52                }
53            })
54            .collect()
55    }
56}
57
58use expression::bound::Bound;
59use expression::AsExpression;
60
61macro_rules! array_as_expression {
62    ($ty:ty, $sql_type:ty) => {
63        impl<'a, 'b, ST, T> AsExpression<$sql_type> for $ty {
64            type Expression = Bound<$sql_type, Self>;
65
66            fn as_expression(self) -> Self::Expression {
67                Bound::new(self)
68            }
69        }
70    };
71}
72
73array_as_expression!(&'a [T], Array<ST>);
74array_as_expression!(&'a [T], Nullable<Array<ST>>);
75array_as_expression!(&'a &'b [T], Array<ST>);
76array_as_expression!(&'a &'b [T], Nullable<Array<ST>>);
77array_as_expression!(Vec<T>, Array<ST>);
78array_as_expression!(Vec<T>, Nullable<Array<ST>>);
79array_as_expression!(&'a Vec<T>, Array<ST>);
80array_as_expression!(&'a Vec<T>, Nullable<Array<ST>>);
81array_as_expression!(&'a &'b Vec<T>, Array<ST>);
82array_as_expression!(&'a &'b Vec<T>, Nullable<Array<ST>>);
83
84impl<ST, T> ToSql<Array<ST>, Pg> for [T]
85where
86    Pg: HasSqlType<ST>,
87    T: ToSql<ST, Pg>,
88{
89    fn to_sql<W: Write>(&self, out: &mut Output<W, Pg>) -> serialize::Result {
90        let num_dimensions = 1;
91        out.write_i32::<NetworkEndian>(num_dimensions)?;
92        let flags = 0;
93        out.write_i32::<NetworkEndian>(flags)?;
94        let element_oid = Pg::metadata(out.metadata_lookup()).oid;
95        out.write_u32::<NetworkEndian>(element_oid)?;
96        out.write_i32::<NetworkEndian>(self.len() as i32)?;
97        let lower_bound = 1;
98        out.write_i32::<NetworkEndian>(lower_bound)?;
99
100        let mut buffer = out.with_buffer(Vec::new());
101        for elem in self.iter() {
102            let is_null = elem.to_sql(&mut buffer)?;
103            if let IsNull::No = is_null {
104                out.write_i32::<NetworkEndian>(buffer.len() as i32)?;
105                out.write_all(&buffer)?;
106                buffer.clear();
107            } else {
108                // https://github.com/postgres/postgres/blob/82f8107b92c9104ec9d9465f3f6a4c6dab4c124a/src/backend/utils/adt/arrayfuncs.c#L1461
109                out.write_i32::<NetworkEndian>(-1)?;
110            }
111        }
112
113        Ok(IsNull::No)
114    }
115}
116
117impl<ST, T> ToSql<Nullable<Array<ST>>, Pg> for [T]
118where
119    [T]: ToSql<Array<ST>, Pg>,
120{
121    fn to_sql<W: Write>(&self, out: &mut Output<W, Pg>) -> serialize::Result {
122        ToSql::<Array<ST>, Pg>::to_sql(self, out)
123    }
124}
125
126impl<ST, T> ToSql<Array<ST>, Pg> for Vec<T>
127where
128    [T]: ToSql<Array<ST>, Pg>,
129    T: fmt::Debug,
130{
131    fn to_sql<W: Write>(&self, out: &mut Output<W, Pg>) -> serialize::Result {
132        (self as &[T]).to_sql(out)
133    }
134}
135
136impl<ST, T> ToSql<Nullable<Array<ST>>, Pg> for Vec<T>
137where
138    Vec<T>: ToSql<Array<ST>, Pg>,
139{
140    fn to_sql<W: Write>(&self, out: &mut Output<W, Pg>) -> serialize::Result {
141        ToSql::<Array<ST>, Pg>::to_sql(self, out)
142    }
143}