mod aggregate;
mod builtin;
mod index;
mod macros;
mod method;
#[cfg(all(test, feature = "kv-mem"))]
mod parity_tests;
mod projection;
mod registry;
mod signature;
use std::fmt::Debug;
pub use aggregate::{Accumulator, AggregateFunction};
use anyhow::Result;
pub use index::{
IndexContext, IndexContextKind, IndexFunction, KnnContext, MatchInfo, MatchesContext,
};
pub use method::MethodDescriptor;
pub use projection::ProjectionFunction;
pub use registry::FunctionRegistry;
pub(crate) use registry::experimental_target;
pub use signature::Signature;
use crate::exec::BoxFut;
use crate::exec::physical_expr::EvalContext;
use crate::expr::Kind;
use crate::val::Value;
pub(crate) fn check_arity(
name: &str,
args: &[Value],
lower: usize,
upper: Option<usize>,
) -> Result<()> {
if args.len() < lower || upper.map(|x| args.len() > x).unwrap_or(false) {
let message = if let Some(upper) = upper {
if upper == lower {
if upper == 0 {
"Expected no arguments".to_string()
} else if upper == 1 {
"Expected 1 argument".to_string()
} else {
format!("Expected {upper} arguments")
}
} else {
format!("Expected {lower} to {upper} arguments")
}
} else if lower == 0 {
"Expected zero or more arguments".to_string()
} else {
format!("Expected {lower} or more arguments")
};
anyhow::bail!(crate::expr::Error::InvalidFunctionArguments {
name: name.to_owned(),
message,
});
}
Ok(())
}
pub trait ScalarFunction: Send + Sync + Debug {
fn name(&self) -> &'static str;
#[allow(unused)]
fn signature(&self) -> Signature;
#[allow(unused)]
fn return_type(&self, _arg_types: &[Kind]) -> Result<Kind> {
Ok(self.signature().returns)
}
fn required_context(&self) -> crate::exec::ContextLevel {
crate::exec::ContextLevel::Root
}
fn is_pure(&self) -> bool {
true
}
fn is_async(&self) -> bool {
false
}
fn is_deterministic(&self) -> bool {
true
}
fn invoke(&self, _args: Vec<Value>) -> Result<Value> {
Err(anyhow::anyhow!("Function '{}' requires context or async execution", self.name()))
}
#[allow(unused_variables)]
fn invoke_async<'a>(
&'a self,
ctx: &'a EvalContext<'_>,
args: Vec<Value>,
) -> BoxFut<'a, Result<Value>> {
Box::pin(async move { self.invoke(args) })
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct NonDeterministic<F>(pub F);
impl<F: ScalarFunction> ScalarFunction for NonDeterministic<F> {
fn name(&self) -> &'static str {
self.0.name()
}
fn signature(&self) -> Signature {
self.0.signature()
}
fn return_type(&self, arg_types: &[Kind]) -> Result<Kind> {
self.0.return_type(arg_types)
}
fn required_context(&self) -> crate::exec::ContextLevel {
self.0.required_context()
}
fn is_pure(&self) -> bool {
self.0.is_pure()
}
fn is_async(&self) -> bool {
self.0.is_async()
}
fn is_deterministic(&self) -> bool {
false
}
fn invoke(&self, args: Vec<Value>) -> Result<Value> {
self.0.invoke(args)
}
fn invoke_async<'a>(
&'a self,
ctx: &'a EvalContext<'_>,
args: Vec<Value>,
) -> BoxFut<'a, Result<Value>> {
self.0.invoke_async(ctx, args)
}
}