Skip to main content

reifydb_evaluate/expression/
call.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use reifydb_core::value::column::{
5	ColumnWithName,
6	buffer::ColumnBuffer,
7	columns::Columns,
8	view::group_by::{GroupId, GroupRows},
9};
10use reifydb_routine_abi::{FunctionKind, context::FunctionContext, error::RoutineError};
11use reifydb_rql::expression::{CallExpression, Expression, name::display_label};
12use reifydb_value::{error::Error, fragment::Fragment, value::value_type::ValueType};
13
14use crate::{Result, error::EvaluateError, expression::context::EvalContext};
15
16pub(crate) fn call_builtin(ctx: &EvalContext, call: &CallExpression, arguments: Columns) -> Result<ColumnWithName> {
17	let function_name = call.func.0.text();
18	let fn_fragment = call.func.0.clone();
19	let result_label = display_label(&Expression::Call(call.clone()));
20
21	assert!(
22		ctx.symbols.get_function(function_name).is_none(),
23		"UDF '{}' should have been hoisted to UdfEvalNode",
24		function_name
25	);
26
27	let routine = ctx.routines.get_function(function_name).ok_or_else(|| -> Error {
28		EvaluateError::UnknownFunction {
29			name: function_name.to_string(),
30			fragment: fn_fragment.clone(),
31		}
32		.into()
33	})?;
34
35	let mut fn_ctx = FunctionContext {
36		fragment: fn_fragment.clone(),
37		identity: ctx.identity,
38		row_count: ctx.row_count,
39		runtime_context: ctx.runtime_context,
40	};
41
42	if ctx.is_aggregate_context && routine.kinds().contains(&FunctionKind::Aggregate) {
43		let mut accumulator =
44			routine.accumulator(&mut fn_ctx).ok_or_else(|| RoutineError::FunctionExecutionFailed {
45				function: fn_fragment.clone(),
46				reason: format!("Function {} is not an aggregate", function_name),
47			})?;
48
49		let column = if call.args.is_empty() {
50			ColumnWithName {
51				name: Fragment::internal("dummy"),
52				data: ColumnBuffer::with_capacity(ValueType::Int4, ctx.row_count),
53			}
54		} else {
55			ColumnWithName::new(arguments.name_at(0).clone(), arguments[0].clone())
56		};
57
58		let all_rows: GroupRows = vec![(GroupId(0), (0..ctx.row_count).collect())];
59
60		accumulator
61			.update(&Columns::new(vec![column]), &all_rows)
62			.map_err(|e| e.with_context(fn_fragment.clone(), false))?;
63
64		let (_keys, result_data) = accumulator.finalize().map_err(|e| e.with_context(fn_fragment, false))?;
65
66		return Ok(ColumnWithName::new(result_label.clone(), result_data));
67	}
68
69	let result_columns = routine.call(&mut fn_ctx, &arguments).map_err(|e| e.with_context(fn_fragment, false))?;
70
71	if result_columns.is_empty() {
72		return Err(RoutineError::FunctionExecutionFailed {
73			function: call.func.0.clone(),
74			reason: "Function returned no columns".to_string(),
75		}
76		.into());
77	}
78	let result_data = result_columns.data_at(0).clone();
79	Ok(ColumnWithName::new(result_label, result_data))
80}