odbc_api/buffers/
text_column.rs

1use crate::{
2    DataType, Error,
3    buffers::Resize,
4    columnar_bulk_inserter::BoundInputSlice,
5    error::TooLargeBufferSize,
6    handles::{
7        ASSUMED_MAX_LENGTH_OF_W_VARCHAR, CData, CDataMut, HasDataType, Statement, StatementRef,
8    },
9};
10
11use super::{ColumnBuffer, Indicator};
12
13use log::debug;
14use odbc_sys::{CDataType, NULL_DATA};
15use std::{cmp::min, ffi::c_void, mem::size_of, num::NonZeroUsize, panic};
16use widestring::U16Str;
17
18/// A column buffer for character data. The actual encoding used may depend on your system locale.
19pub type CharColumn = TextColumn<u8>;
20
21/// This buffer uses wide characters which implies UTF-16 encoding. UTF-8 encoding is preferable for
22/// most applications, but contrary to its sibling [`crate::buffers::CharColumn`] this buffer types
23/// implied encoding does not depend on the system locale.
24pub type WCharColumn = TextColumn<u16>;
25
26/// A buffer intended to be bound to a column of a cursor. Elements of the buffer will contain a
27/// variable amount of characters up to a maximum string length. Since most SQL types have a string
28/// representation this buffer can be bound to a column of almost any type, ODBC driver and driver
29/// manager should take care of the conversion. Since elements of this type have variable length an
30/// indicator buffer needs to be bound, whether the column is nullable or not, and therefore does
31/// not matter for this buffer.
32///
33/// Character type `C` is intended to be either `u8` or `u16`.
34#[derive(Debug)]
35pub struct TextColumn<C> {
36    /// Maximum text length without terminating zero.
37    max_str_len: usize,
38    /// All the characters of all the elements in the buffer. The first character of the n-th
39    /// element is at index `n * (max_str_len + 1)`.
40    values: Vec<C>,
41    /// Elements in this buffer are either `NULL_DATA` or hold the length of the element in value
42    /// with the same index. Please note that this value may be larger than `max_str_len` if the
43    /// text has been truncated.
44    indicators: Vec<isize>,
45}
46
47impl<C> TextColumn<C> {
48    /// This will allocate a value and indicator buffer for `batch_size` elements. Each value may
49    /// have a maximum length of `max_str_len`. This implies that `max_str_len` is increased by
50    /// one in order to make space for the null terminating zero at the end of strings. Uses a
51    /// fallible allocation for creating the buffer. In applications often the `max_str_len` size
52    /// of the buffer, might be directly inspired by the maximum size of the type, as reported, by
53    /// ODBC. Which might get exceedingly large for types like VARCHAR(MAX)
54    pub fn try_new(batch_size: usize, max_str_len: usize) -> Result<Self, TooLargeBufferSize>
55    where
56        C: Default + Copy,
57    {
58        // Element size is +1 to account for terminating zero
59        let element_size = max_str_len + 1;
60        let len = element_size * batch_size;
61        let mut values = Vec::new();
62        values
63            .try_reserve_exact(len)
64            .map_err(|_| TooLargeBufferSize {
65                num_elements: batch_size,
66                // We want the element size in bytes
67                element_size: element_size * size_of::<C>(),
68            })?;
69        values.resize(len, C::default());
70        Ok(TextColumn {
71            max_str_len,
72            values,
73            indicators: vec![0; batch_size],
74        })
75    }
76
77    /// This will allocate a value and indicator buffer for `batch_size` elements. Each value may
78    /// have a maximum length of `max_str_len`. This implies that `max_str_len` is increased by
79    /// one in order to make space for the null terminating zero at the end of strings. All
80    /// indicators are set to [`crate::sys::NULL_DATA`] by default.
81    pub fn new(batch_size: usize, max_str_len: usize) -> Self
82    where
83        C: Default + Copy,
84    {
85        // Element size is +1 to account for terminating zero
86        let element_size = max_str_len + 1;
87        let len = element_size * batch_size;
88        let mut values = Vec::new();
89        values.reserve_exact(len);
90        values.resize(len, C::default());
91        TextColumn {
92            max_str_len,
93            values,
94            indicators: vec![NULL_DATA; batch_size],
95        }
96    }
97
98    /// Bytes of string at the specified position. Includes interior nuls, but excludes the
99    /// terminating nul.
100    ///
101    /// The column buffer does not know how many elements were in the last row group, and therefore
102    /// can not guarantee the accessed element to be valid and in a defined state. It also can not
103    /// panic on accessing an undefined element. It will panic however if `row_index` is larger or
104    /// equal to the maximum number of elements in the buffer.
105    pub fn value_at(&self, row_index: usize) -> Option<&[C]> {
106        self.content_length_at(row_index).map(|length| {
107            let offset = row_index * (self.max_str_len + 1);
108            &self.values[offset..offset + length]
109        })
110    }
111
112    /// Maximum length of elements
113    pub fn max_len(&self) -> usize {
114        self.max_str_len
115    }
116
117    /// Indicator value at the specified position. Useful to detect truncation of data.
118    ///
119    /// The column buffer does not know how many elements were in the last row group, and therefore
120    /// can not guarantee the accessed element to be valid and in a defined state. It also can not
121    /// panic on accessing an undefined element. It will panic however if `row_index` is larger or
122    /// equal to the maximum number of elements in the buffer.
123    pub fn indicator_at(&self, row_index: usize) -> Indicator {
124        Indicator::from_isize(self.indicators[row_index])
125    }
126
127    /// Length of value at the specified position. This is different from an indicator as it refers
128    /// to the length of the value in the buffer, not to the length of the value in the datasource.
129    /// The two things are different for truncated values.
130    pub fn content_length_at(&self, row_index: usize) -> Option<usize> {
131        match self.indicator_at(row_index) {
132            Indicator::Null => None,
133            // Seen no total in the wild then binding shorter buffer to fixed sized CHAR in MSSQL.
134            Indicator::NoTotal => Some(self.max_str_len),
135            Indicator::Length(length_in_bytes) => {
136                let length_in_chars = length_in_bytes / size_of::<C>();
137                let length = min(self.max_str_len, length_in_chars);
138                Some(length)
139            }
140        }
141    }
142
143    /// Finds an indiactor larger than the maximum element size in the range [0, num_rows).
144    ///
145    /// After fetching data we may want to know if any value has been truncated due to the buffer
146    /// not being able to hold elements of that size. This method checks the indicator buffer
147    /// element wise.
148    pub fn has_truncated_values(&self, num_rows: usize) -> Option<Indicator> {
149        let max_bin_length = self.max_str_len * size_of::<C>();
150        self.indicators
151            .iter()
152            .copied()
153            .take(num_rows)
154            .find_map(|indicator| {
155                let indicator = Indicator::from_isize(indicator);
156                indicator.is_truncated(max_bin_length).then_some(indicator)
157            })
158    }
159
160    /// Changes the maximum string length the buffer can hold. This operation is useful if you find
161    /// an unexpected large input string during insertion.
162    ///
163    /// This is however costly, as not only does the new buffer have to be allocated, but all values
164    /// have to copied from the old to the new buffer.
165    ///
166    /// This method could also be used to reduce the maximum string length, which would truncate
167    /// strings in the process.
168    ///
169    /// This method does not adjust indicator buffers as these might hold values larger than the
170    /// maximum string length.
171    ///
172    /// # Parameters
173    ///
174    /// * `new_max_str_len`: New maximum string length without terminating zero.
175    /// * `num_rows`: Number of valid rows currently stored in this buffer.
176    pub fn resize_max_str(&mut self, new_max_str_len: usize, num_rows: usize)
177    where
178        C: Default + Copy,
179    {
180        debug!(
181            "Rebinding text column buffer with {} elements. Maximum string length {} => {}",
182            num_rows, self.max_str_len, new_max_str_len
183        );
184
185        let batch_size = self.indicators.len();
186        // Allocate a new buffer large enough to hold a batch of strings with maximum length.
187        let mut new_values = vec![C::default(); (new_max_str_len + 1) * batch_size];
188        // Copy values from old to new buffer.
189        let max_copy_length = min(self.max_str_len, new_max_str_len);
190        for ((&indicator, old_value), new_value) in self
191            .indicators
192            .iter()
193            .zip(self.values.chunks_exact_mut(self.max_str_len + 1))
194            .zip(new_values.chunks_exact_mut(new_max_str_len + 1))
195            .take(num_rows)
196        {
197            match Indicator::from_isize(indicator) {
198                Indicator::Null => (),
199                Indicator::NoTotal => {
200                    // There is no good choice here in case we are expanding the buffer. Since
201                    // NO_TOTAL indicates that we use the entire buffer, but in truth it would now
202                    // be padded with 0. I currently cannot think of any use case there it would
203                    // matter.
204                    new_value[..max_copy_length].clone_from_slice(&old_value[..max_copy_length]);
205                }
206                Indicator::Length(num_bytes_len) => {
207                    let num_bytes_to_copy = min(num_bytes_len / size_of::<C>(), max_copy_length);
208                    new_value[..num_bytes_to_copy].copy_from_slice(&old_value[..num_bytes_to_copy]);
209                }
210            }
211        }
212        self.values = new_values;
213        self.max_str_len = new_max_str_len;
214    }
215
216    /// Sets the value of the buffer at index at Null or the specified binary Text. This method will
217    /// panic on out of bounds index, or if input holds a text which is larger than the maximum
218    /// allowed element length. `input` must be specified without the terminating zero.
219    pub fn set_value(&mut self, index: usize, input: Option<&[C]>)
220    where
221        C: Default + Copy,
222    {
223        if let Some(input) = input {
224            self.set_mut(index, input.len()).copy_from_slice(input);
225        } else {
226            self.indicators[index] = NULL_DATA;
227        }
228    }
229
230    /// Can be used to set a value at a specific row index without performing a memcopy on an input
231    /// slice and instead provides direct access to the underlying buffer.
232    ///
233    /// In situations there the memcopy can not be avoided anyway [`Self::set_value`] is likely to
234    /// be more convenient. This method is very useful if you want to `write!` a string value to the
235    /// buffer and the binary (**!**) length of the formatted string is known upfront.
236    ///
237    /// # Example: Write timestamp to text column.
238    ///
239    /// ```
240    /// use odbc_api::buffers::TextColumn;
241    /// use std::io::Write;
242    ///
243    /// /// Writes times formatted as hh::mm::ss.fff
244    /// fn write_time(
245    ///     col: &mut TextColumn<u8>,
246    ///     index: usize,
247    ///     hours: u8,
248    ///     minutes: u8,
249    ///     seconds: u8,
250    ///     milliseconds: u16)
251    /// {
252    ///     write!(
253    ///         col.set_mut(index, 12),
254    ///         "{:02}:{:02}:{:02}.{:03}",
255    ///         hours, minutes, seconds, milliseconds
256    ///     ).unwrap();
257    /// }
258    /// ```
259    pub fn set_mut(&mut self, index: usize, length: usize) -> &mut [C]
260    where
261        C: Default,
262    {
263        if length > self.max_str_len {
264            panic!(
265                "Tried to insert a value into a text buffer which is larger than the maximum \
266                allowed string length for the buffer."
267            );
268        }
269        self.indicators[index] = (length * size_of::<C>()).try_into().unwrap();
270        let start = (self.max_str_len + 1) * index;
271        let end = start + length;
272        // Let's insert a terminating zero at the end to be on the safe side, in case the ODBC
273        // driver would not care about the value in the index buffer and only look for the
274        // terminating zero.
275        self.values[end] = C::default();
276        &mut self.values[start..end]
277    }
278
279    /// Fills the column with NULL, between From and To
280    pub fn fill_null(&mut self, from: usize, to: usize) {
281        for index in from..to {
282            self.indicators[index] = NULL_DATA;
283        }
284    }
285
286    /// Provides access to the raw underlying value buffer. Normal applications should have little
287    /// reason to call this method. Yet it may be useful for writing bindings which copy directly
288    /// from the ODBC in memory representation into other kinds of buffers.
289    ///
290    /// The buffer contains the bytes for every non null valid element, padded to the maximum string
291    /// length. The content of the padding bytes is undefined. Usually ODBC drivers write a
292    /// terminating zero at the end of each string. For the actual value length call
293    /// [`Self::content_length_at`]. Any element starts at index * ([`Self::max_len`] + 1).
294    pub fn raw_value_buffer(&self, num_valid_rows: usize) -> &[C] {
295        &self.values[..(self.max_str_len + 1) * num_valid_rows]
296    }
297
298    /// The maximum number of rows the TextColumn can hold.
299    pub fn row_capacity(&self) -> usize {
300        self.values.len()
301    }
302}
303
304impl WCharColumn {
305    /// The string slice at the specified position as `U16Str`. Includes interior nuls, but excludes
306    /// the terminating nul.
307    ///
308    /// # Safety
309    ///
310    /// The column buffer does not know how many elements were in the last row group, and therefore
311    /// can not guarantee the accessed element to be valid and in a defined state. It also can not
312    /// panic on accessing an undefined element. It will panic however if `row_index` is larger or
313    /// equal to the maximum number of elements in the buffer.
314    pub unsafe fn ustr_at(&self, row_index: usize) -> Option<&U16Str> {
315        self.value_at(row_index).map(U16Str::from_slice)
316    }
317}
318
319unsafe impl<C: 'static> ColumnBuffer for TextColumn<C>
320where
321    TextColumn<C>: CDataMut + HasDataType,
322{
323    type View<'a> = TextColumnView<'a, C>;
324
325    fn view(&self, valid_rows: usize) -> TextColumnView<'_, C> {
326        TextColumnView {
327            num_rows: valid_rows,
328            col: self,
329        }
330    }
331
332    fn fill_default(&mut self, from: usize, to: usize) {
333        self.fill_null(from, to)
334    }
335
336    /// Maximum number of text strings this column may hold.
337    fn capacity(&self) -> usize {
338        self.indicators.len()
339    }
340
341    fn has_truncated_values(&self, num_rows: usize) -> Option<Indicator> {
342        let max_bin_length = self.max_str_len * size_of::<C>();
343        self.indicators
344            .iter()
345            .copied()
346            .take(num_rows)
347            .find_map(|indicator| {
348                let indicator = Indicator::from_isize(indicator);
349                indicator.is_truncated(max_bin_length).then_some(indicator)
350            })
351    }
352}
353
354/// Allows read only access to the valid part of a text column.
355///
356/// You may ask, why is this type required, should we not just be able to use `&TextColumn`? The
357/// problem with `TextColumn` is, that it is a buffer, but it has no idea how many of its members
358/// are actually valid, and have been returned with the last row group of the the result set. That
359/// number is maintained on the level of the entire column buffer. So a text column knows the number
360/// of valid rows, in addition to holding a reference to the buffer, in order to guarantee, that
361/// every element acccessed through it, is valid.
362#[derive(Debug, Clone, Copy)]
363pub struct TextColumnView<'c, C> {
364    num_rows: usize,
365    col: &'c TextColumn<C>,
366}
367
368impl<'c, C> TextColumnView<'c, C> {
369    /// The number of valid elements in the text column.
370    pub fn len(&self) -> usize {
371        self.num_rows
372    }
373
374    /// True if, and only if there are no valid rows in the column buffer.
375    pub fn is_empty(&self) -> bool {
376        self.num_rows == 0
377    }
378
379    /// Slice of text at the specified row index without terminating zero. `None` if the value is
380    /// `NULL`. This method will panic if the index is larger than the number of valid rows in the
381    /// view as returned by [`Self::len`].
382    pub fn get(&self, index: usize) -> Option<&'c [C]> {
383        self.col.value_at(index)
384    }
385
386    /// Iterator over the valid elements of the text buffer
387    pub fn iter(&self) -> TextColumnIt<'c, C> {
388        TextColumnIt {
389            pos: 0,
390            num_rows: self.num_rows,
391            col: self.col,
392        }
393    }
394
395    /// Length of value at the specified position. This is different from an indicator as it refers
396    /// to the length of the value in the buffer, not to the length of the value in the datasource.
397    /// The two things are different for truncated values.
398    pub fn content_length_at(&self, row_index: usize) -> Option<usize> {
399        if row_index >= self.num_rows {
400            panic!("Row index points beyond the range of valid values.")
401        }
402        self.col.content_length_at(row_index)
403    }
404
405    /// Provides access to the raw underlying value buffer. Normal applications should have little
406    /// reason to call this method. Yet it may be useful for writing bindings which copy directly
407    /// from the ODBC in memory representation into other kinds of buffers.
408    ///
409    /// The buffer contains the bytes for every non null valid element, padded to the maximum string
410    /// length. The content of the padding bytes is undefined. Usually ODBC drivers write a
411    /// terminating zero at the end of each string. For the actual value length call
412    /// [`Self::content_length_at`]. Any element starts at index * ([`Self::max_len`] + 1).
413    pub fn raw_value_buffer(&self) -> &'c [C] {
414        self.col.raw_value_buffer(self.num_rows)
415    }
416
417    pub fn max_len(&self) -> usize {
418        self.col.max_len()
419    }
420
421    /// `Some` if any value is truncated.
422    ///
423    /// After fetching data we may want to know if any value has been truncated due to the buffer
424    /// not being able to hold elements of that size. This method checks the indicator buffer
425    /// element wise.
426    pub fn has_truncated_values(&self) -> Option<Indicator> {
427        self.col.has_truncated_values(self.num_rows)
428    }
429}
430
431unsafe impl<'a, C: 'static> BoundInputSlice<'a> for TextColumn<C> {
432    type SliceMut = TextColumnSliceMut<'a, C>;
433
434    unsafe fn as_view_mut(
435        &'a mut self,
436        parameter_index: u16,
437        stmt: StatementRef<'a>,
438    ) -> Self::SliceMut {
439        TextColumnSliceMut {
440            column: self,
441            stmt,
442            parameter_index,
443        }
444    }
445}
446
447/// A view to a mutable array parameter text buffer, which allows for filling the buffer with
448/// values.
449pub struct TextColumnSliceMut<'a, C> {
450    column: &'a mut TextColumn<C>,
451    // Needed to rebind the column in case of resize
452    stmt: StatementRef<'a>,
453    // Also needed to rebind the column in case of resize
454    parameter_index: u16,
455}
456
457impl<C> TextColumnSliceMut<'_, C>
458where
459    C: Default + Copy + Send,
460{
461    /// Sets the value of the buffer at index at Null or the specified binary Text. This method will
462    /// panic on out of bounds index, or if input holds a text which is larger than the maximum
463    /// allowed element length. `element` must be specified without the terminating zero.
464    pub fn set_cell(&mut self, row_index: usize, element: Option<&[C]>) {
465        self.column.set_value(row_index, element)
466    }
467
468    /// Ensures that the buffer is large enough to hold elements of `element_length`. Does nothing
469    /// if the buffer is already large enough. Otherwise it will reallocate and rebind the buffer.
470    /// The first `num_rows_to_copy` will be copied from the old value buffer to the new
471    /// one. This makes this an extremely expensive operation.
472    pub fn ensure_max_element_length(
473        &mut self,
474        element_length: usize,
475        num_rows_to_copy: usize,
476    ) -> Result<(), Error>
477    where
478        TextColumn<C>: HasDataType + CData,
479    {
480        // Column buffer is not large enough to hold the element. We must allocate a larger buffer
481        // in order to hold it. This invalidates the pointers previously bound to the statement. So
482        // we rebind them.
483        if element_length > self.column.max_len() {
484            let new_max_str_len = element_length;
485            self.column
486                .resize_max_str(new_max_str_len, num_rows_to_copy);
487            unsafe {
488                self.stmt
489                    .bind_input_parameter(self.parameter_index, self.column)
490                    .into_result(&self.stmt)?
491            }
492        }
493        Ok(())
494    }
495
496    /// Can be used to set a value at a specific row index without performing a memcopy on an input
497    /// slice and instead provides direct access to the underlying buffer.
498    ///
499    /// In situations there the memcopy can not be avoided anyway [`Self::set_cell`] is likely to
500    /// be more convenient. This method is very useful if you want to `write!` a string value to the
501    /// buffer and the binary (**!**) length of the formatted string is known upfront.
502    ///
503    /// # Example: Write timestamp to text column.
504    ///
505    /// ```
506    /// use odbc_api::buffers::TextColumnSliceMut;
507    /// use std::io::Write;
508    ///
509    /// /// Writes times formatted as hh::mm::ss.fff
510    /// fn write_time(
511    ///     col: &mut TextColumnSliceMut<u8>,
512    ///     index: usize,
513    ///     hours: u8,
514    ///     minutes: u8,
515    ///     seconds: u8,
516    ///     milliseconds: u16)
517    /// {
518    ///     write!(
519    ///         col.set_mut(index, 12),
520    ///         "{:02}:{:02}:{:02}.{:03}",
521    ///         hours, minutes, seconds, milliseconds
522    ///     ).unwrap();
523    /// }
524    /// ```
525    pub fn set_mut(&mut self, index: usize, length: usize) -> &mut [C] {
526        self.column.set_mut(index, length)
527    }
528}
529
530/// Iterator over a text column. See [`TextColumnView::iter`]
531#[derive(Debug)]
532pub struct TextColumnIt<'c, C> {
533    pos: usize,
534    num_rows: usize,
535    col: &'c TextColumn<C>,
536}
537
538impl<'c, C> TextColumnIt<'c, C> {
539    fn next_impl(&mut self) -> Option<Option<&'c [C]>> {
540        if self.pos == self.num_rows {
541            None
542        } else {
543            let ret = Some(self.col.value_at(self.pos));
544            self.pos += 1;
545            ret
546        }
547    }
548}
549
550impl<'c> Iterator for TextColumnIt<'c, u8> {
551    type Item = Option<&'c [u8]>;
552
553    fn next(&mut self) -> Option<Self::Item> {
554        self.next_impl()
555    }
556
557    fn size_hint(&self) -> (usize, Option<usize>) {
558        let len = self.num_rows - self.pos;
559        (len, Some(len))
560    }
561}
562
563impl ExactSizeIterator for TextColumnIt<'_, u8> {}
564
565impl<'c> Iterator for TextColumnIt<'c, u16> {
566    type Item = Option<&'c U16Str>;
567
568    fn next(&mut self) -> Option<Self::Item> {
569        self.next_impl().map(|opt| opt.map(U16Str::from_slice))
570    }
571
572    fn size_hint(&self) -> (usize, Option<usize>) {
573        let len = self.num_rows - self.pos;
574        (len, Some(len))
575    }
576}
577
578impl ExactSizeIterator for TextColumnIt<'_, u16> {}
579
580unsafe impl CData for CharColumn {
581    fn cdata_type(&self) -> CDataType {
582        CDataType::Char
583    }
584
585    fn indicator_ptr(&self) -> *const isize {
586        self.indicators.as_ptr()
587    }
588
589    fn value_ptr(&self) -> *const c_void {
590        self.values.as_ptr() as *const c_void
591    }
592
593    fn buffer_length(&self) -> isize {
594        (self.max_str_len + 1).try_into().unwrap()
595    }
596}
597
598unsafe impl CDataMut for CharColumn {
599    fn mut_indicator_ptr(&mut self) -> *mut isize {
600        self.indicators.as_mut_ptr()
601    }
602
603    fn mut_value_ptr(&mut self) -> *mut c_void {
604        self.values.as_mut_ptr() as *mut c_void
605    }
606}
607
608impl HasDataType for CharColumn {
609    fn data_type(&self) -> DataType {
610        DataType::Varchar {
611            length: NonZeroUsize::new(self.max_str_len),
612        }
613    }
614}
615
616unsafe impl CData for WCharColumn {
617    fn cdata_type(&self) -> CDataType {
618        CDataType::WChar
619    }
620
621    fn indicator_ptr(&self) -> *const isize {
622        self.indicators.as_ptr()
623    }
624
625    fn value_ptr(&self) -> *const c_void {
626        self.values.as_ptr() as *const c_void
627    }
628
629    fn buffer_length(&self) -> isize {
630        ((self.max_str_len + 1) * 2).try_into().unwrap()
631    }
632}
633
634unsafe impl CDataMut for WCharColumn {
635    fn mut_indicator_ptr(&mut self) -> *mut isize {
636        self.indicators.as_mut_ptr()
637    }
638
639    fn mut_value_ptr(&mut self) -> *mut c_void {
640        self.values.as_mut_ptr() as *mut c_void
641    }
642}
643
644impl HasDataType for WCharColumn {
645    fn data_type(&self) -> DataType {
646        if self.max_str_len <= ASSUMED_MAX_LENGTH_OF_W_VARCHAR {
647            DataType::WVarchar {
648                length: NonZeroUsize::new(self.max_str_len),
649            }
650        } else {
651            DataType::WLongVarchar {
652                length: NonZeroUsize::new(self.max_str_len),
653            }
654        }
655    }
656}
657
658impl<C> Resize for TextColumn<C>
659where
660    C: Clone + Default,
661{
662    fn resize(&mut self, new_capacity: usize) {
663        self.values
664            .resize((self.max_str_len + 1) * new_capacity, C::default());
665        self.indicators.resize(new_capacity, NULL_DATA);
666    }
667}
668
669#[cfg(test)]
670mod test {
671    use crate::buffers::{Resize, TextColumn};
672
673    #[test]
674    fn resize_text_column_buffer() {
675        // Given a text column buffer with two elements
676        let mut col = TextColumn::<u8>::new(2, 10);
677        col.set_value(0, Some(b"Hello"));
678        col.set_value(1, Some(b"World"));
679
680        // When we resize it to hold 3 elements
681        col.resize(3);
682
683        // Then the first two elements are still there, and the third is None
684        assert_eq!(col.value_at(0), Some(b"Hello".as_ref()));
685        assert_eq!(col.value_at(1), Some(b"World".as_ref()));
686        assert_eq!(col.value_at(2), None);
687    }
688}