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}