Skip to main content

odbc_api/buffers/
dyn_column_buffer.rs

1use super::{
2    BinColumn, BinColumnSlice, BinColumnSliceMut, BufferDesc, ColumnBuffer, ColumnarBuffer,
3    Indicator, NullableSlice, NullableSliceMut, Resize, Slice, TextColumn, TextColumnSlice,
4    TextColumnSliceMut, column_with_indicator::ColumnWithIndicator,
5};
6
7use crate::{
8    BoundInputSlice, Error,
9    fixed_sized::Pod,
10    handles::{CData, CDataMut, StatementRef},
11};
12
13use std::{any::Any, collections::HashSet, ffi::c_void};
14
15/// Columnar buffer with dynamic column types decided at runtime.
16///
17/// Go to buffer implementation for this crate if fetching data. If you have no reason to use
18/// something else use this.
19pub type ColumnarDynBuffer = ColumnarBuffer<BoxColumnBuffer>;
20
21impl ColumnarDynBuffer {
22    /// Allocates a [`ColumnarDynBuffer`] fitting the buffer descriptions.
23    pub fn from_descs(capacity: usize, descs: impl IntoIterator<Item = BufferDesc>) -> Self {
24        let mut column_index = 0;
25        let columns = descs
26            .into_iter()
27            .map(move |desc| {
28                let buffer = desc.column_buffer(capacity);
29                column_index += 1;
30                (column_index, buffer)
31            })
32            .collect();
33        unsafe { ColumnarBuffer::new_unchecked(capacity, columns) }
34    }
35
36    /// Allocates a [`ColumnarDynBuffer`] fitting the buffer descriptions. If not enough memory is
37    /// available to allocate the buffers this function fails with
38    /// [`Error::TooLargeColumnBufferSize`]. This function is slower than [`Self::from_descs`] which
39    /// would just panic if not enough memory is available for allocation.
40    pub fn try_from_descs(
41        capacity: usize,
42        descs: impl IntoIterator<Item = BufferDesc>,
43    ) -> Result<Self, Error> {
44        let mut column_index = 0;
45        let columns = descs
46            .into_iter()
47            .map(move |desc| {
48                let buffer = desc
49                    .try_column_buffer(capacity)
50                    .map_err(|source| source.add_context(column_index))?;
51                column_index += 1;
52                Ok::<_, Error>((column_index, buffer))
53            })
54            .collect::<Result<_, _>>()?;
55        Ok(unsafe { ColumnarBuffer::new_unchecked(capacity, columns) })
56    }
57
58    /// Allows you to pass the buffer descriptions together with a one based column index referring
59    /// the column, the buffer is supposed to bind to. This allows you also to ignore columns in a
60    /// result set, by not binding them at all. There is no restriction on the order of column
61    /// indices passed, but the function will panic, if the indices are not unique.
62    pub fn from_descs_and_indices(
63        max_rows: usize,
64        description: impl Iterator<Item = (u16, BufferDesc)>,
65    ) -> Self {
66        let columns: Vec<_> = description
67            .map(|(col_index, buffer_desc)| (col_index, buffer_desc.column_buffer(max_rows)))
68            .collect();
69
70        // Assert uniqueness of indices
71        let mut indices = HashSet::new();
72        if columns
73            .iter()
74            .any(move |&(col_index, _)| !indices.insert(col_index))
75        {
76            panic!("Column indices must be unique.")
77        }
78
79        ColumnarBuffer::new(columns)
80    }
81}
82
83/// Heap allocated column buffer with dynamic type. Intended to be used as a type parameter for
84/// [`ColumnarBuffer`] to enable columns of different types only known at runtime to be bound to the
85/// same block cursor.
86pub type BoxColumnBuffer = Box<dyn AnyColumnBuffer>;
87
88/// A [`ColumnBuffer`] that additionally carries runtime type information, so a trait object can
89/// be downcast to the concrete buffer type it was constructed from. Intended for [`ColumnarBuffer`]
90/// with heterogenous columns, i.e. integer and text columns.
91///
92/// This trait also inherits from`Send` so we can use it in concurrent bulk fetches using double
93/// buffering in multiple threads. Currently there are no buffer implementations which are not
94/// `Send`. So a distinction between `Send` and non-`Send` buffers is assumed to cause more friction
95/// rather than being helpful.
96pub trait AnyColumnBuffer: ColumnBuffer + Resize + Any + Send {}
97
98impl<T> AnyColumnBuffer for T where T: ColumnBuffer + Resize + Any + Send {}
99
100unsafe impl CData for BoxColumnBuffer {
101    fn cdata_type(&self) -> odbc_sys::CDataType {
102        self.as_ref().cdata_type()
103    }
104
105    fn indicator_ptr(&self) -> *const isize {
106        self.as_ref().indicator_ptr()
107    }
108
109    fn value_ptr(&self) -> *const c_void {
110        self.as_ref().value_ptr()
111    }
112
113    fn buffer_length(&self) -> isize {
114        self.as_ref().buffer_length()
115    }
116}
117
118unsafe impl CDataMut for BoxColumnBuffer {
119    fn mut_indicator_ptr(&mut self) -> *mut isize {
120        self.as_mut().mut_indicator_ptr()
121    }
122
123    fn mut_value_ptr(&mut self) -> *mut c_void {
124        self.as_mut().mut_value_ptr()
125    }
126}
127
128unsafe impl ColumnBuffer for BoxColumnBuffer {
129    fn capacity(&self) -> usize {
130        self.as_ref().capacity()
131    }
132
133    fn has_truncated_values(&self, num_rows: usize) -> Option<Indicator> {
134        self.as_ref().has_truncated_values(num_rows)
135    }
136}
137
138unsafe impl Slice for BoxColumnBuffer {
139    type Slice<'a> = AnyColumnBufferSlice<'a>;
140
141    fn slice(&self, valid_rows: usize) -> AnyColumnBufferSlice<'_> {
142        AnyColumnBufferSlice {
143            buffer: self,
144            valid_rows,
145        }
146    }
147}
148
149/// Enables reading the valid contents of a column buffer, while it is bound to a block cursor
150/// as [`BoxColumnBuffer>`].
151#[derive(Clone, Copy)]
152pub struct AnyColumnBufferSlice<'a> {
153    buffer: &'a BoxColumnBuffer,
154    valid_rows: usize,
155}
156
157impl<'a> AnyColumnBufferSlice<'a> {
158    /// Fetch the associated slice if we know the underlying buffer to be of type `T`. Usually it is
159    /// more convenient to use methods which some amount of knowledge about the buffer types used by
160    /// this crate. I.e. it is better to use [`Self::as_text`], [`Self::as_wide_text`],
161    /// [`Self::as_binary`], [`Self::as_slice`] or [`Self::as_nullable_slice`] instead. The
162    /// exception would be if you use a custom column buffer type provided by your application
163    /// rather than `odbc-api` itself.
164    pub fn of<T>(self) -> Option<T::Slice<'a>>
165    where
166        T: Slice + 'static,
167    {
168        let buffer: &dyn Any = self.buffer.as_ref();
169        let buffer = buffer.downcast_ref::<T>()?;
170        Some(T::slice(buffer, self.valid_rows))
171    }
172
173    /// Use this if you know the underlying buffer holds narrow (e.g. UTF-8) character data and you
174    /// want to read it.
175    ///
176    /// `Some` if the the underlying buffer is of type [`TextColumn`] with `u8` values. Same as
177    /// `Self::of::<TextColumn<u8>>`.
178    pub fn as_text(self) -> Option<TextColumnSlice<'a, u8>> {
179        self.of::<TextColumn<u8>>()
180    }
181
182    /// Use this if you know the underlying buffer holds wide (i.e. UTF-16) character data and you
183    /// want to read it.
184    ///
185    /// `Some` if the the underlying buffer is of type [`TextColumn`] with `u16` values. Same as
186    /// `Self::of::<TextColumn<u16>>`.
187    pub fn as_wide_text(self) -> Option<TextColumnSlice<'a, u16>> {
188        self.of::<TextColumn<u16>>()
189    }
190
191    /// Use this if you know the underlying buffer holds binary data and you want to read it.
192    ///
193    /// `Some` if the the underlying buffer is of type [`BinColumn`]. Same as
194    /// `Self::of::<BinColumn>`.
195    pub fn as_binary(self) -> Option<BinColumnSlice<'a>> {
196        self.of::<BinColumn>()
197    }
198
199    /// Use this if you know the underlying buffer to hold **non-nullable** data of type `T`.
200    pub fn as_slice<T>(self) -> Option<&'a [T]>
201    where
202        T: Pod,
203    {
204        self.of::<Vec<T>>()
205    }
206
207    /// Use this if you know the underlying buffer to hold **nullable** data of type `T`.
208    pub fn as_nullable_slice<T>(self) -> Option<NullableSlice<'a, T>>
209    where
210        T: Pod,
211    {
212        self.of::<ColumnWithIndicator<T>>()
213    }
214}
215
216unsafe impl<'a> BoundInputSlice<'a> for BoxColumnBuffer {
217    type SliceMut = BoxColumBufferRefMut<'a>;
218
219    unsafe fn as_view_mut(
220        &'a mut self,
221        parameter_index: u16,
222        stmt: StatementRef<'a>,
223    ) -> BoxColumBufferRefMut<'a> {
224        BoxColumBufferRefMut {
225            buffer: self,
226            parameter_index,
227            stmt,
228        }
229    }
230}
231
232/// Fat reference to a [`BoxColumnBuffer`] that is bound as an array parameter to a [`Statement`].
233///
234/// This reference allows mutating the buffer contents, e.g. in order to reuse the same bound buffer
235/// for inserting multiple batches of data. If any operation is performed which would invalidate the
236/// column buffer will be automatically re-bound to the statement.
237pub struct BoxColumBufferRefMut<'a> {
238    /// Column buffer which will be downcast to the concrete type provided by the user in the `of`
239    /// method.
240    buffer: &'a mut BoxColumnBuffer,
241    /// We need the parameter index to re-bind the column to the same parameter placeholder should
242    /// the buffer be invalidated.
243    parameter_index: u16,
244    /// A handle to the statement to which the column buffer is bound. We need a reference to
245    /// support operations that may invalidate the buffer. Using the statement handle we can re-bind
246    /// after.
247    stmt: StatementRef<'a>,
248}
249
250impl<'a> BoxColumBufferRefMut<'a> {
251    /// Fetch the associated slice if we know the underlying buffer to be of type `T`.
252    pub fn of<T>(self) -> Option<T::SliceMut>
253    where
254        T: BoundInputSlice<'a> + 'static,
255    {
256        let buffer: &mut dyn Any = self.buffer.as_mut();
257        let buffer = buffer.downcast_mut::<T>()?;
258        let view_mut = unsafe { buffer.as_view_mut(self.parameter_index, self.stmt) };
259        Some(view_mut)
260    }
261
262    /// Use this if you know the underlying buffer holds wide (i.e. UTF-16) character data and you
263    /// want to access it.
264    ///
265    /// `Some` if the the underlying buffer is of type [`TextColumn`] with `u16` values. Same as
266    /// `Self::of::<TextColumn<u16>>`.
267    pub fn as_wide_text(self) -> Option<TextColumnSliceMut<'a, u16>> {
268        self.of::<TextColumn<u16>>()
269    }
270
271    /// Use this if you know the underlying buffer holds narrow (e.g. UTF-8) character data and you
272    /// want to access it.
273    ///
274    /// `Some` if the the underlying buffer is of type [`TextColumn`] with `u8` values. Same as
275    /// `Self::of::<TextColumn<u8>>`.
276    pub fn as_text(self) -> Option<TextColumnSliceMut<'a, u8>> {
277        self.of::<TextColumn<u8>>()
278    }
279
280    /// Use this if you know the underlying buffer holds binary data and you want to access it.
281    ///
282    /// `Some` if the the underlying buffer is of type [`BinColumn`]. Same as
283    /// `Self::of::<BinColumn>`.
284    pub fn as_binary(self) -> Option<BinColumnSliceMut<'a>> {
285        self.of::<BinColumn>()
286    }
287
288    pub fn as_slice<T>(self) -> Option<&'a mut [T]>
289    where
290        T: Pod,
291    {
292        self.of::<Vec<T>>()
293    }
294
295    pub fn as_nullable_slice<T>(self) -> Option<NullableSliceMut<'a, T>>
296    where
297        T: Pod,
298    {
299        self.of::<ColumnWithIndicator<T>>()
300    }
301}
302
303impl Resize for BoxColumnBuffer {
304    fn resize(&mut self, new_capacity: usize) {
305        self.as_mut().resize(new_capacity);
306    }
307}
308
309#[cfg(test)]
310mod tests {
311    use super::{BoxColumnBuffer, BufferDesc, ColumnarDynBuffer, Slice};
312
313    #[test]
314    #[should_panic(expected = "Column indices must be unique.")]
315    fn assert_unique_column_indices() {
316        let bd = BufferDesc::I32 { nullable: false };
317        ColumnarDynBuffer::from_descs_and_indices(1, [(1, bd), (2, bd), (1, bd)].iter().cloned());
318    }
319
320    #[test]
321    fn box_column_buffer_is_resize() {
322        // Given an `BoxColumnBuffer` with a capacity of 2 and values [1, 2]
323        let mut buffer: BoxColumnBuffer = Box::new(vec![1i32, 2]);
324
325        // When we resize it to a capacity of 4
326        buffer.resize(4);
327
328        // Then the buffer should still have the same values in the first two positions and the
329        // remaining positions should be filled with default values (0 for i32)
330        assert_eq!(buffer.slice(4).as_slice(), Some([1i32, 2, 0, 0].as_slice()));
331        // And the capacity should be 4
332        assert_eq!(buffer.capacity(), 4);
333    }
334
335    #[test]
336    fn slice_should_only_contain_part_of_the_buffer() {
337        let buffer: BoxColumnBuffer = Box::new(vec![1i32, 2, 3]);
338        let view = buffer.slice(2);
339        assert_eq!(Some([1, 2].as_slice()), view.as_slice::<i32>());
340    }
341
342    #[test]
343    fn slice_should_be_none_if_types_mismatch() {
344        let buffer: BoxColumnBuffer = Box::new(vec![1i32, 2, 3]);
345        let view = buffer.slice(3);
346        assert_eq!(None, view.as_slice::<i16>());
347    }
348
349    #[test]
350    fn nullable_slice_should_be_none_if_buffer_is_non_nullable() {
351        let buffer: BoxColumnBuffer = Box::new(vec![1i32, 2, 3]);
352        let view = buffer.slice(3);
353        assert!(view.as_nullable_slice::<i32>().is_none());
354    }
355}