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