use crate::{
handles::{CData, CDataMut, Input},
DataType,
};
use log::debug;
use odbc_sys::{CDataType, NULL_DATA};
use std::{cmp::min, convert::TryInto, ffi::c_void};
pub struct TextColumn {
max_str_len: usize,
values: Vec<u8>,
indicators: Vec<isize>,
}
impl TextColumn {
pub fn new(batch_size: usize, max_str_len: usize) -> Self {
TextColumn {
max_str_len,
values: vec![0; (max_str_len + 1) * batch_size],
indicators: vec![0; batch_size],
}
}
pub unsafe fn value_at(&self, row_index: usize) -> Option<&[u8]> {
let str_len = self.indicators[row_index];
if str_len == NULL_DATA {
None
} else {
let offset = row_index * (self.max_str_len + 1);
let length = min(self.max_str_len, str_len as usize);
Some(&self.values[offset..offset + length])
}
}
pub fn rebind(&mut self, new_max_str_len: usize, num_rows: usize) {
debug!(
"Rebinding text column buffer with {} elements. Maximum string length {} => {}",
num_rows, self.max_str_len, new_max_str_len
);
let batch_size = self.indicators.len();
let mut new_values = vec![0u8; (new_max_str_len + 1) * batch_size];
let max_copy_length = min(self.max_str_len, new_max_str_len);
for ((&indicator, old_value), new_value) in self
.indicators
.iter()
.zip(self.values.chunks_exact_mut(self.max_str_len + 1))
.zip(new_values.chunks_exact_mut(new_max_str_len + 1))
.take(num_rows)
{
if indicator != NULL_DATA {
let num_bytes_to_copy = min(indicator as usize, max_copy_length);
new_value[..num_bytes_to_copy].copy_from_slice(&old_value[..num_bytes_to_copy]);
}
}
self.values = new_values;
self.max_str_len = new_max_str_len;
}
pub fn append(&mut self, index: usize, text: Option<&[u8]>) {
if let Some(text) = text {
if text.len() > self.max_str_len {
let new_max_str_len = (text.len() as f64 * 1.2) as usize;
self.rebind(new_max_str_len, index)
}
let offset = index * (self.max_str_len + 1);
self.values[offset..offset + text.len()].copy_from_slice(text);
self.values[offset + text.len()] = 0;
self.indicators[index] = text.len().try_into().unwrap();
} else {
self.indicators[index] = NULL_DATA;
}
}
}
unsafe impl CData for TextColumn {
fn cdata_type(&self) -> CDataType {
CDataType::Char
}
fn indicator_ptr(&self) -> *const isize {
self.indicators.as_ptr()
}
fn value_ptr(&self) -> *const c_void {
self.values.as_ptr() as *const c_void
}
fn buffer_length(&self) -> isize {
(self.max_str_len + 1).try_into().unwrap()
}
}
unsafe impl Input for TextColumn {
fn data_type(&self) -> DataType {
DataType::Varchar {
length: self.max_str_len,
}
}
}
unsafe impl CDataMut for TextColumn {
fn mut_indicator_ptr(&mut self) -> *mut isize {
self.indicators.as_mut_ptr()
}
fn mut_value_ptr(&mut self) -> *mut c_void {
self.values.as_mut_ptr() as *mut c_void
}
}