use std::ffi::c_void;
use crate::{
Connection, Result,
callback::{contain_callback, drop_boxed},
error::Error,
inner_connection::InnerConnection,
};
use super::ffi;
mod function;
mod value;
#[cfg(feature = "vtab-arrow")]
pub mod arrow;
#[cfg(feature = "vtab-arrow")]
pub use self::arrow::{
arrow_arraydata_to_query_params, arrow_ffi_to_query_params, arrow_recordbatch_to_query_params,
record_batch_to_duckdb_data_chunk, to_duckdb_logical_type, to_duckdb_logical_type_for_field, to_duckdb_type_id,
};
pub use function::{BindInfo, InitInfo, TableFunction, TableFunctionInfo};
pub use value::Value;
use crate::core::{DataChunkHandle, LogicalTypeHandle};
use ffi::{duckdb_bind_info, duckdb_data_chunk, duckdb_function_info, duckdb_init_info};
pub trait VTab: Sized {
type InitData: Sized + Send + Sync;
type BindData: Sized + Send + Sync;
fn bind(bind: &BindInfo) -> Result<Self::BindData, Box<dyn std::error::Error>>;
fn init(init: &InitInfo) -> Result<Self::InitData, Box<dyn std::error::Error>>;
fn func(func: &TableFunctionInfo<Self>, output: &mut DataChunkHandle) -> Result<(), Box<dyn std::error::Error>>;
fn supports_pushdown() -> bool {
false
}
fn parameters() -> Option<Vec<LogicalTypeHandle>> {
None
}
fn named_parameters() -> Option<Vec<(String, LogicalTypeHandle)>> {
None
}
}
unsafe extern "C" fn func<T>(info: duckdb_function_info, output: duckdb_data_chunk)
where
T: VTab,
{
let info = TableFunctionInfo::<T>::from(info);
contain_callback(&info, || unsafe {
let mut data_chunk_handle = DataChunkHandle::new_unowned(output);
T::func(&info, &mut data_chunk_handle)
});
}
unsafe extern "C" fn init<T>(info: duckdb_init_info)
where
T: VTab,
{
let info = InitInfo::from(info);
contain_callback(&info, || {
let init_data = T::init(&info)?;
unsafe {
info.set_init_data(
Box::into_raw(Box::new(init_data)) as *mut c_void,
Some(drop_boxed::<T::InitData>),
);
}
Ok(())
});
}
unsafe extern "C" fn bind<T>(info: duckdb_bind_info)
where
T: VTab,
{
let info = BindInfo::from(info);
contain_callback(&info, || {
let bind_data = T::bind(&info)?;
unsafe {
info.set_bind_data(
Box::into_raw(Box::new(bind_data)) as *mut c_void,
Some(drop_boxed::<T::BindData>),
);
}
Ok(())
});
}
impl Connection {
#[inline]
pub fn register_table_function<T: VTab>(&self, name: &str) -> Result<()> {
let table_function = TableFunction::default();
table_function
.set_name(name)
.supports_pushdown(T::supports_pushdown())
.set_bind(Some(bind::<T>))
.set_init(Some(init::<T>))
.set_function(Some(func::<T>));
for ty in T::parameters().unwrap_or_default() {
table_function.add_parameter(&ty);
}
for (name, ty) in T::named_parameters().unwrap_or_default() {
table_function.add_named_parameter(&name, &ty);
}
self.db.borrow_mut().register_table_function(table_function)
}
#[inline]
pub fn register_table_function_with_extra_info<T: VTab, E>(&self, name: &str, extra_info: &E) -> Result<()>
where
E: Clone + Send + Sync + 'static,
{
let table_function = TableFunction::default();
table_function
.set_name(name)
.supports_pushdown(T::supports_pushdown())
.set_bind(Some(bind::<T>))
.set_init(Some(init::<T>))
.set_function(Some(func::<T>))
.set_extra_info(extra_info.clone());
for ty in T::parameters().unwrap_or_default() {
table_function.add_parameter(&ty);
}
for (name, ty) in T::named_parameters().unwrap_or_default() {
table_function.add_named_parameter(&name, &ty);
}
self.db.borrow_mut().register_table_function(table_function)
}
}
impl InnerConnection {
pub fn register_table_function(&mut self, table_function: TableFunction) -> Result<()> {
unsafe {
let rc = ffi::duckdb_register_table_function(self.con, table_function.ptr);
if rc != ffi::DuckDBSuccess {
return Err(Error::DuckDBFailure(ffi::Error::new(rc), None));
}
}
Ok(())
}
}
#[cfg(test)]
mod tests;