1use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt};
2use core::fmt;
3use std::io::Write;
4
5use crate::deserialize::{self, FromSql, FromSqlRow};
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")]
12#[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) -> NdArray<T> {
NdArray {
dims: ::core::clone::Clone::clone(&self.dims),
data: ::core::clone::Clone::clone(&self.data),
}
}
}Clone, #[automatically_derived]
impl<T: ::core::cmp::PartialEq> ::core::cmp::PartialEq for NdArray<T> {
#[inline]
fn eq(&self, other: &NdArray<T>) -> 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: &NdArray<T>)
-> ::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: &NdArray<T>) -> ::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)]
13#[diesel(sql_type = Array<T>)]
14pub struct NdArray<T> {
18 pub dims: Vec<usize>,
20 pub data: Vec<T>,
28}
29
30#[cfg(feature = "postgres_backend")]
31impl<T> HasSqlType<Array<T>> for Pg
32where
33 Pg: HasSqlType<T>,
34{
35 fn metadata(lookup: &mut Self::MetadataLookup) -> PgTypeMetadata {
36 match <Pg as HasSqlType<T>>::metadata(lookup).0 {
37 Ok(tpe) => PgTypeMetadata::new(tpe.array_oid, 0),
38 c @ Err(_) => PgTypeMetadata(c),
39 }
40 }
41}
42
43#[cfg(feature = "postgres_backend")]
44impl<T, ST> FromSql<Array<ST>, Pg> for Vec<T>
45where
46 T: FromSql<ST, Pg>,
47{
48 fn from_sql(value: PgValue<'_>) -> deserialize::Result<Self> {
49 let mut bytes = value.as_bytes();
50 let num_dimensions = bytes.read_i32::<NetworkEndian>()?;
51 let has_null = bytes.read_i32::<NetworkEndian>()? != 0;
52 let _oid = bytes.read_i32::<NetworkEndian>()?;
53
54 if num_dimensions == 0 {
55 return Ok(Vec::new());
56 }
57
58 let num_elements = bytes.read_i32::<NetworkEndian>()?;
59 let _lower_bound = bytes.read_i32::<NetworkEndian>()?;
60
61 if num_dimensions != 1 {
62 return Err("multi-dimensional arrays are not supported".into());
63 }
64
65 (0..num_elements)
66 .map(|_| -> deserialize::Result<_> {
67 let elem_size = bytes.read_i32::<NetworkEndian>()?;
68 if has_null && elem_size == -1 {
69 T::from_nullable_sql(None)
70 } else {
71 let (elem_bytes, new_bytes) = bytes
72 .split_at_checked(elem_size.try_into()?)
73 .ok_or_else(|| {
74 ::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!(
75 "Invalid element byte count: Expected at least {elem_size} bytes, but only {} bytes were received",
76 bytes.len()
77 )
78 })?;
79 bytes = new_bytes;
80 T::from_sql(PgValue::new_internal(elem_bytes, &value))
81 }
82 })
83 .collect()
84 }
85}
86
87#[cfg(feature = "postgres_backend")]
88impl<T, ST> FromSql<Array<ST>, Pg> for NdArray<T>
89where
90 T: FromSql<ST, Pg>,
91{
92 fn from_sql(value: PgValue<'_>) -> deserialize::Result<Self> {
93 let mut bytes = value.as_bytes();
94 let num_dimensions = bytes.read_i32::<NetworkEndian>()?;
95 let has_null = bytes.read_i32::<NetworkEndian>()? != 0;
96 let _oid = bytes.read_i32::<NetworkEndian>()?;
97
98 if num_dimensions == 0 {
99 return Ok(NdArray {
100 dims: Vec::new(),
101 data: Vec::new(),
102 });
103 }
104
105 let num_dims: usize = num_dimensions
106 .try_into()
107 .map_err(|_| "number of dimensions must be positive")?;
108
109 let dims = (0..num_dims)
110 .map(|_| {
111 let num_elements = bytes.read_i32::<NetworkEndian>()?;
112 let _lower_bound = bytes.read_i32::<NetworkEndian>()?;
113
114 let dim: usize = num_elements
115 .try_into()
116 .map_err(|_| "array dimension length must be positive")?;
117 Ok(dim)
118 })
119 .collect::<deserialize::Result<Vec<_>>>()?;
120
121 let data = (0..dims.iter().product::<usize>())
122 .map(|_| -> deserialize::Result<T> {
123 let elem_size = bytes.read_i32::<NetworkEndian>()?;
124 if has_null && elem_size == -1 {
125 T::from_nullable_sql(None)
126 } else {
127 let (elem_bytes, new_bytes) = bytes
128 .split_at_checked(elem_size.try_into()?)
129 .ok_or_else(|| {
130 ::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!(
131 "Invalid element byte count: Expected at least {elem_size} bytes, but only {} bytes were received",
132 bytes.len()
133 )
134 })?;
135 bytes = new_bytes;
136 T::from_sql(PgValue::new_internal(elem_bytes, &value))
137 }
138 })
139 .collect::<deserialize::Result<Vec<T>>>()?;
140 Ok(NdArray { dims, data })
141 }
142}
143
144use crate::expression::AsExpression;
145use crate::expression::bound::Bound;
146
147macro_rules! array_as_expression {
148 ($ty:ty, $sql_type:ty) => {
149 #[cfg(feature = "postgres_backend")]
150 #[allow(clippy::extra_unused_lifetimes)]
153 impl<'a, 'b, ST: 'static, T> AsExpression<$sql_type> for $ty {
154 type Expression = Bound<$sql_type, Self>;
155
156 fn as_expression(self) -> Self::Expression {
157 Bound::new(self)
158 }
159 }
160 };
161}
162
163#[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>);
164#[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>>);
165#[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>);
166#[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>>);
167#[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>);
168#[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>>);
169#[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>);
170#[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>>);
171#[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>);
172#[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>>);
173
174#[cfg(feature = "postgres_backend")]
175impl<ST, T> ToSql<Array<ST>, Pg> for [T]
176where
177 Pg: HasSqlType<ST>,
178 T: ToSql<ST, Pg>,
179{
180 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
181 let num_dimensions = 1;
182 out.write_i32::<NetworkEndian>(num_dimensions)?;
183 let flags = 0;
184 out.write_i32::<NetworkEndian>(flags)?;
185 let element_oid = Pg::metadata(out.metadata_lookup()).oid()?;
186 out.write_u32::<NetworkEndian>(element_oid)?;
187 out.write_i32::<NetworkEndian>(self.len().try_into()?)?;
188 let lower_bound = 1;
189 out.write_i32::<NetworkEndian>(lower_bound)?;
190
191 let mut buffer = Vec::new();
194
195 for elem in self.iter() {
196 let is_null = {
197 let mut temp_buffer = Output::new(ByteWrapper(&mut buffer), out.metadata_lookup());
198 elem.to_sql(&mut temp_buffer)?
199 };
200
201 if let IsNull::No = is_null {
202 out.write_i32::<NetworkEndian>(buffer.len().try_into()?)?;
203 out.write_all(&buffer)?;
204 buffer.clear();
205 } else {
206 out.write_i32::<NetworkEndian>(-1)?;
208 }
209 }
210
211 Ok(IsNull::No)
212 }
213}
214
215#[cfg(feature = "postgres_backend")]
216impl<ST, T> ToSql<Nullable<Array<ST>>, Pg> for [T]
217where
218 [T]: ToSql<Array<ST>, Pg>,
219 ST: 'static,
220{
221 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
222 ToSql::<Array<ST>, Pg>::to_sql(self, out)
223 }
224}
225
226#[cfg(feature = "postgres_backend")]
227impl<ST, T> ToSql<Array<ST>, Pg> for Vec<T>
228where
229 ST: 'static,
230 [T]: ToSql<Array<ST>, Pg>,
231 T: fmt::Debug,
232{
233 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
234 (self as &[T]).to_sql(out)
235 }
236}
237
238#[cfg(feature = "postgres_backend")]
239impl<ST, T> ToSql<Nullable<Array<ST>>, Pg> for Vec<T>
240where
241 ST: 'static,
242 Vec<T>: ToSql<Array<ST>, Pg>,
243{
244 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
245 ToSql::<Array<ST>, Pg>::to_sql(self, out)
246 }
247}
248
249#[cfg(test)]
250mod tests {
251 use byteorder::{NetworkEndian, WriteBytesExt};
252
253 use crate::data_types::NdArray;
254 use crate::deserialize::FromSql;
255 use crate::pg::{Pg, PgValue};
256 use crate::sql_types::{Array, Integer};
257
258 #[test]
259 fn check_invalid_element_size_for_array() {
260 let mut value = Vec::<u8>::new();
262
263 value.write_i32::<NetworkEndian>(1).unwrap();
265 value.write_i32::<NetworkEndian>(0).unwrap();
267 value.write_i32::<NetworkEndian>(0).unwrap();
269 value.write_i32::<NetworkEndian>(2).unwrap();
271 value.write_i32::<NetworkEndian>(0).unwrap();
273 value.write_i32::<NetworkEndian>(6).unwrap();
275 value.write_i32::<NetworkEndian>(42).unwrap();
277
278 let value = PgValue::for_test(&value);
279 let res = <Vec<i32> as FromSql<Array<Integer>, Pg>>::from_sql(value);
280 assert!(res.is_err());
281 assert_eq!(
282 format!("{}", res.unwrap_err()),
283 "Invalid element byte count: Expected at least 6 bytes, but only 4 bytes were received",
284 );
285
286 let mut value = Vec::<u8>::new();
288
289 value.write_i32::<NetworkEndian>(1).unwrap();
291 value.write_i32::<NetworkEndian>(0).unwrap();
293 value.write_i32::<NetworkEndian>(0).unwrap();
295 value.write_i32::<NetworkEndian>(2).unwrap();
297 value.write_i32::<NetworkEndian>(0).unwrap();
299 value.write_i32::<NetworkEndian>(4).unwrap();
301 value.write_i32::<NetworkEndian>(42).unwrap();
303
304 let value = PgValue::for_test(&value);
305 let res = <Vec<i32> as FromSql<Array<Integer>, Pg>>::from_sql(value);
306 assert!(res.is_err());
307 assert_eq!(
308 format!("{}", res.unwrap_err()),
309 "failed to fill whole buffer"
310 );
311 }
312
313 #[test]
314 fn check_invalid_element_size_for_multidimensional_array() {
315 let mut value = Vec::<u8>::new();
317
318 value.write_i32::<NetworkEndian>(1).unwrap();
320 value.write_i32::<NetworkEndian>(0).unwrap();
322 value.write_i32::<NetworkEndian>(0).unwrap();
324 value.write_i32::<NetworkEndian>(2).unwrap();
326 value.write_i32::<NetworkEndian>(0).unwrap();
328 value.write_i32::<NetworkEndian>(6).unwrap();
330 value.write_i32::<NetworkEndian>(42).unwrap();
332
333 let value = PgValue::for_test(&value);
334 let res = <NdArray<i32> as FromSql<Array<Integer>, Pg>>::from_sql(value);
335 assert!(res.is_err());
336 assert_eq!(
337 format!("{}", res.unwrap_err()),
338 "Invalid element byte count: Expected at least 6 bytes, but only 4 bytes were received",
339 );
340
341 let mut value = Vec::<u8>::new();
343
344 value.write_i32::<NetworkEndian>(1).unwrap();
346 value.write_i32::<NetworkEndian>(0).unwrap();
348 value.write_i32::<NetworkEndian>(0).unwrap();
350 value.write_i32::<NetworkEndian>(2).unwrap();
352 value.write_i32::<NetworkEndian>(0).unwrap();
354 value.write_i32::<NetworkEndian>(4).unwrap();
356 value.write_i32::<NetworkEndian>(42).unwrap();
358
359 let value = PgValue::for_test(&value);
360 let res = <NdArray<i32> as FromSql<Array<Integer>, Pg>>::from_sql(value);
361 assert!(res.is_err());
362 assert_eq!(
363 format!("{}", res.unwrap_err()),
364 "failed to fill whole buffer"
365 );
366 }
367}