Fold a function call by recursively folding its arguments, dispatching through the shared scalar evaluator, and converting the result back to `SqlValue`. Only folds functions that are present in `registry`, so callers can distinguish "unknown function" from "known function, all args folded".
(
name: &str,
args: &[SqlExpr],
registry: &FunctionRegistry,
)
| 212 | /// callers can distinguish "unknown function" from "known function, all |
| 213 | /// args folded". |
| 214 | pub fn fold_function_call( |
| 215 | name: &str, |
| 216 | args: &[SqlExpr], |
| 217 | registry: &FunctionRegistry, |
| 218 | ) -> Option<SqlValue> { |
| 219 | // Gate on registry so unknown-function paths keep their existing |
| 220 | // fallbacks instead of collapsing to SqlValue::Null. Aggregates and |
| 221 | // window functions aren't foldable — they need a row stream. |
| 222 | let meta = registry.lookup(name)?; |
| 223 | if matches!( |
| 224 | meta.category, |
| 225 | FunctionCategory::Aggregate | FunctionCategory::Window |
| 226 | ) { |
| 227 | return None; |
| 228 | } |
| 229 | |
| 230 | let folded_args: Vec<Value> = args |
| 231 | .iter() |
| 232 | .map(|a| fold_constant(a, registry).map(sql_to_ndb_value)) |
| 233 | .collect::<Option<_>>()?; |
| 234 | |
| 235 | let result = nodedb_query::functions::eval_function(&name.to_lowercase(), &folded_args); |
| 236 | Some(ndb_to_sql_value(result)) |
| 237 | } |
| 238 | |
| 239 | fn sql_to_ndb_value(v: SqlValue) -> Value { |
| 240 | match v { |
no test coverage detected