Skip to content

Commit c63f7c3

Browse files
committed
Align VScalar State with DuckDB's extra_info semantics
1 parent 8084b57 commit c63f7c3

File tree

3 files changed

+30
-19
lines changed

3 files changed

+30
-19
lines changed

crates/duckdb/src/vscalar/arrow.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,13 @@ impl ArrowFunctionSignature {
7373

7474
/// A trait for scalar functions that accept and return arrow types that can be registered with DuckDB
7575
pub trait VArrowScalar: Sized {
76-
/// State that persists across invocations of the scalar function (the lifetime of the connection)
77-
/// The state can be accessed by multiple threads, so it must be `Send + Sync`.
78-
type State: Default + Sized + Send + Sync;
76+
/// State set at registration time. Persists for the lifetime of the catalog entry.
77+
/// Shared across worker threads and invocations — must not be modified during execution.
78+
/// Must be `'static` as it is stored in DuckDB and may outlive the current stack frame.
79+
type State: Default + Sized + Send + Sync + 'static;
7980

8081
/// The actual function that is called by DuckDB
81-
fn invoke(info: &Self::State, input: RecordBatch) -> Result<Arc<dyn Array>, Box<dyn std::error::Error>>;
82+
fn invoke(state: &Self::State, input: RecordBatch) -> Result<Arc<dyn Array>, Box<dyn std::error::Error>>;
8283

8384
/// The possible signatures of the scalar function. These will result in DuckDB scalar function overloads.
8485
/// The invoke method should be able to handle all of these signatures.
@@ -92,11 +93,11 @@ where
9293
type State = T::State;
9394

9495
unsafe fn invoke(
95-
info: &Self::State,
96+
state: &Self::State,
9697
input: &mut DataChunkHandle,
9798
out: &mut dyn WritableVector,
9899
) -> Result<(), Box<dyn std::error::Error>> {
99-
let array = T::invoke(info, data_chunk_to_arrow(input)?)?;
100+
let array = T::invoke(state, data_chunk_to_arrow(input)?)?;
100101
write_arrow_array_to_vector(&array, out)
101102
}
102103

@@ -200,8 +201,8 @@ mod test {
200201
impl VArrowScalar for ArrowOverloaded {
201202
type State = MockState;
202203

203-
fn invoke(s: &Self::State, input: RecordBatch) -> Result<Arc<dyn Array>, Box<dyn std::error::Error>> {
204-
assert_eq!("some meta", s.info);
204+
fn invoke(state: &Self::State, input: RecordBatch) -> Result<Arc<dyn Array>, Box<dyn std::error::Error>> {
205+
assert_eq!("some meta", state.info);
205206

206207
let a = input.column(0);
207208
let b = input.column(1);

crates/duckdb/src/vscalar/function.rs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,25 +112,34 @@ impl ScalarFunction {
112112
self
113113
}
114114

115-
/// Assigns extra information to the scalar function that can be fetched during binding, etc.
115+
/// Assigns extra information to the scalar function using raw pointers.
116+
///
117+
/// For most use cases, prefer [`set_extra_info`](Self::set_extra_info) which handles memory management automatically.
116118
///
117119
/// # Arguments
118-
/// * `extra_info`: The extra information
119-
/// * `destroy`: The callback that will be called to destroy the bind data (if any)
120+
/// * `extra_info`: The extra information as a raw pointer
121+
/// * `destroy`: The callback that will be called to destroy the data (if any)
120122
///
121123
/// # Safety
122-
unsafe fn set_extra_info_impl(&self, extra_info: *mut c_void, destroy: duckdb_delete_callback_t) {
124+
/// The caller must ensure that `extra_info` is a valid pointer and that `destroy`
125+
/// properly cleans up the data when called.
126+
pub unsafe fn set_extra_info_raw(&self, extra_info: *mut c_void, destroy: duckdb_delete_callback_t) {
123127
duckdb_scalar_function_set_extra_info(self.ptr, extra_info, destroy);
124128
}
125129

130+
/// Assigns extra information to the scalar function that can be fetched during execution.
131+
///
132+
/// # Arguments
133+
/// * `info`: The extra information to store
134+
/// ```
126135
pub fn set_extra_info<T>(&self, info: T) -> &Self
127136
where
128-
T: Send + Sync,
137+
T: Send + Sync + 'static,
129138
{
130139
unsafe {
131140
let t = Box::new(info);
132141
let c_void = Box::into_raw(t) as *mut c_void;
133-
self.set_extra_info_impl(c_void, Some(drop_ptr::<T>));
142+
self.set_extra_info_raw(c_void, Some(drop_ptr::<T>));
134143
}
135144
self
136145
}

crates/duckdb/src/vscalar/mod.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@ pub use arrow::{ArrowFunctionSignature, ArrowScalarParams, VArrowScalar};
2323

2424
/// Duckdb scalar function trait
2525
pub trait VScalar: Sized {
26-
/// State that persists across invocations of the scalar function (the lifetime of the connection)
27-
/// The state can be accessed by multiple threads, so it must be `Send + Sync`.
28-
type State: Sized + Send + Sync;
26+
/// State set at registration time. Persists for the lifetime of the catalog entry.
27+
/// Shared across worker threads and invocations — must not be modified during execution.
28+
/// Must be `'static` as it is stored in DuckDB and may outlive the current stack frame.
29+
type State: Sized + Send + Sync + 'static;
2930
/// The actual function
3031
///
3132
/// # Safety
@@ -132,7 +133,7 @@ where
132133
}
133134

134135
impl Connection {
135-
/// Register the given ScalarFunction with default state
136+
/// Register the given ScalarFunction with default state.
136137
#[inline]
137138
pub fn register_scalar_function<S: VScalar>(&self, name: &str) -> crate::Result<()>
138139
where
@@ -149,7 +150,7 @@ impl Connection {
149150
self.db.borrow_mut().register_scalar_function_set(set)
150151
}
151152

152-
/// Register the given ScalarFunction with custom state
153+
/// Register the given ScalarFunction with custom state.
153154
#[inline]
154155
pub fn register_scalar_function_with_state<S: VScalar>(&self, name: &str, state: &S::State) -> crate::Result<()>
155156
where

0 commit comments

Comments
 (0)