linkrs_query/executor/expression/functions/udf/
dynamic.rs1use super::error::UdfError;
8use super::plugin::{check_arity, UdfDestroyFn, UdfPlugin};
9use crate::executor::expression::{ExpressionError, ExpressionErrorType};
10use libloading::Library;
11use linkrs_core::Value;
12use std::panic::{catch_unwind, AssertUnwindSafe};
13use std::sync::Arc;
14
15pub struct DynamicPlugin {
17 raw: *mut dyn UdfPlugin,
18 destroy: UdfDestroyFn,
19 _lib: Arc<Library>,
21 path: String,
23}
24
25unsafe impl Send for DynamicPlugin {}
28unsafe impl Sync for DynamicPlugin {}
29
30impl DynamicPlugin {
31 pub unsafe fn from_raw(
38 raw: *mut dyn UdfPlugin,
39 destroy: UdfDestroyFn,
40 lib: Arc<Library>,
41 path: String,
42 ) -> Result<Self, UdfError> {
43 if raw.is_null() {
44 return Err(UdfError::NullPlugin(path));
45 }
46 Ok(Self {
47 raw,
48 destroy,
49 _lib: lib,
50 path,
51 })
52 }
53
54 fn inner(&self) -> &dyn UdfPlugin {
55 unsafe { &*self.raw }
56 }
57}
58
59impl Drop for DynamicPlugin {
60 fn drop(&mut self) {
61 unsafe {
62 (self.destroy)(self.raw);
63 }
64 }
65}
66
67impl std::fmt::Debug for DynamicPlugin {
68 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69 f.debug_struct("DynamicPlugin")
70 .field("name", &self.inner().name())
71 .field("path", &self.path)
72 .finish()
73 }
74}
75
76impl UdfPlugin for DynamicPlugin {
77 fn name(&self) -> &str {
78 self.inner().name()
79 }
80
81 fn description(&self) -> &str {
82 self.inner().description()
83 }
84
85 fn min_arity(&self) -> usize {
86 self.inner().min_arity()
87 }
88
89 fn max_arity(&self) -> usize {
90 self.inner().max_arity()
91 }
92
93 fn is_pure(&self) -> bool {
94 self.inner().is_pure()
95 }
96
97 fn execute(&self, args: &[Value]) -> Result<Value, ExpressionError> {
98 check_arity(self.inner(), args)?;
99 let name = self.inner().name().to_string();
100 match catch_unwind(AssertUnwindSafe(|| self.inner().execute(args))) {
101 Ok(result) => result,
102 Err(payload) => {
103 let detail = payload
104 .downcast_ref::<String>()
105 .cloned()
106 .or_else(|| payload.downcast_ref::<&str>().map(|s| s.to_string()))
107 .unwrap_or_else(|| "unknown panic".to_string());
108 Err(ExpressionError::new(
109 ExpressionErrorType::FunctionExecutionError,
110 UdfError::ExecutionPanic(name, detail).to_string(),
111 ))
112 }
113 }
114 }
115}
116
117pub fn execute_isolated(plugin: &dyn UdfPlugin, args: &[Value]) -> Result<Value, ExpressionError> {
122 check_arity(plugin, args)?;
123 let name = plugin.name().to_string();
124 match catch_unwind(AssertUnwindSafe(|| plugin.execute(args))) {
125 Ok(result) => result,
126 Err(payload) => {
127 let detail = payload
128 .downcast_ref::<String>()
129 .cloned()
130 .or_else(|| payload.downcast_ref::<&str>().map(|s| s.to_string()))
131 .unwrap_or_else(|| "unknown panic".to_string());
132 Err(ExpressionError::new(
133 ExpressionErrorType::FunctionExecutionError,
134 UdfError::ExecutionPanic(name, detail).to_string(),
135 ))
136 }
137 }
138}