clt_database/ext/
dynamic.rs1use 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#[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 }
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}