1use std::{collections::BTreeMap, sync::LazyLock};
5
6use reifydb_core::value::column::columns::Columns;
7use reifydb_engine::{
8 expression::{
9 compile::compile_expression,
10 context::{CompileContext, EvalContext},
11 },
12 vm::stack::SymbolTable,
13};
14use reifydb_routine::routine::registry::Routines;
15use reifydb_rql::expression::Expression;
16use reifydb_runtime::context::RuntimeContext;
17use reifydb_type::{
18 Result,
19 params::Params,
20 value::{Value, identity::IdentityId},
21};
22
23static EMPTY_PARAMS: Params = Params::None;
24static EMPTY_SYMBOL_TABLE: LazyLock<SymbolTable> = LazyLock::new(SymbolTable::new);
25
26pub fn evaluate_operator_config(
33 expressions: &[Expression],
34 routines: &Routines,
35 runtime_context: &RuntimeContext,
36) -> Result<BTreeMap<String, Value>> {
37 let mut result = BTreeMap::new();
38
39 let compile_ctx = CompileContext {
40 symbols: &EMPTY_SYMBOL_TABLE,
41 };
42
43 let empty_columns = Columns::empty();
44
45 let session = EvalContext {
46 params: &EMPTY_PARAMS,
47 symbols: &EMPTY_SYMBOL_TABLE,
48 routines,
49 runtime_context,
50 arena: None,
51 identity: IdentityId::root(),
52 is_aggregate_context: false,
53 columns: Columns::empty(),
54 row_count: 1,
55 target: None,
56 take: None,
57 };
58 let exec_ctx = session.with_eval(empty_columns, 1);
59
60 for expr in expressions {
61 if let Expression::Alias(alias_expr) = expr {
62 let key = alias_expr.alias.name().to_string();
63
64 let expr = compile_expression(&compile_ctx, &alias_expr.expression)?;
65 let column = expr.execute(&exec_ctx)?;
66
67 let value = if !column.data().is_empty() {
68 column.data().get_value(0)
69 } else {
70 Value::none()
71 };
72 result.insert(key, value);
73 }
74 }
75
76 Ok(result)
77}
78
79#[cfg(test)]
80pub mod tests {
81 use reifydb_routine::routine::registry::Routines;
82 use reifydb_rql::expression::{AliasExpression, ConstantExpression, Expression, IdentExpression};
83 use reifydb_runtime::context::{RuntimeContext, clock::Clock};
84 use reifydb_type::{fragment::Fragment, value::Value};
85
86 use super::evaluate_operator_config;
87
88 fn create_alias_expression(alias_name: &str, inner_expression: Expression) -> Expression {
89 Expression::Alias(AliasExpression {
90 alias: IdentExpression(Fragment::internal(alias_name)),
91 expression: Box::new(inner_expression),
92 fragment: Fragment::testing_empty(),
93 })
94 }
95
96 fn create_constant_text(text: &str) -> Expression {
97 Expression::Constant(ConstantExpression::Text {
98 fragment: Fragment::internal(text),
99 })
100 }
101
102 fn create_constant_number(num: i64) -> Expression {
103 Expression::Constant(ConstantExpression::Number {
104 fragment: Fragment::internal(num.to_string()),
105 })
106 }
107
108 fn create_constant_bool(value: bool) -> Expression {
109 Expression::Constant(ConstantExpression::Bool {
110 fragment: Fragment::internal(value.to_string()),
111 })
112 }
113
114 fn create_constant_undefined() -> Expression {
115 Expression::Constant(ConstantExpression::None {
116 fragment: Fragment::internal("none"),
117 })
118 }
119
120 #[test]
121 fn test_empty_expressions() {
122 let routines = Routines::empty();
123 let runtime_context = RuntimeContext::with_clock(Clock::Real);
124 let expressions: Vec<Expression> = vec![];
125
126 let result = evaluate_operator_config(&expressions, &routines, &runtime_context).unwrap();
127
128 assert!(result.is_empty());
129 }
130
131 #[test]
132 fn test_single_alias_string() {
133 let routines = Routines::empty();
134 let runtime_context = RuntimeContext::with_clock(Clock::Real);
135 let expressions = vec![create_alias_expression("key1", create_constant_text("value1"))];
136
137 let result = evaluate_operator_config(&expressions, &routines, &runtime_context).unwrap();
138
139 assert_eq!(result.len(), 1);
140 assert_eq!(result.get("key1"), Some(&Value::Utf8("value1".into())));
141 }
142
143 #[test]
144 fn test_single_alias_number() {
145 let routines = Routines::empty();
146 let runtime_context = RuntimeContext::with_clock(Clock::Real);
147 let expressions = vec![create_alias_expression("count", create_constant_number(42))];
148
149 let result = evaluate_operator_config(&expressions, &routines, &runtime_context).unwrap();
150
151 assert_eq!(result.len(), 1);
152 assert_eq!(result.get("count"), Some(&Value::Int1(42)));
153 }
154
155 #[test]
156 fn test_single_alias_bool() {
157 let routines = Routines::empty();
158 let runtime_context = RuntimeContext::with_clock(Clock::Real);
159 let expressions = vec![create_alias_expression("enabled", create_constant_bool(true))];
160
161 let result = evaluate_operator_config(&expressions, &routines, &runtime_context).unwrap();
162
163 assert_eq!(result.len(), 1);
164 assert_eq!(result.get("enabled"), Some(&Value::Boolean(true)));
165 }
166
167 #[test]
168 fn test_single_alias_undefined() {
169 let routines = Routines::empty();
170 let runtime_context = RuntimeContext::with_clock(Clock::Real);
171 let expressions = vec![create_alias_expression("optional", create_constant_undefined())];
172
173 let result = evaluate_operator_config(&expressions, &routines, &runtime_context).unwrap();
174
175 assert_eq!(result.len(), 1);
176 assert_eq!(result.get("optional"), Some(&Value::none()));
177 }
178
179 #[test]
180 fn test_multiple_aliases() {
181 let routines = Routines::empty();
182 let runtime_context = RuntimeContext::with_clock(Clock::Real);
183 let expressions = vec![
184 create_alias_expression("key1", create_constant_text("value1")),
185 create_alias_expression("key2", create_constant_number(100)),
186 create_alias_expression("key3", create_constant_bool(false)),
187 ];
188
189 let result = evaluate_operator_config(&expressions, &routines, &runtime_context).unwrap();
190
191 assert_eq!(result.len(), 3);
192 assert_eq!(result.get("key1"), Some(&Value::Utf8("value1".into())));
193 assert_eq!(result.get("key2"), Some(&Value::Int1(100)));
194 assert_eq!(result.get("key3"), Some(&Value::Boolean(false)));
195 }
196
197 #[test]
198 fn test_non_alias_expressions_skipped() {
199 let routines = Routines::empty();
200 let runtime_context = RuntimeContext::with_clock(Clock::Real);
201 let expressions = vec![
202 create_alias_expression("valid", create_constant_text("included")),
203 create_constant_text("standalone"),
204 create_constant_number(999),
205 ];
206
207 let result = evaluate_operator_config(&expressions, &routines, &runtime_context).unwrap();
208
209 assert_eq!(result.len(), 1);
210 assert_eq!(result.get("valid"), Some(&Value::Utf8("included".into())));
211 }
212
213 #[test]
214 fn test_only_non_alias_expressions() {
215 let routines = Routines::empty();
216 let runtime_context = RuntimeContext::with_clock(Clock::Real);
217 let expressions = vec![
218 create_constant_text("standalone"),
219 create_constant_number(999),
220 create_constant_bool(true),
221 ];
222
223 let result = evaluate_operator_config(&expressions, &routines, &runtime_context).unwrap();
224
225 assert!(result.is_empty());
226 }
227
228 #[test]
229 fn test_unknown_function_returns_error() {
230 let routines = Routines::empty();
231 let runtime_context = RuntimeContext::with_clock(Clock::Real);
232 let expressions = vec![create_alias_expression("custom_function", create_constant_text("data"))];
233
234 let result = evaluate_operator_config(&expressions, &routines, &runtime_context);
235
236 assert!(result.is_ok());
237 }
238}