Skip to main content

duckdb/vtab/
function.rs

1use super::{
2    drop_boxed,
3    ffi::{
4        duckdb_bind_add_result_column, duckdb_bind_get_extra_info, duckdb_bind_get_named_parameter,
5        duckdb_bind_get_parameter, duckdb_bind_get_parameter_count, duckdb_bind_info, duckdb_bind_set_bind_data,
6        duckdb_bind_set_cardinality, duckdb_bind_set_error, duckdb_create_table_function, duckdb_data_chunk,
7        duckdb_delete_callback_t, duckdb_destroy_table_function, duckdb_table_function,
8        duckdb_table_function_add_named_parameter, duckdb_table_function_add_parameter, duckdb_table_function_init_t,
9        duckdb_table_function_set_bind, duckdb_table_function_set_extra_info, duckdb_table_function_set_function,
10        duckdb_table_function_set_init, duckdb_table_function_set_local_init, duckdb_table_function_set_name,
11        duckdb_table_function_supports_projection_pushdown, idx_t,
12    },
13    LogicalTypeHandle, VTab, Value,
14};
15use std::{
16    ffi::{c_void, CString},
17    fmt::Debug,
18    marker::PhantomData,
19    os::raw::c_char,
20};
21
22/// An interface to store and retrieve data during the function bind stage
23#[derive(Debug)]
24pub struct BindInfo {
25    ptr: duckdb_bind_info,
26}
27
28impl BindInfo {
29    /// Adds a result column to the output of the table function.
30    ///
31    /// # Arguments
32    ///  * `name`: The name of the column
33    ///  * `type`: The logical type of the column
34    pub fn add_result_column(&self, column_name: &str, column_type: LogicalTypeHandle) {
35        let c_str = CString::new(column_name).unwrap();
36        unsafe {
37            duckdb_bind_add_result_column(self.ptr, c_str.as_ptr() as *const c_char, column_type.ptr);
38        }
39    }
40
41    /// Report that an error has occurred while calling bind.
42    ///
43    /// # Arguments
44    ///  * `error`: The error message
45    pub fn set_error(&self, error: &str) {
46        let c_str = CString::new(error).unwrap();
47        unsafe {
48            duckdb_bind_set_error(self.ptr, c_str.as_ptr() as *const c_char);
49        }
50    }
51    /// Sets the user-provided bind data in the bind object. This object can be retrieved again during execution.
52    ///
53    /// # Arguments
54    ///  * `data`: The bind data object.
55    ///  * `free_function`: The callback that will be called to destroy the bind data (if any)
56    ///
57    /// # Safety
58    /// `data` must be a valid pointer, and `free_function` must properly free it.
59    pub unsafe fn set_bind_data(&self, data: *mut c_void, free_function: Option<unsafe extern "C" fn(*mut c_void)>) {
60        duckdb_bind_set_bind_data(self.ptr, data, free_function);
61    }
62
63    /// Retrieves the number of regular (non-named) parameters to the function.
64    pub fn get_parameter_count(&self) -> u64 {
65        unsafe { duckdb_bind_get_parameter_count(self.ptr) }
66    }
67
68    /// Retrieves the parameter at the given index.
69    ///
70    /// # Arguments
71    ///  * `index`: The index of the parameter to get
72    ///
73    /// returns: The value of the parameter
74    ///
75    /// # Panics
76    /// If requested parameter is out of range for function definition
77    pub fn get_parameter(&self, param_index: u64) -> Value {
78        unsafe {
79            let ptr = duckdb_bind_get_parameter(self.ptr, param_index);
80            if ptr.is_null() {
81                panic!("{param_index} is out of range for function definition");
82            } else {
83                Value::from(ptr)
84            }
85        }
86    }
87
88    /// Retrieves the named parameter with the given name.
89    ///
90    /// # Arguments
91    /// * `name`: The name of the parameter to get
92    ///
93    /// returns: The value of the parameter
94    pub fn get_named_parameter(&self, name: &str) -> Option<Value> {
95        unsafe {
96            let name = &CString::new(name).unwrap();
97            let ptr = duckdb_bind_get_named_parameter(self.ptr, name.as_ptr());
98            if ptr.is_null() {
99                None
100            } else {
101                Some(Value::from(ptr))
102            }
103        }
104    }
105
106    /// Sets the cardinality estimate for the table function, used for optimization.
107    ///
108    /// # Arguments
109    /// * `cardinality`: The cardinality estimate
110    /// * `is_exact`: Whether or not the cardinality estimate is exact, or an approximation
111    pub fn set_cardinality(&self, cardinality: idx_t, is_exact: bool) {
112        unsafe { duckdb_bind_set_cardinality(self.ptr, cardinality, is_exact) }
113    }
114    /// Retrieves the extra info of the function as set in [`TableFunction::set_extra_info`].
115    pub fn get_extra_info<T>(&self) -> *const T {
116        unsafe { duckdb_bind_get_extra_info(self.ptr).cast() }
117    }
118}
119
120impl From<duckdb_bind_info> for BindInfo {
121    fn from(ptr: duckdb_bind_info) -> Self {
122        Self { ptr }
123    }
124}
125
126use super::ffi::{
127    duckdb_init_get_bind_data, duckdb_init_get_column_count, duckdb_init_get_column_index, duckdb_init_get_extra_info,
128    duckdb_init_info, duckdb_init_set_error, duckdb_init_set_init_data, duckdb_init_set_max_threads,
129};
130
131/// An interface to store and retrieve data during the function init stage
132#[derive(Debug)]
133pub struct InitInfo(duckdb_init_info);
134
135impl From<duckdb_init_info> for InitInfo {
136    fn from(ptr: duckdb_init_info) -> Self {
137        Self(ptr)
138    }
139}
140
141impl InitInfo {
142    /// # Safety
143    pub unsafe fn set_init_data(&self, data: *mut c_void, freeer: Option<unsafe extern "C" fn(*mut c_void)>) {
144        unsafe {
145            duckdb_init_set_init_data(self.0, data, freeer);
146        }
147    }
148
149    /// Returns the column indices of the projected columns at the specified positions.
150    ///
151    /// This function must be used if projection pushdown is enabled to figure out which columns to emit.
152    ///
153    /// returns: The column indices at which to get the projected column index
154    pub fn get_column_indices(&self) -> Vec<idx_t> {
155        let mut indices;
156        unsafe {
157            let column_count = duckdb_init_get_column_count(self.0);
158            indices = Vec::with_capacity(column_count as usize);
159            for i in 0..column_count {
160                indices.push(duckdb_init_get_column_index(self.0, i))
161            }
162        }
163        indices
164    }
165
166    /// Retrieves the extra info of the function as set in [`TableFunction::set_extra_info`].
167    pub fn get_extra_info<T>(&self) -> *const T {
168        unsafe { duckdb_init_get_extra_info(self.0).cast() }
169    }
170
171    /// Gets the bind data set by [`BindInfo::set_bind_data`] during the bind.
172    ///
173    /// Note that the bind data should be considered as read-only.
174    /// For tracking state, use the init data instead.
175    ///
176    /// # Arguments
177    /// * `returns`: The bind data object
178    pub fn get_bind_data<T>(&self) -> *const T {
179        unsafe { duckdb_init_get_bind_data(self.0).cast() }
180    }
181
182    /// Sets how many threads can process this table function in parallel (default: 1)
183    ///
184    /// # Arguments
185    /// * `max_threads`: The maximum amount of threads that can process this table function
186    pub fn set_max_threads(&self, max_threads: idx_t) {
187        unsafe { duckdb_init_set_max_threads(self.0, max_threads) }
188    }
189
190    /// Report that an error has occurred while calling init.
191    ///
192    /// # Arguments
193    /// * `error`: The error message
194    pub fn set_error(&self, error: &str) {
195        let c_str = CString::new(error).unwrap();
196        unsafe { duckdb_init_set_error(self.0, c_str.as_ptr()) }
197    }
198}
199
200/// A function that returns a queryable table
201#[derive(Debug)]
202pub struct TableFunction {
203    pub(crate) ptr: duckdb_table_function,
204}
205
206impl Drop for TableFunction {
207    fn drop(&mut self) {
208        unsafe {
209            duckdb_destroy_table_function(&mut self.ptr);
210        }
211    }
212}
213
214impl TableFunction {
215    /// Sets whether or not the given table function supports projection pushdown.
216    ///
217    /// If this is set to true, the system will provide a list of all required columns in the `init` stage through
218    /// the [`InitInfo::get_column_indices`] method.
219    /// If this is set to false (the default), the system will expect all columns to be projected.
220    ///
221    /// # Arguments
222    ///  * `pushdown`: True if the table function supports projection pushdown, false otherwise.
223    pub fn supports_pushdown(&self, supports: bool) -> &Self {
224        unsafe {
225            duckdb_table_function_supports_projection_pushdown(self.ptr, supports);
226        }
227        self
228    }
229
230    /// Adds a parameter to the table function.
231    ///
232    /// # Arguments
233    ///  * `logical_type`: The type of the parameter to add.
234    pub fn add_parameter(&self, logical_type: &LogicalTypeHandle) -> &Self {
235        unsafe {
236            duckdb_table_function_add_parameter(self.ptr, logical_type.ptr);
237        }
238        self
239    }
240
241    /// Adds a named parameter to the table function.
242    ///
243    /// # Arguments
244    /// * `name`: The name of the parameter to add.
245    /// * `logical_type`: The type of the parameter to add.
246    pub fn add_named_parameter(&self, name: &str, logical_type: &LogicalTypeHandle) -> &Self {
247        unsafe {
248            let string = CString::new(name).unwrap();
249            duckdb_table_function_add_named_parameter(self.ptr, string.as_ptr(), logical_type.ptr);
250        }
251        self
252    }
253
254    /// Sets the main function of the table function
255    ///
256    /// # Arguments
257    ///  * `function`: The function
258    pub fn set_function(
259        &self,
260        func: Option<unsafe extern "C" fn(info: duckdb_function_info, output: duckdb_data_chunk)>,
261    ) -> &Self {
262        unsafe {
263            duckdb_table_function_set_function(self.ptr, func);
264        }
265        self
266    }
267
268    /// Sets the init function of the table function
269    ///
270    /// # Arguments
271    ///  * `function`: The init function
272    pub fn set_init(&self, init_func: Option<unsafe extern "C" fn(duckdb_init_info)>) -> &Self {
273        unsafe {
274            duckdb_table_function_set_init(self.ptr, init_func);
275        }
276        self
277    }
278
279    /// Sets the bind function of the table function
280    ///
281    /// # Arguments
282    ///  * `function`: The bind function
283    pub fn set_bind(&self, bind_func: Option<unsafe extern "C" fn(duckdb_bind_info)>) -> &Self {
284        unsafe {
285            duckdb_table_function_set_bind(self.ptr, bind_func);
286        }
287        self
288    }
289
290    /// Creates a new empty table function.
291    pub fn new() -> Self {
292        Self {
293            ptr: unsafe { duckdb_create_table_function() },
294        }
295    }
296
297    /// Sets the name of the given table function.
298    ///
299    /// # Arguments
300    ///  * `name`: The name of the table function
301    pub fn set_name(&self, name: &str) -> &Self {
302        unsafe {
303            let string = CString::from_vec_unchecked(name.as_bytes().into());
304            duckdb_table_function_set_name(self.ptr, string.as_ptr());
305        }
306        self
307    }
308
309    /// Assigns extra information to the table function using raw pointers.
310    ///
311    /// For most use cases, prefer [`set_extra_info`](Self::set_extra_info) which handles memory management automatically.
312    ///
313    /// # Arguments
314    /// * `extra_info`: The extra information as a raw pointer
315    /// * `destroy`: The callback that will be called to destroy the data (if any)
316    ///
317    /// # Safety
318    /// The caller must ensure that `extra_info` is a valid pointer and that `destroy`
319    /// properly cleans up the data when called.
320    pub unsafe fn set_extra_info_raw(&self, extra_info: *mut c_void, destroy: duckdb_delete_callback_t) {
321        duckdb_table_function_set_extra_info(self.ptr, extra_info, destroy);
322    }
323
324    /// Assigns extra information to the table function that can be fetched during binding, init, and execution.
325    ///
326    /// # Arguments
327    /// * `info`: The extra information to store
328    pub fn set_extra_info<T>(&self, info: T) -> &Self
329    where
330        T: Send + Sync + 'static,
331    {
332        unsafe {
333            let boxed = Box::new(info);
334            let ptr = Box::into_raw(boxed) as *mut c_void;
335            self.set_extra_info_raw(ptr, Some(drop_boxed::<T>));
336        }
337        self
338    }
339
340    /// Sets the thread-local init function of the table function
341    ///
342    /// # Arguments
343    /// * `init`: The init function
344    pub fn set_local_init(&self, init: duckdb_table_function_init_t) {
345        unsafe { duckdb_table_function_set_local_init(self.ptr, init) };
346    }
347}
348
349impl Default for TableFunction {
350    fn default() -> Self {
351        Self::new()
352    }
353}
354
355use super::ffi::{
356    duckdb_function_get_bind_data, duckdb_function_get_extra_info, duckdb_function_get_init_data,
357    duckdb_function_get_local_init_data, duckdb_function_info, duckdb_function_set_error,
358};
359
360/// An interface to store and retrieve data during the function execution stage
361#[derive(Debug)]
362pub struct TableFunctionInfo<V: VTab> {
363    ptr: duckdb_function_info,
364    _vtab: PhantomData<V>,
365}
366
367impl<V: VTab> TableFunctionInfo<V> {
368    /// Report that an error has occurred while executing the function.
369    ///
370    /// # Arguments
371    ///  * `error`: The error message
372    pub fn set_error(&self, error: &str) {
373        let c_str = CString::new(error).unwrap();
374        unsafe {
375            duckdb_function_set_error(self.ptr, c_str.as_ptr());
376        }
377    }
378
379    /// Gets the bind data set by [`BindInfo::set_bind_data`] during the bind.
380    ///
381    /// Note that the bind data should be considered as read-only.
382    /// For tracking state, use the init data instead.
383    pub fn get_bind_data(&self) -> &V::BindData {
384        unsafe {
385            let bind_data: *const V::BindData = duckdb_function_get_bind_data(self.ptr).cast();
386            bind_data.as_ref().unwrap()
387        }
388    }
389
390    /// Get a reference to the init data set by [`InitInfo::set_init_data`] during the init.
391    ///
392    /// This returns a shared reference because the init data is shared between multiple threads.
393    /// It may internally be mutable.
394    ///
395    /// # Arguments
396    /// * `returns`: The init data object
397    pub fn get_init_data(&self) -> &V::InitData {
398        // Safety: A pointer to a box of the init data is stored during vtab init.
399        unsafe {
400            let init_data: *const V::InitData = duckdb_function_get_init_data(self.ptr).cast();
401            init_data.as_ref().unwrap()
402        }
403    }
404
405    /// Retrieves the extra info of the function as set in [`TableFunction::set_extra_info`].
406    pub fn get_extra_info<T>(&self) -> *mut T {
407        unsafe { duckdb_function_get_extra_info(self.ptr).cast() }
408    }
409
410    /// Gets the thread-local init data set by [`InitInfo::set_init_data`] during the local_init.
411    ///
412    /// # Arguments
413    /// * `returns`: The init data object
414    pub fn get_local_init_data<T>(&self) -> *mut T {
415        unsafe { duckdb_function_get_local_init_data(self.ptr).cast() }
416    }
417}
418
419impl<V: VTab> From<duckdb_function_info> for TableFunctionInfo<V> {
420    fn from(ptr: duckdb_function_info) -> Self {
421        Self {
422            ptr,
423            _vtab: PhantomData,
424        }
425    }
426}