linkrs_query/executor/expression/functions/udf/plugin.rs
1//! UDF plugin interface.
2//!
3//! A dynamically loaded library must export `udf_create` / `udf_destroy`
4//! and may optionally export `udf_abi_version` for compatibility checks.
5
6use crate::executor::expression::{ExpressionError, ExpressionErrorType};
7use linkrs_core::Value;
8
9/// Factory function type exported by a UDF dynamic library.
10#[allow(improper_ctypes_definitions)]
11pub type UdfCreateFn = unsafe extern "C" fn() -> *mut dyn UdfPlugin;
12
13/// Destructor function type exported by a UDF dynamic library.
14#[allow(improper_ctypes_definitions)]
15pub type UdfDestroyFn = unsafe extern "C" fn(*mut dyn UdfPlugin);
16
17/// Optional ABI version probe exported by a UDF dynamic library.
18pub type UdfAbiVersionFn = unsafe extern "C" fn() -> u32;
19
20/// Symbol name of the plugin factory function.
21pub const UDF_CREATE_SYMBOL: &str = "udf_create";
22/// Symbol name of the plugin destructor function.
23pub const UDF_DESTROY_SYMBOL: &str = "udf_destroy";
24/// Symbol name of the optional ABI version probe.
25pub const UDF_ABI_VERSION_SYMBOL: &str = "udf_abi_version";
26
27/// ABI version implemented by the host. Plugins reporting a different
28/// version through `udf_abi_version` are rejected at load time.
29pub const UDF_ABI_VERSION: u32 = 1;
30
31/// Plugin interface implemented by user defined functions.
32///
33/// The trait mirrors the metadata required by `CustomFunction` so a plugin
34/// can be registered without additional glue code. Only argument values and
35/// the return value cross the library boundary; plugins never observe
36/// database internals.
37pub trait UdfPlugin: Send + Sync {
38 /// Function name used for registration (case-insensitive).
39 fn name(&self) -> &str;
40
41 /// Human readable description.
42 fn description(&self) -> &str;
43
44 /// Minimum number of accepted arguments.
45 fn min_arity(&self) -> usize;
46
47 /// Maximum number of accepted arguments.
48 fn max_arity(&self) -> usize;
49
50 /// Whether the function is deterministic (same input always yields same output).
51 fn is_pure(&self) -> bool;
52
53 /// Execute the function.
54 fn execute(&self, args: &[Value]) -> Result<Value, ExpressionError>;
55}
56
57/// Shared ownership handle used to register a plugin as a custom function.
58pub type SharedPlugin = std::sync::Arc<dyn UdfPlugin>;
59
60/// Validate the argument count against the plugin-declared range.
61pub fn check_arity(plugin: &dyn UdfPlugin, args: &[Value]) -> Result<(), ExpressionError> {
62 if args.len() < plugin.min_arity() || args.len() > plugin.max_arity() {
63 return Err(ExpressionError::new(
64 ExpressionErrorType::InvalidArgumentCount,
65 format!(
66 "function '{}' expects {}..={} argument(s), got {}",
67 plugin.name(),
68 plugin.min_arity(),
69 plugin.max_arity(),
70 args.len(),
71 ),
72 ));
73 }
74 Ok(())
75}