Skip to main content

odbc_api/buffers/
column_with_indicator.rs

1use super::{ColumnBuffer, Indicator, Resize, Slice};
2use crate::{
3    BoundInputSlice,
4    fixed_sized::{Bit, Pod},
5    handles::{CData, CDataMut, StatementRef},
6};
7use odbc_sys::{Date, NULL_DATA, Time, Timestamp};
8use std::{
9    ffi::c_void,
10    mem::size_of,
11    ptr::{null, null_mut},
12};
13
14pub type OptF64Column = ColumnWithIndicator<f64>;
15pub type OptF32Column = ColumnWithIndicator<f32>;
16pub type OptDateColumn = ColumnWithIndicator<Date>;
17pub type OptTimestampColumn = ColumnWithIndicator<Timestamp>;
18pub type OptTimeColumn = ColumnWithIndicator<Time>;
19pub type OptI8Column = ColumnWithIndicator<i8>;
20pub type OptI16Column = ColumnWithIndicator<i16>;
21pub type OptI32Column = ColumnWithIndicator<i32>;
22pub type OptI64Column = ColumnWithIndicator<i64>;
23pub type OptU8Column = ColumnWithIndicator<u8>;
24pub type OptBitColumn = ColumnWithIndicator<Bit>;
25
26/// Column buffer for fixed-size type, also binding an indicator buffer to handle NULL.
27#[derive(Debug)]
28pub struct ColumnWithIndicator<T> {
29    values: Vec<T>,
30    indicators: Vec<isize>,
31}
32
33impl<T> ColumnWithIndicator<T>
34where
35    T: Default + Clone,
36{
37    pub fn new(batch_size: usize) -> Self {
38        Self {
39            values: vec![T::default(); batch_size],
40            indicators: vec![NULL_DATA; batch_size],
41        }
42    }
43
44    /// Create a writer which writes to the first `n` elements of the buffer.
45    pub fn writer_n(&mut self, n: usize) -> NullableSliceMut<'_, T> {
46        NullableSliceMut {
47            indicators: &mut self.indicators[0..n],
48            values: &mut self.values[0..n],
49        }
50    }
51}
52
53/// Iterates over the elements of a column buffer. Returned by
54/// [`crate::buffers::ColumnarBuffer::column`] as part of an [`crate::buffers::AnySlice`].
55#[derive(Debug, Clone, Copy)]
56pub struct NullableSlice<'a, T> {
57    indicators: &'a [isize],
58    values: &'a [T],
59}
60
61impl<'a, T> NullableSlice<'a, T> {
62    /// `true` if the slice has a length of `0`.
63    pub fn is_empty(&self) -> bool {
64        self.values.is_empty()
65    }
66
67    /// Number of entries in this slice of the buffer
68    pub fn len(&self) -> usize {
69        self.values.len()
70    }
71
72    /// Read access to the underlying raw value and indicator buffer.
73    ///
74    /// The number of elements in the buffer is equal to the number of rows returned in the current
75    /// result set. Yet the content of any value, those associated value in the indicator buffer is
76    /// [`crate::sys::NULL_DATA`] is undefined.
77    ///
78    /// This method is useful for writing performant bindings to datastructures with similar binary
79    /// layout, as it allows for using memcopy rather than iterating over individual values.
80    ///
81    /// # Example
82    ///
83    /// ```
84    /// use odbc_api::{buffers::NullableSlice, sys::NULL_DATA};
85    ///
86    /// // Memcopy the values out of the buffer, and make a mask of bools indicating the NULL
87    /// // values.
88    /// fn copy_values_and_make_mask(odbc_slice: NullableSlice<i32>) -> (Vec<i32>, Vec<bool>) {
89    ///     let (values, indicators) = odbc_slice.raw_values();
90    ///     let values = values.to_vec();
91    ///     // Create array of bools indicating null values.
92    ///     let mask: Vec<bool> = indicators
93    ///         .iter()
94    ///         .map(|&indicator| indicator != NULL_DATA)
95    ///         .collect();
96    ///     (values, mask)
97    /// }
98    /// ```
99    pub fn raw_values(&self) -> (&'a [T], &'a [isize]) {
100        (self.values, self.indicators)
101    }
102
103    /// Access the n-th element. `None` if the indicater is `NULL_DATA`.
104    pub fn get(&self, index: usize) -> Option<&'a T> {
105        if self.indicators[index] == NULL_DATA {
106            None
107        } else {
108            Some(&self.values[index])
109        }
110    }
111}
112
113impl<'a, T> Iterator for NullableSlice<'a, T> {
114    type Item = Option<&'a T>;
115
116    fn next(&mut self) -> Option<Self::Item> {
117        if let Some(&ind) = self.indicators.first() {
118            let item = if ind == NULL_DATA {
119                None
120            } else {
121                Some(&self.values[0])
122            };
123            self.indicators = &self.indicators[1..];
124            self.values = &self.values[1..];
125            Some(item)
126        } else {
127            None
128        }
129    }
130}
131
132unsafe impl<T> CData for ColumnWithIndicator<T>
133where
134    T: Pod,
135{
136    fn cdata_type(&self) -> odbc_sys::CDataType {
137        T::C_DATA_TYPE
138    }
139
140    fn indicator_ptr(&self) -> *const isize {
141        self.indicators.as_ptr()
142    }
143
144    fn value_ptr(&self) -> *const c_void {
145        self.values.as_ptr() as *const c_void
146    }
147
148    fn buffer_length(&self) -> isize {
149        size_of::<T>().try_into().unwrap()
150    }
151}
152
153unsafe impl<T> CDataMut for ColumnWithIndicator<T>
154where
155    T: Pod,
156{
157    fn mut_indicator_ptr(&mut self) -> *mut isize {
158        self.indicators.as_mut_ptr()
159    }
160
161    fn mut_value_ptr(&mut self) -> *mut c_void {
162        self.values.as_mut_ptr() as *mut c_void
163    }
164}
165
166unsafe impl<T> ColumnBuffer for ColumnWithIndicator<T>
167where
168    T: Pod,
169{
170    fn capacity(&self) -> usize {
171        self.indicators.len()
172    }
173
174    fn has_truncated_values(&self, _num_rows: usize) -> Option<Indicator> {
175        None
176    }
177}
178
179unsafe impl<T> Slice for ColumnWithIndicator<T>
180where
181    T: Pod,
182{
183    type Slice<'a> = NullableSlice<'a, T>;
184
185    fn slice(&self, valid_rows: usize) -> NullableSlice<'_, T> {
186        NullableSlice {
187            indicators: &self.indicators[0..valid_rows],
188            values: &self.values[0..valid_rows],
189        }
190    }
191}
192
193unsafe impl<'a, T> BoundInputSlice<'a> for ColumnWithIndicator<T>
194where
195    T: Pod + 'static,
196{
197    type SliceMut = NullableSliceMut<'a, T>;
198
199    unsafe fn as_view_mut(
200        &'a mut self,
201        _parameter_index: u16,
202        _stmt: StatementRef<'a>,
203    ) -> NullableSliceMut<'a, T> {
204        self.writer_n(self.capacity())
205    }
206}
207
208unsafe impl<T> CData for Vec<T>
209where
210    T: Pod,
211{
212    fn cdata_type(&self) -> odbc_sys::CDataType {
213        T::C_DATA_TYPE
214    }
215
216    fn indicator_ptr(&self) -> *const isize {
217        null()
218    }
219
220    fn value_ptr(&self) -> *const c_void {
221        self.as_ptr() as *const c_void
222    }
223
224    fn buffer_length(&self) -> isize {
225        size_of::<T>().try_into().unwrap()
226    }
227}
228
229unsafe impl<T> CDataMut for Vec<T>
230where
231    T: Pod,
232{
233    fn mut_indicator_ptr(&mut self) -> *mut isize {
234        null_mut()
235    }
236
237    fn mut_value_ptr(&mut self) -> *mut c_void {
238        self.as_mut_ptr() as *mut c_void
239    }
240}
241
242unsafe impl<'a, T> BoundInputSlice<'a> for Vec<T>
243where
244    T: Pod + 'static,
245{
246    type SliceMut = &'a mut [T];
247
248    unsafe fn as_view_mut(
249        &'a mut self,
250        _parameter_index: u16,
251        _stmt: StatementRef<'a>,
252    ) -> &'a mut [T] {
253        self.as_mut_slice()
254    }
255}
256
257/// Used to fill a column buffer with an iterator. Returned by
258/// [`crate::ColumnarBulkInserter::column_mut`] as part of an [`crate::buffers::AnySliceMut`].
259#[derive(Debug)]
260pub struct NullableSliceMut<'a, T> {
261    indicators: &'a mut [isize],
262    values: &'a mut [T],
263}
264
265impl<T> NullableSliceMut<'_, T> {
266    /// `true` if the slice has a length of `0`.
267    pub fn is_empty(&self) -> bool {
268        self.values.is_empty()
269    }
270
271    /// Number of entries in this slice of the buffer
272    pub fn len(&self) -> usize {
273        self.values.len()
274    }
275
276    /// Sets the value at the specified index. Use `None` to specify a `NULL` value.
277    pub fn set_cell(&mut self, index: usize, cell: Option<T>) {
278        if let Some(value) = cell {
279            self.indicators[index] = 0;
280            self.values[index] = value;
281        } else {
282            self.indicators[index] = NULL_DATA;
283        }
284    }
285
286    /// Write access to the underlying raw value and indicator buffer.
287    ///
288    /// The number of elements in the buffer is equal to `len`.
289    ///
290    /// This method is useful for writing performant bindings to datastructures with similar binary
291    /// layout, as it allows for using memcopy rather than iterating over individual values.
292    ///
293    /// # Example
294    ///
295    /// ```
296    /// use odbc_api::{buffers::NullableSliceMut, sys::NULL_DATA};
297    ///
298    /// // Memcopy the values into the buffer, and set indicators according to mask
299    /// // values.
300    /// fn copy_values_and_make_mask(
301    ///     new_values: &[i32],
302    ///     mask: &[bool],
303    ///     odbc_slice: &mut NullableSliceMut<i32>)
304    /// {
305    ///     let (values, indicators) = odbc_slice.raw_values();
306    ///     values.copy_from_slice(new_values);
307    ///     // Create array of bools indicating null values.
308    ///     indicators.iter_mut().zip(mask.iter()).for_each(|(indicator, &mask)| {
309    ///         *indicator = if mask {
310    ///             0
311    ///         } else {
312    ///             NULL_DATA
313    ///         }
314    ///     });
315    /// }
316    /// ```
317    pub fn raw_values(&mut self) -> (&mut [T], &mut [isize]) {
318        (self.values, self.indicators)
319    }
320}
321
322impl<T> NullableSliceMut<'_, T> {
323    /// Writes the elements returned by the iterator into the buffer, starting at the beginning.
324    /// Writes elements until the iterator returns `None` or the buffer can not hold more elements.
325    pub fn write(&mut self, it: impl Iterator<Item = Option<T>>) {
326        for (index, item) in it.enumerate().take(self.values.len()) {
327            self.set_cell(index, item)
328        }
329    }
330}
331
332impl<T> Resize for ColumnWithIndicator<T>
333where
334    T: Default + Clone,
335{
336    fn resize(&mut self, new_size: usize) {
337        self.values.resize(new_size, T::default());
338        self.indicators.resize(new_size, NULL_DATA);
339    }
340}
341
342#[cfg(test)]
343mod tests {
344    use crate::buffers::{Resize, Slice};
345
346    use super::ColumnWithIndicator;
347
348    #[test]
349    fn column_with_indicator_is_resize() {
350        // Given a column with indicator with two elements `1` and `2`
351        let mut column = ColumnWithIndicator::<i32>::new(2);
352        let mut writer = column.writer_n(2);
353        writer.set_cell(0, Some(1));
354        writer.set_cell(1, Some(2));
355
356        // When we resize it to 3 elements
357        column.resize(3);
358
359        // Then the first two elements are still `1` and `2`, and the third is NULL
360        let slice = column.slice(3);
361        assert_eq!(slice.get(0), Some(&1));
362        assert_eq!(slice.get(1), Some(&2));
363        assert_eq!(slice.get(2), None);
364    }
365}