1#[cfg(clt_turso_feature = "fs")]
2mod dynamic;
3mod vtab_xconnect;
4use crate::index_method::backing_btree::BackingBtreeIndexMethod;
5#[cfg(all(clt_turso_feature = "fts", not(target_family = "wasm")))]
6use crate::index_method::fts::{FtsIndexMethod, FTS_INDEX_METHOD_NAME};
7use crate::index_method::toy_vector_sparse_ivf::VectorSparseInvertedIndexMethod;
8use crate::index_method::{
9 BACKING_BTREE_INDEX_METHOD_NAME, TOY_VECTOR_SPARSE_IVF_INDEX_METHOD_NAME,
10};
11use crate::schema::{Schema, Table};
12use crate::sync::atomic::{AtomicU64, Ordering};
13use crate::sync::Mutex;
14#[cfg(all(target_os = "linux", clt_turso_feature = "io_uring", not(miri)))]
15use crate::UringIO;
16#[cfg(all(
17 target_os = "windows",
18 clt_turso_feature = "experimental_win_iocp",
19 not(miri)
20))]
21use crate::WindowsIOCP;
22
23use crate::{function::ExternalFunc, Connection, Database};
24use crate::{vtab::VirtualTable, SymbolTable};
25#[cfg(clt_turso_feature = "fs")]
26use crate::{LimboError, IO};
27#[cfg(clt_turso_feature = "fs")]
28pub use dynamic::{add_builtin_vfs_extensions, add_vfs_module, list_vfs_modules, VfsMod};
29use std::{
30 ffi::{c_char, c_void, CStr, CString},
31 sync::Arc,
32};
33use turso_ext::{
34 ContextDestructor, ExtensionApi, InitAggFunction, ResultCode, ScalarFunction, VTabKind,
35 VTabModuleImpl, ValueDestructor,
36};
37pub use turso_ext::{FinalizeFunction, StepFunction, Value as ExtValue, ValueType as ExtValueType};
38pub use vtab_xconnect::{execute, prepare_stmt};
39
40#[repr(C)]
43pub struct ExtensionCtx {
44 syms: *mut SymbolTable,
45 schema: *mut c_void,
46 prepare_context_generation: *const AtomicU64,
49}
50
51pub(crate) unsafe extern "C" fn register_vtab_module(
52 ctx: *mut c_void,
53 name: *const c_char,
54 module: VTabModuleImpl,
55 kind: VTabKind,
56) -> ResultCode {
57 if name.is_null() || ctx.is_null() {
58 return ResultCode::Error;
59 }
60
61 let c_str = unsafe { CString::from_raw(name as *mut c_char) };
62 let name_str = match c_str.to_str() {
63 Ok(s) => s.to_string(),
64 Err(_) => return ResultCode::Error,
65 };
66
67 let ext_ctx = unsafe { &mut *(ctx as *mut ExtensionCtx) };
68 let module = Arc::new(module);
69 let vmodule = VTabImpl {
70 module_kind: kind,
71 implementation: module,
72 };
73
74 unsafe {
75 let syms = &mut *ext_ctx.syms;
76 syms.vtab_modules.insert(name_str.clone(), vmodule.into());
77 if !ext_ctx.prepare_context_generation.is_null() {
78 (*ext_ctx.prepare_context_generation).fetch_add(1, Ordering::Release);
79 }
80
81 if kind == VTabKind::TableValuedFunction {
82 if let Ok(vtab) = VirtualTable::function(&name_str, syms) {
83 let table = Arc::new(Table::Virtual(vtab));
84 let mutex = &*(ext_ctx.schema as *mut Mutex<Arc<Schema>>);
85 let mut guard = mutex.lock();
86 let Ok(schema) = Schema::try_make_mut(&mut guard) else {
87 return ResultCode::Error;
88 };
89 schema.tables.insert(name_str, table);
90 } else {
91 return ResultCode::Error;
92 }
93 }
94 }
95 ResultCode::OK
96}
97
98#[derive(Clone)]
99pub struct VTabImpl {
100 pub module_kind: VTabKind,
101 pub implementation: Arc<VTabModuleImpl>,
102}
103
104pub(crate) unsafe fn register_scalar_function(
105 ctx: *mut c_void,
106 name: *const c_char,
107 func: ScalarFunction,
108) -> ResultCode {
109 unsafe { register_scalar_function_with_options(ctx, name, -1, false, 0, func, None, None) }
110}
111
112pub(crate) unsafe extern "C" fn register_scalar_function_with_options(
113 ctx: *mut c_void,
114 name: *const c_char,
115 argc: i32,
116 deterministic: bool,
117 context: usize,
118 callback: ScalarFunction,
119 context_destructor: Option<ContextDestructor>,
120 value_destructor: Option<ValueDestructor>,
121) -> ResultCode {
122 if ctx.is_null() || name.is_null() || argc < -1 {
123 return ResultCode::InvalidArgs;
124 }
125 let c_str = unsafe { CStr::from_ptr(name) };
126 let name_str = match c_str.to_str() {
127 Ok(s) => crate::util::normalize_ident(s),
128 Err(_) => return ResultCode::InvalidArgs,
129 };
130 let ext_ctx = unsafe { &mut *(ctx as *mut ExtensionCtx) };
131 unsafe {
132 (*ext_ctx.syms).functions.insert(
133 name_str.clone(),
134 Arc::new(ExternalFunc::new_scalar(
135 name_str,
136 argc,
137 deterministic,
138 context,
139 callback,
140 context_destructor,
141 value_destructor,
142 )),
143 );
144 if !ext_ctx.prepare_context_generation.is_null() {
145 (*ext_ctx.prepare_context_generation).fetch_add(1, Ordering::Release);
146 }
147 }
148 ResultCode::OK
149}
150
151pub(crate) unsafe extern "C" fn unregister_function(
152 ctx: *mut c_void,
153 name: *const c_char,
154) -> ResultCode {
155 if ctx.is_null() || name.is_null() {
156 return ResultCode::InvalidArgs;
157 }
158 let c_str = unsafe { CStr::from_ptr(name) };
159 let name_str = match c_str.to_str() {
160 Ok(s) => crate::util::normalize_ident(s),
161 Err(_) => return ResultCode::InvalidArgs,
162 };
163 let ext_ctx = unsafe { &mut *(ctx as *mut ExtensionCtx) };
164 unsafe {
165 if (*ext_ctx.syms).functions.remove(&name_str).is_none() {
166 return ResultCode::NotFound;
167 }
168 if !ext_ctx.prepare_context_generation.is_null() {
169 (*ext_ctx.prepare_context_generation).fetch_add(1, Ordering::Release);
170 }
171 }
172 ResultCode::OK
173}
174
175pub(crate) unsafe extern "C" fn register_aggregate_function(
176 ctx: *mut c_void,
177 name: *const c_char,
178 args: i32,
179 context: usize,
180 init_func: InitAggFunction,
181 step_func: StepFunction,
182 finalize_func: FinalizeFunction,
183 context_destructor: Option<ContextDestructor>,
184 aggregate_destructor: Option<ContextDestructor>,
185 value_destructor: Option<ValueDestructor>,
186) -> ResultCode {
187 if ctx.is_null() || name.is_null() || args < -1 {
188 return ResultCode::InvalidArgs;
189 }
190 let c_str = unsafe { CStr::from_ptr(name) };
191 let name_str = match c_str.to_str() {
192 Ok(s) => crate::util::normalize_ident(s),
193 Err(_) => return ResultCode::InvalidArgs,
194 };
195 let ext_ctx = unsafe { &mut *(ctx as *mut ExtensionCtx) };
196 unsafe {
197 (*ext_ctx.syms).functions.insert(
198 name_str.clone(),
199 Arc::new(ExternalFunc::new_aggregate(
200 name_str,
201 args,
202 context,
203 (init_func, step_func, finalize_func),
204 context_destructor,
205 aggregate_destructor,
206 value_destructor,
207 )),
208 );
209 if !ext_ctx.prepare_context_generation.is_null() {
210 (*ext_ctx.prepare_context_generation).fetch_add(1, Ordering::Release);
211 }
212 }
213 ResultCode::OK
214}
215
216impl Database {
217 #[cfg(clt_turso_feature = "fs")]
218 #[allow(clippy::arc_with_non_send_sync, dead_code)]
219 pub fn open_with_vfs(
220 &self,
221 path: &str,
222 vfs: &str,
223 ) -> crate::Result<(Arc<dyn IO>, Arc<Database>)> {
224 use crate::{MemoryIO, SyscallIO};
225 use dynamic::get_vfs_modules;
226
227 let io: Arc<dyn IO> = match vfs {
228 "memory" => Arc::new(MemoryIO::new()),
229 #[cfg(clt_turso_feature = "io_memory_yield")]
230 "memory_yield" => Arc::new(crate::MemoryYieldIO::new()),
231 "syscall" => Arc::new(SyscallIO::new()?),
232 #[cfg(all(target_os = "linux", clt_turso_feature = "io_uring", not(miri)))]
233 "io_uring" => Arc::new(UringIO::new()?),
234 #[cfg(all(
235 target_os = "windows",
236 clt_turso_feature = "experimental_win_iocp",
237 not(miri)
238 ))]
239 "experimental_win_iocp" => Arc::new(WindowsIOCP::new()?),
240 other => match get_vfs_modules().iter().find(|v| v.0 == vfs) {
241 Some((_, vfs)) => vfs.clone(),
242 None => {
243 return Err(LimboError::InvalidArgument(format!("no such VFS: {other}")));
244 }
245 },
246 };
247 let db = Self::open_file(io.clone(), path)?;
248 Ok((io, db))
249 }
250
251 pub fn register_global_builtin_extensions(&self) -> Result<(), String> {
254 {
255 let mut syms = self.builtin_syms.write();
256 syms.index_methods.insert(
257 TOY_VECTOR_SPARSE_IVF_INDEX_METHOD_NAME.to_string(),
258 Arc::new(VectorSparseInvertedIndexMethod),
259 );
260 syms.index_methods.insert(
261 BACKING_BTREE_INDEX_METHOD_NAME.to_string(),
262 Arc::new(BackingBtreeIndexMethod),
263 );
264 #[cfg(all(clt_turso_feature = "fts", not(target_family = "wasm")))]
265 syms.index_methods
266 .insert(FTS_INDEX_METHOD_NAME.to_string(), Arc::new(FtsIndexMethod));
267 }
268 let syms = self.builtin_syms.data_ptr();
269 let schema_mutex_ptr =
271 &*self.schema as *const Mutex<Arc<Schema>> as *mut Mutex<Arc<Schema>>;
272 let ctx = Box::into_raw(Box::new(ExtensionCtx {
273 syms,
274 schema: schema_mutex_ptr as *mut c_void,
275 prepare_context_generation: std::ptr::null(),
276 }));
277 #[allow(unused)]
278 let mut ext_api = ExtensionApi {
279 ctx: ctx as *mut c_void,
280 register_scalar_function: register_scalar_function_with_options,
281 register_aggregate_function,
282 unregister_function,
283 register_vtab_module,
284 #[cfg(clt_turso_feature = "fs")]
285 vfs_interface: turso_ext::VfsInterface {
286 register_vfs: dynamic::register_vfs,
287 builtin_vfs: std::ptr::null_mut(),
288 builtin_vfs_count: 0,
289 },
290 };
291
292 #[cfg(clt_turso_feature = "uuid")]
293 crate::uuid::register_extension(&mut ext_api);
294 #[cfg(clt_turso_feature = "series")]
295 crate::series::register_extension(&mut ext_api);
296 #[cfg(clt_turso_feature = "time")]
297 crate::time::register_extension(&mut ext_api);
298 #[cfg(clt_turso_feature = "percentile")]
299 crate::percentile::register_extension(&mut ext_api);
300 crate::regexp::register_extension(&mut ext_api);
301 #[cfg(clt_turso_feature = "fs")]
302 {
303 let vfslist = add_builtin_vfs_extensions(Some(ext_api)).map_err(|e| e.to_string())?;
304 for (name, vfs) in vfslist {
305 add_vfs_module(name, vfs);
306 }
307 }
308 let _ = unsafe { Box::from_raw(ctx) };
309 Ok(())
310 }
311}
312
313impl Connection {
314 pub unsafe fn _build_turso_ext(&self) -> ExtensionApi {
332 let schema_mutex_ptr =
333 &*self.db.schema as *const Mutex<Arc<Schema>> as *mut Mutex<Arc<Schema>>;
334 let ctx = ExtensionCtx {
335 syms: self.syms.data_ptr(),
336 schema: schema_mutex_ptr as *mut c_void,
337 prepare_context_generation: &self.prepare_context_generation as *const _,
338 };
339 let ctx = Box::into_raw(Box::new(ctx)) as *mut c_void;
340 ExtensionApi {
341 ctx,
342 register_scalar_function: register_scalar_function_with_options,
343 register_aggregate_function,
344 unregister_function,
345 register_vtab_module,
346 #[cfg(clt_turso_feature = "fs")]
347 vfs_interface: turso_ext::VfsInterface {
348 register_vfs: dynamic::register_vfs,
349 builtin_vfs: std::ptr::null_mut(),
350 builtin_vfs_count: 0,
351 },
352 }
353 }
354
355 pub unsafe fn _free_extension_ctx(&self, api: ExtensionApi) {
359 if api.ctx.is_null() {
360 return;
361 }
362 let _ = unsafe { Box::from_raw(api.ctx as *mut ExtensionCtx) };
363 }
364}