Skip to main content

odbc_api/buffers/
dyn_column_buffer.rs

1use super::{
2    BinColumn, BinColumnSlice, BufferDesc, ColumnBuffer, ColumnarBuffer, Indicator, NullableSlice,
3    Slice, TextColumn, TextColumnSlice, column_with_indicator::ColumnWithIndicator,
4};
5
6use crate::{
7    Error,
8    fixed_sized::Pod,
9    handles::{CData, CDataMut},
10};
11
12use std::{any::Any, collections::HashSet, ffi::c_void};
13
14/// Columnar buffer with dynamic column types decided at runtime.
15///
16/// Go to buffer implementation for this crate if fetching data. If you have no reason to use
17/// something else use this.
18pub type ColumnarDynBuffer = ColumnarBuffer<BoxColumnBuffer>;
19
20impl ColumnarDynBuffer {
21    /// Allocates a [`ColumnarDynBuffer`] fitting the buffer descriptions.
22    pub fn from_descs(capacity: usize, descs: impl IntoIterator<Item = BufferDesc>) -> Self {
23        let mut column_index = 0;
24        let columns = descs
25            .into_iter()
26            .map(move |desc| {
27                let buffer = desc.column_buffer(capacity);
28                column_index += 1;
29                (column_index, buffer)
30            })
31            .collect();
32        unsafe { ColumnarBuffer::new_unchecked(capacity, columns) }
33    }
34
35    /// Allocates a [`ColumnarDynBuffer`] fitting the buffer descriptions. If not enough memory is
36    /// available to allocate the buffers this function fails with
37    /// [`Error::TooLargeColumnBufferSize`]. This function is slower than [`Self::from_descs`] which
38    /// would just panic if not enough memory is available for allocation.
39    pub fn try_from_descs(
40        capacity: usize,
41        descs: impl IntoIterator<Item = BufferDesc>,
42    ) -> Result<Self, Error> {
43        let mut column_index = 0;
44        let columns = descs
45            .into_iter()
46            .map(move |desc| {
47                let buffer = desc
48                    .try_column_buffer(capacity)
49                    .map_err(|source| source.add_context(column_index))?;
50                column_index += 1;
51                Ok::<_, Error>((column_index, buffer))
52            })
53            .collect::<Result<_, _>>()?;
54        Ok(unsafe { ColumnarBuffer::new_unchecked(capacity, columns) })
55    }
56
57    /// Allows you to pass the buffer descriptions together with a one based column index referring
58    /// the column, the buffer is supposed to bind to. This allows you also to ignore columns in a
59    /// result set, by not binding them at all. There is no restriction on the order of column
60    /// indices passed, but the function will panic, if the indices are not unique.
61    pub fn from_descs_and_indices(
62        max_rows: usize,
63        description: impl Iterator<Item = (u16, BufferDesc)>,
64    ) -> Self {
65        let columns: Vec<_> = description
66            .map(|(col_index, buffer_desc)| (col_index, buffer_desc.column_buffer(max_rows)))
67            .collect();
68
69        // Assert uniqueness of indices
70        let mut indices = HashSet::new();
71        if columns
72            .iter()
73            .any(move |&(col_index, _)| !indices.insert(col_index))
74        {
75            panic!("Column indices must be unique.")
76        }
77
78        ColumnarBuffer::new(columns)
79    }
80}
81
82/// Heap allocated column buffer with dynamic type. Intended to be used as a type parameter for
83/// [`ColumnarBuffer`] to enable columns of different types only known at runtime to be bound to the
84/// same block cursor.
85pub type BoxColumnBuffer = Box<dyn AnyColumnBuffer>;
86
87/// A [`ColumnBuffer`] that additionally carries runtime type information, so a trait object can
88/// be downcast to the concrete buffer type it was constructed from. Intended for [`ColumnarBuffer`]
89/// with heterogenous columns, i.e. integer and text columns.
90///
91/// This trait also inherits from`Send` so we can use it in concurrent bulk fetches using double
92/// buffering in multiple threads. Currently there are no buffer implementations which are not
93/// `Send`. So a distinction between `Send` and non-`Send` buffers is assumed to cause more friction
94/// rather than being helpful.
95pub trait AnyColumnBuffer: ColumnBuffer + Any + Send {}
96
97impl<T> AnyColumnBuffer for T where T: ColumnBuffer + Any + Send {}
98
99unsafe impl CData for Box<dyn AnyColumnBuffer> {
100    fn cdata_type(&self) -> odbc_sys::CDataType {
101        self.as_ref().cdata_type()
102    }
103
104    fn indicator_ptr(&self) -> *const isize {
105        self.as_ref().indicator_ptr()
106    }
107
108    fn value_ptr(&self) -> *const c_void {
109        self.as_ref().value_ptr()
110    }
111
112    fn buffer_length(&self) -> isize {
113        self.as_ref().buffer_length()
114    }
115}
116
117unsafe impl CDataMut for Box<dyn AnyColumnBuffer> {
118    fn mut_indicator_ptr(&mut self) -> *mut isize {
119        self.as_mut().mut_indicator_ptr()
120    }
121
122    fn mut_value_ptr(&mut self) -> *mut c_void {
123        self.as_mut().mut_value_ptr()
124    }
125}
126
127unsafe impl ColumnBuffer for Box<dyn AnyColumnBuffer> {
128    fn capacity(&self) -> usize {
129        self.as_ref().capacity()
130    }
131
132    fn has_truncated_values(&self, num_rows: usize) -> Option<Indicator> {
133        self.as_ref().has_truncated_values(num_rows)
134    }
135}
136
137unsafe impl Slice for Box<dyn AnyColumnBuffer> {
138    type Slice<'a> = AnyColumnBufferSlice<'a>;
139
140    fn slice(&self, valid_rows: usize) -> AnyColumnBufferSlice<'_> {
141        AnyColumnBufferSlice {
142            buffer: self,
143            valid_rows,
144        }
145    }
146}
147
148/// Enables reading the valid contents of a column buffer, while it is bound to a block cursor
149/// as [`Box<dyn AnyColumnBuffer>`].
150#[derive(Clone, Copy)]
151pub struct AnyColumnBufferSlice<'a> {
152    buffer: &'a BoxColumnBuffer,
153    valid_rows: usize,
154}
155
156impl<'a> AnyColumnBufferSlice<'a> {
157    /// Fetch the associated slice if we know the underlying buffer to be of type `T`.
158    pub fn of<T>(self) -> Option<T::Slice<'a>>
159    where
160        T: Slice + 'static,
161    {
162        let buffer: &dyn Any = self.buffer.as_ref();
163        let buffer = buffer.downcast_ref::<T>()?;
164        Some(T::slice(buffer, self.valid_rows))
165    }
166
167    /// Use this if you know the underlying buffer holds narrow (e.g. UTF-8) character data and you
168    /// want to read it.
169    ///
170    /// `Some` if the the underlying buffer is of type [`TextColumn`] with `u8` values. Same as
171    /// `Self::of::<TextColumn<u8>>`.
172    pub fn as_text(self) -> Option<TextColumnSlice<'a, u8>> {
173        self.of::<TextColumn<u8>>()
174    }
175
176    /// Use this if you know the underlying buffer holds wide (i.e. UTF-16) character data and you
177    /// want to read it.
178    ///
179    /// `Some` if the the underlying buffer is of type [`TextColumn`] with `u16` values. Same as
180    /// `Self::of::<TextColumn<u16>>`.
181    pub fn as_wide_text(self) -> Option<TextColumnSlice<'a, u16>> {
182        self.of::<TextColumn<u16>>()
183    }
184
185    /// Use this if you know the underlying buffer holds binary data and you want to read it.
186    ///
187    /// `Some` if the the underlying buffer is of type [`BinColumn`]. Same as
188    /// `Self::of::<BinColumn>`.
189    pub fn as_binary(self) -> Option<BinColumnSlice<'a>> {
190        self.of::<BinColumn>()
191    }
192
193    /// Use this if you know the underlying buffer to hold **non-nullable** data of type `T`.
194    pub fn as_slice<T>(self) -> Option<&'a [T]>
195    where
196        T: Pod,
197    {
198        self.of::<Vec<T>>()
199    }
200
201    /// Use this if you know the underlying buffer to hold **nullable** data of type `T`.
202    pub fn as_nullable_slice<T>(self) -> Option<NullableSlice<'a, T>>
203    where
204        T: Pod,
205    {
206        self.of::<ColumnWithIndicator<T>>()
207    }
208}
209
210#[cfg(test)]
211mod tests {
212
213    use super::{BufferDesc, ColumnarDynBuffer};
214
215    #[test]
216    #[should_panic(expected = "Column indices must be unique.")]
217    fn assert_unique_column_indices() {
218        let bd = BufferDesc::I32 { nullable: false };
219        ColumnarDynBuffer::from_descs_and_indices(1, [(1, bd), (2, bd), (1, bd)].iter().cloned());
220    }
221}