use crate::{
buffers::Indicator,
handles::{CData, CDataMut, HasDataType},
DataType,
};
use log::debug;
use odbc_sys::{CDataType, NULL_DATA};
use std::{cmp::min, convert::TryInto, ffi::c_void, mem::size_of};
use widestring::U16Str;
pub type CharColumn = TextColumn<u8>;
pub type WCharColumn = TextColumn<u16>;
#[derive(Debug)]
pub struct TextColumn<C> {
max_str_len: usize,
values: Vec<C>,
indicators: Vec<isize>,
}
impl<C> TextColumn<C> {
pub fn new(batch_size: usize, max_str_len: usize) -> Self
where
C: Default + Copy,
{
TextColumn {
max_str_len,
values: vec![C::default(); (max_str_len + 1) * batch_size],
indicators: vec![0; batch_size],
}
}
pub unsafe fn value_at(&self, row_index: usize) -> Option<&[C]> {
match self.indicator_at(row_index) {
Indicator::Null => None,
Indicator::NoTotal => {
let offset = row_index * (self.max_str_len + 1);
Some(&self.values[offset..offset + self.max_str_len])
}
Indicator::Length(length_in_bytes) => {
let offset = row_index * (self.max_str_len + 1);
let length_in_chars = length_in_bytes / size_of::<C>();
let length = min(self.max_str_len, length_in_chars);
Some(&self.values[offset..offset + length])
}
}
}
pub fn max_len(&self) -> usize {
self.max_str_len
}
pub unsafe fn indicator_at(&self, row_index: usize) -> Indicator {
Indicator::from_isize(self.indicators[row_index])
}
pub fn rebind(&mut self, new_max_str_len: usize, num_rows: usize)
where
C: Default + Copy,
{
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![C::default(); (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)
{
match Indicator::from_isize(indicator) {
Indicator::Null => (),
Indicator::NoTotal => {
new_value[..max_copy_length].clone_from_slice(&old_value[..max_copy_length]);
}
Indicator::Length(num_bytes_len) => {
let num_bytes_to_copy = min(num_bytes_len / size_of::<C>(), 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 set_max_len(&mut self, new_max_len: usize)
where
C: Default + Copy,
{
let batch_size = self.indicators.len();
let new_values = vec![C::default(); (new_max_len + 1) * batch_size];
self.fill_null(0, batch_size);
self.values = new_values;
self.max_str_len = new_max_len;
}
pub fn append(&mut self, index: usize, text: Option<&[C]>)
where
C: Default + Copy,
{
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()] = C::default();
self.indicators[index] = (text.len() * size_of::<C>()).try_into().unwrap();
} else {
self.indicators[index] = NULL_DATA;
}
}
pub unsafe fn iter(&self, num_rows: usize) -> TextColumnIt<'_, C> {
TextColumnIt {
pos: 0,
num_rows,
col: &self,
}
}
pub fn set_value(&mut self, index: usize, input: Option<&[C]>)
where
C: Default + Copy,
{
if let Some(input) = input {
self.set_mut(index, input.len()).copy_from_slice(input);
} else {
self.indicators[index] = NULL_DATA;
}
}
pub fn set_mut(&mut self, index: usize, length: usize) -> &mut [C]
where
C: Default,
{
if length > self.max_str_len {
panic!(
"Tried to insert a value into a text buffer which is larger than the maximum \
allowed string length for the buffer."
);
}
self.indicators[index] = (length * size_of::<C>()).try_into().unwrap();
let start = (self.max_str_len + 1) * index;
let end = start + length;
self.values[end] = C::default();
&mut self.values[start..end]
}
pub fn fill_null(&mut self, from: usize, to: usize) {
for index in from..to {
self.indicators[index] = NULL_DATA;
}
}
pub fn writer_n(&mut self, n: usize) -> TextColumnWriter<'_, C> {
TextColumnWriter {
column: self,
to: n,
}
}
}
impl WCharColumn {
pub unsafe fn ustr_at(&self, row_index: usize) -> Option<&U16Str> {
self.value_at(row_index).map(U16Str::from_slice)
}
}
#[derive(Debug)]
pub struct TextColumnIt<'c, C> {
pos: usize,
num_rows: usize,
col: &'c TextColumn<C>,
}
impl<'c, C> TextColumnIt<'c, C> {
fn next_impl(&mut self) -> Option<Option<&'c [C]>> {
if self.pos == self.num_rows {
None
} else {
let ret = unsafe { Some(self.col.value_at(self.pos)) };
self.pos += 1;
ret
}
}
}
impl<'c> Iterator for TextColumnIt<'c, u8> {
type Item = Option<&'c [u8]>;
fn next(&mut self) -> Option<Self::Item> {
self.next_impl()
}
}
impl<'c> Iterator for TextColumnIt<'c, u16> {
type Item = Option<&'c U16Str>;
fn next(&mut self) -> Option<Self::Item> {
self.next_impl().map(|opt| opt.map(U16Str::from_slice))
}
}
#[derive(Debug)]
pub struct TextColumnWriter<'a, C> {
column: &'a mut TextColumn<C>,
to: usize,
}
impl<'a, C> TextColumnWriter<'a, C>
where
C: Default + Copy,
{
pub fn write<'b>(&mut self, it: impl Iterator<Item = Option<&'b [C]>>)
where
C: 'b,
{
for (index, item) in it.enumerate().take(self.to) {
self.column.set_value(index, item)
}
}
pub fn max_len(&self) -> usize {
self.column.max_len()
}
pub fn set_max_len(&mut self, new_max_len: usize) {
self.column.set_max_len(new_max_len)
}
pub fn rebind(&mut self, new_max_str_len: usize, num_rows: usize) {
self.column.rebind(new_max_str_len, num_rows)
}
pub fn set_value(&mut self, index: usize, value: Option<&[C]>) {
self.column.set_value(index, value)
}
pub fn append(&mut self, index: usize, text: Option<&[C]>) {
self.column.append(index, text)
}
pub fn set_mut(&mut self, index: usize, length: usize) -> &mut [C] {
self.column.set_mut(index, length)
}
}
unsafe impl CData for CharColumn {
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 CDataMut for CharColumn {
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
}
}
unsafe impl HasDataType for CharColumn {
fn data_type(&self) -> DataType {
DataType::Varchar {
length: self.max_str_len,
}
}
}
unsafe impl CData for WCharColumn {
fn cdata_type(&self) -> CDataType {
CDataType::WChar
}
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) * 2).try_into().unwrap()
}
}
unsafe impl CDataMut for WCharColumn {
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
}
}
unsafe impl HasDataType for WCharColumn {
fn data_type(&self) -> DataType {
DataType::WVarchar {
length: self.max_str_len,
}
}
}