From 3d188eba0fc9d386fe41fb063f9e7637e5d55617 Mon Sep 17 00:00:00 2001 From: PThorpe92 Date: Sat, 18 Jan 2025 22:35:29 -0500 Subject: [PATCH] Enable staticly linking with builtin extensions --- Cargo.lock | 2 ++ core/Cargo.toml | 10 ++++++--- core/ext/mod.rs | 27 +++++++++++++++++++++++ core/lib.rs | 4 ++++ extensions/percentile/Cargo.toml | 5 ++++- extensions/uuid/Cargo.toml | 4 +++- extensions/uuid/src/lib.rs | 10 ++++++++- macros/src/args.rs | 37 +++++++++++++++++++++----------- macros/src/lib.rs | 21 ++++++++++++++---- 9 files changed, 98 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 540fb77d2..f2f9a47c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1270,6 +1270,8 @@ dependencies = [ "libloading", "limbo_ext", "limbo_macros", + "limbo_percentile", + "limbo_uuid", "log", "miette", "mimalloc", diff --git a/core/Cargo.toml b/core/Cargo.toml index c378d8bcc..0d8d6d0e2 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -14,15 +14,17 @@ name = "limbo_core" path = "lib.rs" [features] -default = ["fs", "json", "uuid", "io_uring"] +default = ["fs", "json", "uuid", "io_uring", "percentile", "static"] fs = [] json = [ "dep:jsonb", "dep:pest", "dep:pest_derive", ] -uuid = ["dep:uuid"] +uuid = ["dep:uuid", "limbo_uuid"] io_uring = ["dep:io-uring", "rustix/io_uring"] +percentile = ["limbo_percentile"] +static = ["limbo_uuid/static", "limbo_percentile/static"] [target.'cfg(target_os = "linux")'.dependencies] io-uring = { version = "0.6.1", optional = true } @@ -33,6 +35,7 @@ rustix = "0.38.34" [target.'cfg(not(target_family = "wasm"))'.dependencies] mimalloc = { version = "*", default-features = false } +libloading = "0.8.6" [dependencies] limbo_ext = { path = "../extensions/core" } @@ -58,8 +61,9 @@ rand = "0.8.5" bumpalo = { version = "3.16.0", features = ["collections", "boxed"] } limbo_macros = { path = "../macros" } uuid = { version = "1.11.0", features = ["v4", "v7"], optional = true } +limbo_uuid = { path = "../extensions/uuid", optional = true, features = ["static"] } +limbo_percentile = { path = "../extensions/percentile", optional = true, features = ["static"] } miette = "7.4.0" -libloading = "0.8.6" [target.'cfg(not(target_family = "windows"))'.dev-dependencies] pprof = { version = "0.14.0", features = ["criterion", "flamegraph"] } diff --git a/core/ext/mod.rs b/core/ext/mod.rs index cf3fa6109..6dca7eb58 100644 --- a/core/ext/mod.rs +++ b/core/ext/mod.rs @@ -73,4 +73,31 @@ impl Database { register_aggregate_function, } } + + pub fn register_builtins(&self) -> Result<(), String> { + #[cfg(feature = "uuid")] + self.register_uuid()?; + #[cfg(feature = "percentile")] + self.register_percentile()?; + Ok(()) + } + + #[cfg(feature = "uuid")] + pub fn register_uuid(&self) -> Result<(), String> { + let ext_api = Box::new(self.build_limbo_ext()); + if unsafe { !::limbo_uuid::register_extension_static(&ext_api).is_ok() } { + return Err("Failed to register uuid extension".to_string()); + } + Ok(()) + } + + #[cfg(feature = "percentile")] + pub fn register_percentile(&self) -> Result<(), String> { + let ext_api = self.build_limbo_ext(); + let res = unsafe { ::limbo_percentile::register_extension(&ext_api) }; + if !res.is_ok() { + return Err("Failed to register percentile extension".to_string()); + } + Ok(()) + } } diff --git a/core/lib.rs b/core/lib.rs index f093762fb..977064b0b 100644 --- a/core/lib.rs +++ b/core/lib.rs @@ -1,4 +1,5 @@ mod error; +#[cfg(not(target_family = "wasm"))] mod ext; mod function; mod io; @@ -138,6 +139,9 @@ impl Database { _shared_wal: shared_wal.clone(), syms, }; + if let Err(e) = db.register_builtins() { + return Err(LimboError::ExtensionError(e)); + } let db = Arc::new(db); let conn = Rc::new(Connection { db: db.clone(), diff --git a/extensions/percentile/Cargo.toml b/extensions/percentile/Cargo.toml index 91340b813..55bd89b48 100644 --- a/extensions/percentile/Cargo.toml +++ b/extensions/percentile/Cargo.toml @@ -7,7 +7,10 @@ license.workspace = true repository.workspace = true [lib] -crate-type = ["cdylib"] +crate-type = ["cdylib", "lib"] + +[features] +static = [] [dependencies] limbo_ext = { path = "../core" } diff --git a/extensions/uuid/Cargo.toml b/extensions/uuid/Cargo.toml index 2b5a80a1c..764a75845 100644 --- a/extensions/uuid/Cargo.toml +++ b/extensions/uuid/Cargo.toml @@ -9,8 +9,10 @@ repository.workspace = true [lib] crate-type = ["cdylib", "lib"] +[features] +static= [] [dependencies] -limbo_ext = { path = "../core"} +limbo_ext = { path = "../core" } uuid = { version = "1.11.0", features = ["v4", "v7"] } log = "0.4.20" diff --git a/extensions/uuid/src/lib.rs b/extensions/uuid/src/lib.rs index 670ba8b18..cfcb879e5 100644 --- a/extensions/uuid/src/lib.rs +++ b/extensions/uuid/src/lib.rs @@ -1,7 +1,9 @@ use limbo_ext::{register_extension, scalar, Value, ValueType}; +#[cfg(feature = "static")] register_extension! { - scalars: { uuid4_str, uuid4_blob, uuid7_str, uuid7, uuid7_ts, uuid_str, uuid_blob } + static_build: true, + scalars: {uuid4_str, uuid4_blob, uuid7_str, uuid7, uuid7_ts, uuid_str, uuid_blob }, } #[scalar(name = "uuid4_str", alias = "gen_random_uuid")] @@ -133,3 +135,9 @@ fn uuid_to_unix(uuid: &[u8; 16]) -> u64 { | ((uuid[4] as u64) << 8) | (uuid[5] as u64) } + +#[cfg(not(feature = "static"))] +register_extension! { + static_build: false, + scalars: { uuid4_str, uuid4_blob, uuid7_str, uuid7, uuid7_ts, uuid_str, uuid_blob } +} diff --git a/macros/src/args.rs b/macros/src/args.rs index ec65be8b4..e81453c75 100644 --- a/macros/src/args.rs +++ b/macros/src/args.rs @@ -6,43 +6,56 @@ use syn::{Ident, LitStr, Token}; pub(crate) struct RegisterExtensionInput { pub aggregates: Vec, pub scalars: Vec, + pub is_static: bool, } impl syn::parse::Parse for RegisterExtensionInput { fn parse(input: syn::parse::ParseStream) -> syn::Result { let mut aggregates = Vec::new(); let mut scalars = Vec::new(); + let mut is_static = false; while !input.is_empty() { if input.peek(syn::Ident) && input.peek2(Token![:]) { let section_name: Ident = input.parse()?; input.parse::()?; - let content; - syn::braced!(content in input); - if section_name == "aggregates" { - aggregates = Punctuated::::parse_terminated(&content)? - .into_iter() - .collect(); - } else if section_name == "scalars" { - scalars = Punctuated::::parse_terminated(&content)? + if section_name == "static_build" { + let val: syn::LitBool = input.parse()?; + is_static = val.value; + + if input.peek(Token![,]) { + input.parse::()?; + } + } else if section_name == "aggregates" || section_name == "scalars" { + let content; + syn::braced!(content in input); + + let parsed_items = Punctuated::::parse_terminated(&content)? .into_iter() .collect(); + + if section_name == "aggregates" { + aggregates = parsed_items; + } else { + scalars = parsed_items; + } + + if input.peek(Token![,]) { + input.parse::()?; + } } else { return Err(syn::Error::new(section_name.span(), "Unknown section")); } } else { return Err(input.error("Expected aggregates: or scalars: section")); } - - if input.peek(Token![,]) { - input.parse::()?; - } } Ok(Self { aggregates, scalars, + is_static, }) } } diff --git a/macros/src/lib.rs b/macros/src/lib.rs index ffb1e1524..c8e60ec7d 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -359,10 +359,10 @@ pub fn derive_agg_func(input: TokenStream) -> TokenStream { #[proc_macro] pub fn register_extension(input: TokenStream) -> TokenStream { let input_ast = parse_macro_input!(input as RegisterExtensionInput); - let RegisterExtensionInput { aggregates, scalars, + is_static, } = input_ast; let scalar_calls = scalars.iter().map(|scalar_ident| { @@ -390,15 +390,28 @@ pub fn register_extension(input: TokenStream) -> TokenStream { } }); - let expanded = quote! { - #[no_mangle] - pub extern "C" fn register_extension(api: &::limbo_ext::ExtensionApi) -> ::limbo_ext::ResultCode { + let expanded = if is_static { + quote! { + pub unsafe extern "C" fn register_extension_static(api: &::limbo_ext::ExtensionApi) -> ::limbo_ext::ResultCode { let api = unsafe { &*api }; #(#scalar_calls)* #(#aggregate_calls)* ::limbo_ext::ResultCode::OK + } + } + } else { + quote! { + #[no_mangle] + pub unsafe extern "C" fn register_extension(api: &::limbo_ext::ExtensionApi) -> ::limbo_ext::ResultCode { + let api = unsafe { &*api }; + #(#scalar_calls)* + + #(#aggregate_calls)* + + ::limbo_ext::ResultCode::OK + } } };