mirror of
https://github.com/aljazceru/turso.git
synced 2026-02-08 09:44:21 +01:00
Merge 'Add load_extension function, resolve shared lib extensions' from Preston Thorpe
This PR adds the `load_extension` function, and allows for platform
agnostic arguments: e.g. `select
load_extension('target/debug/liblimbo_uuid');` omitting the file
extension.
Closes #680
This commit is contained in:
@@ -120,7 +120,7 @@ Feature support of [sqlite expr syntax](https://www.sqlite.org/lang_expr.html).
|
||||
| like(X,Y,Z) | Yes | |
|
||||
| likelihood(X,Y) | No | |
|
||||
| likely(X) | No | |
|
||||
| load_extension(X) | No | |
|
||||
| load_extension(X) | Yes | sqlite3 extensions not yet supported |
|
||||
| load_extension(X,Y) | No | |
|
||||
| lower(X) | Yes | |
|
||||
| ltrim(X) | Yes | |
|
||||
|
||||
@@ -325,7 +325,10 @@ impl Limbo {
|
||||
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
fn handle_load_extension(&mut self, path: &str) -> Result<(), String> {
|
||||
self.conn.load_extension(path).map_err(|e| e.to_string())
|
||||
let ext_path = limbo_core::resolve_ext_path(path).map_err(|e| e.to_string())?;
|
||||
self.conn
|
||||
.load_extension(ext_path)
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
fn display_in_memory(&mut self) -> std::io::Result<()> {
|
||||
|
||||
@@ -136,6 +136,8 @@ pub enum ScalarFunc {
|
||||
ZeroBlob,
|
||||
LastInsertRowid,
|
||||
Replace,
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
LoadExtension,
|
||||
}
|
||||
|
||||
impl Display for ScalarFunc {
|
||||
@@ -185,6 +187,8 @@ impl Display for ScalarFunc {
|
||||
Self::LastInsertRowid => "last_insert_rowid".to_string(),
|
||||
Self::Replace => "replace".to_string(),
|
||||
Self::DateTime => "datetime".to_string(),
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
Self::LoadExtension => "load_extension".to_string(),
|
||||
};
|
||||
write!(f, "{}", str)
|
||||
}
|
||||
@@ -426,6 +430,8 @@ impl Func {
|
||||
"tan" => Ok(Self::Math(MathFunc::Tan)),
|
||||
"tanh" => Ok(Self::Math(MathFunc::Tanh)),
|
||||
"trunc" => Ok(Self::Math(MathFunc::Trunc)),
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
"load_extension" => Ok(Self::Scalar(ScalarFunc::LoadExtension)),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
31
core/lib.rs
31
core/lib.rs
@@ -182,7 +182,7 @@ impl Database {
|
||||
}
|
||||
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
pub fn load_extension(&self, path: &str) -> Result<()> {
|
||||
pub fn load_extension<P: AsRef<std::ffi::OsStr>>(&self, path: P) -> Result<()> {
|
||||
let api = Box::new(self.build_limbo_extension());
|
||||
let lib =
|
||||
unsafe { Library::new(path).map_err(|e| LimboError::ExtensionError(e.to_string()))? };
|
||||
@@ -401,7 +401,7 @@ impl Connection {
|
||||
}
|
||||
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
pub fn load_extension(&self, path: &str) -> Result<()> {
|
||||
pub fn load_extension<P: AsRef<std::ffi::OsStr>>(&self, path: P) -> Result<()> {
|
||||
Database::load_extension(self.db.as_ref(), path)
|
||||
}
|
||||
|
||||
@@ -515,6 +515,33 @@ impl std::fmt::Debug for SymbolTable {
|
||||
}
|
||||
}
|
||||
|
||||
fn is_shared_library(path: &std::path::Path) -> bool {
|
||||
path.extension()
|
||||
.map_or(false, |ext| ext == "so" || ext == "dylib" || ext == "dll")
|
||||
}
|
||||
|
||||
pub fn resolve_ext_path(extpath: &str) -> Result<std::path::PathBuf> {
|
||||
let path = std::path::Path::new(extpath);
|
||||
if !path.exists() {
|
||||
if is_shared_library(path) {
|
||||
return Err(LimboError::ExtensionError(format!(
|
||||
"Extension file not found: {}",
|
||||
extpath
|
||||
)));
|
||||
};
|
||||
let maybe = path.with_extension(std::env::consts::DLL_EXTENSION);
|
||||
maybe
|
||||
.exists()
|
||||
.then_some(maybe)
|
||||
.ok_or(LimboError::ExtensionError(format!(
|
||||
"Extension file not found: {}",
|
||||
extpath
|
||||
)))
|
||||
} else {
|
||||
Ok(path.to_path_buf())
|
||||
}
|
||||
}
|
||||
|
||||
impl SymbolTable {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
|
||||
@@ -1092,6 +1092,19 @@ pub fn translate_expr(
|
||||
});
|
||||
Ok(target_register)
|
||||
}
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
ScalarFunc::LoadExtension => {
|
||||
let args = expect_arguments_exact!(args, 1, srf);
|
||||
let reg =
|
||||
translate_and_mark(program, referenced_tables, &args[0], resolver)?;
|
||||
program.emit_insn(Insn::Function {
|
||||
constant_mask: 0,
|
||||
start_reg: reg,
|
||||
dest: target_register,
|
||||
func: func_ctx,
|
||||
});
|
||||
Ok(target_register)
|
||||
}
|
||||
ScalarFunc::Random => {
|
||||
if args.is_some() {
|
||||
crate::bail_parse_error!(
|
||||
|
||||
@@ -41,7 +41,7 @@ use crate::{
|
||||
json::json_arrow_extract, json::json_arrow_shift_extract, json::json_error_position,
|
||||
json::json_extract, json::json_type,
|
||||
};
|
||||
use crate::{Connection, Result, Rows, TransactionState, DATABASE_VERSION};
|
||||
use crate::{resolve_ext_path, Connection, Result, Rows, TransactionState, DATABASE_VERSION};
|
||||
use datetime::{exec_date, exec_datetime_full, exec_julianday, exec_time, exec_unixepoch};
|
||||
use insn::{
|
||||
exec_add, exec_bit_and, exec_bit_not, exec_bit_or, exec_divide, exec_multiply, exec_remainder,
|
||||
@@ -1863,6 +1863,14 @@ impl Program {
|
||||
let replacement = &state.registers[*start_reg + 2];
|
||||
state.registers[*dest] = exec_replace(source, pattern, replacement);
|
||||
}
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
ScalarFunc::LoadExtension => {
|
||||
let extension = &state.registers[*start_reg];
|
||||
let ext = resolve_ext_path(&extension.to_string())?;
|
||||
if let Some(conn) = self.connection.upgrade() {
|
||||
conn.load_extension(ext)?;
|
||||
}
|
||||
}
|
||||
},
|
||||
crate::function::Func::External(f) => {
|
||||
call_external_function! {f.func, *dest, state, arg_count, *start_reg };
|
||||
|
||||
Reference in New Issue
Block a user