odbc_api/buffers/
column_with_indicator.rs

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