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 mut cursor = conn.execute("SELECT first_name, last_name FROM Persons", ())?
51///         .expect("SELECT must yield a result set");
52///     let mut block_cursor = cursor.bind_buffer(buffer)?;
53///
54///     while let Some(batch) = block_cursor.fetch()? {
55///         for (first, last) in batch.iter() {
56///             let first = first.as_str()
57///                 .expect("First name must be UTF-8")
58///                 .expect("First Name must not be NULL");
59///             let last = last.as_str()
60///                 .expect("Last name must be UTF-8")
61///                 .expect("Last Name must not be NULL");
62///             println!("Hello {first} {last}!")
63///         }
64///     }
65///     Ok(())
66/// }
67/// ```
68///
69/// To fetch rows with this buffer type `R` must implement [`FetchRow`]. This is currently
70/// implemented for tuple types. Each element of these tuples must implement [`FetchRowMember`].
71///
72/// Currently supported are: `f64`, `f32`, [`odbc_sys::Date`], [`odbc_sys::Timestamp`],
73/// [`odbc_sys::Time`], `i16`, `u36`, `i32`, `u32`, `i8`, `u8`, `Bit`, `i64`, `u64` and
74/// [`crate::parameter::VarCharArray`]. Fixed sized types can be wrapped in [`crate::Nullable`].
75pub struct RowVec<R> {
76    /// A mutable pointer to num_rows_fetched is passed to the C-API. It is used to write back the
77    /// number of fetched rows. `num_rows` is heap allocated, so the pointer is not invalidated,
78    /// even if the `ColumnarBuffer` instance is moved in memory.
79    num_rows: Box<usize>,
80    /// Here we actually store the rows. The length of `rows` is the capacity of the `RowWiseBuffer`
81    /// instance. It must not be 0.
82    rows: Vec<R>,
83}
84
85impl<R> RowVec<R> {
86    /// Allocates a new Row wise buffer, which can at most `capacity` number of rows in a single
87    /// call to [`crate::BlockCursor::fetch`].
88    ///
89    /// Panics if `capacity` is `0``.
90    pub fn new(capacity: usize) -> Self
91    where
92        R: Default + Clone + Copy,
93    {
94        if capacity == 0 {
95            panic!("RowWiseBuffer must have a capacity of at least `1`.")
96        }
97        RowVec {
98            num_rows: Box::new(0),
99            rows: vec![R::default(); capacity],
100        }
101    }
102
103    /// Number of valid rows in the buffer.
104    pub fn num_rows(&self) -> usize {
105        *self.num_rows
106    }
107}
108
109impl<R> Deref for RowVec<R> {
110    type Target = [R];
111
112    fn deref(&self) -> &[R] {
113        &self.rows[..*self.num_rows]
114    }
115}
116
117unsafe impl<R> RowSetBuffer for RowVec<R>
118where
119    R: FetchRow,
120{
121    fn bind_type(&self) -> usize {
122        mem::size_of::<R>()
123    }
124
125    fn row_array_size(&self) -> usize {
126        self.rows.len()
127    }
128
129    fn mut_num_fetch_rows(&mut self) -> &mut usize {
130        &mut self.num_rows
131    }
132
133    unsafe fn bind_colmuns_to_cursor(&mut self, cursor: StatementRef<'_>) -> Result<(), Error> {
134        let first = self
135            .rows
136            .first_mut()
137            .expect("rows in Row Wise buffers must not be empty.");
138        first.bind_columns_to_cursor(cursor)
139    }
140
141    fn find_truncation(&self) -> Option<TruncationInfo> {
142        self.rows
143            .iter()
144            .take(*self.num_rows)
145            .find_map(|row| row.find_truncation())
146    }
147}
148
149/// Can be used as a member of a [`FetchRow`] and bound to a column during row wise fetching.
150///
151/// # Safety
152///
153/// Must only be implemented for types completly representable by consequtive bytes. While members
154/// can bind to Variadic types the length of the type buffering them must be known at compile time.
155/// E.g. [`crate::parameter::VarCharArray`] can also bind to Variadic types but is fixed length at
156/// compile time.
157pub unsafe trait FetchRowMember: CDataMut + Copy {
158    /// `Some` if the indicator indicates truncation. Always `None` for fixed sized types.
159    fn find_truncation(&self, buffer_index: usize) -> Option<TruncationInfo> {
160        self.indicator().map(|indicator| TruncationInfo {
161            indicator: indicator.length(),
162            buffer_index,
163        })
164    }
165
166    /// Indicator for variable sized or nullable types, `None` for fixed sized types.
167    fn indicator(&self) -> Option<Indicator>;
168
169    /// Bind row element to column. Only called for the first row in a row wise buffer.
170    ///
171    /// # Safety
172    ///
173    /// It is the callers responsibility to ensure `self` lives for the duration of the binding.
174    unsafe fn bind_to_col(
175        &mut self,
176        col_index: u16,
177        cursor: &mut StatementRef<'_>,
178    ) -> Result<(), Error> {
179        cursor.bind_col(col_index, self).into_result(cursor)
180    }
181}
182
183macro_rules! impl_bind_columns_to_cursor {
184    ($offset:expr, $cursor:ident,) => (
185        Ok(())
186    );
187    ($offset:expr, $cursor:ident, $head:ident, $($tail:ident,)*) => (
188        {
189            $head.bind_to_col($offset, &mut $cursor)?;
190            impl_bind_columns_to_cursor!($offset+1, $cursor, $($tail,)*)
191        }
192    );
193}
194
195macro_rules! impl_find_truncation {
196    ($offset:expr,) => (
197        None
198    );
199    ($offset:expr, $head:ident, $($tail:ident,)*) => (
200        {
201            if let Some(truncation_info) = $head.find_truncation($offset) {
202                return Some(truncation_info);
203            }
204            impl_find_truncation!($offset+1, $($tail,)*)
205        }
206    );
207}
208
209macro_rules! impl_fetch_row_for_tuple{
210    ($($t:ident)*) => (
211        #[allow(unused_mut)]
212        #[allow(unused_variables)]
213        #[allow(non_snake_case)]
214        unsafe impl<$($t:FetchRowMember,)*> FetchRow for ($($t,)*)
215        {
216            unsafe fn bind_columns_to_cursor(&mut self, mut cursor: StatementRef<'_>) -> Result<(), Error> {
217                let ($(ref mut $t,)*) = self;
218                impl_bind_columns_to_cursor!(1, cursor, $($t,)*)
219            }
220
221            fn find_truncation(&self) -> Option<TruncationInfo> {
222                let ($(ref $t,)*) = self;
223                impl_find_truncation!(0, $($t,)*)
224            }
225        }
226    );
227}
228
229impl_fetch_row_for_tuple! {}
230impl_fetch_row_for_tuple! { A }
231impl_fetch_row_for_tuple! { A B }
232impl_fetch_row_for_tuple! { A B C }
233impl_fetch_row_for_tuple! { A B C D }
234impl_fetch_row_for_tuple! { A B C D E }
235impl_fetch_row_for_tuple! { A B C D E F }
236impl_fetch_row_for_tuple! { A B C D E F G }
237impl_fetch_row_for_tuple! { A B C D E F G H }
238impl_fetch_row_for_tuple! { A B C D E F G H I }
239impl_fetch_row_for_tuple! { A B C D E F G H I J }
240impl_fetch_row_for_tuple! { A B C D E F G H I J K }
241impl_fetch_row_for_tuple! { A B C D E F G H I J K L }
242impl_fetch_row_for_tuple! { A B C D E F G H I J K L M }
243impl_fetch_row_for_tuple! { A B C D E F G H I J K L M N }
244impl_fetch_row_for_tuple! { A B C D E F G H I J K L M N O }
245impl_fetch_row_for_tuple! { A B C D E F G H I J K L M N O P }
246impl_fetch_row_for_tuple! { A B C D E F G H I J K L M N O P Q }
247impl_fetch_row_for_tuple! { A B C D E F G H I J K L M N O P Q R }
248impl_fetch_row_for_tuple! { A B C D E F G H I J K L M N O P Q R S }
249impl_fetch_row_for_tuple! { A B C D E F G H I J K L M N O P Q R S T }
250impl_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 }
251impl_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 }
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 U V W }
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 V W X }
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 W X Y }
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 X Y Z}
256
257#[cfg(test)]
258mod tests {
259
260    use super::RowVec;
261
262    #[derive(Default, Clone, Copy)]
263    struct DummyRow;
264
265    #[test]
266    #[should_panic]
267    fn construction_should_panic_on_capacity_zero() {
268        RowVec::<DummyRow>::new(0);
269    }
270
271    #[test]
272    #[should_panic]
273    fn index_should_panic_on_out_of_bound_access() {
274        let buffer = RowVec::<DummyRow>::new(1);
275        // Access within the capacity of rows, but the buffer is still empty.
276        let _ = buffer[0];
277    }
278}