1use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt};
2use std::fmt;
3use std::io::Write;
4
5use crate::deserialize::{self, FromSql};
6use crate::pg::{Pg, PgTypeMetadata, PgValue};
7use crate::query_builder::bind_collector::ByteWrapper;
8use crate::serialize::{self, IsNull, Output, ToSql};
9use crate::sql_types::{Array, HasSqlType, Nullable};
10
11#[cfg(feature = "postgres_backend")]
12impl<T> HasSqlType<Array<T>> for Pg
13where
14 Pg: HasSqlType<T>,
15{
16 fn metadata(lookup: &mut Self::MetadataLookup) -> PgTypeMetadata {
17 match <Pg as HasSqlType<T>>::metadata(lookup).0 {
18 Ok(tpe) => PgTypeMetadata::new(tpe.array_oid, 0),
19 c @ Err(_) => PgTypeMetadata(c),
20 }
21 }
22}
23
24#[cfg(feature = "postgres_backend")]
25impl<T, ST> FromSql<Array<ST>, Pg> for Vec<T>
26where
27 T: FromSql<ST, Pg>,
28{
29 fn from_sql(value: PgValue<'_>) -> deserialize::Result<Self> {
30 let mut bytes = value.as_bytes();
31 let num_dimensions = bytes.read_i32::<NetworkEndian>()?;
32 let has_null = bytes.read_i32::<NetworkEndian>()? != 0;
33 let _oid = bytes.read_i32::<NetworkEndian>()?;
34
35 if num_dimensions == 0 {
36 return Ok(Vec::new());
37 }
38
39 let num_elements = bytes.read_i32::<NetworkEndian>()?;
40 let _lower_bound = bytes.read_i32::<NetworkEndian>()?;
41
42 if num_dimensions != 1 {
43 return Err("multi-dimensional arrays are not supported".into());
44 }
45
46 (0..num_elements)
47 .map(|_| {
48 let elem_size = bytes.read_i32::<NetworkEndian>()?;
49 if has_null && elem_size == -1 {
50 T::from_nullable_sql(None)
51 } else {
52 let (elem_bytes, new_bytes) = bytes.split_at(elem_size.try_into()?);
53 bytes = new_bytes;
54 T::from_sql(PgValue::new_internal(elem_bytes, &value))
55 }
56 })
57 .collect()
58 }
59}
60
61use crate::expression::bound::Bound;
62use crate::expression::AsExpression;
63
64macro_rules! array_as_expression {
65 ($ty:ty, $sql_type:ty) => {
66 #[cfg(feature = "postgres_backend")]
67 #[allow(clippy::extra_unused_lifetimes)]
70 impl<'a, 'b, ST: 'static, T> AsExpression<$sql_type> for $ty {
71 type Expression = Bound<$sql_type, Self>;
72
73 fn as_expression(self) -> Self::Expression {
74 Bound::new(self)
75 }
76 }
77 };
78}
79
80array_as_expression!(&'a [T], Array<ST>);
81array_as_expression!(&'a [T], Nullable<Array<ST>>);
82array_as_expression!(&'a &'b [T], Array<ST>);
83array_as_expression!(&'a &'b [T], Nullable<Array<ST>>);
84array_as_expression!(Vec<T>, Array<ST>);
85array_as_expression!(Vec<T>, Nullable<Array<ST>>);
86array_as_expression!(&'a Vec<T>, Array<ST>);
87array_as_expression!(&'a Vec<T>, Nullable<Array<ST>>);
88array_as_expression!(&'a &'b Vec<T>, Array<ST>);
89array_as_expression!(&'a &'b Vec<T>, Nullable<Array<ST>>);
90
91#[cfg(feature = "postgres_backend")]
92impl<ST, T> ToSql<Array<ST>, Pg> for [T]
93where
94 Pg: HasSqlType<ST>,
95 T: ToSql<ST, Pg>,
96{
97 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
98 let num_dimensions = 1;
99 out.write_i32::<NetworkEndian>(num_dimensions)?;
100 let flags = 0;
101 out.write_i32::<NetworkEndian>(flags)?;
102 let element_oid = Pg::metadata(out.metadata_lookup()).oid()?;
103 out.write_u32::<NetworkEndian>(element_oid)?;
104 out.write_i32::<NetworkEndian>(self.len().try_into()?)?;
105 let lower_bound = 1;
106 out.write_i32::<NetworkEndian>(lower_bound)?;
107
108 let mut buffer = Vec::new();
111
112 for elem in self.iter() {
113 let is_null = {
114 let mut temp_buffer = Output::new(ByteWrapper(&mut buffer), out.metadata_lookup());
115 elem.to_sql(&mut temp_buffer)?
116 };
117
118 if let IsNull::No = is_null {
119 out.write_i32::<NetworkEndian>(buffer.len().try_into()?)?;
120 out.write_all(&buffer)?;
121 buffer.clear();
122 } else {
123 out.write_i32::<NetworkEndian>(-1)?;
125 }
126 }
127
128 Ok(IsNull::No)
129 }
130}
131
132#[cfg(feature = "postgres_backend")]
133impl<ST, T> ToSql<Nullable<Array<ST>>, Pg> for [T]
134where
135 [T]: ToSql<Array<ST>, Pg>,
136 ST: 'static,
137{
138 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
139 ToSql::<Array<ST>, Pg>::to_sql(self, out)
140 }
141}
142
143#[cfg(feature = "postgres_backend")]
144impl<ST, T> ToSql<Array<ST>, Pg> for Vec<T>
145where
146 ST: 'static,
147 [T]: ToSql<Array<ST>, Pg>,
148 T: fmt::Debug,
149{
150 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
151 (self as &[T]).to_sql(out)
152 }
153}
154
155#[cfg(feature = "postgres_backend")]
156impl<ST, T> ToSql<Nullable<Array<ST>>, Pg> for Vec<T>
157where
158 ST: 'static,
159 Vec<T>: ToSql<Array<ST>, Pg>,
160{
161 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
162 ToSql::<Array<ST>, Pg>::to_sql(self, out)
163 }
164}