Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add bindings for Scalar function API #348

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/rust.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:
name: Download duckdb
with:
repository: "duckdb/duckdb"
tag: "v0.10.1"
tag: "v1.0.0"
fileName: ${{ matrix.duckdb }}
out-file-path: .

Expand Down
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ members = [
]

[workspace.package]
version = "0.10.2"
version = "1.0.0"
authors = ["wangfenjin <[email protected]>"]
edition = "2021"
repository = "https://github.com/wangfenjin/duckdb-rs"
Expand All @@ -19,8 +19,8 @@ license = "MIT"
categories = ["database"]

[workspace.dependencies]
duckdb = { version = "0.10.2", path = "crates/duckdb" }
libduckdb-sys = { version = "0.10.2", path = "crates/libduckdb-sys" }
duckdb = { version = "1.0.0", path = "crates/duckdb" }
libduckdb-sys = { version = "1.0.0", path = "crates/libduckdb-sys" }
duckdb-loadable-macros = { version = "0.1.1", path = "crates/duckdb-loadable-macros" }
autocfg = "1.0"
bindgen = { version = "0.69", default-features = false }
Expand Down
2 changes: 1 addition & 1 deletion crates/duckdb/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "duckdb"
version = "0.10.2"
version = "1.0.0"
authors.workspace = true
edition.workspace = true
repository.workspace = true
Expand Down
102 changes: 98 additions & 4 deletions crates/duckdb/src/vtab/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
duckdb_table_function_add_named_parameter, duckdb_table_function_add_parameter, duckdb_table_function_init_t,
duckdb_table_function_set_bind, duckdb_table_function_set_extra_info, duckdb_table_function_set_function,
duckdb_table_function_set_init, duckdb_table_function_set_local_init, duckdb_table_function_set_name,
duckdb_table_function_supports_projection_pushdown, idx_t,
duckdb_table_function_supports_projection_pushdown, duckdb_vector, idx_t,
},
LogicalType, Value,
};
Expand All @@ -19,7 +19,7 @@
/// An interface to store and retrieve data during the function bind stage
#[derive(Debug)]
pub struct BindInfo {
ptr: *mut c_void,
ptr: duckdb_bind_info,
}

