Skip to main content

sqlite3_ext/vtab/
function.rs

1use super::{
2    super::{
3        ffi,
4        function::{Context, InternalContext},
5        types::*,
6        value::*,
7    },
8    ConstraintOp, VTab,
9};
10use std::{
11    borrow::Cow,
12    cell::{Cell, RefCell},
13    os::raw::c_int,
14    pin::Pin,
15    slice,
16};
17
18type CFunc = unsafe extern "C" fn(*mut ffi::sqlite3_context, c_int, *mut *mut ffi::sqlite3_value);
19
20/// A collection of methods overloaded by a virtual table.
21///
22/// This object is responsible for storing the data associated with overloaded functions. All
23/// functions stored in the list must last for the entire lifetime of the virtual table.
24pub struct VTabFunctionList<'vtab, T: VTab<'vtab>> {
25    list: RefCell<Vec<Pin<Box<VTabFunction<'vtab, T>>>>>,
26}
27
28impl<'vtab, T: VTab<'vtab>> Default for VTabFunctionList<'vtab, T> {
29    fn default() -> Self {
30        Self {
31            list: RefCell::new(Vec::new()),
32        }
33    }
34}
35
36impl<'vtab, T: VTab<'vtab> + 'vtab> VTabFunctionList<'vtab, T> {
37    fn _add(
38        &self,
39        n_args: i32,
40        name: impl Into<Cow<'vtab, str>>,
41        constraint: Option<ConstraintOp>,
42        func: Box<dyn Fn(&'vtab T, &InternalContext, &mut [&mut ValueRef]) + 'vtab>,
43    ) {
44        assert!((-1..128).contains(&n_args), "n_args invalid");
45        if let Some(c) = &constraint {
46            c.assert_valid_function_constraint();
47        }
48        self.list
49            .borrow_mut()
50            .push(VTabFunction::new(n_args, name, constraint, func));
51    }
52
53    /// Add a scalar function to the list.
54    ///
55    /// This method adds a function with the given name and n_args to the list of
56    /// overloaded functions. Note that when looking for applicable overloads, a function
57    /// with the correct n_args value will be selected before a function with n_args of -1.
58    ///
59    /// A constraint may be provided. If it is, then the constraint will be provided as an
60    /// [IndexInfoConstraint](super::index_info::IndexInfoConstraint) to [VTab::best_index].
61    ///
62    /// The function and all closed variables must live for the duration of the virtual
63    /// table.
64    pub fn add<F>(
65        &self,
66        n_args: i32,
67        name: impl Into<Cow<'vtab, str>>,
68        constraint: Option<ConstraintOp>,
69        func: F,
70    ) where
71        F: Fn(&Context, &mut [&mut ValueRef]) -> Result<()> + 'vtab,
72    {
73        let func = wrap_fn(func);
74        self._add(n_args, name, constraint, func);
75    }
76
77    /// Add a method to the list.
78    ///
79    /// This function works similarly to [add](VTabFunctionList::add), except the function
80    /// will receive the virtual table as the first parameter.
81    pub fn add_method<F>(
82        &self,
83        n_args: i32,
84        name: impl Into<Cow<'vtab, str>>,
85        constraint: Option<ConstraintOp>,
86        func: F,
87    ) where
88        F: Fn(&'vtab T, &Context, &mut [&mut ValueRef]) -> Result<()> + 'vtab,
89    {
90        let func = wrap_method(func);
91        self._add(n_args, name, constraint, func);
92    }
93
94    /// Find the best overridden implementation of a function in this list. Prefer a
95    /// precise number of arguments, but fall back to overloads which accept any number of
96    /// arguments.
97    ///
98    /// This method is coupled with bind until `feature(cell_filter_map)` is ready.
99    pub(crate) fn find(
100        &self,
101        vtab: &'vtab T,
102        n_args: i32,
103        name: &str,
104    ) -> Option<((CFunc, *mut ::std::os::raw::c_void), Option<ConstraintOp>)> {
105        let list = self.list.borrow();
106        let found = [n_args, -1]
107            .into_iter()
108            .find_map(|n_args| list.iter().find(|f| f.n_args == n_args && f.name == name));
109        found.map(|r| (r.bind(vtab), r.constraint))
110    }
111}
112
113fn wrap_fn<'vtab, T, F>(
114    func: F,
115) -> Box<dyn Fn(&'vtab T, &InternalContext, &mut [&mut ValueRef]) + 'vtab>
116where
117    T: VTab<'vtab>,
118    F: Fn(&Context, &mut [&mut ValueRef]) -> Result<()> + 'vtab,
119{
120    Box::new(
121        move |_: &T, ic: &InternalContext, a: &mut [&mut ValueRef]| {
122            let ctx = unsafe { Context::from_ptr(ic.as_ptr()) };
123            if let Err(e) = func(ctx, a) {
124                ctx.set_result(e).unwrap();
125            }
126        },
127    )
128}
129
130fn wrap_method<'vtab, T, F>(
131    func: F,
132) -> Box<dyn Fn(&'vtab T, &InternalContext, &mut [&mut ValueRef]) + 'vtab>
133where
134    T: VTab<'vtab>,
135    F: Fn(&'vtab T, &Context, &mut [&mut ValueRef]) -> Result<()> + 'vtab,
136{
137    Box::new(
138        move |t: &T, ic: &InternalContext, a: &mut [&mut ValueRef]| {
139            let ctx = unsafe { Context::from_ptr(ic.as_ptr()) };
140            if let Err(e) = func(t, ctx, a) {
141                ctx.set_result(e).unwrap();
142            }
143        },
144    )
145}
146
147struct VTabFunction<'vtab, T: VTab<'vtab>> {
148    n_args: i32,
149    name: Cow<'vtab, str>,
150    constraint: Option<ConstraintOp>,
151    vtab: Cell<Option<&'vtab T>>,
152    func: Box<dyn Fn(&'vtab T, &InternalContext, &mut [&mut ValueRef]) + 'vtab>,
153}
154
155impl<'vtab, T: VTab<'vtab>> VTabFunction<'vtab, T> {
156    pub fn new(
157        n_args: i32,
158        name: impl Into<Cow<'vtab, str>>,
159        constraint: Option<ConstraintOp>,
160        func: Box<dyn Fn(&'vtab T, &InternalContext, &mut [&mut ValueRef]) + 'vtab>,
161    ) -> Pin<Box<Self>> {
162        Box::pin(Self {
163            n_args,
164            name: name.into(),
165            constraint,
166            vtab: Cell::new(None),
167            func,
168        })
169    }
170
171    pub fn bind(&self, vtab: &'vtab T) -> (CFunc, *mut ::std::os::raw::c_void) {
172        self.vtab.set(Some(vtab));
173        (call_vtab_method::<T>, self as *const Self as *mut Self as _)
174    }
175
176    pub fn invoke(&self, ic: &InternalContext, a: &mut [&mut ValueRef]) {
177        (*self.func)(self.vtab.get().unwrap(), ic, a);
178    }
179}
180
181unsafe extern "C" fn call_vtab_method<'vtab, T>(
182    context: *mut ffi::sqlite3_context,
183    argc: i32,
184    argv: *mut *mut ffi::sqlite3_value,
185) where
186    T: VTab<'vtab> + 'vtab,
187{
188    let ic = InternalContext::from_ptr(context);
189    let vtab_function = ic.user_data::<VTabFunction<'vtab, T>>();
190    let args = slice::from_raw_parts_mut(argv as *mut &mut ValueRef, argc as _);
191    vtab_function.invoke(ic, args);
192}