Skip to main content

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}