use std::sync::Arc;
use surrealdb_types::{SqlFormat, ToSql};
use super::helpers::{args_access_mode, args_required_context, evaluate_args};
use crate::exec::physical_expr::{EvalContext, PhysicalExpr};
use crate::exec::{AccessMode, BoxFut};
use crate::expr::FlowResult;
use crate::val::Value;
#[derive(Debug, Clone)]
pub struct BuiltinFunctionExec {
pub(crate) name: String,
pub(crate) arguments: Vec<Arc<dyn PhysicalExpr>>,
pub(crate) func_required_context: crate::exec::ContextLevel,
pub(crate) plan_depth: u32,
}
impl PhysicalExpr for BuiltinFunctionExec {
fn name(&self) -> &'static str {
"BuiltinFunction"
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn required_context(&self) -> crate::exec::ContextLevel {
let args_ctx = args_required_context(&self.arguments);
args_ctx.max(self.func_required_context)
}
fn evaluate<'a>(&'a self, ctx: EvalContext<'a>) -> BoxFut<'a, FlowResult<Value>> {
Box::pin(async move {
ctx.check_allowed_function(&self.name)?;
let registry = ctx.exec_ctx.function_registry();
let func = registry.get(&self.name).ok_or_else(|| {
anyhow::anyhow!("Unknown function '{}' - not found in function registry", self.name)
})?;
let args = evaluate_args(&self.arguments, ctx.clone()).await?;
if func.is_pure() && !func.is_async() {
Ok(func.invoke(args)?)
} else {
let mut ctx = ctx;
ctx.plan_depth = self.plan_depth;
Ok(func.invoke_async(&ctx, args).await?)
}
})
}
fn access_mode(&self) -> AccessMode {
let func_mode = if matches!(self.name.as_str(), "api::invoke" | "eval::surql" | "eval::gql")
{
AccessMode::ReadWrite
} else {
AccessMode::ReadOnly
};
func_mode.combine(args_access_mode(&self.arguments))
}
}
impl ToSql for BuiltinFunctionExec {
fn fmt_sql(&self, f: &mut String, _fmt: SqlFormat) {
f.push_str(&self.name);
f.push_str("(...)");
}
}