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;
pub(crate) fn builtin_can_write(name: &str) -> bool {
crate::expr::method::is_writer_builtin(name)
}
#[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 let Some(target) = crate::exec::function::experimental_target(&self.name)
&& !ctx.capabilities().allows_experimental(&target)
{
return Err(crate::exec::Error::InvalidFunction {
name: self.name.clone(),
message: format!("Experimental feature {target} is not enabled"),
}
.into());
}
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 builtin_can_write(&self.name) {
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("(...)");
}
}