odbc_api/buffers/
row_vec.rs

1use std::{mem, ops::Deref};
2
3use crate::{
4    buffers::Indicator,
5    handles::{CDataMut, Statement, StatementRef},
6    Error, RowSetBuffer, TruncationInfo,
7};
8
9/// [`FetchRow`]s can be bound to a [`crate::Cursor`] to enable row wise (bulk) fetching of data as
10/// opposed to column wise fetching. Since all rows are bound to a C-API in a contigious block of
11/// memory the row itself should be representable as such. Concretly that means that types like
12/// `String` can not be supported directly by [`FetchRow`]s for efficient bulk fetching, due to the
13/// fact it points to data on the heap.
14///
15/// This trait is implement by tuples of [`FetchRowMember`]. In addition it can also be derived
16/// for structs there all members implement [`FetchRowMember`] using the `Fetch` derive macro if the
17/// optional derive feature is activated.
18///
19/// # Safety
20///
21/// * All the bound buffers need to be valid for the lifetime of the row.
22/// * The offsets into the memory for the field representing a column, must be constant for all
23///   types of the row. This is required to make the row suitable for fetching in bulk, as only the
24///   first row is bound explicitly, and the bindings for all consequitive rows is calculated by
25///   taking the size of the row in bytes multiplied by buffer index.
26pub unsafe trait FetchRow: Copy {
27    /// Binds the columns of the result set to members of the row.
28    ///
29    /// # Safety
30    ///
31    /// Caller must ensure self is alive and not moved in memory for the duration of the binding.
32    unsafe fn bind_columns_to_cursor(&mut self, cursor: StatementRef<'_>) -> Result<(), Error>;
33
34    /// If it exists, this returns the "buffer index" of a member, which has been truncated.
35    fn find_truncation(&self) -> Option<TruncationInfo>;
36}
37
38/// A row wise buffer intended to be bound with [crate::Cursor::bind_buffer] in order to obtain
39/// results from a cursor.
40///
41/// # Example
42///
43/// ```
44/// use odbc_api::{Connection, Error, Cursor, parameter::VarCharArray, buffers::RowVec};
45///
46/// fn send_greetings(conn: &mut Connection) -> Result<(), Error> {
47///     let max_rows_in_batch = 250;
48///     type Row = (VarCharArray<255>, VarCharArray<255>);
49///     let buffer = RowVec::<Row>::new(max_rows_in_batch);
50///     let query = "SELECT first_name, last_name FROM Persons";
51///     let parameters = ();
52///     let timeout_sec = None;
53///     let mut cursor = conn.execute(query, parameters, timeout_sec)?
54///         .expect("SELECT must yield a result set");
55///     let mut block_cursor = cursor.bind_buffer(buffer)?;
56///
57///     while let Some(batch) = block_cursor.fetch()? {
58///         for (first, last) in batch.iter() {
59///             let first = first.as_str()
60///                 .expect("First name must be UTF-8")
61///                 .expect("First Name must not be NULL");
62///             let last = last.as_str()
63///                 .expect("Last name must be UTF-8")
64///                 .expect("Last Name must not be NULL");
65///             println!("Hello {first} {last}!")
66///         }
67///     }
68///     Ok(())
69/// }
70/// ```
71///
72/// To fetch rows with this buffer type `R` must implement [`FetchRow`]. This is currently
73/// implemented for tuple types. Each element of these tuples must implement [`FetchRowMember`].
74///
75/// Currently supported are: `f64`, `f32`, [`odbc_sys::Date`], [`odbc_sys::Timestamp`],
76/// [`odbc_sys::Time`], `i16`, `u36`, `i32`, `u32`, `i8`, `u8`, `Bit`, `i64`, `u64` and
77/// [`crate::parameter::VarCharArray`]. Fixed sized types can be wrapped in [`crate::Nullable`].
78pub struct RowVec<R> {
79    /// A mutable pointer to num_rows_fetched is passed to the C-API. It is used to write back the
80    /// number of fetched rows. `num_rows` is heap allocated, so the pointer is not invalidated,
81    /// even if the `ColumnarBuffer` instance is moved in memory.
82    num_rows: Box<usize>,
83    /// Here we actually store the rows. The length of `rows` is the capacity of the `RowWiseBuffer`
84    /// instance. It must not be 0.
85    rows: Vec<R>,
86}
87
88impl<R> RowVec<R> {
89    /// Allocates a new Row wise buffer, which can at most `capacity` number of rows in a single
90    /// call to [`crate::BlockCursor::fetch`].
91    ///
92    /// Panics if `capacity` is `0``.
93    pub fn new(capacity: usize) -> Self
94    where
95        R: Default + Clone + Copy,
96    {
97        if capacity == 0 {
98            panic!("RowWiseBuffer must have a capacity of at least `1`.")
99        }
100        RowVec {
101            num_rows: Box::new(0),
102            rows: vec![R::default(); capacity],
103        }
104    }
105
106    /// Number of valid rows in the buffer.
107    pub fn num_rows(&self) -> usize {
108        *self.num_rows
109    }
110}
111
112impl<R> Deref for RowVec<R> {
113    type Target = [R];
114
115    fn deref(&self) -> &[R] {
116        &self.rows[..*self.num_rows]
117    }
118}
119
120unsafe impl<R> RowSetBuffer for RowVec<R>
121where
122    R: FetchRow,
123{
124    fn bind_type(&self) -> usize {
125        mem::size_of::<R>()
126    }
127
128    fn row_array_size(&self) -> usize {
129        self.rows.len()
130    }
131
132    fn mut_num_fetch_rows(&mut self) -> &mut usize {
133        &mut self.num_rows
134    }
135
136    unsafe fn bind_colmuns_to_cursor(&mut self, cursor: StatementRef<'_>) -> Result<(), Error> {
137        let first = self
138            .rows
139            .first_mut()
140            .expect("rows in Row Wise buffers must not be empty.");
141        first.bind_columns_to_cursor(cursor)
142    }
143
144    fn find_truncation(&self) -> Option<TruncationInfo> {
145        self.rows
146            .iter()
147            .take(*self.num_rows)
148            .find_map(|row| row.find_truncation())
149    }
150}
151
152/// Can be used as a member of a [`FetchRow`] and bound to a column during row wise fetching.
153///
154/// # Safety
155///
156/// Must only be implemented for types completly representable by consequtive bytes. While members
157/// can bind to Variadic types the length of the type buffering them must be known at compile time.
158/// E.g. [`crate::parameter::VarCharArray`] can also bind to Variadic types but is fixed length at
159/// compile time.
160pub unsafe trait FetchRowMember: CDataMut + Copy {
161    /// `Some` if the indicator indicates truncation. Always `None` for fixed sized types.
162    fn find_truncation(&self, buffer_index: usize) -> Option<TruncationInfo> {
163        self.indicator().map(|indicator| TruncationInfo {
164            indicator: indicator.length(),
165            buffer_index,
166        })
167    }
168
169    /// Indicator for variable sized or nullable types, `None` for fixed sized types.
170    fn indicator(&self) -> Option<Indicator>;
171
172    /// Bind row element to column. Only called for the first row in a row wise buffer.
173    ///
174    /// # Safety
175    ///
176    /// It is the callers responsibility to ensure `self` lives for the duration of the binding.
177    unsafe fn bind_to_col(
178        &mut self,
179        col_index: u16,
180        cursor: &mut StatementRef<'_>,
181    ) -> Result<(), Error> {
182        cursor.bind_col(col_index, self).into_result(cursor)
183    }
184}
185
186macro_rules! impl_bind_columns_to_cursor {
187    ($offset:expr, $cursor:ident,) => (
188        Ok(())
189    );
190    ($offset:expr, $cursor:ident, $head:ident, $($tail:ident,)*) => (
191        {
192            $head.bind_to_col($offset, &mut $cursor)?;
193            impl_bind_columns_to_cursor!($offset+1, $cursor, $($tail,)*)
194        }
195    );
196}
197
198macro_rules! impl_find_truncation {
199    ($offset:expr,) => (
200        None
201    );
202    ($offset:expr, $head:ident, $($tail:ident,)*) => (
203        {
204            if let Some(truncation_info) = $head.find_truncation($offset) {
205                return Some(truncation_info);
206            }
207            impl_find_truncation!($offset+1, $($tail,)*)
208        }
209    );
210}
211
212macro_rules! impl_fetch_row_for_tuple{
213    ($($t:ident)*) => (
214        #[allow(unused_mut)]
215        #[allow(unused_variables)]
216        #[allow(non_snake_case)]
217        unsafe impl<$($t:FetchRowMember,)*> FetchRow for ($($t,)*)
218        {
219            unsafe fn bind_columns_to_cursor(&mut self, mut cursor: StatementRef<'_>) -> Result<(), Error> {
220                let ($(ref mut $t,)*) = self;
221                impl_bind_columns_to_cursor!(1, cursor, $($t,)*)
222            }
223
224            fn find_truncation(&self) -> Option<TruncationInfo> {
225                let ($(ref $t,)*) = self;
226                impl_find_truncation!(0, $($t,)*)
227            }
228        }
229    );
230}
231
232impl_fetch_row_for_tuple! {}
233impl_fetch_row_for_tuple! { A }
234impl_fetch_row_for_tuple! { A B }
235impl_fetch_row_for_tuple! { A B C }
236impl_fetch_row_for_tuple! { A B C D }
237impl_fetch_row_for_tuple! { A B C D E }
238impl_fetch_row_for_tuple! { A B C D E F }
239impl_fetch_row_for_tuple! { A B C D E F G }
240impl_fetch_row_for_tuple! { A B C D E F G H }
241impl_fetch_row_for_tuple! { A B C D E F G H I }
242impl_fetch_row_for_tuple! { A B C D E F G H I J }
243impl_fetch_row_for_tuple! { A B C D E F G H I J K }
244impl_fetch_row_for_tuple! { A B C D E F G H I J K L }
245impl_fetch_row_for_tuple! { A B C D E F G H I J K L M }
246impl_fetch_row_for_tuple! { A B C D E F G H I J K L M N }
247impl_fetch_row_for_tuple! { A B C D E F G H I J K L M N O }
248impl_fetch_row_for_tuple! { A B C D E F G H I J K L M N O P }
249impl_fetch_row_for_tuple! { A B C D E F G H I J K L M N O P Q }
250impl_fetch_row_for_tuple! { A B C D E F G H I J K L M N O P Q R }
251impl_fetch_row_for_tuple! { A B C D E F G H I J K L M N O P Q R S }
252impl_fetch_row_for_tuple! { A B C D E F G H I J K L M N O P Q R S T }
253impl_fetch_row_for_tuple! { A B C D E F G H I J K L M N O P Q R S T U }
254impl_fetch_row_for_tuple! { A B C D E F G H I J K L M N O P Q R S T U V }
255impl_fetch_row_for_tuple! { A B C D E F G H I J K L M N O P Q R S T U V W }
256impl_fetch_row_for_tuple! { A B C D E F G H I J K L M N O P Q R S T U V W X }
257impl_fetch_row_for_tuple! { A B C D E F G H I J K L M N O P Q R S T U V W X Y }
258impl_fetch_row_for_tuple! { A B C D E F G H I J K L M N O P Q R S T U V W X Y Z}
259
260#[cfg(test)]
261mod tests {
262
263    use super::RowVec;
264
265    #[derive(Default, Clone, Copy)]
266    struct DummyRow;
267
268    #[test]
269    #[should_panic]
270    fn construction_should_panic_on_capacity_zero() {
271        RowVec::<DummyRow>::new(0);
272    }
273
274    #[test]
275    #[should_panic]
276    fn index_should_panic_on_out_of_bound_access() {
277        let buffer = RowVec::<DummyRow>::new(1);
278        // Access within the capacity of rows, but the buffer is still empty.
279        let _ = buffer[0];
280    }
281}