Skip to main content

clt_database/ext/
dynamic.rs

1use crate::{
2    ext::{
3        register_aggregate_function, register_scalar_function_with_options, register_vtab_module,
4        unregister_function,
5    },
6    Connection, LimboError,
7};
8#[cfg(not(target_family = "wasm"))]
9use libloading::{Library, Symbol};
10use std::{
11    ffi::{c_char, CString},
12    sync::{Arc, Mutex, OnceLock},
13};
14use turso_ext::{ExtensionApi, ExtensionApiRef, ExtensionEntryPoint, ResultCode, VfsImpl};
15
16#[cfg(not(target_family = "wasm"))]
17type ExtensionStore = Vec<(Arc<Library>, ExtensionApiRef)>;
18#[cfg(not(target_family = "wasm"))]
19static EXTENSIONS: OnceLock<Arc<Mutex<ExtensionStore>>> = OnceLock::new();
20#[cfg(not(target_family = "wasm"))]
21pub fn get_extension_libraries() -> Arc<Mutex<ExtensionStore>> {
22    EXTENSIONS
23        .get_or_init(|| Arc::new(Mutex::new(Vec::new())))
24        .clone()
25}
26
27type Vfs = (String, Arc<VfsMod>);
28static VFS_MODULES: OnceLock<Mutex<Vec<Vfs>>> = OnceLock::new();
29
30#[derive(Clone, Debug)]
31pub struct VfsMod {
32    pub ctx: *const VfsImpl,
33}
34
35unsafe impl Send for VfsMod {}
36unsafe impl Sync for VfsMod {}
37crate::assert::assert_send_sync!(VfsMod);
38
39impl Connection {
40    #[cfg(not(target_family = "wasm"))]
41    pub fn load_extension<P: AsRef<std::ffi::OsStr>>(
42        self: &Arc<Connection>,
43        path: P,
44    ) -> crate::Result<()> {
45        use turso_ext::ExtensionApiRef;
46
47        let api = Box::new(unsafe { self._build_turso_ext() });
48        let lib =
49            unsafe { Library::new(path).map_err(|e| LimboError::ExtensionError(e.to_string()))? };
50        let entry: Symbol<ExtensionEntryPoint> = unsafe {
51            lib.get(b"register_extension")
52                .map_err(|e| LimboError::ExtensionError(e.to_string()))?
53        };
54        let api_ptr: *const ExtensionApi = Box::into_raw(api);
55        let api_ref = ExtensionApiRef { api: api_ptr };
56        let result_code = unsafe { entry(api_ptr) };
57        if result_code.is_ok() {
58            let extensions = get_extension_libraries();
59            extensions
60                .lock()
61                .map_err(|_| {
62                    LimboError::ExtensionError("Error locking extension libraries".to_string())
63                })?
64                .push((Arc::new(lib), api_ref));
65            if self.is_db_initialized() {
66                self.reparse_schema_after_extension_load()?;
67            }
68            Ok(())
69        } else {
70            if !api_ptr.is_null() {
71                let _ = unsafe { Box::from_raw(api_ptr.cast_mut()) };
72            }
73            Err(LimboError::ExtensionError(
74                "Extension registration failed".to_string(),
75            ))
76        }
77    }
78}
79
80#[allow(clippy::arc_with_non_send_sync)]
81pub(crate) unsafe extern "C" fn register_vfs(
82    name: *const c_char,
83    vfs: *const VfsImpl,
84) -> ResultCode {
85    if name.is_null() || vfs.is_null() {
86        return ResultCode::Error;
87    }
88    let c_str = unsafe { CString::from_raw(name as *mut _) };
89    let name_str = match c_str.to_str() {
90        Ok(s) => s.to_string(),
91        Err(_) => return ResultCode::Error,
92    };
93    add_vfs_module(name_str, Arc::new(VfsMod { ctx: vfs }));
94    ResultCode::OK
95}
96
97/// Get pointers to all the vfs extensions that need to be built in at compile time.
98/// any other types that are defined in the same extension will not be registered
99/// until the database file is opened and `register_builtins` is called.
100#[cfg(clt_turso_feature = "fs")]
101#[allow(clippy::arc_with_non_send_sync)]
102pub fn add_builtin_vfs_extensions(
103    api: Option<ExtensionApi>,
104) -> crate::Result<Vec<(String, Arc<VfsMod>)>> {
105    use turso_ext::VfsInterface;
106
107    let mut vfslist: Vec<*const VfsImpl> = Vec::new();
108    let mut api = match api {
109        None => ExtensionApi {
110            ctx: std::ptr::null_mut(),
111            register_scalar_function: register_scalar_function_with_options,
112            register_aggregate_function,
113            unregister_function,
114            register_vtab_module,
115            vfs_interface: VfsInterface {
116                register_vfs,
117                builtin_vfs: vfslist.as_mut_ptr(),
118                builtin_vfs_count: 0,
119            },
120        },
121        Some(mut api) => {
122            api.vfs_interface.builtin_vfs = vfslist.as_mut_ptr();
123            api
124        }
125    };
126    register_static_vfs_modules(&mut api);
127    let mut vfslist = Vec::with_capacity(api.vfs_interface.builtin_vfs_count as usize);
128    let slice = unsafe {
129        std::slice::from_raw_parts_mut(
130            api.vfs_interface.builtin_vfs,
131            api.vfs_interface.builtin_vfs_count as usize,
132        )
133    };
134    for vfs in slice {
135        if vfs.is_null() {
136            continue;
137        }
138        let vfsimpl = unsafe { &**vfs };
139        let name = unsafe {
140            CString::from_raw(vfsimpl.name as *mut _)
141                .to_str()
142                .map_err(|_| {
143                    LimboError::ExtensionError("unable to register vfs extension".to_string())
144                })?
145                .to_string()
146        };
147        vfslist.push((
148            name,
149            Arc::new(VfsMod {
150                ctx: vfsimpl as *const _,
151            }),
152        ));
153    }
154    Ok(vfslist)
155}
156
157#[allow(dead_code)]
158#[cfg(clt_turso_feature = "fs")]
159fn register_static_vfs_modules(_api: &mut ExtensionApi) {
160    /* Placeholder for any VFS modules to build in at compile time */
161}
162
163pub fn add_vfs_module(name: String, vfs: Arc<VfsMod>) {
164    let mut modules = VFS_MODULES
165        .get_or_init(|| Mutex::new(Vec::new()))
166        .lock()
167        .unwrap();
168    if !modules.iter().any(|v| v.0 == name) {
169        modules.push((name, vfs));
170    }
171}
172
173pub fn list_vfs_modules() -> Vec<String> {
174    VFS_MODULES
175        .get_or_init(|| Mutex::new(Vec::new()))
176        .lock()
177        .unwrap()
178        .iter()
179        .map(|v| v.0.clone())
180        .collect()
181}
182
183pub fn get_vfs_modules() -> Vec<Vfs> {
184    VFS_MODULES
185        .get_or_init(|| Mutex::new(Vec::new()))
186        .lock()
187        .unwrap()
188        .clone()
189}