1use bytes::Buf;
2use std::borrow::Cow;
3use cdbc::decode::Decode;
4use cdbc::encode::{Encode, IsNull};
5use cdbc::error::BoxDynError;
6use crate::type_info::PgType;
7use crate::{PgArgumentBuffer, PgTypeInfo, PgValueFormat, PgValueRef, Postgres};
8use cdbc::types::Type;
9
10pub trait PgHasArrayType {
11 fn array_type_info() -> PgTypeInfo;
12 fn array_compatible(ty: &PgTypeInfo) -> bool {
13 *ty == Self::array_type_info()
14 }
15}
16
17impl<T> PgHasArrayType for Option<T>
18where
19 T: PgHasArrayType,
20{
21 fn array_type_info() -> PgTypeInfo {
22 T::array_type_info()
23 }
24
25 fn array_compatible(ty: &PgTypeInfo) -> bool {
26 T::array_compatible(ty)
27 }
28}
29
30impl<T> Type<Postgres> for [T]
31where
32 T: PgHasArrayType,
33{
34 fn type_info() -> PgTypeInfo {
35 T::array_type_info()
36 }
37
38 fn compatible(ty: &PgTypeInfo) -> bool {
39 T::array_compatible(ty)
40 }
41}
42
43impl<T> Type<Postgres> for Vec<T>
44where
45 T: PgHasArrayType,
46{
47 fn type_info() -> PgTypeInfo {
48 T::array_type_info()
49 }
50
51 fn compatible(ty: &PgTypeInfo) -> bool {
52 T::array_compatible(ty)
53 }
54}
55
56impl<'q, T> Encode<'q, Postgres> for Vec<T>
57where
58 for<'a> &'a [T]: Encode<'q, Postgres>,
59 T: Encode<'q, Postgres>,
60{
61 #[inline]
62 fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
63 self.as_slice().encode_by_ref(buf)
64 }
65}
66
67impl<'q, T> Encode<'q, Postgres> for &'_ [T]
68where
69 T: Encode<'q, Postgres> + Type<Postgres>,
70{
71 fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
72 let type_info = if self.len() < 1 {
73 T::type_info()
74 } else {
75 self[0].produces().unwrap_or_else(T::type_info)
76 };
77
78 buf.extend(&1_i32.to_be_bytes()); buf.extend(&0_i32.to_be_bytes()); match type_info.0 {
83 PgType::DeclareWithName(name) => buf.patch_type_by_name(&name),
84
85 ty => {
86 buf.extend(&ty.oid().to_be_bytes());
87 }
88 }
89
90 buf.extend(&(self.len() as i32).to_be_bytes()); buf.extend(&1_i32.to_be_bytes()); for element in self.iter() {
94 buf.encode(element);
95 }
96
97 IsNull::No
98 }
99}
100
101impl<'r, T> Decode<'r, Postgres> for Vec<T>
102where
103 T: for<'a> Decode<'a, Postgres> + Type<Postgres>,
104{
105 fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
106 let format = value.format();
107
108 match format {
109 PgValueFormat::Binary => {
110 let mut buf = value.as_bytes()?;
113
114 let ndim = buf.get_i32();
116
117 if ndim == 0 {
118 return Ok(Vec::new());
120 }
121
122 if ndim != 1 {
123 return Err(format!("encountered an array of {} dimensions; only one-dimensional arrays are supported", ndim).into());
124 }
125
126 let _flags = buf.get_i32();
130
131 let element_type_oid = buf.get_u32();
133 let element_type_info: PgTypeInfo = PgTypeInfo::try_from_oid(element_type_oid)
134 .or_else(|| value.type_info.try_array_element().map(Cow::into_owned))
135 .unwrap_or_else(|| PgTypeInfo(PgType::DeclareWithOid(element_type_oid)));
136
137 let len = buf.get_i32();
139
140 let lower = buf.get_i32();
142
143 if lower != 1 {
144 return Err(format!("encountered an array with a lower bound of {} in the first dimension; only arrays starting at one are supported", lower).into());
145 }
146
147 let mut elements = Vec::with_capacity(len as usize);
148
149 for _ in 0..len {
150 elements.push(T::decode(PgValueRef::get(
151 &mut buf,
152 format,
153 element_type_info.clone(),
154 ))?)
155 }
156
157 Ok(elements)
158 }
159
160 PgValueFormat::Text => {
161 let element_type_info = T::type_info();
163
164 let s = value.as_str()?;
165
166 let s = &s[1..(s.len() - 1)];
170
171 if s.is_empty() {
172 return Ok(Vec::new());
174 }
175
176 let delimiter = ',';
183 let mut done = false;
184 let mut in_quotes = false;
185 let mut in_escape = false;
186 let mut value = String::with_capacity(10);
187 let mut chars = s.chars();
188 let mut elements = Vec::with_capacity(4);
189
190 while !done {
191 loop {
192 match chars.next() {
193 Some(ch) => match ch {
194 _ if in_escape => {
195 value.push(ch);
196 in_escape = false;
197 }
198
199 '"' => {
200 in_quotes = !in_quotes;
201 }
202
203 '\\' => {
204 in_escape = true;
205 }
206
207 _ if ch == delimiter && !in_quotes => {
208 break;
209 }
210
211 _ => {
212 value.push(ch);
213 }
214 },
215
216 None => {
217 done = true;
218 break;
219 }
220 }
221 }
222
223 let value_opt = if value == "NULL" {
224 None
225 } else {
226 Some(value.as_bytes())
227 };
228
229 elements.push(T::decode(PgValueRef {
230 value: value_opt,
231 row: None,
232 type_info: element_type_info.clone(),
233 format,
234 })?);
235
236 value.clear();
237 }
238
239 Ok(elements)
240 }
241 }
242 }
243}