mirror of
https://github.com/aljazceru/turso.git
synced 2025-12-17 08:34:19 +01:00
267 lines
8.9 KiB
Rust
267 lines
8.9 KiB
Rust
#[cfg(feature = "fs")]
|
|
mod dynamic;
|
|
mod vtab_xconnect;
|
|
use crate::index_method::backing_btree::BackingBtreeIndexMethod;
|
|
use crate::index_method::toy_vector_sparse_ivf::VectorSparseInvertedIndexMethod;
|
|
use crate::index_method::{
|
|
BACKING_BTREE_INDEX_METHOD_NAME, TOY_VECTOR_SPARSE_IVF_INDEX_METHOD_NAME,
|
|
};
|
|
use crate::schema::{Schema, Table};
|
|
#[cfg(all(target_os = "linux", feature = "io_uring", not(miri)))]
|
|
use crate::UringIO;
|
|
use crate::{function::ExternalFunc, Connection, Database};
|
|
use crate::{vtab::VirtualTable, SymbolTable};
|
|
#[cfg(feature = "fs")]
|
|
use crate::{LimboError, IO};
|
|
#[cfg(feature = "fs")]
|
|
pub use dynamic::{add_builtin_vfs_extensions, add_vfs_module, list_vfs_modules, VfsMod};
|
|
use parking_lot::Mutex;
|
|
use std::{
|
|
ffi::{c_char, c_void, CStr, CString},
|
|
sync::Arc,
|
|
};
|
|
use turso_ext::{
|
|
ExtensionApi, InitAggFunction, ResultCode, ScalarFunction, VTabKind, VTabModuleImpl,
|
|
};
|
|
pub use turso_ext::{FinalizeFunction, StepFunction, Value as ExtValue, ValueType as ExtValueType};
|
|
pub use vtab_xconnect::{execute, prepare_stmt};
|
|
|
|
/// The context passed to extensions to register with Core
|
|
/// along with the function pointers
|
|
#[repr(C)]
|
|
pub struct ExtensionCtx {
|
|
syms: *mut SymbolTable,
|
|
schema: *mut c_void,
|
|
}
|
|
|
|
pub(crate) unsafe extern "C" fn register_vtab_module(
|
|
ctx: *mut c_void,
|
|
name: *const c_char,
|
|
module: VTabModuleImpl,
|
|
kind: VTabKind,
|
|
) -> ResultCode {
|
|
if name.is_null() || ctx.is_null() {
|
|
return ResultCode::Error;
|
|
}
|
|
|
|
let c_str = unsafe { CString::from_raw(name as *mut c_char) };
|
|
let name_str = match c_str.to_str() {
|
|
Ok(s) => s.to_string(),
|
|
Err(_) => return ResultCode::Error,
|
|
};
|
|
|
|
let ext_ctx = unsafe { &mut *(ctx as *mut ExtensionCtx) };
|
|
let module = Arc::new(module);
|
|
let vmodule = VTabImpl {
|
|
module_kind: kind,
|
|
implementation: module,
|
|
};
|
|
|
|
unsafe {
|
|
let syms = &mut *ext_ctx.syms;
|
|
syms.vtab_modules.insert(name_str.clone(), vmodule.into());
|
|
|
|
if kind == VTabKind::TableValuedFunction {
|
|
if let Ok(vtab) = VirtualTable::function(&name_str, syms) {
|
|
// Use the schema handler to insert the table
|
|
let table = Arc::new(Table::Virtual(vtab));
|
|
let mutex = &*(ext_ctx.schema as *mut Mutex<Arc<Schema>>);
|
|
let guard = mutex.lock();
|
|
let schema_ptr = Arc::as_ptr(&*guard) as *mut Schema;
|
|
(*schema_ptr).tables.insert(name_str, table);
|
|
} else {
|
|
return ResultCode::Error;
|
|
}
|
|
}
|
|
}
|
|
ResultCode::OK
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct VTabImpl {
|
|
pub module_kind: VTabKind,
|
|
pub implementation: Arc<VTabModuleImpl>,
|
|
}
|
|
|
|
pub(crate) unsafe extern "C" fn register_scalar_function(
|
|
ctx: *mut c_void,
|
|
name: *const c_char,
|
|
func: ScalarFunction,
|
|
) -> ResultCode {
|
|
let c_str = unsafe { CStr::from_ptr(name) };
|
|
let name_str = match c_str.to_str() {
|
|
Ok(s) => s.to_string(),
|
|
Err(_) => return ResultCode::InvalidArgs,
|
|
};
|
|
if ctx.is_null() {
|
|
return ResultCode::Error;
|
|
}
|
|
let ext_ctx = unsafe { &mut *(ctx as *mut ExtensionCtx) };
|
|
unsafe {
|
|
(*ext_ctx.syms).functions.insert(
|
|
name_str.clone(),
|
|
Arc::new(ExternalFunc::new_scalar(name_str, func)),
|
|
);
|
|
}
|
|
ResultCode::OK
|
|
}
|
|
|
|
pub(crate) unsafe extern "C" fn register_aggregate_function(
|
|
ctx: *mut c_void,
|
|
name: *const c_char,
|
|
args: i32,
|
|
init_func: InitAggFunction,
|
|
step_func: StepFunction,
|
|
finalize_func: FinalizeFunction,
|
|
) -> ResultCode {
|
|
let c_str = unsafe { CStr::from_ptr(name) };
|
|
let name_str = match c_str.to_str() {
|
|
Ok(s) => s.to_string(),
|
|
Err(_) => return ResultCode::InvalidArgs,
|
|
};
|
|
if ctx.is_null() {
|
|
return ResultCode::Error;
|
|
}
|
|
let ext_ctx = unsafe { &mut *(ctx as *mut ExtensionCtx) };
|
|
unsafe {
|
|
(*ext_ctx.syms).functions.insert(
|
|
name_str.clone(),
|
|
Arc::new(ExternalFunc::new_aggregate(
|
|
name_str,
|
|
args,
|
|
(init_func, step_func, finalize_func),
|
|
)),
|
|
);
|
|
}
|
|
ResultCode::OK
|
|
}
|
|
|
|
impl Database {
|
|
#[cfg(feature = "fs")]
|
|
#[allow(clippy::arc_with_non_send_sync, dead_code)]
|
|
pub fn open_with_vfs(
|
|
&self,
|
|
path: &str,
|
|
vfs: &str,
|
|
) -> crate::Result<(Arc<dyn IO>, Arc<Database>)> {
|
|
use crate::{MemoryIO, SyscallIO};
|
|
use dynamic::get_vfs_modules;
|
|
|
|
let io: Arc<dyn IO> = match vfs {
|
|
"memory" => Arc::new(MemoryIO::new()),
|
|
"syscall" => Arc::new(SyscallIO::new()?),
|
|
#[cfg(all(target_os = "linux", feature = "io_uring", not(miri)))]
|
|
"io_uring" => Arc::new(UringIO::new()?),
|
|
other => match get_vfs_modules().iter().find(|v| v.0 == vfs) {
|
|
Some((_, vfs)) => vfs.clone(),
|
|
None => {
|
|
return Err(LimboError::InvalidArgument(format!("no such VFS: {other}")));
|
|
}
|
|
},
|
|
};
|
|
let db = Self::open_file(io.clone(), path, false, false)?;
|
|
Ok((io, db))
|
|
}
|
|
|
|
/// Register any built-in extensions that can be stored on the Database so we do not have
|
|
/// to register these once-per-connection, and the connection can just extend its symbol table
|
|
pub fn register_global_builtin_extensions(&self) -> Result<(), String> {
|
|
{
|
|
let mut syms = self.builtin_syms.write();
|
|
syms.index_methods.insert(
|
|
TOY_VECTOR_SPARSE_IVF_INDEX_METHOD_NAME.to_string(),
|
|
Arc::new(VectorSparseInvertedIndexMethod),
|
|
);
|
|
syms.index_methods.insert(
|
|
BACKING_BTREE_INDEX_METHOD_NAME.to_string(),
|
|
Arc::new(BackingBtreeIndexMethod),
|
|
);
|
|
}
|
|
let syms = self.builtin_syms.data_ptr();
|
|
// Pass the mutex pointer and the appropriate handler
|
|
let schema_mutex_ptr = &self.schema as *const Mutex<Arc<Schema>> as *mut Mutex<Arc<Schema>>;
|
|
let ctx = Box::into_raw(Box::new(ExtensionCtx {
|
|
syms,
|
|
schema: schema_mutex_ptr as *mut c_void,
|
|
}));
|
|
#[allow(unused)]
|
|
let mut ext_api = ExtensionApi {
|
|
ctx: ctx as *mut c_void,
|
|
register_scalar_function,
|
|
register_aggregate_function,
|
|
register_vtab_module,
|
|
#[cfg(feature = "fs")]
|
|
vfs_interface: turso_ext::VfsInterface {
|
|
register_vfs: dynamic::register_vfs,
|
|
builtin_vfs: std::ptr::null_mut(),
|
|
builtin_vfs_count: 0,
|
|
},
|
|
};
|
|
|
|
#[cfg(feature = "uuid")]
|
|
crate::uuid::register_extension(&mut ext_api);
|
|
#[cfg(feature = "series")]
|
|
crate::series::register_extension(&mut ext_api);
|
|
#[cfg(feature = "fs")]
|
|
{
|
|
let vfslist = add_builtin_vfs_extensions(Some(ext_api)).map_err(|e| e.to_string())?;
|
|
for (name, vfs) in vfslist {
|
|
add_vfs_module(name, vfs);
|
|
}
|
|
}
|
|
let _ = unsafe { Box::from_raw(ctx) };
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Connection {
|
|
/// Build the connection's extension api context for manually registering an extension.
|
|
/// you probably want to use `Connection::load_extension(path)`.
|
|
///
|
|
/// # Safety
|
|
/// Only to be used when registering a staticly linked extension manually.
|
|
/// You should only ever call this method on your applications startup,
|
|
/// The caller is responsible for calling `_free_extension_ctx` after registering the
|
|
/// extension.
|
|
///
|
|
/// usage:
|
|
/// ```ignore
|
|
/// let ext_api = conn._build_turso_ext();
|
|
/// unsafe {
|
|
/// my_extension::register_extension(&mut ext_api);
|
|
/// conn._free_extension_ctx(ext_api);
|
|
/// }
|
|
///```
|
|
pub unsafe fn _build_turso_ext(&self) -> ExtensionApi {
|
|
let schema_mutex_ptr =
|
|
&self.db.schema as *const Mutex<Arc<Schema>> as *mut Mutex<Arc<Schema>>;
|
|
let ctx = ExtensionCtx {
|
|
syms: self.syms.data_ptr(),
|
|
schema: schema_mutex_ptr as *mut c_void,
|
|
};
|
|
let ctx = Box::into_raw(Box::new(ctx)) as *mut c_void;
|
|
ExtensionApi {
|
|
ctx,
|
|
register_scalar_function,
|
|
register_aggregate_function,
|
|
register_vtab_module,
|
|
#[cfg(feature = "fs")]
|
|
vfs_interface: turso_ext::VfsInterface {
|
|
register_vfs: dynamic::register_vfs,
|
|
builtin_vfs: std::ptr::null_mut(),
|
|
builtin_vfs_count: 0,
|
|
},
|
|
}
|
|
}
|
|
|
|
/// Free the connection's extension libary context after registering an extension manually.
|
|
/// # Safety
|
|
/// Only to be used if you have previously called Connection::build_turso_ext
|
|
pub unsafe fn _free_extension_ctx(&self, api: ExtensionApi) {
|
|
if api.ctx.is_null() {
|
|
return;
|
|
}
|
|
let _ = unsafe { Box::from_raw(api.ctx as *mut ExtensionCtx) };
|
|
}
|
|
}
|