use super::{
BinColumn, BinColumnSlice, BinColumnSliceMut, BufferDesc, ColumnBuffer, ColumnarBuffer,
Indicator, NullableSlice, NullableSliceMut, Resize, Slice, TextColumn, TextColumnSlice,
TextColumnSliceMut, column_with_indicator::ColumnWithIndicator,
};
use crate::{
BoundInputSlice, Error,
fixed_sized::Pod,
handles::{CData, CDataMut, StatementRef},
};
use std::{any::Any, collections::HashSet, ffi::c_void};
pub type ColumnarDynBuffer = ColumnarBuffer<BoxColumnBuffer>;
impl ColumnarDynBuffer {
pub fn from_descs(capacity: usize, descs: impl IntoIterator<Item = BufferDesc>) -> Self {
let mut column_index = 0;
let columns = descs
.into_iter()
.map(move |desc| {
let buffer = desc.column_buffer(capacity);
column_index += 1;
(column_index, buffer)
})
.collect();
unsafe { ColumnarBuffer::new_unchecked(capacity, columns) }
}
pub fn try_from_descs(
capacity: usize,
descs: impl IntoIterator<Item = BufferDesc>,
) -> Result<Self, Error> {
let mut column_index = 0;
let columns = descs
.into_iter()
.map(move |desc| {
let buffer = desc
.try_column_buffer(capacity)
.map_err(|source| source.add_context(column_index))?;
column_index += 1;
Ok::<_, Error>((column_index, buffer))
})
.collect::<Result<_, _>>()?;
Ok(unsafe { ColumnarBuffer::new_unchecked(capacity, columns) })
}
pub fn from_descs_and_indices(
max_rows: usize,
description: impl Iterator<Item = (u16, BufferDesc)>,
) -> Self {
let columns: Vec<_> = description
.map(|(col_index, buffer_desc)| (col_index, buffer_desc.column_buffer(max_rows)))
.collect();
let mut indices = HashSet::new();
if columns
.iter()
.any(move |&(col_index, _)| !indices.insert(col_index))
{
panic!("Column indices must be unique.")
}
ColumnarBuffer::new(columns)
}
}
pub type BoxColumnBuffer = Box<dyn AnyColumnBuffer>;
pub trait AnyColumnBuffer: ColumnBuffer + Resize + Any + Send {}
impl<T> AnyColumnBuffer for T where T: ColumnBuffer + Resize + Any + Send {}
unsafe impl CData for BoxColumnBuffer {
fn cdata_type(&self) -> odbc_sys::CDataType {
self.as_ref().cdata_type()
}
fn indicator_ptr(&self) -> *const isize {
self.as_ref().indicator_ptr()
}
fn value_ptr(&self) -> *const c_void {
self.as_ref().value_ptr()
}
fn buffer_length(&self) -> isize {
self.as_ref().buffer_length()
}
}
unsafe impl CDataMut for BoxColumnBuffer {
fn mut_indicator_ptr(&mut self) -> *mut isize {
self.as_mut().mut_indicator_ptr()
}
fn mut_value_ptr(&mut self) -> *mut c_void {
self.as_mut().mut_value_ptr()
}
}
unsafe impl ColumnBuffer for BoxColumnBuffer {
fn capacity(&self) -> usize {
self.as_ref().capacity()
}
fn has_truncated_values(&self, num_rows: usize) -> Option<Indicator> {
self.as_ref().has_truncated_values(num_rows)
}
}
unsafe impl Slice for BoxColumnBuffer {
type Slice<'a> = AnyColumnBufferSlice<'a>;
fn slice(&self, valid_rows: usize) -> AnyColumnBufferSlice<'_> {
AnyColumnBufferSlice {
buffer: self,
valid_rows,
}
}
}
#[derive(Clone, Copy)]
pub struct AnyColumnBufferSlice<'a> {
buffer: &'a BoxColumnBuffer,
valid_rows: usize,
}
impl<'a> AnyColumnBufferSlice<'a> {
pub fn of<T>(self) -> Option<T::Slice<'a>>
where
T: Slice + 'static,
{
let buffer: &dyn Any = self.buffer.as_ref();
let buffer = buffer.downcast_ref::<T>()?;
Some(T::slice(buffer, self.valid_rows))
}
pub fn as_text(self) -> Option<TextColumnSlice<'a, u8>> {
self.of::<TextColumn<u8>>()
}
pub fn as_wide_text(self) -> Option<TextColumnSlice<'a, u16>> {
self.of::<TextColumn<u16>>()
}
pub fn as_binary(self) -> Option<BinColumnSlice<'a>> {
self.of::<BinColumn>()
}
pub fn as_slice<T>(self) -> Option<&'a [T]>
where
T: Pod,
{
self.of::<Vec<T>>()
}
pub fn as_nullable_slice<T>(self) -> Option<NullableSlice<'a, T>>
where
T: Pod,
{
self.of::<ColumnWithIndicator<T>>()
}
}
unsafe impl<'a> BoundInputSlice<'a> for BoxColumnBuffer {
type SliceMut = BoxColumBufferRefMut<'a>;
unsafe fn as_view_mut(
&'a mut self,
parameter_index: u16,
stmt: StatementRef<'a>,
) -> BoxColumBufferRefMut<'a> {
BoxColumBufferRefMut {
buffer: self,
parameter_index,
stmt,
}
}
}
pub struct BoxColumBufferRefMut<'a> {
buffer: &'a mut BoxColumnBuffer,
parameter_index: u16,
stmt: StatementRef<'a>,
}
impl<'a> BoxColumBufferRefMut<'a> {
pub fn of<T>(self) -> Option<T::SliceMut>
where
T: BoundInputSlice<'a> + 'static,
{
let buffer: &mut dyn Any = self.buffer.as_mut();
let buffer = buffer.downcast_mut::<T>()?;
let view_mut = unsafe { buffer.as_view_mut(self.parameter_index, self.stmt) };
Some(view_mut)
}
pub fn as_wide_text(self) -> Option<TextColumnSliceMut<'a, u16>> {
self.of::<TextColumn<u16>>()
}
pub fn as_text(self) -> Option<TextColumnSliceMut<'a, u8>> {
self.of::<TextColumn<u8>>()
}
pub fn as_binary(self) -> Option<BinColumnSliceMut<'a>> {
self.of::<BinColumn>()
}
pub fn as_slice<T>(self) -> Option<&'a mut [T]>
where
T: Pod,
{
self.of::<Vec<T>>()
}
pub fn as_nullable_slice<T>(self) -> Option<NullableSliceMut<'a, T>>
where
T: Pod,
{
self.of::<ColumnWithIndicator<T>>()
}
}
impl Resize for BoxColumnBuffer {
fn resize(&mut self, new_capacity: usize) {
self.as_mut().resize(new_capacity);
}
}
#[cfg(test)]
mod tests {
use super::{BoxColumnBuffer, BufferDesc, ColumnarDynBuffer, Slice};
#[test]
#[should_panic(expected = "Column indices must be unique.")]
fn assert_unique_column_indices() {
let bd = BufferDesc::I32 { nullable: false };
ColumnarDynBuffer::from_descs_and_indices(1, [(1, bd), (2, bd), (1, bd)].iter().cloned());
}
#[test]
fn box_column_buffer_is_resize() {
let mut buffer: BoxColumnBuffer = Box::new(vec![1i32, 2]);
buffer.resize(4);
assert_eq!(buffer.slice(4).as_slice(), Some([1i32, 2, 0, 0].as_slice()));
assert_eq!(buffer.capacity(), 4);
}
#[test]
fn slice_should_only_contain_part_of_the_buffer() {
let buffer: BoxColumnBuffer = Box::new(vec![1i32, 2, 3]);
let view = buffer.slice(2);
assert_eq!(Some([1, 2].as_slice()), view.as_slice::<i32>());
}
#[test]
fn slice_should_be_none_if_types_mismatch() {
let buffer: BoxColumnBuffer = Box::new(vec![1i32, 2, 3]);
let view = buffer.slice(3);
assert_eq!(None, view.as_slice::<i16>());
}
#[test]
fn nullable_slice_should_be_none_if_buffer_is_non_nullable() {
let buffer: BoxColumnBuffer = Box::new(vec![1i32, 2, 3]);
let view = buffer.slice(3);
assert!(view.as_nullable_slice::<i32>().is_none());
}
}