impl BindInfo {
Expand Down Expand Up @@ -264,7 +264,7 @@
///
/// # Arguments
/// * `function`: The init function
pub fn set_init(&self, init_func: Option<unsafe extern "C" fn(*mut c_void)>) -> &Self {
pub fn set_init(&self, init_func: Option<unsafe extern "C" fn(duckdb_init_info)>) -> &Self {
unsafe {
duckdb_table_function_set_init(self.ptr, init_func);
}
Expand All @@ -275,7 +275,7 @@
///
/// # Arguments
/// * `function`: The bind function
pub fn set_bind(&self, bind_func: Option<unsafe extern "C" fn(*mut c_void)>) -> &Self {
pub fn set_bind(&self, bind_func: Option<unsafe extern "C" fn(duckdb_bind_info)>) -> &Self {
unsafe {
duckdb_table_function_set_bind(self.ptr, bind_func);
}
Expand Down Expand Up @@ -385,3 +385,97 @@
Self(ptr)
}
}

use super::ffi::{
duckdb_create_scalar_function, duckdb_destroy_scalar_function, duckdb_scalar_function,

Check failure on line 390 in crates/duckdb/src/vtab/function.rs

View workflow job for this annotation

GitHub Actions / Test x86_64-pc-windows-msvc

unresolved imports `super::ffi::duckdb_create_scalar_function`, `super::ffi::duckdb_destroy_scalar_function`, `super::ffi::duckdb_scalar_function`, `super::ffi::duckdb_scalar_function_add_parameter`, `super::ffi::duckdb_scalar_function_set_extra_info`, `super::ffi::duckdb_scalar_function_set_function`, `super::ffi::duckdb_scalar_function_set_name`, `super::ffi::duckdb_scalar_function_set_return_type`
duckdb_scalar_function_add_parameter, duckdb_scalar_function_set_extra_info, duckdb_scalar_function_set_function,
duckdb_scalar_function_set_name, duckdb_scalar_function_set_return_type,
};

/// A function that returns a queryable scalar function
#[derive(Debug)]
pub struct ScalarFunction {
pub(crate) ptr: duckdb_scalar_function,
}

impl Drop for ScalarFunction {
fn drop(&mut self) {
unsafe {
duckdb_destroy_scalar_function(&mut self.ptr);
}
}
}

impl ScalarFunction {
/// Adds a parameter to the scalar function.
///
/// # Arguments
/// * `logical_type`: The type of the parameter to add.
pub fn add_parameter(&self, logical_type: &LogicalType) -> &Self {
unsafe {
duckdb_scalar_function_add_parameter(self.ptr, logical_type.ptr);
}
self
}

/// Sets the return type of the scalar function.
///
/// # Arguments
/// * `logical_type`: The return type of the scalar function.
pub fn set_return_type(&self, logical_type: &LogicalType) -> &Self {
unsafe {
duckdb_scalar_function_set_return_type(self.ptr, logical_type.ptr);
}
self
}

/// Sets the main function of the scalar function
///
/// # Arguments
/// * `function`: The function
pub fn set_function(
&self,
func: Option<unsafe extern "C" fn(info: duckdb_function_info, input: duckdb_data_chunk, output: duckdb_vector)>,
) -> &Self {
unsafe {
duckdb_scalar_function_set_function(self.ptr, func);
}
self
}

/// Creates a new empty scalar function.
pub fn new() -> Self {
Self {
ptr: unsafe { duckdb_create_scalar_function() },
}
}

/// Sets the name of the given scalar function.
///
/// # Arguments
/// * `name`: The name of the scalar function
pub fn set_name(&self, name: &str) -> &ScalarFunction {
unsafe {
let string = CString::from_vec_unchecked(name.as_bytes().into());
duckdb_scalar_function_set_name(self.ptr, string.as_ptr());
}
self
}

/// Assigns extra information to the scalar function that can be fetched during binding, etc.
///
/// # Arguments
/// * `extra_info`: The extra information
/// * `destroy`: The callback that will be called to destroy the bind data (if any)
///
/// # Safety
pub unsafe fn set_extra_info(&self, extra_info: *mut c_void, destroy: duckdb_delete_callback_t) {
duckdb_scalar_function_set_extra_info(self.ptr, extra_info, destroy);
}
}

impl Default for ScalarFunction {
fn default() -> Self {
Self::new()
}
}
72 changes: 70 additions & 2 deletions crates/duckdb/src/vtab/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@
mod excel;

pub use data_chunk::DataChunk;
pub use function::{BindInfo, FunctionInfo, InitInfo, TableFunction};
pub use function::{BindInfo, FunctionInfo, InitInfo, ScalarFunction, TableFunction};
pub use logical_type::{LogicalType, LogicalTypeId};
pub use value::Value;
pub use vector::{FlatVector, Inserter, ListVector, StructVector, Vector};

use ffi::{duckdb_bind_info, duckdb_data_chunk, duckdb_function_info, duckdb_init_info};
use ffi::{duckdb_bind_info, duckdb_data_chunk, duckdb_function_info, duckdb_init_info, duckdb_vector};

use ffi::duckdb_malloc;
use std::mem::size_of;
Expand Down Expand Up @@ -161,6 +161,48 @@
}
}

/// Duckdb scalar function trait
///
pub trait VScalar: Sized {
/// The actual function
///
/// # Safety
///
/// This function is unsafe because it:
///
/// - Dereferences multiple raw pointers (`func``).
///
unsafe fn func(
func: &FunctionInfo,
input: &mut DataChunk,
output: &mut FlatVector,
) -> Result<(), Box<dyn std::error::Error>>;
/// The parameters of the table function
/// default is None
fn parameters() -> Option<Vec<LogicalType>> {
None
}

/// The return type of the scalar function
/// default is None
fn return_type() -> LogicalType {
panic!("return_type not implemented")
}
}

unsafe extern "C" fn scalar_func<T>(info: duckdb_function_info, input: duckdb_data_chunk, output: duckdb_vector)
where
T: VScalar,
{
let info = FunctionInfo::from(info);
let mut input = DataChunk::from(input);
let mut output_vector = FlatVector::from(output);
let result = T::func(&info, &mut input, &mut output_vector);
if result.is_err() {
info.set_error(&result.err().unwrap().to_string());
}
}

impl Connection {
/// Register the given TableFunction with the current db
#[inline]
Expand All @@ -180,6 +222,21 @@
}
self.db.borrow_mut().register_table_function(table_function)
}

/// Register the given ScalarFunction with the current db
#[inline]
pub fn register_scalar_function<S: VScalar>(&self, name: &str) -> Result<()> {
let scalar_function = ScalarFunction::default();
scalar_function
.set_name(name)
.set_return_type(&S::return_type())
//.set_extra_info()
.set_function(Some(scalar_func::<S>));
for ty in S::parameters().unwrap_or_default() {
scalar_function.add_parameter(&ty);
}
self.db.borrow_mut().register_scalar_function(scalar_function)
}
}

impl InnerConnection {
Expand All @@ -193,6 +250,17 @@
}
Ok(())
}

/// Register the given ScalarFunction with the current db
pub fn register_scalar_function(&mut self, scalar_function: ScalarFunction) -> Result<()> {
unsafe {
let rc = ffi::duckdb_register_scalar_function(self.con, scalar_function.ptr);

Check failure on line 257 in crates/duckdb/src/vtab/mod.rs

View workflow job for this annotation

GitHub Actions / Test x86_64-pc-windows-msvc

cannot find function `duckdb_register_scalar_function` in crate `ffi`
if rc != ffi::DuckDBSuccess {
return Err(Error::DuckDBFailure(ffi::Error::new(rc), None));
}
}
Ok(())
}
}

#[cfg(test)]
Expand Down
2 changes: 1 addition & 1 deletion crates/libduckdb-sys/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "libduckdb-sys"
version = "0.10.2"
version = "1.0.0"
authors.workspace = true
edition.workspace = true
license.workspace = true
Expand Down
Binary file modified crates/libduckdb-sys/duckdb.tar.gz
Binary file not shown.
Loading
Loading