1use std::{collections::HashMap, 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_function::registry::Functions;
15use reifydb_rql::expression::Expression;
16use reifydb_runtime::clock::Clock;
17use reifydb_type::{Result, params::Params, value::Value};
18
19static EMPTY_PARAMS: Params = Params::None;
20static EMPTY_SYMBOL_TABLE: LazyLock<SymbolTable> = LazyLock::new(|| SymbolTable::new());
21
22pub fn evaluate_operator_config(
40 expressions: &[Expression],
41 functions: &Functions,
42 clock: &Clock,
43) -> Result<HashMap<String, Value>> {
44 let mut result = HashMap::new();
45
46 let compile_ctx = CompileContext {
47 functions,
48 symbol_table: &EMPTY_SYMBOL_TABLE,
49 };
50
51 let empty_columns = Columns::empty();
52
53 let exec_ctx = EvalContext {
54 target: None,
55 columns: empty_columns,
56 row_count: 1, take: None,
58 params: &EMPTY_PARAMS,
59 symbol_table: &EMPTY_SYMBOL_TABLE,
60 is_aggregate_context: false,
61 functions,
62 clock,
63 arena: None,
64 };
65
66 for expr in expressions {
67 match expr {
68 Expression::Alias(alias_expr) => {
69 let key = alias_expr.alias.name().to_string();
70
71 let expr = compile_expression(&compile_ctx, &alias_expr.expression)?;
72 let column = expr.execute(&exec_ctx)?;
73
74 let value = if column.data().len() > 0 {
75 column.data().get_value(0)
76 } else {
77 Value::none()
78 };
79 result.insert(key, value);
80 }
81 _ => {}
82 }
83 }
84
85 Ok(result)
86}
87
88#[cfg(test)]
89pub mod tests {
90 use reifydb_function::registry::Functions;
91 use reifydb_rql::expression::{AliasExpression, ConstantExpression, Expression, IdentExpression};
92 use reifydb_runtime::clock::Clock;
93 use reifydb_type::{fragment::Fragment, value::Value};
94
95 use super::evaluate_operator_config;
96
97 fn create_alias_expression(alias_name: &str, inner_expression: Expression) -> Expression {
98 Expression::Alias(AliasExpression {
99 alias: IdentExpression(Fragment::internal(alias_name.to_string())),
100 expression: Box::new(inner_expression),
101 fragment: Fragment::testing_empty(),
102 })
103 }
104
105 fn create_constant_text(text: &str) -> Expression {
106 Expression::Constant(ConstantExpression::Text {
107 fragment: Fragment::internal(text.to_string()),
108 })
109 }
110
111 fn create_constant_number(num: i64) -> Expression {
112 Expression::Constant(ConstantExpression::Number {
113 fragment: Fragment::internal(num.to_string()),
114 })
115 }
116
117 fn create_constant_bool(value: bool) -> Expression {
118 Expression::Constant(ConstantExpression::Bool {
119 fragment: Fragment::internal(value.to_string()),
120 })
121 }
122
123 fn create_constant_undefined() -> Expression {
124 Expression::Constant(ConstantExpression::None {
125 fragment: Fragment::internal("none".to_string()),
126 })
127 }
128
129 #[test]
130 fn test_empty_expressions() {
131 let functions = Functions::builder().build();
132 let clock = Clock::default();
133 let expressions: Vec<Expression> = vec![];
134
135 let result = evaluate_operator_config(&expressions, &functions, &clock).unwrap();
136
137 assert!(result.is_empty());
138 }
139
140 #[test]
141 fn test_single_alias_string() {
142 let functions = Functions::builder().build();
143 let clock = Clock::default();
144 let expressions = vec![create_alias_expression("key1", create_constant_text("value1"))];
145
146 let result = evaluate_operator_config(&expressions, &functions, &clock).unwrap();
147
148 assert_eq!(result.len(), 1);
149 assert_eq!(result.get("key1"), Some(&Value::Utf8("value1".into())));
150 }
151
152 #[test]
153 fn test_single_alias_number() {
154 let functions = Functions::builder().build();
155 let clock = Clock::default();
156 let expressions = vec![create_alias_expression("count", create_constant_number(42))];
157
158 let result = evaluate_operator_config(&expressions, &functions, &clock).unwrap();
159
160 assert_eq!(result.len(), 1);
161 assert_eq!(result.get("count"), Some(&Value::Int1(42)));
162 }
163
164 #[test]
165 fn test_single_alias_bool() {
166 let functions = Functions::builder().build();
167 let clock = Clock::default();
168 let expressions = vec![create_alias_expression("enabled", create_constant_bool(true))];
169
170 let result = evaluate_operator_config(&expressions, &functions, &clock).unwrap();
171
172 assert_eq!(result.len(), 1);
173 assert_eq!(result.get("enabled"), Some(&Value::Boolean(true)));
174 }
175
176 #[test]
177 fn test_single_alias_undefined() {
178 let functions = Functions::builder().build();
179 let clock = Clock::default();
180 let expressions = vec![create_alias_expression("optional", create_constant_undefined())];
181
182 let result = evaluate_operator_config(&expressions, &functions, &clock).unwrap();
183
184 assert_eq!(result.len(), 1);
185 assert_eq!(result.get("optional"), Some(&Value::none()));
186 }
187
188 #[test]
189 fn test_multiple_aliases() {
190 let functions = Functions::builder().build();
191 let clock = Clock::default();
192 let expressions = vec![
193 create_alias_expression("key1", create_constant_text("value1")),
194 create_alias_expression("key2", create_constant_number(100)),
195 create_alias_expression("key3", create_constant_bool(false)),
196 ];
197
198 let result = evaluate_operator_config(&expressions, &functions, &clock).unwrap();
199
200 assert_eq!(result.len(), 3);
201 assert_eq!(result.get("key1"), Some(&Value::Utf8("value1".into())));
202 assert_eq!(result.get("key2"), Some(&Value::Int1(100)));
203 assert_eq!(result.get("key3"), Some(&Value::Boolean(false)));
204 }
205
206 #[test]
209 fn test_non_alias_expressions_skipped() {
210 let functions = Functions::builder().build();
211 let clock = Clock::default();
212 let expressions = vec![
213 create_alias_expression("valid", create_constant_text("included")),
214 create_constant_text("standalone"), create_constant_number(999), ];
217
218 let result = evaluate_operator_config(&expressions, &functions, &clock).unwrap();
219
220 assert_eq!(result.len(), 1);
221 assert_eq!(result.get("valid"), Some(&Value::Utf8("included".into())));
222 }
223
224 #[test]
225 fn test_only_non_alias_expressions() {
226 let functions = Functions::builder().build();
227 let clock = Clock::default();
228 let expressions =
229 vec![create_constant_text("text"), create_constant_number(42), create_constant_bool(true)];
230
231 let result = evaluate_operator_config(&expressions, &functions, &clock).unwrap();
232
233 assert!(result.is_empty());
234 }
235
236 #[test]
239 fn test_all_basic_value_types() {
240 let functions = Functions::builder().build();
241 let clock = Clock::default();
242 let expressions = vec![
243 create_alias_expression("text_val", create_constant_text("hello")),
244 create_alias_expression("num_val", create_constant_number(-42)),
245 create_alias_expression("bool_true", create_constant_bool(true)),
246 create_alias_expression("bool_false", create_constant_bool(false)),
247 create_alias_expression("undef_val", create_constant_undefined()),
248 ];
249
250 let result = evaluate_operator_config(&expressions, &functions, &clock).unwrap();
251
252 assert_eq!(result.len(), 5);
253 assert_eq!(result.get("text_val"), Some(&Value::Utf8("hello".into())));
254 assert_eq!(result.get("num_val"), Some(&Value::Int1(-42)));
255 assert_eq!(result.get("bool_true"), Some(&Value::Boolean(true)));
256 assert_eq!(result.get("bool_false"), Some(&Value::Boolean(false)));
257 assert_eq!(result.get("undef_val"), Some(&Value::none()));
258 }
259
260 #[test]
261 fn test_duplicate_alias_names_last_wins() {
262 let functions = Functions::builder().build();
263 let clock = Clock::default();
264 let expressions = vec![
265 create_alias_expression("key", create_constant_text("first")),
266 create_alias_expression("key", create_constant_text("second")),
267 create_alias_expression("key", create_constant_number(42)),
268 ];
269
270 let result = evaluate_operator_config(&expressions, &functions, &clock).unwrap();
271
272 assert_eq!(result.len(), 1);
273 assert_eq!(result.get("key"), Some(&Value::Int1(42)));
274 }
275
276 #[test]
277 fn test_empty_string_value() {
278 let functions = Functions::builder().build();
279 let clock = Clock::default();
280 let expressions = vec![create_alias_expression("empty", create_constant_text(""))];
281
282 let result = evaluate_operator_config(&expressions, &functions, &clock).unwrap();
283
284 assert_eq!(result.len(), 1);
285 assert_eq!(result.get("empty"), Some(&Value::Utf8("".into())));
286 }
287
288 #[test]
289 fn test_special_characters_in_alias_name() {
290 let functions = Functions::builder().build();
291 let clock = Clock::default();
292 let expressions = vec![
293 create_alias_expression("key_with_underscore", create_constant_number(1)),
294 create_alias_expression("keyWithCamelCase", create_constant_number(2)),
295 create_alias_expression("key123", create_constant_number(3)),
296 ];
297
298 let result = evaluate_operator_config(&expressions, &functions, &clock).unwrap();
299
300 assert_eq!(result.len(), 3);
301 assert_eq!(result.get("key_with_underscore"), Some(&Value::Int1(1)));
302 assert_eq!(result.get("keyWithCamelCase"), Some(&Value::Int1(2)));
303 assert_eq!(result.get("key123"), Some(&Value::Int1(3)));
304 }
305
306 #[test]
307 fn test_large_number_values() {
308 let functions = Functions::builder().build();
309 let clock = Clock::default();
310 let expressions = vec![
311 create_alias_expression("small", create_constant_number(0)),
312 create_alias_expression("large_positive", create_constant_number(i64::MAX)),
313 create_alias_expression("large_negative", create_constant_number(i64::MIN)),
314 ];
315
316 let result = evaluate_operator_config(&expressions, &functions, &clock).unwrap();
317
318 assert_eq!(result.len(), 3);
319 assert_eq!(result.get("small"), Some(&Value::Int1(0)));
320 assert_eq!(result.get("large_positive"), Some(&Value::Int8(i64::MAX)));
321 assert_eq!(result.get("large_negative"), Some(&Value::Int8(i64::MIN)));
322 }
323}