sqlx_core_oldapi/postgres/types/
array.rs1use bytes::Buf;
2use std::borrow::Cow;
3
4use crate::decode::Decode;
5use crate::encode::{Encode, IsNull};
6use crate::error::BoxDynError;
7use crate::postgres::type_info::PgType;
8use crate::postgres::types::Oid;
9use crate::postgres::{PgArgumentBuffer, PgTypeInfo, PgValueFormat, PgValueRef, Postgres};
10use crate::types::Type;
11
12pub trait PgHasArrayType {
13 fn array_type_info() -> PgTypeInfo;
14 fn array_compatible(ty: &PgTypeInfo) -> bool {
15 *ty == Self::array_type_info()
16 }
17}
18
19impl<T> PgHasArrayType for Option<T>
20where
21 T: PgHasArrayType,
22{
23 fn array_type_info() -> PgTypeInfo {
24 T::array_type_info()
25 }
26
27 fn array_compatible(ty: &PgTypeInfo) -> bool {
28 T::array_compatible(ty)
29 }
30}
31
32impl<T> Type<Postgres> for [T]
33where
34 T: PgHasArrayType,
35{
36 fn type_info() -> PgTypeInfo {
37 T::array_type_info()
38 }
39
40 fn compatible(ty: &PgTypeInfo) -> bool {
41 T::array_compatible(ty)
42 }
43}
44
45impl<T> Type<Postgres> for Vec<T>
46where
47 T: PgHasArrayType,
48{
49 fn type_info() -> PgTypeInfo {
50 T::array_type_info()
51 }
52
53 fn compatible(ty: &PgTypeInfo) -> bool {
54 T::array_compatible(ty)
55 }
56}
57
58impl<T, const N: usize> Type<Postgres> for [T; N]
59where
60 T: PgHasArrayType,
61{
62 fn type_info() -> PgTypeInfo {
63 T::array_type_info()
64 }
65
66 fn compatible(ty: &PgTypeInfo) -> bool {
67 T::array_compatible(ty)
68 }
69}
70
71impl<'q, T> Encode<'q, Postgres> for Vec<T>
72where
73 for<'a> &'a [T]: Encode<'q, Postgres>,
74 T: Encode<'q, Postgres>,
75{
76 #[inline]
77 fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
78 self.as_slice().encode_by_ref(buf)
79 }
80}
81
82impl<'q, T, const N: usize> Encode<'q, Postgres> for [T; N]
83where
84 for<'a> &'a [T]: Encode<'q, Postgres>,
85 T: Encode<'q, Postgres>,
86{
87 fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
88 self.as_slice().encode_by_ref(buf)
89 }
90}
91
92impl<'q, T> Encode<'q, Postgres> for &'_ [T]
93where
94 T: Encode<'q, Postgres> + Type<Postgres>,
95{
96 fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
97 let type_info = if self.len() < 1 {
98 T::type_info()
99 } else {
100 self[0].produces().unwrap_or_else(T::type_info)
101 };
102
103 buf.extend(&1_i32.to_be_bytes()); buf.extend(&0_i32.to_be_bytes()); match type_info.0 {
108 PgType::DeclareWithName(name) => buf.patch_type_by_name(&name),
109
110 ty => {
111 buf.extend(&ty.oid().0.to_be_bytes());
112 }
113 }
114
115 let len = i32::try_from(self.len()).unwrap_or(i32::MAX);
116
117 buf.extend(len.to_be_bytes()); buf.extend(&1_i32.to_be_bytes()); for element in self.iter().take(usize::try_from(len).unwrap()) {
121 buf.encode(element);
122 }
123
124 IsNull::No
125 }
126}
127
128impl<'r, T, const N: usize> Decode<'r, Postgres> for [T; N]
129where
130 T: for<'a> Decode<'a, Postgres> + Type<Postgres>,
131{
132 fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
133 let vec: Vec<T> = Decode::decode(value)?;
136 let array: [T; N] = vec.try_into().map_err(|_| "wrong number of elements")?;
137 Ok(array)
138 }
139}
140
141impl<'r, T> Decode<'r, Postgres> for Vec<T>
142where
143 T: for<'a> Decode<'a, Postgres> + Type<Postgres>,
144{
145 fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
146 let format = value.format();
147
148 match format {
149 PgValueFormat::Binary => {
150 let mut buf = value.as_bytes()?;
153
154 let ndim = buf.get_i32();
156
157 if ndim == 0 {
158 return Ok(Vec::new());
160 }
161
162 if ndim != 1 {
163 return Err(format!("encountered an array of {} dimensions; only one-dimensional arrays are supported", ndim).into());
164 }
165
166 let _flags = buf.get_i32();
170
171 let element_type_oid = Oid(buf.get_u32());
173 let element_type_info: PgTypeInfo = PgTypeInfo::try_from_oid(element_type_oid)
174 .or_else(|| value.type_info.try_array_element().map(Cow::into_owned))
175 .ok_or_else(|| {
176 BoxDynError::from(format!(
177 "failed to resolve array element type for oid {}",
178 element_type_oid.0
179 ))
180 })?;
181
182 let len = buf.get_i32();
184
185 let lower = buf.get_i32();
187
188 if lower != 1 {
189 return Err(format!("encountered an array with a lower bound of {} in the first dimension; only arrays starting at one are supported", lower).into());
190 }
191
192 let mut elements = Vec::with_capacity(usize::try_from(len).unwrap_or_default());
193
194 for _ in 0..len {
195 elements.push(T::decode(PgValueRef::get(
196 &mut buf,
197 format,
198 element_type_info.clone(),
199 ))?)
200 }
201
202 Ok(elements)
203 }
204
205 PgValueFormat::Text => {
206 let element_type_info = T::type_info();
208
209 let s = value.as_str()?;
210
211 let s = &s[1..(s.len() - 1)];
215
216 if s.is_empty() {
217 return Ok(Vec::new());
219 }
220
221 let delimiter = ',';
228 let mut done = false;
229 let mut in_quotes = false;
230 let mut in_escape = false;
231 let mut value = String::with_capacity(10);
232 let mut chars = s.chars();
233 let mut elements = Vec::with_capacity(4);
234
235 while !done {
236 loop {
237 match chars.next() {
238 Some(ch) => match ch {
239 _ if in_escape => {
240 value.push(ch);
241 in_escape = false;
242 }
243
244 '"' => {
245 in_quotes = !in_quotes;
246 }
247
248 '\\' => {
249 in_escape = true;
250 }
251
252 _ if ch == delimiter && !in_quotes => {
253 break;
254 }
255
256 _ => {
257 value.push(ch);
258 }
259 },
260
261 None => {
262 done = true;
263 break;
264 }
265 }
266 }
267
268 let value_opt = if value == "NULL" {
269 None
270 } else {
271 Some(value.as_bytes())
272 };
273
274 elements.push(T::decode(PgValueRef {
275 value: value_opt,
276 row: None,
277 type_info: element_type_info.clone(),
278 format,
279 })?);
280
281 value.clear();
282 }
283
284 Ok(elements)
285 }
286 }
287 }
288}