reifydb_engine/expression/
eval.rs1use reifydb_core::value::column::{ColumnWithName, cast::cast_column_data};
5use reifydb_rql::expression::Expression;
6
7use crate::{
8 Result,
9 expression::{
10 compile::compile_expression,
11 context::{CompileContext, EvalContext},
12 },
13};
14
15pub fn evaluate(ctx: &EvalContext, expr: &Expression) -> Result<ColumnWithName> {
16 let compile_ctx = CompileContext {
17 symbols: ctx.symbols,
18 };
19 let compiled = compile_expression(&compile_ctx, expr)?;
20 let column = compiled.execute(ctx)?;
21
22 if let Some(ty) = ctx.target.as_ref().map(|c| c.column_type()) {
23 let data = cast_column_data(ctx, column.data(), ty, &expr.lazy_fragment())?;
24 Ok(ColumnWithName {
25 name: column.name,
26 data,
27 })
28 } else {
29 Ok(column)
30 }
31}
32
33#[cfg(test)]
34pub mod tests {
35 use reifydb_core::value::column::buffer::ColumnBuffer;
36 use reifydb_rql::expression::{
37 CastExpression, ConstantExpression,
38 ConstantExpression::Number,
39 Expression::{Cast, Constant, Prefix},
40 PrefixExpression, PrefixOperator, TypeExpression,
41 };
42 use reifydb_value::{fragment::Fragment, value::value_type::ValueType};
43
44 use crate::expression::{context::EvalContext, eval::evaluate};
45
46 #[test]
47 fn test_cast_integer() {
48 let mut ctx = EvalContext::testing();
49 let result = evaluate(
50 &mut ctx,
51 &Cast(CastExpression {
52 fragment: Fragment::testing_empty(),
53 expression: Box::new(Constant(Number {
54 fragment: Fragment::internal("42"),
55 })),
56 to: TypeExpression {
57 fragment: Fragment::testing_empty(),
58 ty: ValueType::Int4,
59 },
60 }),
61 )
62 .unwrap();
63
64 assert_eq!(*result.data(), ColumnBuffer::int4([42]));
65 }
66
67 #[test]
68 fn test_cast_negative_integer() {
69 let mut ctx = EvalContext::testing();
70 let result = evaluate(
71 &mut ctx,
72 &Cast(CastExpression {
73 fragment: Fragment::testing_empty(),
74 expression: Box::new(Prefix(PrefixExpression {
75 operator: PrefixOperator::Minus(Fragment::testing_empty()),
76 expression: Box::new(Constant(Number {
77 fragment: Fragment::internal("42"),
78 })),
79 fragment: Fragment::testing_empty(),
80 })),
81 to: TypeExpression {
82 fragment: Fragment::testing_empty(),
83 ty: ValueType::Int4,
84 },
85 }),
86 )
87 .unwrap();
88
89 assert_eq!(*result.data(), ColumnBuffer::int4([-42]));
90 }
91
92 #[test]
93 fn test_cast_negative_min() {
94 let mut ctx = EvalContext::testing();
95 let result = evaluate(
96 &mut ctx,
97 &Cast(CastExpression {
98 fragment: Fragment::testing_empty(),
99 expression: Box::new(Prefix(PrefixExpression {
100 operator: PrefixOperator::Minus(Fragment::testing_empty()),
101 expression: Box::new(Constant(Number {
102 fragment: Fragment::internal("128"),
103 })),
104 fragment: Fragment::testing_empty(),
105 })),
106 to: TypeExpression {
107 fragment: Fragment::testing_empty(),
108 ty: ValueType::Int1,
109 },
110 }),
111 )
112 .unwrap();
113
114 assert_eq!(*result.data(), ColumnBuffer::int1([-128]));
115 }
116
117 #[test]
118 fn test_cast_float_8() {
119 let mut ctx = EvalContext::testing();
120 let result = evaluate(
121 &mut ctx,
122 &Cast(CastExpression {
123 fragment: Fragment::testing_empty(),
124 expression: Box::new(Constant(Number {
125 fragment: Fragment::internal("4.2"),
126 })),
127 to: TypeExpression {
128 fragment: Fragment::testing_empty(),
129 ty: ValueType::Float8,
130 },
131 }),
132 )
133 .unwrap();
134
135 assert_eq!(*result.data(), ColumnBuffer::float8([4.2]));
136 }
137
138 #[test]
139 fn test_cast_float_4() {
140 let mut ctx = EvalContext::testing();
141 let result = evaluate(
142 &mut ctx,
143 &Cast(CastExpression {
144 fragment: Fragment::testing_empty(),
145 expression: Box::new(Constant(Number {
146 fragment: Fragment::internal("4.2"),
147 })),
148 to: TypeExpression {
149 fragment: Fragment::testing_empty(),
150 ty: ValueType::Float4,
151 },
152 }),
153 )
154 .unwrap();
155
156 assert_eq!(*result.data(), ColumnBuffer::float4([4.2]));
157 }
158
159 #[test]
160 fn test_cast_negative_float_4() {
161 let mut ctx = EvalContext::testing();
162 let result = evaluate(
163 &mut ctx,
164 &Cast(CastExpression {
165 fragment: Fragment::testing_empty(),
166 expression: Box::new(Constant(Number {
167 fragment: Fragment::internal("-1.1"),
168 })),
169 to: TypeExpression {
170 fragment: Fragment::testing_empty(),
171 ty: ValueType::Float4,
172 },
173 }),
174 )
175 .unwrap();
176
177 assert_eq!(*result.data(), ColumnBuffer::float4([-1.1]));
178 }
179
180 #[test]
181 fn test_cast_negative_float_8() {
182 let mut ctx = EvalContext::testing();
183 let result = evaluate(
184 &mut ctx,
185 &Cast(CastExpression {
186 fragment: Fragment::testing_empty(),
187 expression: Box::new(Constant(Number {
188 fragment: Fragment::internal("-1.1"),
189 })),
190 to: TypeExpression {
191 fragment: Fragment::testing_empty(),
192 ty: ValueType::Float8,
193 },
194 }),
195 )
196 .unwrap();
197
198 assert_eq!(*result.data(), ColumnBuffer::float8([-1.1]));
199 }
200
201 #[test]
202 fn test_cast_string_to_bool() {
203 let mut ctx = EvalContext::testing();
204 let result = evaluate(
205 &mut ctx,
206 &Cast(CastExpression {
207 fragment: Fragment::testing_empty(),
208 expression: Box::new(Constant(ConstantExpression::Text {
209 fragment: Fragment::internal("0"),
210 })),
211 to: TypeExpression {
212 fragment: Fragment::testing_empty(),
213 ty: ValueType::Boolean,
214 },
215 }),
216 )
217 .unwrap();
218
219 assert_eq!(*result.data(), ColumnBuffer::bool([false]));
220 }
221
222 #[test]
223 fn test_cast_string_neg_one_to_bool_should_fail() {
224 let mut ctx = EvalContext::testing();
225 let result = evaluate(
226 &mut ctx,
227 &Cast(CastExpression {
228 fragment: Fragment::testing_empty(),
229 expression: Box::new(Constant(ConstantExpression::Text {
230 fragment: Fragment::internal("-1"),
231 })),
232 to: TypeExpression {
233 fragment: Fragment::testing_empty(),
234 ty: ValueType::Boolean,
235 },
236 }),
237 );
238
239 assert!(result.is_err());
240
241 let err = result.unwrap_err();
244 let diagnostic = err.0;
245 assert_eq!(diagnostic.code, "CAST_004");
246 assert!(diagnostic.cause.is_some());
247 let cause = diagnostic.cause.unwrap();
248 assert_eq!(cause.code, "BOOLEAN_003"); }
250
251 #[test]
252 fn test_cast_boolean_to_date_should_fail() {
253 let mut ctx = EvalContext::testing();
254 let result = evaluate(
255 &mut ctx,
256 &Cast(CastExpression {
257 fragment: Fragment::testing_empty(),
258 expression: Box::new(Constant(ConstantExpression::Bool {
259 fragment: Fragment::internal("true"),
260 })),
261 to: TypeExpression {
262 fragment: Fragment::testing_empty(),
263 ty: ValueType::Date,
264 },
265 }),
266 );
267
268 assert!(result.is_err());
269
270 let err = result.unwrap_err();
273 let diagnostic = err.0;
274 assert_eq!(diagnostic.code, "CAST_001");
275 }
276
277 #[test]
278 fn test_cast_text_to_decimal() {
279 let mut ctx = EvalContext::testing();
280 let result = evaluate(
281 &mut ctx,
282 &Cast(CastExpression {
283 fragment: Fragment::testing_empty(),
284 expression: Box::new(Constant(ConstantExpression::Text {
285 fragment: Fragment::internal("123.456789"),
286 })),
287 to: TypeExpression {
288 fragment: Fragment::testing_empty(),
289 ty: ValueType::Decimal,
290 },
291 }),
292 )
293 .unwrap();
294
295 if let ColumnBuffer::Decimal {
296 container,
297 ..
298 } = result.data()
299 {
300 assert_eq!(container.len(), 1);
301 assert!(container.is_defined(0));
302 let value = &container[0];
303 assert_eq!(value.to_string(), "123.456789");
304 } else {
305 panic!("Expected Decimal column data");
306 }
307 }
308}