odbc_api/buffers/text_column.rs
1use crate::{
2 DataType, Error,
3 buffers::{Resize, columnar::Slice},
4 columnar_bulk_inserter::BoundInputSlice,
5 error::TooLargeBufferSize,
6 handles::{
7 ASSUMED_MAX_LENGTH_OF_VARCHAR, ASSUMED_MAX_LENGTH_OF_W_VARCHAR, CData, CDataMut,
8 HasDataType, Statement, StatementRef,
9 },
10};
11
12use super::{ColumnBuffer, Indicator};
13
14use log::trace;
15use odbc_sys::{CDataType, NULL_DATA};
16use std::{any::Any, cmp::min, ffi::c_void, mem::size_of, num::NonZeroUsize, panic};
17use widestring::Utf16Str;
18
19/// A column buffer for character data. The actual encoding used may depend on your system locale.
20pub type CharColumn = TextColumn<u8>;
21
22/// This buffer uses wide characters which implies UTF-16 encoding. UTF-8 encoding is preferable for
23/// most applications, but contrary to its sibling [`crate::buffers::CharColumn`] this buffer type's
24/// implied encoding does not depend on the system locale.
25pub type WCharColumn = TextColumn<u16>;
26
27/// A buffer intended to be bound to a column of a cursor. Elements of the buffer will contain a
28/// variable amount of characters up to a maximum string length. Since most SQL types have a string
29/// representation this buffer can be bound to a column of almost any type, ODBC driver and driver
30/// manager should take care of the conversion. Since elements of this type have variable length, an
31/// indicator buffer needs to be bound, whether the column is nullable or not.
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 #[cfg(not(feature = "structured_logging"))]
181 trace!(
182 "Rebinding text column buffer with {} elements. Maximum string length {} => {}",
183 num_rows, self.max_str_len, new_max_str_len
184 );
185 #[cfg(feature = "structured_logging")]
186 trace!(
187 target: "odbc_api",
188 num_rows = num_rows,
189 old_max_str_len = self.max_str_len,
190 new_max_str_len = new_max_str_len;
191 "Text column buffer resized"
192 );
193
194 let batch_size = self.indicators.len();
195 // Allocate a new buffer large enough to hold a batch of strings with maximum length.
196 let mut new_values = vec![C::default(); (new_max_str_len + 1) * batch_size];
197 // Copy values from old to new buffer.
198 let max_copy_length = min(self.max_str_len, new_max_str_len);
199 for ((&indicator, old_value), new_value) in self
200 .indicators
201 .iter()
202 .zip(self.values.chunks_exact_mut(self.max_str_len + 1))
203 .zip(new_values.chunks_exact_mut(new_max_str_len + 1))
204 .take(num_rows)
205 {
206 match Indicator::from_isize(indicator) {
207 Indicator::Null => (),
208 Indicator::NoTotal => {
209 // There is no good choice here in case we are expanding the buffer. Since
210 // NO_TOTAL indicates that we use the entire buffer, but in truth it would now
211 // be padded with 0. I currently cannot think of any use case there it would
212 // matter.
213 new_value[..max_copy_length].clone_from_slice(&old_value[..max_copy_length]);
214 }
215 Indicator::Length(num_bytes_len) => {
216 let num_bytes_to_copy = min(num_bytes_len / size_of::<C>(), max_copy_length);
217 new_value[..num_bytes_to_copy].copy_from_slice(&old_value[..num_bytes_to_copy]);
218 }
219 }
220 }
221 self.values = new_values;
222 self.max_str_len = new_max_str_len;
223 }
224
225 /// Sets the value of the buffer at index at Null or the specified binary Text. This method will
226 /// panic on out of bounds index, or if input holds a text which is larger than the maximum
227 /// allowed element length. `input` must be specified without the terminating zero.
228 pub fn set_value(&mut self, index: usize, input: Option<&[C]>)
229 where
230 C: Default + Copy,
231 {
232 if let Some(input) = input {
233 self.set_mut(index, input.len()).copy_from_slice(input);
234 } else {
235 self.indicators[index] = NULL_DATA;
236 }
237 }
238
239 /// Can be used to set a value at a specific row index without performing a memcopy on an input
240 /// slice and instead provides direct access to the underlying buffer.
241 ///
242 /// In situations there the memcopy can not be avoided anyway [`Self::set_value`] is likely to
243 /// be more convenient. This method is very useful if you want to `write!` a string value to the
244 /// buffer and the binary (**!**) length of the formatted string is known upfront.
245 ///
246 /// # Example: Write timestamp to text column.
247 ///
248 /// ```
249 /// use odbc_api::buffers::TextColumn;
250 /// use std::io::Write;
251 ///
252 /// /// Writes times formatted as hh::mm::ss.fff
253 /// fn write_time(
254 /// col: &mut TextColumn<u8>,
255 /// index: usize,
256 /// hours: u8,
257 /// minutes: u8,
258 /// seconds: u8,
259 /// milliseconds: u16)
260 /// {
261 /// write!(
262 /// col.set_mut(index, 12),
263 /// "{:02}:{:02}:{:02}.{:03}",
264 /// hours, minutes, seconds, milliseconds
265 /// ).unwrap();
266 /// }
267 /// ```
268 pub fn set_mut(&mut self, index: usize, length: usize) -> &mut [C]
269 where
270 C: Default,
271 {
272 if length > self.max_str_len {
273 panic!(
274 "Tried to insert a value into a text buffer which is larger than the maximum \
275 allowed string length for the buffer."
276 );
277 }
278 self.indicators[index] = (length * size_of::<C>()).try_into().unwrap();
279 let start = (self.max_str_len + 1) * index;
280 let end = start + length;
281 // Let's insert a terminating zero at the end to be on the safe side, in case the ODBC
282 // driver would not care about the value in the index buffer and only look for the
283 // terminating zero.
284 self.values[end] = C::default();
285 &mut self.values[start..end]
286 }
287
288 /// Fills the column with NULL, between From and To
289 pub fn fill_null(&mut self, from: usize, to: usize) {
290 for index in from..to {
291 self.indicators[index] = NULL_DATA;
292 }
293 }
294
295 /// Provides access to the raw underlying value buffer. Normal applications should have little
296 /// reason to call this method. Yet it may be useful for writing bindings which copy directly
297 /// from the ODBC in memory representation into other kinds of buffers.
298 ///
299 /// The buffer contains the bytes for every non null valid element, padded to the maximum string
300 /// length. The content of the padding bytes is undefined. Usually ODBC drivers write a
301 /// terminating zero at the end of each string. For the actual value length call
302 /// [`Self::content_length_at`]. Any element starts at index * ([`Self::max_len`] + 1).
303 pub fn raw_value_buffer(&self, num_valid_rows: usize) -> &[C] {
304 &self.values[..(self.max_str_len + 1) * num_valid_rows]
305 }
306
307 /// The maximum number of rows the TextColumn can hold.
308 pub fn row_capacity(&self) -> usize {
309 self.values.len()
310 }
311}
312
313impl WCharColumn {
314 /// The string slice at the specified position as `Utf16Str`. Includes interior nuls, but
315 /// excludes the terminating nul.
316 ///
317 /// # Safety
318 ///
319 /// The column buffer does not know how many elements were in the last row group, and therefore
320 /// can not guarantee the accessed element to be valid and in a defined state. It also can not
321 /// panic on accessing an undefined element. It will panic however if `row_index` is larger or
322 /// equal to the maximum number of elements in the buffer.
323 pub unsafe fn utf16_str_at(&self, row_index: usize) -> Option<&Utf16Str> {
324 self.value_at(row_index)
325 .map(Utf16Str::from_slice)
326 .transpose()
327 .expect("Wide character encoding must be UTF-16")
328 }
329}
330
331unsafe impl<C> ColumnBuffer for TextColumn<C>
332where
333 TextColumn<C>: CDataMut + HasDataType + Any,
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
353unsafe impl<C: 'static> Slice for TextColumn<C> {
354 type Slice<'a> = TextColumnSlice<'a, C>;
355
356 fn slice(&self, valid_rows: usize) -> TextColumnSlice<'_, C> {
357 TextColumnSlice {
358 num_rows: valid_rows,
359 col: self,
360 }
361 }
362}
363
364/// Allows read-only access to the valid part of a text column.
365///
366/// You may ask, why is this type required, should we not just be able to use `&TextColumn`? The
367/// problem with `TextColumn` is, that it is a buffer, but it has no idea how many of its members
368/// are actually valid, and have been returned with the last row group of the result set. That
369/// number is maintained on the level of the entire column buffer. So a text column knows the number
370/// of valid rows, in addition to holding a reference to the buffer, in order to guarantee, that
371/// every element accessed through it, is valid.
372#[derive(Debug, Clone, Copy)]
373pub struct TextColumnSlice<'c, C> {
374 num_rows: usize,
375 col: &'c TextColumn<C>,
376}
377
378impl<'c, C> TextColumnSlice<'c, C> {
379 /// The number of valid elements in the text column.
380 pub fn len(&self) -> usize {
381 self.num_rows
382 }
383
384 /// True if, and only if there are no valid rows in the column buffer.
385 pub fn is_empty(&self) -> bool {
386 self.num_rows == 0
387 }
388
389 /// Slice of text at the specified row index without terminating zero. `None` if the value is
390 /// `NULL`. This method will panic if the index is larger than the number of valid rows in the
391 /// view as returned by [`Self::len`].
392 pub fn get(&self, index: usize) -> Option<&'c [C]> {
393 self.col.value_at(index)
394 }
395
396 /// Iterator over the valid elements of the text buffer
397 pub fn iter(&self) -> TextColumnIt<'c, C> {
398 TextColumnIt {
399 pos: 0,
400 num_rows: self.num_rows,
401 col: self.col,
402 }
403 }
404
405 /// Length of value at the specified position. This is different from an indicator as it refers
406 /// to the length of the value in the buffer, not to the length of the value in the datasource.
407 /// The two things are different for truncated values.
408 pub fn content_length_at(&self, row_index: usize) -> Option<usize> {
409 if row_index >= self.num_rows {
410 panic!("Row index points beyond the range of valid values.")
411 }
412 self.col.content_length_at(row_index)
413 }
414
415 /// Provides access to the raw underlying value buffer. Normal applications should have little
416 /// reason to call this method. Yet it may be useful for writing bindings which copy directly
417 /// from the ODBC in memory representation into other kinds of buffers.
418 ///
419 /// The buffer contains the bytes for every non null valid element, padded to the maximum string
420 /// length. The content of the padding bytes is undefined. Usually ODBC drivers write a
421 /// terminating zero at the end of each string. For the actual value length call
422 /// [`Self::content_length_at`]. Any element starts at index * ([`Self::max_len`] + 1).
423 pub fn raw_value_buffer(&self) -> &'c [C] {
424 self.col.raw_value_buffer(self.num_rows)
425 }
426
427 pub fn max_len(&self) -> usize {
428 self.col.max_len()
429 }
430
431 /// `Some` if any value is truncated.
432 ///
433 /// After fetching data we may want to know if any value has been truncated due to the buffer
434 /// not being able to hold elements of that size. This method checks the indicator buffer
435 /// element wise.
436 pub fn has_truncated_values(&self) -> Option<Indicator> {
437 self.col.has_truncated_values(self.num_rows)
438 }
439}
440
441unsafe impl<'a, C: 'static> BoundInputSlice<'a> for TextColumn<C> {
442 type SliceMut = TextColumnSliceMut<'a, C>;
443
444 unsafe fn as_view_mut(
445 &'a mut self,
446 parameter_index: u16,
447 stmt: StatementRef<'a>,
448 ) -> Self::SliceMut {
449 TextColumnSliceMut {
450 column: self,
451 stmt,
452 parameter_index,
453 }
454 }
455}
456
457/// A view to a mutable array parameter text buffer, which allows for filling the buffer with
458/// values.
459pub struct TextColumnSliceMut<'a, C> {
460 column: &'a mut TextColumn<C>,
461 // Needed to rebind the column in case of resize
462 stmt: StatementRef<'a>,
463 // Also needed to rebind the column in case of resize
464 parameter_index: u16,
465}
466
467impl<C> TextColumnSliceMut<'_, C>
468where
469 C: Default + Copy + Send,
470{
471 /// Sets the value of the buffer at index at Null or the specified binary Text. This method will
472 /// panic on out of bounds index, or if input holds a text which is larger than the maximum
473 /// allowed element length. `element` must be specified without the terminating zero.
474 pub fn set_cell(&mut self, row_index: usize, element: Option<&[C]>) {
475 self.column.set_value(row_index, element)
476 }
477
478 /// Ensures that the buffer is large enough to hold elements of `element_length`. Does nothing
479 /// if the buffer is already large enough. Otherwise it will reallocate and rebind the buffer.
480 /// The first `num_rows_to_copy` will be copied from the old value buffer to the new
481 /// one. This makes this an extremely expensive operation.
482 pub fn ensure_max_element_length(
483 &mut self,
484 element_length: usize,
485 num_rows_to_copy: usize,
486 ) -> Result<(), Error>
487 where
488 TextColumn<C>: HasDataType + CData,
489 {
490 // Column buffer is not large enough to hold the element. We must allocate a larger buffer
491 // in order to hold it. This invalidates the pointers previously bound to the statement. So
492 // we rebind them.
493 if element_length > self.column.max_len() {
494 let new_max_str_len = element_length;
495 self.column
496 .resize_max_str(new_max_str_len, num_rows_to_copy);
497 unsafe {
498 self.stmt
499 .bind_input_parameter(self.parameter_index, self.column)
500 .into_result(&self.stmt)?
501 }
502 }
503 Ok(())
504 }
505
506 /// Can be used to set a value at a specific row index without performing a memcopy on an input
507 /// slice and instead provides direct access to the underlying buffer.
508 ///
509 /// In situations there the memcopy can not be avoided anyway [`Self::set_cell`] is likely to
510 /// be more convenient. This method is very useful if you want to `write!` a string value to the
511 /// buffer and the binary (**!**) length of the formatted string is known upfront.
512 ///
513 /// # Example: Write timestamp to text column.
514 ///
515 /// ```
516 /// use odbc_api::buffers::TextColumnSliceMut;
517 /// use std::io::Write;
518 ///
519 /// /// Writes times formatted as hh::mm::ss.fff
520 /// fn write_time(
521 /// col: &mut TextColumnSliceMut<u8>,
522 /// index: usize,
523 /// hours: u8,
524 /// minutes: u8,
525 /// seconds: u8,
526 /// milliseconds: u16)
527 /// {
528 /// write!(
529 /// col.set_mut(index, 12),
530 /// "{:02}:{:02}:{:02}.{:03}",
531 /// hours, minutes, seconds, milliseconds
532 /// ).unwrap();
533 /// }
534 /// ```
535 pub fn set_mut(&mut self, index: usize, length: usize) -> &mut [C] {
536 self.column.set_mut(index, length)
537 }
538}
539
540/// Iterator over a text column. See [`TextColumnView::iter`]
541#[derive(Debug)]
542pub struct TextColumnIt<'c, C> {
543 pos: usize,
544 num_rows: usize,
545 col: &'c TextColumn<C>,
546}
547
548impl<'c, C> TextColumnIt<'c, C> {
549 fn next_impl(&mut self) -> Option<Option<&'c [C]>> {
550 if self.pos == self.num_rows {
551 None
552 } else {
553 let ret = Some(self.col.value_at(self.pos));
554 self.pos += 1;
555 ret
556 }
557 }
558}
559
560impl<'c> Iterator for TextColumnIt<'c, u8> {
561 type Item = Option<&'c [u8]>;
562
563 fn next(&mut self) -> Option<Self::Item> {
564 self.next_impl()
565 }
566
567 fn size_hint(&self) -> (usize, Option<usize>) {
568 let len = self.num_rows - self.pos;
569 (len, Some(len))
570 }
571}
572
573impl ExactSizeIterator for TextColumnIt<'_, u8> {}
574
575impl<'c> Iterator for TextColumnIt<'c, u16> {
576 type Item = Option<&'c Utf16Str>;
577
578 fn next(&mut self) -> Option<Option<&'c Utf16Str>> {
579 self.next_impl().map(|opt| {
580 opt.map(Utf16Str::from_slice)
581 .transpose()
582 .expect("Wide character encoding must be UTF-16")
583 })
584 }
585
586 fn size_hint(&self) -> (usize, Option<usize>) {
587 let len = self.num_rows - self.pos;
588 (len, Some(len))
589 }
590}
591
592impl ExactSizeIterator for TextColumnIt<'_, u16> {}
593
594unsafe impl CData for CharColumn {
595 fn cdata_type(&self) -> CDataType {
596 CDataType::Char
597 }
598
599 fn indicator_ptr(&self) -> *const isize {
600 self.indicators.as_ptr()
601 }
602
603 fn value_ptr(&self) -> *const c_void {
604 self.values.as_ptr() as *const c_void
605 }
606
607 fn buffer_length(&self) -> isize {
608 (self.max_str_len + 1).try_into().unwrap()
609 }
610}
611
612unsafe impl CDataMut for CharColumn {
613 fn mut_indicator_ptr(&mut self) -> *mut isize {
614 self.indicators.as_mut_ptr()
615 }
616
617 fn mut_value_ptr(&mut self) -> *mut c_void {
618 self.values.as_mut_ptr() as *mut c_void
619 }
620}
621
622impl HasDataType for CharColumn {
623 fn data_type(&self) -> DataType {
624 if self.max_str_len <= ASSUMED_MAX_LENGTH_OF_VARCHAR {
625 DataType::Varchar {
626 length: NonZeroUsize::new(self.max_str_len),
627 }
628 } else {
629 DataType::LongVarchar {
630 length: NonZeroUsize::new(self.max_str_len),
631 }
632 }
633 }
634}
635
636unsafe impl CData for WCharColumn {
637 fn cdata_type(&self) -> CDataType {
638 CDataType::WChar
639 }
640
641 fn indicator_ptr(&self) -> *const isize {
642 self.indicators.as_ptr()
643 }
644
645 fn value_ptr(&self) -> *const c_void {
646 self.values.as_ptr() as *const c_void
647 }
648
649 fn buffer_length(&self) -> isize {
650 ((self.max_str_len + 1) * 2).try_into().unwrap()
651 }
652}
653
654unsafe impl CDataMut for WCharColumn {
655 fn mut_indicator_ptr(&mut self) -> *mut isize {
656 self.indicators.as_mut_ptr()
657 }
658
659 fn mut_value_ptr(&mut self) -> *mut c_void {
660 self.values.as_mut_ptr() as *mut c_void
661 }
662}
663
664impl HasDataType for WCharColumn {
665 fn data_type(&self) -> DataType {
666 if self.max_str_len <= ASSUMED_MAX_LENGTH_OF_W_VARCHAR {
667 DataType::WVarchar {
668 length: NonZeroUsize::new(self.max_str_len),
669 }
670 } else {
671 DataType::WLongVarchar {
672 length: NonZeroUsize::new(self.max_str_len),
673 }
674 }
675 }
676}
677
678impl<C> Resize for TextColumn<C>
679where
680 C: Clone + Default,
681{
682 fn resize(&mut self, new_capacity: usize) {
683 self.values
684 .resize((self.max_str_len + 1) * new_capacity, C::default());
685 self.indicators.resize(new_capacity, NULL_DATA);
686 }
687}
688
689#[cfg(test)]
690mod tests {
691 use crate::buffers::{Resize, TextColumn};
692
693 #[test]
694 fn resize_text_column_buffer() {
695 // Given a text column buffer with two elements
696 let mut col = TextColumn::<u8>::new(2, 10);
697 col.set_value(0, Some(b"Hello"));
698 col.set_value(1, Some(b"World"));
699
700 // When we resize it to hold 3 elements
701 col.resize(3);
702
703 // Then the first two elements are still there, and the third is None
704 assert_eq!(col.value_at(0), Some(b"Hello".as_ref()));
705 assert_eq!(col.value_at(1), Some(b"World".as_ref()));
706 assert_eq!(col.value_at(2), None);
707 }
708}