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