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}