use crate::{
error::{Error, Result},
ffi::{
duckdb_create_data_chunk, duckdb_data_chunk, duckdb_data_chunk_get_column_count,
duckdb_data_chunk_get_size, duckdb_data_chunk_get_vector, duckdb_data_chunk_set_size,
duckdb_destroy_data_chunk, duckdb_vector_size,
},
};
use super::{
logical_type::LogicalType,
vector::{VectorMut, VectorRef},
};
pub struct DataChunkHandle {
ptr: duckdb_data_chunk,
owned: bool,
}
impl DataChunkHandle {
pub fn new(types: &[LogicalType]) -> Result<Self> {
let mut raw_types: Vec<_> = types.iter().map(LogicalType::as_raw).collect();
let ptr =
unsafe { duckdb_create_data_chunk(raw_types.as_mut_ptr(), raw_types.len() as u64) };
if ptr.is_null() {
return Err(Error::ConversionError(
crate::error::DuckDBConversionError::ConversionError(
"duckdb_create_data_chunk returned null".to_owned(),
),
));
}
Ok(Self { ptr, owned: true })
}
pub(crate) unsafe fn borrowed(ptr: duckdb_data_chunk) -> Self {
Self { ptr, owned: false }
}
pub fn vector(
&self,
idx: usize,
) -> Result<VectorRef<'_>> {
self.check_col(idx)?;
let vec_ptr = unsafe { duckdb_data_chunk_get_vector(self.ptr, idx as u64) };
Ok(unsafe { VectorRef::new(vec_ptr) })
}
pub fn vector_mut(
&mut self,
idx: usize,
) -> Result<VectorMut<'_>> {
self.check_col(idx)?;
let vec_ptr = unsafe { duckdb_data_chunk_get_vector(self.ptr, idx as u64) };
Ok(unsafe { VectorMut::new(vec_ptr) })
}
pub fn vectors_mut(&mut self) -> Result<Vec<VectorMut<'_>>> {
let n = self.num_columns();
(0..n)
.map(|idx| {
let vec_ptr = unsafe { duckdb_data_chunk_get_vector(self.ptr, idx as u64) };
Ok(unsafe { VectorMut::new(vec_ptr) })
})
.collect()
}
pub fn len(&self) -> usize {
unsafe { duckdb_data_chunk_get_size(self.ptr) as usize }
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn num_columns(&self) -> usize {
unsafe { duckdb_data_chunk_get_column_count(self.ptr) as usize }
}
pub fn set_len(
&mut self,
n: usize,
) -> Result<()> {
if n > self.capacity() {
return Err(Error::ConversionError(
crate::error::DuckDBConversionError::ConversionError(format!(
"row count {n} exceeds chunk capacity {}",
self.capacity()
)),
));
}
unsafe { duckdb_data_chunk_set_size(self.ptr, n as u64) };
Ok(())
}
pub fn capacity(&self) -> usize {
unsafe { duckdb_vector_size() as usize }
}
fn check_col(
&self,
idx: usize,
) -> Result<()> {
if idx >= self.num_columns() {
return Err(Error::InvalidColumnIndex(idx));
}
Ok(())
}
}
impl Drop for DataChunkHandle {
fn drop(&mut self) {
if self.owned && !self.ptr.is_null() {
unsafe { duckdb_destroy_data_chunk(&mut self.ptr) };
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn owned_chunk_reports_column_count_and_capacity() {
let types = [LogicalType::of::<i32>().unwrap(), LogicalType::of::<String>().unwrap()];
let chunk = DataChunkHandle::new(&types).unwrap();
assert_eq!(chunk.num_columns(), 2);
assert!(chunk.capacity() > 0);
assert_eq!(chunk.len(), 0);
}
#[test]
fn set_len_within_capacity_succeeds() {
let types = [LogicalType::of::<i32>().unwrap()];
let mut chunk = DataChunkHandle::new(&types).unwrap();
chunk.set_len(3).unwrap();
assert_eq!(chunk.len(), 3);
}
#[test]
fn set_len_beyond_capacity_errors() {
let types = [LogicalType::of::<i32>().unwrap()];
let mut chunk = DataChunkHandle::new(&types).unwrap();
let too_many = chunk.capacity() + 1;
assert!(chunk.set_len(too_many).is_err());
}
#[test]
fn vector_out_of_range_errors() {
let types = [LogicalType::of::<i32>().unwrap()];
let chunk = DataChunkHandle::new(&types).unwrap();
assert!(chunk.vector(1).is_err());
}
#[test]
fn vectors_mut_covers_every_column() {
let types = [LogicalType::of::<i32>().unwrap(), LogicalType::of::<i64>().unwrap()];
let mut chunk = DataChunkHandle::new(&types).unwrap();
let views = chunk.vectors_mut().unwrap();
assert_eq!(views.len(), 2);
}
}