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::expr::idiom::Idiom;
use crate::val::Value;
#[derive(Debug, Clone)]
pub struct ProjectionFunctionExec {
pub(crate) name: String,
pub(crate) arguments: Vec<Arc<dyn PhysicalExpr>>,
pub(crate) func_required_context: crate::exec::ContextLevel,
}
impl PhysicalExpr for ProjectionFunctionExec {
fn name(&self) -> &'static str {
"ProjectionFunction"
}
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 {
if let Some(bindings) = self.evaluate_projection(ctx).await? {
if bindings.len() == 1 {
Ok(bindings.into_iter().next().expect("bindings verified non-empty").1)
} else {
Ok(Value::Array(bindings.into_iter().map(|(_, v)| v).collect()))
}
} else {
Ok(Value::None)
}
})
}
fn access_mode(&self) -> AccessMode {
args_access_mode(&self.arguments)
}
fn is_projection_function(&self) -> bool {
true
}
fn evaluate_projection<'a>(
&'a self,
ctx: EvalContext<'a>,
) -> BoxFut<'a, FlowResult<Option<Vec<(Idiom, Value)>>>> {
Box::pin(async move {
let registry = ctx.exec_ctx.function_registry();
let func = registry.get_projection(&self.name).ok_or_else(|| {
anyhow::anyhow!(
"Unknown projection function '{}' - not found in function registry",
self.name
)
})?;
let args = evaluate_args(&self.arguments, ctx.clone()).await?;
let bindings = func.invoke_async(&ctx, args).await?;
Ok(Some(bindings))
})
}
}
impl ToSql for ProjectionFunctionExec {
fn fmt_sql(&self, f: &mut String, _fmt: SqlFormat) {
f.push_str(&self.name);
f.push_str("(...)");
}
}