alopex_sql/executor/evaluator/
registry.rs1use std::collections::HashMap;
4use std::sync::OnceLock;
5
6use crate::executor::{ExecutorError, Result};
7use crate::planner::typed_expr::TypedExpr;
8use crate::scalar::{self, ScalarSignature};
9use crate::storage::SqlValue;
10
11use super::{EvalContext, conditional, hash, numeric, string, type_fn};
12
13pub type EvalFn = fn(&[SqlValue]) -> Result<SqlValue>;
14pub type LazyEvalFn = fn(&[TypedExpr], &EvalContext<'_>) -> Result<SqlValue>;
15
16pub struct ScalarFunction {
17 pub signature: &'static ScalarSignature,
18 pub eval: EvalFn,
19 pub eval_lazy: Option<LazyEvalFn>,
20}
21
22pub struct ScalarFunctionRegistry {
23 fns: HashMap<&'static str, ScalarFunction>,
24}
25
26impl ScalarFunctionRegistry {
27 fn build() -> Self {
28 let mut fns = HashMap::new();
29 for signature in scalar::signatures() {
30 let (eval, eval_lazy) = evaluator_for(signature.name);
31 fns.insert(
32 signature.name,
33 ScalarFunction {
34 signature,
35 eval,
36 eval_lazy,
37 },
38 );
39 }
40 Self { fns }
41 }
42
43 pub fn get(&self, name: &str) -> Option<&ScalarFunction> {
44 let lower = name.to_ascii_lowercase();
45 self.fns.get(lower.as_str())
46 }
47
48 pub fn contains(&self, name: &str) -> bool {
49 self.get(name).is_some()
50 }
51
52 pub fn names(&self) -> impl Iterator<Item = &'static str> + '_ {
53 self.fns.keys().copied()
54 }
55}
56
57pub fn scalar_registry() -> &'static ScalarFunctionRegistry {
58 static REGISTRY: OnceLock<ScalarFunctionRegistry> = OnceLock::new();
59 REGISTRY.get_or_init(ScalarFunctionRegistry::build)
60}
61
62fn unsupported(values: &[SqlValue]) -> Result<SqlValue> {
63 Err(ExecutorError::Evaluation(
64 crate::executor::EvaluationError::UnsupportedFunction(format!(
65 "unregistered scalar with {} argument(s)",
66 values.len()
67 )),
68 ))
69}
70
71fn system_placeholder(_values: &[SqlValue]) -> Result<SqlValue> {
72 Ok(SqlValue::Null)
75}
76
77fn evaluator_for(name: &str) -> (EvalFn, Option<LazyEvalFn>) {
78 match name {
79 "memory_stats" | "io_stats" | "clear_cache" => (system_placeholder, None),
80 "vector_similarity" => (super::function_call::eval_vector_similarity_values, None),
81 "vector_distance" => (super::function_call::eval_vector_distance_values, None),
82 "vector_dims" => (super::function_call::eval_vector_dims_values, None),
83 "vector_norm" => (super::function_call::eval_vector_norm_values, None),
84 _ => {
85 if let Some(eval) = numeric::eval_for(name) {
86 (eval, None)
87 } else if let Some(eval) = string::eval_for(name) {
88 (eval, None)
89 } else if let Some(eval) = conditional::eval_for(name) {
90 (eval, conditional::lazy_eval_for(name))
91 } else if let Some(eval) = type_fn::eval_for(name) {
92 (eval, None)
93 } else if let Some(eval) = hash::eval_for(name) {
94 (eval, None)
95 } else {
96 (unsupported, None)
97 }
98 }
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105 use std::collections::HashSet;
106
107 #[test]
108 fn registry_keys_match_signatures() {
109 let signatures: HashSet<_> = scalar::signatures().iter().map(|s| s.name).collect();
110 let registry: HashSet<_> = scalar_registry().names().collect();
111 assert_eq!(signatures, registry);
112 }
113}