Skip to content

Commit ca2333d

Browse files
committed
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
2 parents d1b05ac + 23d9d09 commit ca2333d

File tree

6 files changed

+62
-5
lines changed

6 files changed

+62
-5
lines changed

COMPAT.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ Feature support of [sqlite expr syntax](https://www.sqlite.org/lang_expr.html).
120120
| like(X,Y,Z) | Yes | |
121121
| likelihood(X,Y) | No | |
122122
| likely(X) | No | |
123-
| load_extension(X) | No | |
123+
| load_extension(X) | Yes | sqlite3 extensions not yet supported |
124124
| load_extension(X,Y) | No | |
125125
| lower(X) | Yes | |
126126
| ltrim(X) | Yes | |

cli/app.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,10 @@ impl Limbo {
325325

326326
#[cfg(not(target_family = "wasm"))]
327327
fn handle_load_extension(&mut self, path: &str) -> Result<(), String> {
328-
self.conn.load_extension(path).map_err(|e| e.to_string())
328+
let ext_path = limbo_core::resolve_ext_path(path).map_err(|e| e.to_string())?;
329+
self.conn
330+
.load_extension(ext_path)
331+
.map_err(|e| e.to_string())
329332
}
330333

331334
fn display_in_memory(&mut self) -> std::io::Result<()> {

core/function.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ pub enum ScalarFunc {
136136
ZeroBlob,
137137
LastInsertRowid,
138138
Replace,
139+
#[cfg(not(target_family = "wasm"))]
140+
LoadExtension,
139141
}
140142

141143
impl Display for ScalarFunc {
@@ -185,6 +187,8 @@ impl Display for ScalarFunc {
185187
Self::LastInsertRowid => "last_insert_rowid".to_string(),
186188
Self::Replace => "replace".to_string(),
187189
Self::DateTime => "datetime".to_string(),
190+
#[cfg(not(target_family = "wasm"))]
191+
Self::LoadExtension => "load_extension".to_string(),
188192
};
189193
write!(f, "{}", str)
190194
}
@@ -426,6 +430,8 @@ impl Func {
426430
"tan" => Ok(Self::Math(MathFunc::Tan)),
427431
"tanh" => Ok(Self::Math(MathFunc::Tanh)),
428432
"trunc" => Ok(Self::Math(MathFunc::Trunc)),
433+
#[cfg(not(target_family = "wasm"))]
434+
"load_extension" => Ok(Self::Scalar(ScalarFunc::LoadExtension)),
429435
_ => Err(()),
430436
}
431437
}

core/lib.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ impl Database {
182182
}
183183

184184
#[cfg(not(target_family = "wasm"))]
185-
pub fn load_extension(&self, path: &str) -> Result<()> {
185+
pub fn load_extension<P: AsRef<std::ffi::OsStr>>(&self, path: P) -> Result<()> {
186186
let api = Box::new(self.build_limbo_extension());
187187
let lib =
188188
unsafe { Library::new(path).map_err(|e| LimboError::ExtensionError(e.to_string()))? };
@@ -401,7 +401,7 @@ impl Connection {
401401
}
402402

403403
#[cfg(not(target_family = "wasm"))]
404-
pub fn load_extension(&self, path: &str) -> Result<()> {
404+
pub fn load_extension<P: AsRef<std::ffi::OsStr>>(&self, path: P) -> Result<()> {
405405
Database::load_extension(self.db.as_ref(), path)
406406
}
407407

@@ -515,6 +515,33 @@ impl std::fmt::Debug for SymbolTable {
515515
}
516516
}
517517

518+
fn is_shared_library(path: &std::path::Path) -> bool {
519+
path.extension()
520+
.map_or(false, |ext| ext == "so" || ext == "dylib" || ext == "dll")
521+
}
522+
523+
pub fn resolve_ext_path(extpath: &str) -> Result<std::path::PathBuf> {
524+
let path = std::path::Path::new(extpath);
525+
if !path.exists() {
526+
if is_shared_library(path) {
527+
return Err(LimboError::ExtensionError(format!(
528+
"Extension file not found: {}",
529+
extpath
530+
)));
531+
};
532+
let maybe = path.with_extension(std::env::consts::DLL_EXTENSION);
533+
maybe
534+
.exists()
535+
.then_some(maybe)
536+
.ok_or(LimboError::ExtensionError(format!(
537+
"Extension file not found: {}",
538+
extpath
539+
)))
540+
} else {
541+
Ok(path.to_path_buf())
542+
}
543+
}
544+
518545
impl SymbolTable {
519546
pub fn new() -> Self {
520547
Self {

core/translate/expr.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1092,6 +1092,19 @@ pub fn translate_expr(
10921092
});
10931093
Ok(target_register)
10941094
}
1095+
#[cfg(not(target_family = "wasm"))]
1096+
ScalarFunc::LoadExtension => {
1097+
let args = expect_arguments_exact!(args, 1, srf);
1098+
let reg =
1099+
translate_and_mark(program, referenced_tables, &args[0], resolver)?;
1100+
program.emit_insn(Insn::Function {
1101+
constant_mask: 0,
1102+
start_reg: reg,
1103+
dest: target_register,
1104+
func: func_ctx,
1105+
});
1106+
Ok(target_register)
1107+
}
10951108
ScalarFunc::Random => {
10961109
if args.is_some() {
10971110
crate::bail_parse_error!(

core/vdbe/mod.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ use crate::{
4141
json::json_arrow_extract, json::json_arrow_shift_extract, json::json_error_position,
4242
json::json_extract, json::json_type,
4343
};
44-
use crate::{Connection, Result, Rows, TransactionState, DATABASE_VERSION};
44+
use crate::{resolve_ext_path, Connection, Result, Rows, TransactionState, DATABASE_VERSION};
4545
use datetime::{exec_date, exec_datetime_full, exec_julianday, exec_time, exec_unixepoch};
4646
use insn::{
4747
exec_add, exec_bit_and, exec_bit_not, exec_bit_or, exec_divide, exec_multiply, exec_remainder,
@@ -1863,6 +1863,14 @@ impl Program {
18631863
let replacement = &state.registers[*start_reg + 2];
18641864
state.registers[*dest] = exec_replace(source, pattern, replacement);
18651865
}
1866+
#[cfg(not(target_family = "wasm"))]
1867+
ScalarFunc::LoadExtension => {
1868+
let extension = &state.registers[*start_reg];
1869+
let ext = resolve_ext_path(&extension.to_string())?;
1870+
if let Some(conn) = self.connection.upgrade() {
1871+
conn.load_extension(ext)?;
1872+
}
1873+
}
18661874
},
18671875
crate::function::Func::External(f) => {
18681876
call_external_function! {f.func, *dest, state, arg_count, *start_reg };

0 commit comments

Comments
 (0)