1use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt};
2use core::fmt;
3use core::num::NonZeroU32;
4use std::io::Write;
5
6use crate::deserialize::{self, FromSql, FromSqlRow};
7use crate::pg::{Pg, PgTypeMetadata, PgValue};
8use crate::query_builder::bind_collector::ByteWrapper;
9use crate::serialize::{self, IsNull, Output, ToSql};
10use crate::sql_types::{Array, HasSqlType, Nullable};
11
12#[cfg(feature = "postgres_backend")]
13#[derive(#[automatically_derived]
impl<T: ::core::fmt::Debug> ::core::fmt::Debug for NdArray<T> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field2_finish(f, "NdArray",
"dims", &self.dims, "data", &&self.data)
}
}Debug, #[automatically_derived]
impl<T: ::core::clone::Clone> ::core::clone::Clone for NdArray<T> {
#[inline]
fn clone(&self) -> Self {
Self {
dims: ::core::clone::Clone::clone(&self.dims),
data: ::core::clone::Clone::clone(&self.data),
}
}
}Clone, #[automatically_derived]
impl<T: ::core::cmp::PartialEq> ::core::marker::StructuralPartialEq for
NdArray<T> {
}
#[automatically_derived]
impl<T: ::core::cmp::PartialEq> ::core::cmp::PartialEq for NdArray<T> {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.dims == other.dims && self.data == other.data
}
}PartialEq, #[automatically_derived]
impl<T: ::core::cmp::Eq> ::core::cmp::Eq for NdArray<T> {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<Vec<usize>>;
let _: ::core::cmp::AssertParamIsEq<Vec<T>>;
}
}Eq, #[automatically_derived]
impl<T: ::core::hash::Hash> ::core::hash::Hash for NdArray<T> {
#[inline]
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
::core::hash::Hash::hash(&self.dims, state);
::core::hash::Hash::hash(&self.data, state)
}
}Hash, #[automatically_derived]
impl<T: ::core::cmp::PartialOrd> ::core::cmp::PartialOrd for NdArray<T> {
#[inline]
fn partial_cmp(&self, other: &Self)
-> ::core::option::Option<::core::cmp::Ordering> {
match ::core::cmp::PartialOrd::partial_cmp(&self.dims, &other.dims) {
::core::option::Option::Some(::core::cmp::Ordering::Equal) =>
::core::cmp::PartialOrd::partial_cmp(&self.data, &other.data),
cmp => cmp,
}
}
}PartialOrd, #[automatically_derived]
impl<T: ::core::cmp::Ord> ::core::cmp::Ord for NdArray<T> {
#[inline]
fn cmp(&self, other: &Self) -> ::core::cmp::Ordering {
match ::core::cmp::Ord::cmp(&self.dims, &other.dims) {
::core::cmp::Ordering::Equal =>
::core::cmp::Ord::cmp(&self.data, &other.data),
cmp => cmp,
}
}
}Ord, const _: () =
{
use diesel;
impl<'__expr, T> diesel::expression::AsExpression<Array<T>> for
&'__expr NdArray<T> {
type Expression =
diesel::internal::derives::as_expression::Bound<Array<T>,
Self>;
fn as_expression(self)
->
<Self as
diesel::expression::AsExpression<Array<T>>>::Expression {
diesel::internal::derives::as_expression::Bound::new(self)
}
}
#[diagnostic::do_not_recommend]
impl<'__expr, T>
diesel::expression::AsExpression<diesel::sql_types::Nullable<Array<T>>>
for &'__expr NdArray<T> {
type Expression =
diesel::internal::derives::as_expression::Bound<diesel::sql_types::Nullable<Array<T>>,
Self>;
fn as_expression(self)
->
<Self as
diesel::expression::AsExpression<diesel::sql_types::Nullable<Array<T>>>>::Expression {
diesel::internal::derives::as_expression::Bound::new(self)
}
}
#[diagnostic::do_not_recommend]
impl<'__expr, '__expr2, T> diesel::expression::AsExpression<Array<T>>
for &'__expr2 &'__expr NdArray<T> {
type Expression =
diesel::internal::derives::as_expression::Bound<Array<T>,
Self>;
fn as_expression(self)
->
<Self as
diesel::expression::AsExpression<Array<T>>>::Expression {
diesel::internal::derives::as_expression::Bound::new(self)
}
}
#[diagnostic::do_not_recommend]
impl<'__expr, '__expr2, T>
diesel::expression::AsExpression<diesel::sql_types::Nullable<Array<T>>>
for &'__expr2 &'__expr NdArray<T> {
type Expression =
diesel::internal::derives::as_expression::Bound<diesel::sql_types::Nullable<Array<T>>,
Self>;
fn as_expression(self)
->
<Self as
diesel::expression::AsExpression<diesel::sql_types::Nullable<Array<T>>>>::Expression {
diesel::internal::derives::as_expression::Bound::new(self)
}
}
impl<T, __DB>
diesel::serialize::ToSql<diesel::sql_types::Nullable<Array<T>>,
__DB> for NdArray<T> where __DB: diesel::backend::Backend,
Self: diesel::serialize::ToSql<Array<T>, __DB> {
fn to_sql<'__b>(&'__b self,
out: &mut diesel::serialize::Output<'__b, '_, __DB>)
-> diesel::serialize::Result {
diesel::serialize::ToSql::<Array<T>, __DB>::to_sql(self, out)
}
}
impl<T> diesel::expression::AsExpression<Array<T>> for NdArray<T> {
type Expression =
diesel::internal::derives::as_expression::Bound<Array<T>,
Self>;
fn as_expression(self)
->
<Self as
diesel::expression::AsExpression<Array<T>>>::Expression {
diesel::internal::derives::as_expression::Bound::new(self)
}
}
impl<T>
diesel::expression::AsExpression<diesel::sql_types::Nullable<Array<T>>>
for NdArray<T> {
type Expression =
diesel::internal::derives::as_expression::Bound<diesel::sql_types::Nullable<Array<T>>,
Self>;
fn as_expression(self)
->
<Self as
diesel::expression::AsExpression<diesel::sql_types::Nullable<Array<T>>>>::Expression {
diesel::internal::derives::as_expression::Bound::new(self)
}
}
};AsExpression, const _: () =
{
use diesel;
impl<T, __DB, __ST> diesel::deserialize::Queryable<__ST, __DB> for
NdArray<T> where __DB: diesel::backend::Backend,
__ST: diesel::sql_types::SingleValue,
Self: diesel::deserialize::FromSql<__ST, __DB> {
type Row = Self;
fn build(row: Self) -> diesel::deserialize::Result<Self> {
diesel::deserialize::Result::Ok(row)
}
}
};FromSqlRow)]
14#[diesel(sql_type = Array<T>)]
15pub struct NdArray<T> {
19 pub dims: Vec<usize>,
21 pub data: Vec<T>,
29}
30
31#[cfg(feature = "postgres_backend")]
32impl<T> HasSqlType<Array<T>> for Pg
33where
34 Pg: HasSqlType<T>,
35{
36 fn metadata(lookup: &mut Self::MetadataLookup) -> PgTypeMetadata {
37 match <Pg as HasSqlType<T>>::metadata(lookup).0 {
38 Ok(tpe) => PgTypeMetadata::new(tpe.array_oid, 0),
39 c @ Err(_) => PgTypeMetadata(c),
40 }
41 }
42}
43
44#[cfg(feature = "postgres_backend")]
45impl<T, ST> FromSql<Array<ST>, Pg> for Vec<T>
46where
47 T: FromSql<ST, Pg>,
48{
49 fn from_sql(value: PgValue<'_>) -> deserialize::Result<Self> {
50 let mut bytes = value.as_bytes();
51 let num_dimensions = bytes.read_i32::<NetworkEndian>()?;
52 let has_null = bytes.read_i32::<NetworkEndian>()? != 0;
53 let element_oid =
54 NonZeroU32::new(bytes.read_u32::<NetworkEndian>()?).ok_or("Oid's aren't zero")?;
55
56 if num_dimensions == 0 {
57 return Ok(Vec::new());
58 }
59
60 let num_elements = bytes.read_i32::<NetworkEndian>()?;
61 let _lower_bound = bytes.read_i32::<NetworkEndian>()?;
62
63 if num_dimensions != 1 {
64 return Err("multi-dimensional arrays are not supported".into());
65 }
66
67 (0..num_elements)
68 .map(|_| -> deserialize::Result<_> {
69 let elem_size = bytes.read_i32::<NetworkEndian>()?;
70 if has_null && elem_size == -1 {
71 T::from_nullable_sql(None)
72 } else {
73 let (elem_bytes, new_bytes) = bytes
74 .split_at_checked(elem_size.try_into()?)
75 .ok_or_else(|| {
76 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("Invalid element byte count: Expected at least {1} bytes, but only {0} bytes were received",
bytes.len(), elem_size))
})format!(
77 "Invalid element byte count: Expected at least {elem_size} bytes, but only {} bytes were received",
78 bytes.len()
79 )
80 })?;
81 bytes = new_bytes;
82 T::from_sql(PgValue::new_internal(elem_bytes, &element_oid))
83 }
84 })
85 .collect()
86 }
87}
88
89#[cfg(feature = "postgres_backend")]
90impl<T, ST> FromSql<Array<ST>, Pg> for NdArray<T>
91where
92 T: FromSql<ST, Pg>,
93{
94 fn from_sql(value: PgValue<'_>) -> deserialize::Result<Self> {
95 let mut bytes = value.as_bytes();
96 let num_dimensions = bytes.read_i32::<NetworkEndian>()?;
97 let has_null = bytes.read_i32::<NetworkEndian>()? != 0;
98 let element_oid =
99 NonZeroU32::new(bytes.read_u32::<NetworkEndian>()?).ok_or("Oid's aren't zero")?;
100
101 if num_dimensions == 0 {
102 return Ok(NdArray {
103 dims: Vec::new(),
104 data: Vec::new(),
105 });
106 }
107
108 let num_dims: usize = num_dimensions
109 .try_into()
110 .map_err(|_| "number of dimensions must be positive")?;
111
112 let dims = (0..num_dims)
113 .map(|_| {
114 let num_elements = bytes.read_i32::<NetworkEndian>()?;
115 let _lower_bound = bytes.read_i32::<NetworkEndian>()?;
116
117 let dim: usize = num_elements
118 .try_into()
119 .map_err(|_| "array dimension length must be positive")?;
120 Ok(dim)
121 })
122 .collect::<deserialize::Result<Vec<_>>>()?;
123
124 let max_dim = dims
125 .iter()
126 .try_fold(1_usize, |a, b| a.checked_mul(*b))
127 .ok_or("Overflow while deserializing package size")?;
128
129 let data = (0..max_dim)
130 .map(|_| -> deserialize::Result<T> {
131 let elem_size = bytes.read_i32::<NetworkEndian>()?;
132 if has_null && elem_size == -1 {
133 T::from_nullable_sql(None)
134 } else {
135 let (elem_bytes, new_bytes) = bytes
136 .split_at_checked(elem_size.try_into()?)
137 .ok_or_else(|| {
138 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("Invalid element byte count: Expected at least {1} bytes, but only {0} bytes were received",
bytes.len(), elem_size))
})format!(
139 "Invalid element byte count: Expected at least {elem_size} bytes, but only {} bytes were received",
140 bytes.len()
141 )
142 })?;
143 bytes = new_bytes;
144 T::from_sql(PgValue::new_internal(elem_bytes, &element_oid))
145 }
146 })
147 .collect::<deserialize::Result<Vec<T>>>()?;
148 Ok(NdArray { dims, data })
149 }
150}
151
152use crate::expression::AsExpression;
153use crate::expression::bound::Bound;
154
155macro_rules! array_as_expression {
156 ($ty:ty, $sql_type:ty) => {
157 #[cfg(feature = "postgres_backend")]
158 #[allow(clippy::extra_unused_lifetimes)]
161 impl<'a, 'b, ST: 'static, T> AsExpression<$sql_type> for $ty {
162 type Expression = Bound<$sql_type, Self>;
163
164 fn as_expression(self) -> Self::Expression {
165 Bound::new(self)
166 }
167 }
168 };
169}
170
171#[allow(clippy :: extra_unused_lifetimes)]
impl<'a, 'b, ST: 'static, T> AsExpression<Array<ST>> for &'a [T] {
type Expression = Bound<Array<ST>, Self>;
fn as_expression(self) -> Self::Expression { Bound::new(self) }
}array_as_expression!(&'a [T], Array<ST>);
172#[allow(clippy :: extra_unused_lifetimes)]
impl<'a, 'b, ST: 'static, T> AsExpression<Nullable<Array<ST>>> for &'a [T] {
type Expression = Bound<Nullable<Array<ST>>, Self>;
fn as_expression(self) -> Self::Expression { Bound::new(self) }
}array_as_expression!(&'a [T], Nullable<Array<ST>>);
173#[allow(clippy :: extra_unused_lifetimes)]
impl<'a, 'b, ST: 'static, T> AsExpression<Array<ST>> for &'a &'b [T] {
type Expression = Bound<Array<ST>, Self>;
fn as_expression(self) -> Self::Expression { Bound::new(self) }
}array_as_expression!(&'a &'b [T], Array<ST>);
174#[allow(clippy :: extra_unused_lifetimes)]
impl<'a, 'b, ST: 'static, T> AsExpression<Nullable<Array<ST>>> for &'a &'b [T]
{
type Expression = Bound<Nullable<Array<ST>>, Self>;
fn as_expression(self) -> Self::Expression { Bound::new(self) }
}array_as_expression!(&'a &'b [T], Nullable<Array<ST>>);
175#[allow(clippy :: extra_unused_lifetimes)]
impl<'a, 'b, ST: 'static, T> AsExpression<Array<ST>> for Vec<T> {
type Expression = Bound<Array<ST>, Self>;
fn as_expression(self) -> Self::Expression { Bound::new(self) }
}array_as_expression!(Vec<T>, Array<ST>);
176#[allow(clippy :: extra_unused_lifetimes)]
impl<'a, 'b, ST: 'static, T> AsExpression<Nullable<Array<ST>>> for Vec<T> {
type Expression = Bound<Nullable<Array<ST>>, Self>;
fn as_expression(self) -> Self::Expression { Bound::new(self) }
}array_as_expression!(Vec<T>, Nullable<Array<ST>>);
177#[allow(clippy :: extra_unused_lifetimes)]
impl<'a, 'b, ST: 'static, T> AsExpression<Array<ST>> for &'a Vec<T> {
type Expression = Bound<Array<ST>, Self>;
fn as_expression(self) -> Self::Expression { Bound::new(self) }
}array_as_expression!(&'a Vec<T>, Array<ST>);
178#[allow(clippy :: extra_unused_lifetimes)]
impl<'a, 'b, ST: 'static, T> AsExpression<Nullable<Array<ST>>> for &'a Vec<T>
{
type Expression = Bound<Nullable<Array<ST>>, Self>;
fn as_expression(self) -> Self::Expression { Bound::new(self) }
}array_as_expression!(&'a Vec<T>, Nullable<Array<ST>>);
179#[allow(clippy :: extra_unused_lifetimes)]
impl<'a, 'b, ST: 'static, T> AsExpression<Array<ST>> for &'a &'b Vec<T> {
type Expression = Bound<Array<ST>, Self>;
fn as_expression(self) -> Self::Expression { Bound::new(self) }
}array_as_expression!(&'a &'b Vec<T>, Array<ST>);
180#[allow(clippy :: extra_unused_lifetimes)]
impl<'a, 'b, ST: 'static, T> AsExpression<Nullable<Array<ST>>> for
&'a &'b Vec<T> {
type Expression = Bound<Nullable<Array<ST>>, Self>;
fn as_expression(self) -> Self::Expression { Bound::new(self) }
}array_as_expression!(&'a &'b Vec<T>, Nullable<Array<ST>>);
181
182#[cfg(feature = "postgres_backend")]
183impl<ST, T> ToSql<Array<ST>, Pg> for [T]
184where
185 Pg: HasSqlType<ST>,
186 T: ToSql<ST, Pg>,
187{
188 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
189 let num_dimensions = 1;
190 out.write_i32::<NetworkEndian>(num_dimensions)?;
191 let flags_offset = out.reborrow().into_inner().0.len();
193 let flags = 0;
194 out.write_i32::<NetworkEndian>(flags)?;
195 let element_oid = Pg::metadata(out.metadata_lookup()).oid()?;
196 out.write_u32::<NetworkEndian>(element_oid)?;
197 out.write_i32::<NetworkEndian>(self.len().try_into()?)?;
198 let lower_bound = 1;
199 out.write_i32::<NetworkEndian>(lower_bound)?;
200
201 let mut buffer = Vec::new();
204 let mut has_nulls = false;
205
206 for elem in self.iter() {
207 let is_null = {
208 let mut temp_buffer = Output::new(ByteWrapper(&mut buffer), out.metadata_lookup());
209 elem.to_sql(&mut temp_buffer)?
210 };
211
212 if let IsNull::No = is_null {
213 out.write_i32::<NetworkEndian>(buffer.len().try_into()?)?;
214 out.write_all(&buffer)?;
215 buffer.clear();
216 } else {
217 has_nulls = true;
218 out.write_i32::<NetworkEndian>(-1)?;
220 }
221 }
222
223 if has_nulls {
224 out.reborrow().into_inner().0[flags_offset..flags_offset + 4]
225 .copy_from_slice(&1_i32.to_be_bytes());
226 }
227
228 Ok(IsNull::No)
229 }
230}
231
232#[cfg(feature = "postgres_backend")]
233impl<ST, T> ToSql<Nullable<Array<ST>>, Pg> for [T]
234where
235 [T]: ToSql<Array<ST>, Pg>,
236 ST: 'static,
237{
238 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
239 ToSql::<Array<ST>, Pg>::to_sql(self, out)
240 }
241}
242
243#[cfg(feature = "postgres_backend")]
244impl<ST, T> ToSql<Array<ST>, Pg> for Vec<T>
245where
246 ST: 'static,
247 [T]: ToSql<Array<ST>, Pg>,
248 T: fmt::Debug,
249{
250 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
251 (self as &[T]).to_sql(out)
252 }
253}
254
255#[cfg(feature = "postgres_backend")]
256impl<ST, T> ToSql<Nullable<Array<ST>>, Pg> for Vec<T>
257where
258 ST: 'static,
259 Vec<T>: ToSql<Array<ST>, Pg>,
260{
261 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
262 ToSql::<Array<ST>, Pg>::to_sql(self, out)
263 }
264}
265
266#[cfg(test)]
267mod tests {
268 use byteorder::{NetworkEndian, WriteBytesExt};
269
270 use super::ByteWrapper;
271 use crate::data_types::NdArray;
272 use crate::deserialize::{self, FromSql};
273 use crate::pg::{Pg, PgMetadataLookup, PgTypeMetadata, PgValue};
274 use crate::serialize::{Output, ToSql};
275 use crate::sql_types::{Array, Integer, Nullable};
276
277 #[derive(Debug, PartialEq)]
278 struct ElementOid(u32);
279
280 impl FromSql<Integer, Pg> for ElementOid {
281 fn from_sql(value: PgValue<'_>) -> deserialize::Result<Self> {
282 Ok(Self(value.get_oid().get()))
283 }
284 }
285
286 fn one_element_array_value(element_oid: u32) -> Vec<u8> {
287 let mut value = Vec::<u8>::new();
288 value.write_i32::<NetworkEndian>(1).unwrap();
289 value.write_i32::<NetworkEndian>(0).unwrap();
290 value.write_u32::<NetworkEndian>(element_oid).unwrap();
291 value.write_i32::<NetworkEndian>(1).unwrap();
292 value.write_i32::<NetworkEndian>(0).unwrap();
293 value.write_i32::<NetworkEndian>(4).unwrap();
294 value.write_i32::<NetworkEndian>(42).unwrap();
295 value
296 }
297
298 #[test]
299 fn zero_element_oid_is_rejected() {
300 let value = one_element_array_value(0);
301 let value = PgValue::for_test(&value);
302 let res = <Vec<ElementOid> as FromSql<Array<Integer>, Pg>>::from_sql(value);
303 assert!(res.is_err());
304 assert_eq!(format!("{}", res.unwrap_err()), "Oid's aren't zero");
305
306 let value = one_element_array_value(0);
307 let value = PgValue::for_test(&value);
308 let res = <NdArray<ElementOid> as FromSql<Array<Integer>, Pg>>::from_sql(value);
309 assert!(res.is_err());
310 assert_eq!(format!("{}", res.unwrap_err()), "Oid's aren't zero");
311 }
312
313 #[test]
314 fn nonzero_header_oid_is_used_for_element() {
315 let value = one_element_array_value(23);
316 let value = PgValue::for_test(&value);
317 let res = <Vec<ElementOid> as FromSql<Array<Integer>, Pg>>::from_sql(value);
318 assert_eq!(vec![ElementOid(23)], res.unwrap());
319
320 let value = one_element_array_value(23);
321 let value = PgValue::for_test(&value);
322 let res = <NdArray<ElementOid> as FromSql<Array<Integer>, Pg>>::from_sql(value).unwrap();
323 assert_eq!(vec![1], res.dims);
324 assert_eq!(vec![ElementOid(23)], res.data);
325 }
326
327 struct StaticOidsOnly;
329
330 impl PgMetadataLookup for StaticOidsOnly {
331 fn lookup_type(&mut self, type_name: &str, _schema: Option<&str>) -> PgTypeMetadata {
332 panic!("unexpected type lookup for `{type_name}`")
333 }
334 }
335
336 #[test]
337 fn serialized_null_flag_matches_elements() {
338 for (array, flags) in [
341 (vec![Some(7), None, Some(-9)], 1),
342 (vec![Some(7), Some(-9)], 0),
343 ] {
344 let prefix = [0xAA; 3];
346 let mut buffer = prefix.to_vec();
347 ToSql::<Array<Nullable<Integer>>, Pg>::to_sql(
348 &array,
349 &mut Output::new(
350 ByteWrapper(&mut buffer),
351 &mut StaticOidsOnly as &mut dyn PgMetadataLookup,
352 ),
353 )
354 .unwrap();
355
356 let (written_prefix, value) = buffer.split_at(prefix.len());
357 assert_eq!(written_prefix, prefix);
358 assert_eq!(value[4..8], i32::to_be_bytes(flags));
359 let decoded = <Vec<Option<i32>> as FromSql<Array<Nullable<Integer>>, Pg>>::from_sql(
360 PgValue::for_test(value),
361 );
362 assert_eq!(decoded.unwrap(), array);
363 }
364 }
365
366 #[test]
367 fn check_invalid_element_size_for_array() {
368 let mut value = Vec::<u8>::new();
370
371 value.write_i32::<NetworkEndian>(1).unwrap();
373 value.write_i32::<NetworkEndian>(0).unwrap();
375 value.write_u32::<NetworkEndian>(23).unwrap();
377 value.write_i32::<NetworkEndian>(2).unwrap();
379 value.write_i32::<NetworkEndian>(0).unwrap();
381 value.write_i32::<NetworkEndian>(6).unwrap();
383 value.write_i32::<NetworkEndian>(42).unwrap();
385
386 let value = PgValue::for_test(&value);
387 let res = <Vec<i32> as FromSql<Array<Integer>, Pg>>::from_sql(value);
388 assert!(res.is_err());
389 assert_eq!(
390 format!("{}", res.unwrap_err()),
391 "Invalid element byte count: Expected at least 6 bytes, but only 4 bytes were received",
392 );
393
394 let mut value = Vec::<u8>::new();
396
397 value.write_i32::<NetworkEndian>(1).unwrap();
399 value.write_i32::<NetworkEndian>(0).unwrap();
401 value.write_u32::<NetworkEndian>(23).unwrap();
403 value.write_i32::<NetworkEndian>(2).unwrap();
405 value.write_i32::<NetworkEndian>(0).unwrap();
407 value.write_i32::<NetworkEndian>(4).unwrap();
409 value.write_i32::<NetworkEndian>(42).unwrap();
411
412 let value = PgValue::for_test(&value);
413 let res = <Vec<i32> as FromSql<Array<Integer>, Pg>>::from_sql(value);
414 assert!(res.is_err());
415 assert_eq!(
416 format!("{}", res.unwrap_err()),
417 "failed to fill whole buffer"
418 );
419 }
420
421 #[test]
422 fn check_invalid_element_size_for_multidimensional_array() {
423 let mut value = Vec::<u8>::new();
425
426 value.write_i32::<NetworkEndian>(1).unwrap();
428 value.write_i32::<NetworkEndian>(0).unwrap();
430 value.write_u32::<NetworkEndian>(23).unwrap();
432 value.write_i32::<NetworkEndian>(2).unwrap();
434 value.write_i32::<NetworkEndian>(0).unwrap();
436 value.write_i32::<NetworkEndian>(6).unwrap();
438 value.write_i32::<NetworkEndian>(42).unwrap();
440
441 let value = PgValue::for_test(&value);
442 let res = <NdArray<i32> as FromSql<Array<Integer>, Pg>>::from_sql(value);
443 assert!(res.is_err());
444 assert_eq!(
445 format!("{}", res.unwrap_err()),
446 "Invalid element byte count: Expected at least 6 bytes, but only 4 bytes were received",
447 );
448
449 let mut value = Vec::<u8>::new();
451
452 value.write_i32::<NetworkEndian>(1).unwrap();
454 value.write_i32::<NetworkEndian>(0).unwrap();
456 value.write_u32::<NetworkEndian>(23).unwrap();
458 value.write_i32::<NetworkEndian>(2).unwrap();
460 value.write_i32::<NetworkEndian>(0).unwrap();
462 value.write_i32::<NetworkEndian>(4).unwrap();
464 value.write_i32::<NetworkEndian>(42).unwrap();
466
467 let value = PgValue::for_test(&value);
468 let res = <NdArray<i32> as FromSql<Array<Integer>, Pg>>::from_sql(value);
469 assert!(res.is_err());
470 assert_eq!(
471 format!("{}", res.unwrap_err()),
472 "failed to fill whole buffer"
473 );
474 }
475}