sqlx_exasol_impl/types/
array.rs

1use std::{marker::PhantomData, rc::Rc, sync::Arc};
2
3use sqlx_core::{
4    database::Database,
5    encode::{Encode, IsNull},
6    error::BoxDynError,
7    types::Type,
8};
9
10use crate::{arguments::ExaBuffer, Exasol};
11
12/// Marker trait that limits arrays encoding to one dimensioanl arrays.
13///
14/// Implementing it for some type `T` enables relevant [`Type`], [`Encode`] and
15/// [`sqlx_core::decode::Decode`] impls for [`Vec<T>`], `&T` (slices), [`[T;N]`] (arrays),
16/// [`ExaIter`], etc.
17pub trait ExaHasArrayType: Type<Exasol> {}
18
19impl<T> ExaHasArrayType for &T where T: ExaHasArrayType {}
20
21/// Adapter allowing any iterator of encodable values to be treated and passed as a one dimensional
22/// parameter array for a column to Exasol in a single query invocation. Multi dimensional arrays
23/// are not supported. The adapter is needed because [`Encode`] is still a foreign trait and thus
24/// cannot be implemented in a generic manner to all types implementing [`IntoIterator`].
25///
26/// Custom types can be used with the [`ExaIter`] adapter by implementing the [`ExaHasArrayType`]
27/// marker trait.
28///
29/// Note that the [`Encode`] trait requires the ability to encode by reference, thus the adapter
30/// takes a type that implements [`IntoIterator`]. But since iteration requires mutability,
31/// the adaptar also requires [`Clone`]. The adapter definition should steer it towards being used
32/// with cheaply clonable iterators since it expects the iteration elements to be references.
33/// However, care should still be taken so as not to clone expensive [`IntoIterator`] types.
34///
35/// ```rust
36/// # use sqlx_exasol_impl as sqlx_exasol;
37/// use sqlx_exasol::types::ExaIter;
38///
39/// let vector = vec![1, 2, 3];
40/// let borrowed_iter = ExaIter::new(vector.iter().filter(|v| **v % 2 == 0));
41/// ```
42#[derive(Debug)]
43#[repr(transparent)]
44pub struct ExaIter<I, T> {
45    into_iter: I,
46    _marker: PhantomData<fn() -> T>,
47}
48
49impl<I, T> ExaIter<I, T>
50where
51    I: IntoIterator<Item = T> + Clone,
52    T: for<'q> Encode<'q, Exasol> + ExaHasArrayType + Copy,
53{
54    pub fn new(into_iter: I) -> Self {
55        Self {
56            into_iter,
57            _marker: PhantomData,
58        }
59    }
60}
61
62impl<I, T> Type<Exasol> for ExaIter<I, T>
63where
64    I: IntoIterator<Item = T> + Clone,
65    T: ExaHasArrayType + Copy,
66{
67    fn type_info() -> <Exasol as Database>::TypeInfo {
68        T::type_info()
69    }
70}
71
72impl<I, T> Encode<'_, Exasol> for ExaIter<I, T>
73where
74    I: IntoIterator<Item = T> + Clone,
75    T: for<'q> Encode<'q, Exasol> + Copy,
76{
77    fn encode_by_ref(&self, buf: &mut ExaBuffer) -> Result<IsNull, BoxDynError> {
78        buf.append_iter(self.into_iter.clone())?;
79        Ok(IsNull::No)
80    }
81
82    fn size_hint(&self) -> usize {
83        // Brackets [] + items size
84        2 + self
85            .into_iter
86            .clone()
87            .into_iter()
88            .fold(0, |sum, item| sum + item.size_hint())
89    }
90}
91
92macro_rules! forward_arr_type_impl {
93    ($for_type:ty, $($generics:tt)*) => {
94        impl<T, $($generics)*> Type<Exasol> for $for_type
95        where
96            T: ExaHasArrayType,
97        {
98            fn type_info() -> <Exasol as Database>::TypeInfo {
99                T::type_info()
100            }
101        }
102    };
103    ($for_type:ty) => {
104        forward_arr_type_impl!($for_type,);
105    }
106}
107
108macro_rules! forward_arr_encode_impl {
109    ($for_type:ty, $($generics:tt)*) => {
110        impl<T, $($generics)*> Encode<'_, Exasol> for $for_type
111        where
112            for<'q> T: Encode<'q, Exasol> + ExaHasArrayType,
113        {
114            fn encode_by_ref(&self, buf: &mut ExaBuffer) -> Result<IsNull, BoxDynError> {
115                ExaIter::new(AsRef::<[T]>::as_ref(&self)).encode_by_ref(buf)
116            }
117
118            fn size_hint(&self) -> usize {
119                ExaIter::new(AsRef::<[T]>::as_ref(&self)).size_hint()
120            }
121        }
122    };
123    ($for_type:ty) => {
124        forward_arr_encode_impl!($for_type,);
125    }
126}
127
128forward_arr_type_impl!([T]);
129forward_arr_type_impl!([T; N], const N: usize);
130forward_arr_type_impl!(Vec<T>);
131
132forward_arr_encode_impl!(&[T]);
133forward_arr_encode_impl!([T; N], const N: usize);
134forward_arr_encode_impl!(Vec<T>);
135forward_arr_encode_impl!(Box<[T]>);
136forward_arr_encode_impl!(Rc<[T]>);
137forward_arr_encode_impl!(Arc<[T]>);