use std::cmp::min;
use crate::{
CursorImpl, Error,
buffers::{ColumnBuffer, Resize, TextColumn},
execute::execute,
handles::{AsStatementRef, HasDataType, Statement, StatementRef},
};
pub struct ColumnarBulkInserter<S, C> {
statement: S,
parameter_set_size: usize,
capacity: usize,
parameters: Vec<C>,
}
impl<S, C> ColumnarBulkInserter<S, C>
where
S: AsStatementRef,
{
pub unsafe fn new(
mut statement: S,
parameters: Vec<C>,
mapping: impl InputParameterMapping,
) -> Result<Self, Error>
where
C: ColumnBuffer + HasDataType + Send,
{
let stmt = statement.as_stmt_ref();
bind_parameter_buffers_to_statement(¶meters, mapping, stmt)?;
let capacity = parameters
.iter()
.map(|col| col.capacity())
.min()
.unwrap_or(0);
Ok(Self {
statement,
parameter_set_size: 0,
capacity,
parameters,
})
}
pub fn execute(&mut self) -> Result<Option<CursorImpl<StatementRef<'_>>>, Error> {
let mut stmt = self.statement.as_stmt_ref();
unsafe {
if self.parameter_set_size == 0 {
Ok(None)
} else {
stmt.set_paramset_size(self.parameter_set_size);
execute(stmt, None)
}
}
}
pub fn clear(&mut self) {
self.parameter_set_size = 0;
}
pub fn num_rows(&self) -> usize {
self.parameter_set_size
}
pub fn set_num_rows(&mut self, num_rows: usize) {
if num_rows > self.capacity {
panic!(
"Columnar buffer may not be resized to a value higher than the maximum number of \
rows initially specified in the constructor."
);
}
self.parameter_set_size = num_rows;
}
pub fn column_mut<'a>(&'a mut self, buffer_index: usize) -> C::SliceMut
where
C: BoundInputSlice<'a>,
{
unsafe {
self.parameters[buffer_index]
.as_view_mut((buffer_index + 1) as u16, self.statement.as_stmt_ref())
}
}
pub fn capacity(&self) -> usize {
self.capacity
}
pub fn resize(
mut self,
new_capacity: usize,
mapping: impl InputParameterMapping,
) -> Result<Self, Error>
where
C: ColumnBuffer + HasDataType + Resize + Send,
{
assert!(new_capacity > 0, "New capacity must be at least 1.");
for column in &mut self.parameters {
column.resize(new_capacity);
}
self.capacity = new_capacity;
self.parameter_set_size = min(self.parameter_set_size, new_capacity);
let stmt = self.statement.as_stmt_ref();
bind_parameter_buffers_to_statement(&self.parameters, mapping, stmt)?;
Ok(self)
}
}
fn bind_parameter_buffers_to_statement<C>(
parameters: &[C],
mapping: impl InputParameterMapping,
mut stmt: StatementRef<'_>,
) -> Result<(), Error>
where
C: ColumnBuffer + HasDataType + Send,
{
stmt.reset_parameters();
let parameter_indices = 1..(mapping.num_parameters() as u16 + 1);
for parameter_index in parameter_indices {
let column_index = mapping.parameter_index_to_column_index(parameter_index);
let column_buffer = ¶meters[column_index];
if let Err(error) =
unsafe { stmt.bind_input_parameter(parameter_index, column_buffer) }.into_result(&stmt)
{
stmt.reset_parameters();
return Err(error);
}
}
Ok(())
}
pub unsafe trait BoundInputSlice<'a> {
type SliceMut;
unsafe fn as_view_mut(
&'a mut self,
parameter_index: u16,
stmt: StatementRef<'a>,
) -> Self::SliceMut;
}
impl<S> ColumnarBulkInserter<S, TextColumn<u8>> {
pub fn append<'b>(
&mut self,
mut row: impl Iterator<Item = Option<&'b [u8]>>,
) -> Result<(), Error>
where
S: AsStatementRef,
{
if self.capacity == self.parameter_set_size {
panic!("Trying to insert elements into TextRowSet beyond batch size.")
}
let mut col_index = 1;
for column in &mut self.parameters {
let text = row.next().expect(
"Row passed to TextRowSet::append must contain one element for each column.",
);
if let Some(text) = text {
unsafe {
column
.as_view_mut(col_index, self.statement.as_stmt_ref())
.ensure_max_element_length(text.len(), self.parameter_set_size)?;
}
column.set_value(self.parameter_set_size, Some(text));
} else {
column.set_value(self.parameter_set_size, None);
}
col_index += 1;
}
self.parameter_set_size += 1;
Ok(())
}
}
pub trait InputParameterMapping {
fn parameter_index_to_column_index(&self, paramteter_index: u16) -> usize;
fn num_parameters(&self) -> usize;
}
pub struct InOrder {
number_of_parameters: usize,
}
impl InOrder {
pub fn new(number_of_parameters: usize) -> Self {
Self {
number_of_parameters,
}
}
}
impl InputParameterMapping for InOrder {
fn parameter_index_to_column_index(&self, paramteter_index: u16) -> usize {
debug_assert_ne!(0, paramteter_index, "Parameter index must be one based.");
(paramteter_index - 1) as usize
}
fn num_parameters(&self) -> usize {
self.number_of_parameters
}
}