Skip to main content

odbc_api/buffers/
column_with_indicator.rs

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