sqlx_postgres/types/
array.rs1use sqlx_core::bytes::Buf;
2use sqlx_core::types::Text;
3use std::borrow::Cow;
4
5use crate::decode::Decode;
6use crate::encode::{Encode, IsNull};
7use crate::error::BoxDynError;
8use crate::types::Oid;
9use crate::types::Type;
10use crate::{PgArgumentBuffer, PgTypeInfo, PgValueFormat, PgValueRef, Postgres};
11
12pub trait PgHasArrayType {
51 fn array_type_info() -> PgTypeInfo;
52 fn array_compatible(ty: &PgTypeInfo) -> bool {
53 *ty == Self::array_type_info()
54 }
55}
56
57impl<T> PgHasArrayType for &T
58where
59 T: PgHasArrayType,
60{
61 fn array_type_info() -> PgTypeInfo {
62 T::array_type_info()
63 }
64
65 fn array_compatible(ty: &PgTypeInfo) -> bool {
66 T::array_compatible(ty)
67 }
68}
69
70impl<T> PgHasArrayType for Option<T>
71where
72 T: PgHasArrayType,
73{
74 fn array_type_info() -> PgTypeInfo {
75 T::array_type_info()
76 }
77
78 fn array_compatible(ty: &PgTypeInfo) -> bool {
79 T::array_compatible(ty)
80 }
81}
82
83impl<T> PgHasArrayType for Text<T> {
84 fn array_type_info() -> PgTypeInfo {
85 String::array_type_info()
86 }
87
88 fn array_compatible(ty: &PgTypeInfo) -> bool {
89 String::array_compatible(ty)
90 }
91}
92
93impl<T> Type<Postgres> for [T]
94where
95 T: PgHasArrayType,
96{
97 fn type_info() -> PgTypeInfo {
98 T::array_type_info()
99 }
100
101 fn compatible(ty: &PgTypeInfo) -> bool {
102 T::array_compatible(ty)
103 }
104}
105
106impl<T> Type<Postgres> for Vec<T>
107where
108 T: PgHasArrayType,
109{
110 fn type_info() -> PgTypeInfo {
111 T::array_type_info()
112 }
113
114 fn compatible(ty: &PgTypeInfo) -> bool {
115 T::array_compatible(ty)
116 }
117}
118
119impl<T, const N: usize> Type<Postgres> for [T; N]
120where
121 T: PgHasArrayType,
122{
123 fn type_info() -> PgTypeInfo {
124 T::array_type_info()
125 }
126
127 fn compatible(ty: &PgTypeInfo) -> bool {
128 T::array_compatible(ty)
129 }
130}
131
132impl<'q, T> Encode<'q, Postgres> for Vec<T>
133where
134 for<'a> &'a [T]: Encode<'q, Postgres>,
135 T: Encode<'q, Postgres>,
136{
137 #[inline]
138 fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
139 self.as_slice().encode_by_ref(buf)
140 }
141}
142
143impl<'q, T, const N: usize> Encode<'q, Postgres> for [T; N]
144where
145 for<'a> &'a [T]: Encode<'q, Postgres>,
146 T: Encode<'q, Postgres>,
147{
148 fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
149 self.as_slice().encode_by_ref(buf)
150 }
151}
152
153impl<'q, T> Encode<'q, Postgres> for &'_ [T]
154where
155 T: Encode<'q, Postgres> + Type<Postgres>,
156{
157 fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
158 i32::try_from(self.len()).map_err(|_| {
160 format!(
161 "encoded array length is too large for Postgres: {}",
162 self.len()
163 )
164 })?;
165 crate::PgBindIterExt::bind_iter(self.iter()).encode(buf)
166 }
167}
168
169impl<'r, T, const N: usize> Decode<'r, Postgres> for [T; N]
170where
171 T: for<'a> Decode<'a, Postgres> + Type<Postgres>,
172{
173 fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
174 let vec: Vec<T> = Decode::decode(value)?;
177 let array: [T; N] = vec.try_into().map_err(|_| "wrong number of elements")?;
178 Ok(array)
179 }
180}
181
182impl<'r, T> Decode<'r, Postgres> for Vec<T>
183where
184 T: for<'a> Decode<'a, Postgres> + Type<Postgres>,
185{
186 fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
187 let format = value.format();
188
189 match format {
190 PgValueFormat::Binary => {
191 let mut buf = value.as_bytes()?;
194
195 let ndim = buf.get_i32();
197
198 if ndim == 0 {
199 return Ok(Vec::new());
201 }
202
203 if ndim != 1 {
204 return Err(format!("encountered an array of {ndim} dimensions; only one-dimensional arrays are supported").into());
205 }
206
207 let _flags = buf.get_i32();
211
212 let element_type_oid = Oid(buf.get_u32());
214 let element_type_info: PgTypeInfo = PgTypeInfo::try_from_oid(element_type_oid)
215 .or_else(|| value.type_info.try_array_element().map(Cow::into_owned))
216 .ok_or_else(|| {
217 BoxDynError::from(format!(
218 "failed to resolve array element type for oid {}",
219 element_type_oid.0
220 ))
221 })?;
222
223 let len = buf.get_i32();
225
226 let len = usize::try_from(len)
227 .map_err(|_| format!("overflow converting array len ({len}) to usize"))?;
228
229 let lower = buf.get_i32();
231
232 if lower != 1 {
233 return Err(format!("encountered an array with a lower bound of {lower} in the first dimension; only arrays starting at one are supported").into());
234 }
235
236 let mut elements = Vec::with_capacity(len);
237
238 for _ in 0..len {
239 let value_ref = PgValueRef::get(&mut buf, format, element_type_info.clone())?;
240
241 elements.push(T::decode(value_ref)?);
242 }
243
244 Ok(elements)
245 }
246
247 PgValueFormat::Text => {
248 let element_type_info = T::type_info();
250
251 let s = value.as_str()?;
252
253 let s = &s[1..(s.len() - 1)];
257
258 if s.is_empty() {
259 return Ok(Vec::new());
261 }
262
263 let delimiter = ',';
270 let mut done = false;
271 let mut in_quotes = false;
272 let mut in_escape = false;
273 let mut value = String::with_capacity(10);
274 let mut chars = s.chars();
275 let mut elements = Vec::with_capacity(4);
276
277 while !done {
278 loop {
279 match chars.next() {
280 Some(ch) => match ch {
281 _ if in_escape => {
282 value.push(ch);
283 in_escape = false;
284 }
285
286 '"' => {
287 in_quotes = !in_quotes;
288 }
289
290 '\\' => {
291 in_escape = true;
292 }
293
294 _ if ch == delimiter && !in_quotes => {
295 break;
296 }
297
298 _ => {
299 value.push(ch);
300 }
301 },
302
303 None => {
304 done = true;
305 break;
306 }
307 }
308 }
309
310 let value_opt = if value == "NULL" {
311 None
312 } else {
313 Some(value.as_bytes())
314 };
315
316 elements.push(T::decode(PgValueRef {
317 value: value_opt,
318 row: None,
319 type_info: element_type_info.clone(),
320 format,
321 })?);
322
323 value.clear();
324 }
325
326 Ok(elements)
327 }
328 }
329 }
330}