1use std::{mem::discriminant, slice::from_ref, str::FromStr};
5
6use reifydb_core::value::column::{
7 ColumnWithName,
8 buffer::ColumnBuffer,
9 cast::{cast_column_data, error::CastError},
10 columns::Columns,
11};
12use reifydb_rql::expression::{Expression, name::display_label};
13use reifydb_value::{
14 error::{BinaryOp, Error, IntoDiagnostic, LogicalOp, RuntimeErrorKind, TypeError},
15 fragment::Fragment,
16 value::{Value, value_type::ValueType},
17};
18
19use super::{
20 context::CompileContext,
21 option::{binary_op_unwrap_option, unary_op_unwrap_option},
22};
23use crate::{
24 Result,
25 expression::{
26 access::access_lookup,
27 arith::{add::add_columns, div::div_columns, mul::mul_columns, rem::rem_columns, sub::sub_columns},
28 call::call_builtin,
29 compare::{Equal, GreaterThan, GreaterThanEqual, LessThan, LessThanEqual, NotEqual, compare_columns},
30 constant::constant_value,
31 context::EvalContext,
32 logic::{execute_logical_op, try_short_circuit_and, try_short_circuit_or},
33 lookup::column_lookup,
34 parameter::parameter_lookup,
35 prefix::prefix_apply,
36 },
37 stack::Variable,
38};
39
40type SingleExprFn = Box<dyn Fn(&EvalContext) -> Result<ColumnWithName> + Send + Sync>;
41type MultiExprFn = Box<dyn Fn(&EvalContext) -> Result<Vec<ColumnWithName>> + Send + Sync>;
42
43pub struct CompiledExpr {
44 inner: CompiledExprInner,
45 access_column_name: Option<String>,
46}
47
48enum CompiledExprInner {
49 Single(SingleExprFn),
50 Multi(MultiExprFn),
51}
52
53impl CompiledExpr {
54 pub fn new(f: impl Fn(&EvalContext) -> Result<ColumnWithName> + Send + Sync + 'static) -> Self {
55 Self {
56 inner: CompiledExprInner::Single(Box::new(f)),
57 access_column_name: None,
58 }
59 }
60
61 pub fn new_multi(f: impl Fn(&EvalContext) -> Result<Vec<ColumnWithName>> + Send + Sync + 'static) -> Self {
62 Self {
63 inner: CompiledExprInner::Multi(Box::new(f)),
64 access_column_name: None,
65 }
66 }
67
68 pub fn new_access(
69 name: String,
70 f: impl Fn(&EvalContext) -> Result<ColumnWithName> + Send + Sync + 'static,
71 ) -> Self {
72 Self {
73 inner: CompiledExprInner::Single(Box::new(f)),
74 access_column_name: Some(name),
75 }
76 }
77
78 pub fn access_column_name(&self) -> Option<&str> {
79 self.access_column_name.as_deref()
80 }
81
82 pub fn execute(&self, ctx: &EvalContext) -> Result<ColumnWithName> {
83 match &self.inner {
84 CompiledExprInner::Single(f) => f(ctx),
85 CompiledExprInner::Multi(f) => {
86 let columns = f(ctx)?;
87 if columns.len() == 1 {
88 return Ok(columns.into_iter().next().unwrap());
89 }
90 Err(TypeError::Runtime {
91 kind: RuntimeErrorKind::ExpectedSingleColumn {
92 actual: columns.len(),
93 },
94 message: "expression produces more than one column where one is required"
95 .to_string(),
96 }
97 .into())
98 }
99 }
100 }
101
102 pub fn execute_multi(&self, ctx: &EvalContext) -> Result<Vec<ColumnWithName>> {
103 match &self.inner {
104 CompiledExprInner::Single(f) => Ok(vec![f(ctx)?]),
105 CompiledExprInner::Multi(f) => f(ctx),
106 }
107 }
108}
109
110macro_rules! compile_arith {
111 ($ctx:expr, $parent:expr, $e:expr, $op_fn:path) => {{
112 let left = compile_expression($ctx, &$e.left)?;
113 let right = compile_expression($ctx, &$e.right)?;
114 let fragment = $e.full_fragment_owned();
115 let label = display_label($parent);
116 CompiledExpr::new(move |ctx| {
117 let l = left.execute(ctx)?;
118 let r = right.execute(ctx)?;
119 let mut col = $op_fn(ctx, &l, &r, || fragment.clone())?;
120 col.name = label.clone();
121 Ok(col)
122 })
123 }};
124}
125
126macro_rules! compile_compare {
127 ($ctx:expr, $parent:expr, $e:expr, $cmp_type:ty, $binary_op:expr) => {{
128 let left = compile_expression($ctx, &$e.left)?;
129 let right = compile_expression($ctx, &$e.right)?;
130 let fragment = $e.full_fragment_owned();
131 let label = display_label($parent);
132 CompiledExpr::new(move |ctx| {
133 let l = left.execute(ctx)?;
134 let r = right.execute(ctx)?;
135 let mut col = compare_columns::<$cmp_type>(&l, &r, fragment.clone(), |f, l, r| {
136 TypeError::BinaryOperatorNotApplicable {
137 operator: $binary_op,
138 left: l,
139 right: r,
140 fragment: f,
141 }
142 .into_diagnostic()
143 })?;
144 col.name = label.clone();
145 Ok(col)
146 })
147 }};
148}
149
150pub fn compile_expression(_ctx: &CompileContext, expr: &Expression) -> Result<CompiledExpr> {
151 Ok(match expr {
152 Expression::Constant(e) => {
153 let constant = e.clone();
154 let label = display_label(expr);
155 CompiledExpr::new(move |ctx| {
156 let row_count = ctx.take.unwrap_or(ctx.row_count);
157 Ok(ColumnWithName {
158 name: label.clone(),
159 data: constant_value(&constant, row_count)?,
160 })
161 })
162 }
163
164 Expression::Column(e) => {
165 let expr = e.clone();
166 CompiledExpr::new(move |ctx| column_lookup(ctx, &expr))
167 }
168
169 Expression::Variable(e) => {
170 let expr = e.clone();
171 CompiledExpr::new(move |ctx| {
172 let variable_name = expr.name();
173
174 if variable_name == "env" {
175 return Err(TypeError::Runtime {
176 kind: RuntimeErrorKind::VariableIsDataframe {
177 name: variable_name.to_string(),
178 },
179 message: format!(
180 "Variable '{}' contains a dataframe and cannot be used directly in scalar expressions",
181 variable_name
182 ),
183 }
184 .into());
185 }
186
187 match ctx.symbols.get(variable_name) {
188 Some(Variable::Columns {
189 columns,
190 }) if columns.is_scalar() => {
191 let value = columns.scalar_value();
192 let mut data =
193 ColumnBuffer::with_capacity(value.get_type(), ctx.row_count);
194 for _ in 0..ctx.row_count {
195 data.push_value(value.clone());
196 }
197 Ok(ColumnWithName {
198 name: Fragment::internal(variable_name),
199 data,
200 })
201 }
202 Some(Variable::Columns {
203 ..
204 })
205 | Some(Variable::ForIterator {
206 ..
207 })
208 | Some(Variable::Closure(_)) => Err(TypeError::Runtime {
209 kind: RuntimeErrorKind::VariableIsDataframe {
210 name: variable_name.to_string(),
211 },
212 message: format!(
213 "Variable '{}' contains a dataframe and cannot be used directly in scalar expressions",
214 variable_name
215 ),
216 }
217 .into()),
218 None => {
219 if let Some(value) = ctx.params.get_named(variable_name) {
220 let mut data = ColumnBuffer::with_capacity(
221 value.get_type(),
222 ctx.row_count,
223 );
224 for _ in 0..ctx.row_count {
225 data.push_value(value.clone());
226 }
227 return Ok(ColumnWithName {
228 name: Fragment::internal(variable_name),
229 data,
230 });
231 }
232 Err(TypeError::Runtime {
233 kind: RuntimeErrorKind::VariableNotFound {
234 name: variable_name.to_string(),
235 },
236 message: format!("Variable '{}' is not defined", variable_name),
237 }
238 .into())
239 }
240 }
241 })
242 }
243
244 Expression::Parameter(e) => {
245 let expr = e.clone();
246 CompiledExpr::new(move |ctx| parameter_lookup(ctx, &expr))
247 }
248
249 Expression::Alias(e) => {
250 let inner = compile_expression(_ctx, &e.expression)?;
251 let alias = e.alias.0.clone();
252 CompiledExpr::new(move |ctx| {
253 let mut column = inner.execute(ctx)?;
254 column.name = alias.clone();
255 Ok(column)
256 })
257 }
258
259 Expression::Add(e) => compile_arith!(_ctx, expr, e, add_columns),
260 Expression::Sub(e) => compile_arith!(_ctx, expr, e, sub_columns),
261 Expression::Mul(e) => compile_arith!(_ctx, expr, e, mul_columns),
262 Expression::Div(e) => compile_arith!(_ctx, expr, e, div_columns),
263 Expression::Rem(e) => compile_arith!(_ctx, expr, e, rem_columns),
264
265 Expression::Equal(e) => compile_compare!(_ctx, expr, e, Equal, BinaryOp::Equal),
266 Expression::NotEqual(e) => compile_compare!(_ctx, expr, e, NotEqual, BinaryOp::NotEqual),
267 Expression::GreaterThan(e) => compile_compare!(_ctx, expr, e, GreaterThan, BinaryOp::GreaterThan),
268 Expression::GreaterThanEqual(e) => {
269 compile_compare!(_ctx, expr, e, GreaterThanEqual, BinaryOp::GreaterThanEqual)
270 }
271 Expression::LessThan(e) => compile_compare!(_ctx, expr, e, LessThan, BinaryOp::LessThan),
272 Expression::LessThanEqual(e) => compile_compare!(_ctx, expr, e, LessThanEqual, BinaryOp::LessThanEqual),
273
274 Expression::And(e) => {
275 let left = compile_expression(_ctx, &e.left)?;
276 let right = compile_expression(_ctx, &e.right)?;
277 let fragment = e.full_fragment_owned();
278 let label = display_label(expr);
279 CompiledExpr::new(move |ctx| {
280 let l = left.execute(ctx)?;
281 if let Some(mut short) = try_short_circuit_and(&l, &fragment, l.data().len()) {
282 short.name = label.clone();
283 return Ok(short);
284 }
285 let r = right.execute(ctx)?;
286 let mut col = execute_logical_op(&l, &r, &fragment, LogicalOp::And, |a, b| a && b)?;
287 col.name = label.clone();
288 Ok(col)
289 })
290 }
291
292 Expression::Or(e) => {
293 let left = compile_expression(_ctx, &e.left)?;
294 let right = compile_expression(_ctx, &e.right)?;
295 let fragment = e.full_fragment_owned();
296 let label = display_label(expr);
297 CompiledExpr::new(move |ctx| {
298 let l = left.execute(ctx)?;
299 if let Some(mut short) = try_short_circuit_or(&l, &fragment, l.data().len()) {
300 short.name = label.clone();
301 return Ok(short);
302 }
303 let r = right.execute(ctx)?;
304 let mut col = execute_logical_op(&l, &r, &fragment, LogicalOp::Or, |a, b| a || b)?;
305 col.name = label.clone();
306 Ok(col)
307 })
308 }
309
310 Expression::Xor(e) => {
311 let left = compile_expression(_ctx, &e.left)?;
312 let right = compile_expression(_ctx, &e.right)?;
313 let fragment = e.full_fragment_owned();
314 let label = display_label(expr);
315 CompiledExpr::new(move |ctx| {
316 let l = left.execute(ctx)?;
317 let r = right.execute(ctx)?;
318 let mut col = execute_logical_op(&l, &r, &fragment, LogicalOp::Xor, |a, b| a != b)?;
319 col.name = label.clone();
320 Ok(col)
321 })
322 }
323
324 Expression::Prefix(e) => {
325 let inner = compile_expression(_ctx, &e.expression)?;
326 let operator = e.operator.clone();
327 let fragment = e.full_fragment_owned();
328 let label = display_label(expr);
329 CompiledExpr::new(move |ctx| {
330 let column = inner.execute(ctx)?;
331 let mut col = prefix_apply(&column, &operator, &fragment)?;
332 col.name = label.clone();
333 Ok(col)
334 })
335 }
336
337 Expression::Type(e) => {
338 let ty = e.ty.clone();
339 let fragment = e.fragment.clone();
340 CompiledExpr::new(move |ctx| Ok(type_column(ctx, &ty, &fragment)))
341 }
342
343 Expression::AccessSource(e) => {
344 let col_name = e.column.name.text().to_string();
345 let expr = e.clone();
346 CompiledExpr::new_access(col_name, move |ctx| access_lookup(ctx, &expr))
347 }
348
349 Expression::Tuple(e) => {
350 if e.expressions.len() == 1 {
351 let inner = compile_expression(_ctx, &e.expressions[0])?;
352 CompiledExpr::new(move |ctx| inner.execute(ctx))
353 } else {
354 let compiled: Vec<CompiledExpr> = e
355 .expressions
356 .iter()
357 .map(|expr| compile_expression(_ctx, expr))
358 .collect::<Result<Vec<_>>>()?;
359 let fragment = e.fragment.clone();
360 CompiledExpr::new(move |ctx| {
361 let columns: Vec<ColumnWithName> = compiled
362 .iter()
363 .map(|expr| expr.execute(ctx))
364 .collect::<Result<Vec<_>>>()?;
365
366 let len = columns.first().map_or(1, |c| c.data().len());
367 let mut data: Vec<Value> = Vec::with_capacity(len);
368
369 for i in 0..len {
370 let items: Vec<Value> =
371 columns.iter().map(|col| col.data().get_value(i)).collect();
372 data.push(Value::Tuple(items));
373 }
374
375 Ok(ColumnWithName::new(fragment.clone(), ColumnBuffer::any(data)))
376 })
377 }
378 }
379
380 Expression::List(e) => {
381 let compiled: Vec<CompiledExpr> = e
382 .expressions
383 .iter()
384 .map(|expr| compile_expression(_ctx, expr))
385 .collect::<Result<Vec<_>>>()?;
386 let fragment = e.fragment.clone();
387 CompiledExpr::new(move |ctx| {
388 let columns: Vec<ColumnWithName> =
389 compiled.iter().map(|expr| expr.execute(ctx)).collect::<Result<Vec<_>>>()?;
390
391 let len = columns.first().map_or(1, |c| c.data().len());
392 let mut data: Vec<Value> = Vec::with_capacity(len);
393
394 for i in 0..len {
395 let items: Vec<Value> =
396 columns.iter().map(|col| col.data().get_value(i)).collect();
397 data.push(Value::List(items));
398 }
399
400 Ok(ColumnWithName::new(fragment.clone(), ColumnBuffer::any(data)))
401 })
402 }
403
404 Expression::Between(e) => {
405 let value = compile_expression(_ctx, &e.value)?;
406 let lower = compile_expression(_ctx, &e.lower)?;
407 let upper = compile_expression(_ctx, &e.upper)?;
408 let fragment = e.fragment.clone();
409 CompiledExpr::new(move |ctx| {
410 let value_col = value.execute(ctx)?;
411 let lower_col = lower.execute(ctx)?;
412 let upper_col = upper.execute(ctx)?;
413
414 let ge_result = compare_columns::<GreaterThanEqual>(
415 &value_col,
416 &lower_col,
417 fragment.clone(),
418 |f, l, r| {
419 TypeError::BinaryOperatorNotApplicable {
420 operator: BinaryOp::Between,
421 left: l,
422 right: r,
423 fragment: f,
424 }
425 .into_diagnostic()
426 },
427 )?;
428 let le_result = compare_columns::<LessThanEqual>(
429 &value_col,
430 &upper_col,
431 fragment.clone(),
432 |f, l, r| {
433 TypeError::BinaryOperatorNotApplicable {
434 operator: BinaryOp::Between,
435 left: l,
436 right: r,
437 fragment: f,
438 }
439 .into_diagnostic()
440 },
441 )?;
442
443 if !matches!(ge_result.data(), ColumnBuffer::Bool(_))
444 || !matches!(le_result.data(), ColumnBuffer::Bool(_))
445 {
446 return Err(TypeError::BinaryOperatorNotApplicable {
447 operator: BinaryOp::Between,
448 left: value_col.get_type(),
449 right: lower_col.get_type(),
450 fragment: fragment.clone(),
451 }
452 .into());
453 }
454
455 match (ge_result.data(), le_result.data()) {
456 (ColumnBuffer::Bool(ge_container), ColumnBuffer::Bool(le_container)) => {
457 let mut data = Vec::with_capacity(ge_container.len());
458 let mut bitvec = Vec::with_capacity(ge_container.len());
459
460 for i in 0..ge_container.len() {
461 if ge_container.is_defined(i) && le_container.is_defined(i) {
462 data.push(ge_container.data().get(i)
463 && le_container.data().get(i));
464 bitvec.push(true);
465 } else {
466 data.push(false);
467 bitvec.push(false);
468 }
469 }
470
471 Ok(ColumnWithName {
472 name: fragment.clone(),
473 data: ColumnBuffer::bool_with_bitvec(data, bitvec),
474 })
475 }
476 _ => unreachable!(
477 "Both comparison results should be boolean after the check above"
478 ),
479 }
480 })
481 }
482
483 Expression::In(e) => {
484 let list_expressions = match e.list.as_ref() {
485 Expression::Tuple(tuple) => &tuple.expressions,
486 Expression::List(list) => &list.expressions,
487 _ => from_ref(e.list.as_ref()),
488 };
489 let value = compile_expression(_ctx, &e.value)?;
490 let list: Vec<CompiledExpr> = list_expressions
491 .iter()
492 .map(|expr| compile_expression(_ctx, expr))
493 .collect::<Result<Vec<_>>>()?;
494 let negated = e.negated;
495 let fragment = e.fragment.clone();
496 CompiledExpr::new(move |ctx| {
497 if list.is_empty() {
498 let value_col = value.execute(ctx)?;
499 let len = value_col.data().len();
500 let result = vec![negated; len];
501 return Ok(ColumnWithName::new(fragment.clone(), ColumnBuffer::bool(result)));
502 }
503
504 let value_col = value.execute(ctx)?;
505
506 let first_col = list[0].execute(ctx)?;
507 let mut result = compare_columns::<Equal>(
508 &value_col,
509 &first_col,
510 fragment.clone(),
511 |f, l, r| {
512 TypeError::BinaryOperatorNotApplicable {
513 operator: BinaryOp::Equal,
514 left: l,
515 right: r,
516 fragment: f,
517 }
518 .into_diagnostic()
519 },
520 )?;
521
522 for list_expr in list.iter().skip(1) {
523 let list_col = list_expr.execute(ctx)?;
524 let eq_result = compare_columns::<Equal>(
525 &value_col,
526 &list_col,
527 fragment.clone(),
528 |f, l, r| {
529 TypeError::BinaryOperatorNotApplicable {
530 operator: BinaryOp::Equal,
531 left: l,
532 right: r,
533 fragment: f,
534 }
535 .into_diagnostic()
536 },
537 )?;
538 result = combine_bool_columns(result, eq_result, fragment.clone(), |l, r| {
539 l || r
540 })?;
541 }
542
543 if negated {
544 result = negate_column(result, fragment.clone());
545 }
546
547 Ok(result)
548 })
549 }
550
551 Expression::Contains(e) => {
552 let list_expressions = match e.list.as_ref() {
553 Expression::Tuple(tuple) => &tuple.expressions,
554 Expression::List(list) => &list.expressions,
555 _ => from_ref(e.list.as_ref()),
556 };
557 let value = compile_expression(_ctx, &e.value)?;
558 let list: Vec<CompiledExpr> = list_expressions
559 .iter()
560 .map(|expr| compile_expression(_ctx, expr))
561 .collect::<Result<Vec<_>>>()?;
562 let fragment = e.fragment.clone();
563 CompiledExpr::new(move |ctx| {
564 let value_col = value.execute(ctx)?;
565
566 if list.is_empty() {
567 let len = value_col.data().len();
568 let result = vec![true; len];
569 return Ok(ColumnWithName::new(fragment.clone(), ColumnBuffer::bool(result)));
570 }
571
572 let first_col = list[0].execute(ctx)?;
573 let mut result = list_contains_element(&value_col, &first_col, &fragment)?;
574
575 for list_expr in list.iter().skip(1) {
576 let list_col = list_expr.execute(ctx)?;
577 let element_result = list_contains_element(&value_col, &list_col, &fragment)?;
578 result = combine_bool_columns(
579 result,
580 element_result,
581 fragment.clone(),
582 |l, r| l && r,
583 )?;
584 }
585
586 Ok(result)
587 })
588 }
589
590 Expression::Cast(e) => {
591 let label = display_label(expr);
592 if let Expression::Constant(const_expr) = e.expression.as_ref() {
593 let const_expr = const_expr.clone();
594 let target_type = e.to.ty.clone();
595 let inner_fragment = e.expression.full_fragment_owned();
596 CompiledExpr::new(move |ctx| {
597 let row_count = ctx.take.unwrap_or(ctx.row_count);
598 let data = constant_value(&const_expr, row_count)?;
599 let casted = if data.get_type() == target_type {
600 data
601 } else {
602 apply_cast(ctx, &data, &target_type, &inner_fragment)?
603 };
604 Ok(ColumnWithName::new(label.clone(), casted))
605 })
606 } else {
607 let inner = compile_expression(_ctx, &e.expression)?;
608 let target_type = e.to.ty.clone();
609 let inner_fragment = e.expression.full_fragment_owned();
610 CompiledExpr::new(move |ctx| {
611 let column = inner.execute(ctx)?;
612 let casted = apply_cast(ctx, column.data(), &target_type, &inner_fragment)?;
613 Ok(ColumnWithName::new(label.clone(), casted))
614 })
615 }
616 }
617
618 Expression::If(e) => {
619 let condition = compile_expression(_ctx, &e.condition)?;
620 let then_expr = compile_expressions(_ctx, from_ref(e.then_expr.as_ref()))?;
621 let else_ifs: Vec<(CompiledExpr, Vec<CompiledExpr>)> = e
622 .else_ifs
623 .iter()
624 .map(|ei| {
625 Ok((
626 compile_expression(_ctx, &ei.condition)?,
627 compile_expressions(_ctx, from_ref(ei.then_expr.as_ref()))?,
628 ))
629 })
630 .collect::<Result<Vec<_>>>()?;
631 let else_branch: Option<Vec<CompiledExpr>> = match &e.else_expr {
632 Some(expr) => Some(compile_expressions(_ctx, from_ref(expr.as_ref()))?),
633 None => None,
634 };
635 let fragment = e.fragment.clone();
636 CompiledExpr::new_multi(move |ctx| {
637 execute_if_multi(ctx, &condition, &then_expr, &else_ifs, &else_branch, &fragment)
638 })
639 }
640
641 Expression::Map(e) => {
642 let expressions = compile_expressions(_ctx, &e.expressions)?;
643 CompiledExpr::new_multi(move |ctx| execute_projection_multi(ctx, &expressions))
644 }
645
646 Expression::Extend(e) => {
647 let expressions = compile_expressions(_ctx, &e.expressions)?;
648 CompiledExpr::new_multi(move |ctx| execute_projection_multi(ctx, &expressions))
649 }
650
651 Expression::Call(e) => {
652 let compiled_args: Vec<CompiledExpr> =
653 e.args.iter().map(|arg| compile_expression(_ctx, arg)).collect::<Result<Vec<_>>>()?;
654 let type_named_args: Vec<Option<(ValueType, Fragment)>> =
655 e.args.iter()
656 .map(|arg| match arg {
657 Expression::Column(column) => ValueType::from_str(column.0.name.text())
658 .ok()
659 .map(|ty| (ty, column.0.name.clone())),
660 _ => None,
661 })
662 .collect();
663 let expr = e.clone();
664 CompiledExpr::new(move |ctx| {
665 let type_positions = ctx
666 .routines
667 .get_function(expr.func.0.text())
668 .map(|function| function.type_argument_positions().to_vec())
669 .unwrap_or_default();
670 let mut arg_columns = Vec::with_capacity(compiled_args.len());
671 for (index, compiled_arg) in compiled_args.iter().enumerate() {
672 match &type_named_args[index] {
673 Some((ty, fragment)) if type_positions.contains(&index) => {
674 arg_columns.push(type_column(ctx, ty, fragment));
675 }
676 _ => arg_columns.push(compiled_arg.execute(ctx)?),
677 }
678 }
679 let arguments = Columns::new(arg_columns);
680 call_builtin(ctx, &expr, arguments)
681 })
682 }
683
684 Expression::SumTypeConstructor(_) => {
685 panic!(
686 "SumTypeConstructor in expression context - constructors should be expanded by InlineDataNode before expression compilation"
687 );
688 }
689
690 Expression::IsVariant(e) => {
691 let col_name = match e.expression.as_ref() {
692 Expression::Column(c) => c.0.name.text().to_string(),
693 other => display_label(other).text().to_string(),
694 };
695 let tag_col_name = format!("{}_tag", col_name);
696 let tag = e.tag.expect("IS variant tag must be resolved before compilation");
697 let fragment = e.fragment.clone();
698 CompiledExpr::new(move |ctx| {
699 if let Some(tag_col) =
700 ctx.columns.iter().find(|c| c.name().text() == tag_col_name.as_str())
701 {
702 match tag_col.data() {
703 ColumnBuffer::Uint1(container) => {
704 let results: Vec<bool> = container
705 .iter()
706 .take(ctx.row_count)
707 .map(|v| v == Some(tag))
708 .collect();
709 Ok(ColumnWithName::new(
710 fragment.clone(),
711 ColumnBuffer::bool(results),
712 ))
713 }
714 _ => Ok(ColumnWithName {
715 name: fragment.clone(),
716 data: ColumnBuffer::none_typed(
717 ValueType::Boolean,
718 ctx.row_count,
719 ),
720 }),
721 }
722 } else {
723 Ok(ColumnWithName {
724 name: fragment.clone(),
725 data: ColumnBuffer::none_typed(ValueType::Boolean, ctx.row_count),
726 })
727 }
728 })
729 }
730
731 Expression::FieldAccess(e) => {
732 let field_name = e.field.text().to_string();
733
734 let var_name = match e.object.as_ref() {
735 Expression::Variable(var_expr) => Some(var_expr.name().to_string()),
736 _ => None,
737 };
738 let object = compile_expression(_ctx, &e.object)?;
739 CompiledExpr::new(move |ctx| {
740 if let Some(ref variable_name) = var_name {
741 match ctx.symbols.get(variable_name) {
742 Some(Variable::Columns {
743 columns,
744 }) if !columns.is_scalar() => {
745 let col_pos = columns
746 .names
747 .iter()
748 .position(|n| n.text() == field_name);
749 match col_pos {
750 Some(pos) => {
751 let value = columns.columns[pos].get_value(0);
752 let row_count =
753 ctx.take.unwrap_or(ctx.row_count);
754 let mut data = ColumnBuffer::with_capacity(
755 value.get_type(),
756 row_count,
757 );
758 for _ in 0..row_count {
759 data.push_value(value.clone());
760 }
761 Ok(ColumnWithName {
762 name: Fragment::internal(&field_name),
763 data,
764 })
765 }
766 None => {
767 let available: Vec<String> = columns
768 .names
769 .iter()
770 .map(|n| n.text().to_string())
771 .collect();
772 Err(TypeError::Runtime {
773 kind: RuntimeErrorKind::FieldNotFound {
774 variable: variable_name
775 .to_string(),
776 field: field_name.to_string(),
777 available,
778 },
779 message: format!(
780 "Field '{}' not found on variable '{}'",
781 field_name, variable_name
782 ),
783 }
784 .into())
785 }
786 }
787 }
788 Some(Variable::Columns {
789 ..
790 })
791 | Some(Variable::Closure(_)) => Err(TypeError::Runtime {
792 kind: RuntimeErrorKind::FieldNotFound {
793 variable: variable_name.to_string(),
794 field: field_name.to_string(),
795 available: vec![],
796 },
797 message: format!(
798 "Field '{}' not found on variable '{}'",
799 field_name, variable_name
800 ),
801 }
802 .into()),
803 Some(Variable::ForIterator {
804 ..
805 }) => Err(TypeError::Runtime {
806 kind: RuntimeErrorKind::VariableIsDataframe {
807 name: variable_name.to_string(),
808 },
809 message: format!(
810 "Variable '{}' contains a dataframe and cannot be used directly in scalar expressions",
811 variable_name
812 ),
813 }
814 .into()),
815 None => Err(TypeError::Runtime {
816 kind: RuntimeErrorKind::VariableNotFound {
817 name: variable_name.to_string(),
818 },
819 message: format!("Variable '{}' is not defined", variable_name),
820 }
821 .into()),
822 }
823 } else {
824 let _obj_col = object.execute(ctx)?;
825 Err(TypeError::Runtime {
826 kind: RuntimeErrorKind::FieldNotFound {
827 variable: "<expression>".to_string(),
828 field: field_name.to_string(),
829 available: vec![],
830 },
831 message: format!(
832 "Field '{}' not found on variable '<expression>'",
833 field_name
834 ),
835 }
836 .into())
837 }
838 })
839 }
840 })
841}
842
843fn compile_expressions(ctx: &CompileContext, exprs: &[Expression]) -> Result<Vec<CompiledExpr>> {
844 exprs.iter().map(|e| compile_expression(ctx, e)).collect()
845}
846
847fn type_column(ctx: &EvalContext, ty: &ValueType, fragment: &Fragment) -> ColumnWithName {
848 let row_count = ctx.take.unwrap_or(ctx.row_count);
849 let values: Vec<Value> = (0..row_count).map(|_| Value::Type(ty.clone())).collect();
850 ColumnWithName::new(fragment.text(), ColumnBuffer::any(values))
851}
852
853fn combine_bool_columns(
854 left: ColumnWithName,
855 right: ColumnWithName,
856 fragment: Fragment,
857 combine_fn: fn(bool, bool) -> bool,
858) -> Result<ColumnWithName> {
859 binary_op_unwrap_option(&left, &right, fragment.clone(), |left, right| match (left.data(), right.data()) {
860 (ColumnBuffer::Bool(l), ColumnBuffer::Bool(r)) => {
861 let len = l.len();
862 let mut data = Vec::with_capacity(len);
863 let mut bitvec = Vec::with_capacity(len);
864
865 for i in 0..len {
866 let l_defined = l.is_defined(i);
867 let r_defined = r.is_defined(i);
868 let l_val = l.data().get(i);
869 let r_val = r.data().get(i);
870
871 if l_defined && r_defined {
872 data.push(combine_fn(l_val, r_val));
873 bitvec.push(true);
874 } else {
875 data.push(false);
876 bitvec.push(false);
877 }
878 }
879
880 Ok(ColumnWithName {
881 name: fragment.clone(),
882 data: ColumnBuffer::bool_with_bitvec(data, bitvec),
883 })
884 }
885 _ => {
886 unreachable!("combine_bool_columns should only be called with boolean columns")
887 }
888 })
889}
890
891fn list_items_contain(items: &[Value], element: &Value, fragment: &Fragment) -> bool {
892 if items.iter().any(|item| item == element) {
893 return true;
894 }
895 if items.is_empty() {
896 return false;
897 }
898
899 if let Some(items_buf) = build_homogeneous_buffer(items) {
900 let elems_buf = ColumnBuffer::from_many(element.clone(), items.len());
901 let items_col = ColumnWithName::new(fragment.clone(), items_buf);
902 let elems_col = ColumnWithName::new(fragment.clone(), elems_buf);
903 return compare_columns::<Equal>(&items_col, &elems_col, fragment.clone(), |f, l, r| {
904 TypeError::BinaryOperatorNotApplicable {
905 operator: BinaryOp::Equal,
906 left: l,
907 right: r,
908 fragment: f,
909 }
910 .into_diagnostic()
911 })
912 .map(|c| bool_column_has_true(&c))
913 .unwrap_or(false);
914 }
915
916 list_items_contain_per_item(items, element, fragment)
917}
918
919fn list_items_contain_per_item(items: &[Value], element: &Value, fragment: &Fragment) -> bool {
920 items.iter().any(|item| {
921 let item_col = ColumnWithName::new(fragment.clone(), ColumnBuffer::from(item.clone()));
922 let elem_col = ColumnWithName::new(fragment.clone(), ColumnBuffer::from(element.clone()));
923 compare_columns::<Equal>(&item_col, &elem_col, fragment.clone(), |f, l, r| {
924 TypeError::BinaryOperatorNotApplicable {
925 operator: BinaryOp::Equal,
926 left: l,
927 right: r,
928 fragment: f,
929 }
930 .into_diagnostic()
931 })
932 .ok()
933 .and_then(|c| match c.data() {
934 ColumnBuffer::Bool(b) => Some(b.data().get(0)),
935 _ => None,
936 })
937 .unwrap_or(false)
938 })
939}
940
941fn bool_column_has_true(col: &ColumnWithName) -> bool {
942 match col.data() {
943 ColumnBuffer::Bool(b) => b.data().any(),
944 ColumnBuffer::Option {
945 inner,
946 bitvec,
947 } => match inner.as_ref() {
948 ColumnBuffer::Bool(b) => {
949 let n = bitvec.len().min(b.len());
950 (0..n).any(|i| bitvec.get(i) && b.data().get(i))
951 }
952 _ => false,
953 },
954 _ => false,
955 }
956}
957
958fn build_homogeneous_buffer(items: &[Value]) -> Option<ColumnBuffer> {
959 let first = items.first()?;
960 let first_disc = discriminant(first);
961 if !items.iter().all(|v| discriminant(v) == first_disc) {
962 return None;
963 }
964
965 macro_rules! collect {
966 ($variant:ident, $constructor:ident, |$x:ident| $convert:expr) => {{
967 let data: Vec<_> = items
968 .iter()
969 .map(|v| match v {
970 Value::$variant($x) => $convert,
971 _ => unreachable!("homogeneous check guarantees variant"),
972 })
973 .collect();
974 Some(ColumnBuffer::$constructor(data))
975 }};
976 }
977
978 match first {
979 Value::Boolean(_) => collect!(Boolean, bool, |x| *x),
980 Value::Float4(_) => collect!(Float4, float4, |x| x.value()),
981 Value::Float8(_) => collect!(Float8, float8, |x| x.value()),
982 Value::Int1(_) => collect!(Int1, int1, |x| *x),
983 Value::Int2(_) => collect!(Int2, int2, |x| *x),
984 Value::Int4(_) => collect!(Int4, int4, |x| *x),
985 Value::Int8(_) => collect!(Int8, int8, |x| *x),
986 Value::Int16(_) => collect!(Int16, int16, |x| *x),
987 Value::Uint1(_) => collect!(Uint1, uint1, |x| *x),
988 Value::Uint2(_) => collect!(Uint2, uint2, |x| *x),
989 Value::Uint4(_) => collect!(Uint4, uint4, |x| *x),
990 Value::Uint8(_) => collect!(Uint8, uint8, |x| *x),
991 Value::Uint16(_) => collect!(Uint16, uint16, |x| *x),
992 Value::Utf8(_) => collect!(Utf8, utf8, |x| x.clone()),
993 Value::Date(_) => collect!(Date, date, |x| *x),
994 Value::DateTime(_) => collect!(DateTime, datetime, |x| *x),
995 Value::Time(_) => collect!(Time, time, |x| *x),
996 Value::Duration(_) => collect!(Duration, duration, |x| *x),
997 Value::Uuid4(_) => collect!(Uuid4, uuid4, |x| *x),
998 Value::Uuid7(_) => collect!(Uuid7, uuid7, |x| *x),
999 Value::IdentityId(_) => collect!(IdentityId, identity_id, |x| *x),
1000 Value::Blob(_) => collect!(Blob, blob, |x| x.clone()),
1001 Value::Int(_) => collect!(Int, int, |x| x.clone()),
1002 Value::Uint(_) => collect!(Uint, uint, |x| x.clone()),
1003 Value::Decimal(_) => collect!(Decimal, decimal, |x| x.clone()),
1004 Value::DictionaryId(_) => collect!(DictionaryId, dictionary_id, |x| *x),
1005
1006 _ => None,
1007 }
1008}
1009
1010fn list_contains_element(
1011 list_col: &ColumnWithName,
1012 element_col: &ColumnWithName,
1013 fragment: &Fragment,
1014) -> Result<ColumnWithName> {
1015 let len = list_col.data().len();
1016 let mut data = Vec::with_capacity(len);
1017
1018 for i in 0..len {
1019 let list_value = list_col.data().get_value(i);
1020 let element_value = element_col.data().get_value(i);
1021
1022 let contained = match &list_value {
1023 Value::List(items) => list_items_contain(items, &element_value, fragment),
1024 Value::Tuple(items) => list_items_contain(items, &element_value, fragment),
1025 Value::Any(boxed) => match boxed.as_ref() {
1026 Value::List(items) => list_items_contain(items, &element_value, fragment),
1027 Value::Tuple(items) => list_items_contain(items, &element_value, fragment),
1028 _ => false,
1029 },
1030 _ => false,
1031 };
1032 data.push(contained);
1033 }
1034
1035 Ok(ColumnWithName::new(fragment.clone(), ColumnBuffer::bool(data)))
1036}
1037
1038fn negate_column(col: ColumnWithName, fragment: Fragment) -> ColumnWithName {
1039 unary_op_unwrap_option(&col, |col| match col.data() {
1040 ColumnBuffer::Bool(container) => {
1041 let len = container.len();
1042 let mut data = Vec::with_capacity(len);
1043 let mut bitvec = Vec::with_capacity(len);
1044
1045 for i in 0..len {
1046 if container.is_defined(i) {
1047 data.push(!container.data().get(i));
1048 bitvec.push(true);
1049 } else {
1050 data.push(false);
1051 bitvec.push(false);
1052 }
1053 }
1054
1055 Ok(ColumnWithName {
1056 name: fragment.clone(),
1057 data: ColumnBuffer::bool_with_bitvec(data, bitvec),
1058 })
1059 }
1060 _ => unreachable!("negate_column should only be called with boolean columns"),
1061 })
1062 .unwrap()
1063}
1064
1065fn is_truthy(value: &Value) -> bool {
1066 match value {
1067 Value::Boolean(true) => true,
1068 Value::Boolean(false) => false,
1069 Value::None {
1070 ..
1071 } => false,
1072 Value::Int1(0) | Value::Int2(0) | Value::Int4(0) | Value::Int8(0) | Value::Int16(0) => false,
1073 Value::Uint1(0) | Value::Uint2(0) | Value::Uint4(0) | Value::Uint8(0) | Value::Uint16(0) => false,
1074 Value::Int1(_) | Value::Int2(_) | Value::Int4(_) | Value::Int8(_) | Value::Int16(_) => true,
1075 Value::Uint1(_) | Value::Uint2(_) | Value::Uint4(_) | Value::Uint8(_) | Value::Uint16(_) => true,
1076 Value::Utf8(s) => !s.is_empty(),
1077 _ => true,
1078 }
1079}
1080
1081fn describe_branch(columns: &[ColumnWithName]) -> Vec<String> {
1082 columns.iter().map(|col| format!("{}: {}", col.name.text(), col.data().get_type())).collect()
1083}
1084
1085fn execute_if_multi(
1086 ctx: &EvalContext,
1087 condition: &CompiledExpr,
1088 then_expr: &[CompiledExpr],
1089 else_ifs: &[(CompiledExpr, Vec<CompiledExpr>)],
1090 else_branch: &Option<Vec<CompiledExpr>>,
1091 _fragment: &Fragment,
1092) -> Result<Vec<ColumnWithName>> {
1093 const NO_BRANCH: usize = usize::MAX;
1094
1095 let condition_column = condition.execute(ctx)?;
1096
1097 let else_index = else_ifs.len() + 1;
1098 let mut selection: Vec<usize> = Vec::with_capacity(ctx.row_count);
1099 let mut unresolved: Vec<usize> = Vec::new();
1100
1101 for row_idx in 0..ctx.row_count {
1102 if is_truthy(&condition_column.data().get_value(row_idx)) {
1103 selection.push(0);
1104 } else {
1105 selection.push(NO_BRANCH);
1106 unresolved.push(row_idx);
1107 }
1108 }
1109
1110 for (offset, (else_if_condition, _)) in else_ifs.iter().enumerate() {
1111 if unresolved.is_empty() {
1112 break;
1113 }
1114 let else_if_column = else_if_condition.execute(ctx)?;
1115 unresolved.retain(|&row_idx| {
1116 if is_truthy(&else_if_column.data().get_value(row_idx)) {
1117 selection[row_idx] = offset + 1;
1118 false
1119 } else {
1120 true
1121 }
1122 });
1123 }
1124
1125 if else_branch.is_some() {
1126 for &row_idx in &unresolved {
1127 selection[row_idx] = else_index;
1128 }
1129 }
1130
1131 let mut evaluated: Vec<Option<Vec<ColumnWithName>>> = (0..=else_index).map(|_| None).collect();
1132 for &branch in &selection {
1133 if branch == NO_BRANCH || evaluated[branch].is_some() {
1134 continue;
1135 }
1136 let columns = if branch == 0 {
1137 execute_multi_exprs(ctx, then_expr)?
1138 } else if branch < else_index {
1139 execute_multi_exprs(ctx, &else_ifs[branch - 1].1)?
1140 } else {
1141 execute_multi_exprs(ctx, else_branch.as_ref().unwrap())?
1142 };
1143 evaluated[branch] = Some(columns);
1144 }
1145
1146 let mut layout: Option<(Vec<ValueType>, Vec<String>)> = None;
1147 for columns in evaluated.iter().flatten() {
1148 let Some((expected, expected_names)) = layout.as_mut() else {
1149 layout = Some((
1150 columns.iter().map(|col| col.data().get_type().inner_type().clone()).collect(),
1151 describe_branch(columns),
1152 ));
1153 continue;
1154 };
1155
1156 let mut disagrees = columns.len() != expected.len();
1157 if !disagrees {
1158 for (slot, col) in expected.iter_mut().zip(columns.iter()) {
1159 let incoming = col.data().get_type().inner_type().clone();
1160 if *slot == ValueType::Any {
1161 *slot = incoming;
1162 } else if incoming != ValueType::Any && incoming != *slot {
1163 disagrees = true;
1164 break;
1165 }
1166 }
1167 }
1168
1169 if disagrees {
1170 return Err(TypeError::Runtime {
1171 kind: RuntimeErrorKind::ConditionalBranchMismatch {
1172 expected: expected_names.clone(),
1173 actual: describe_branch(columns),
1174 fragment: _fragment.clone(),
1175 },
1176 message: "conditional branches produce different columns".to_string(),
1177 }
1178 .into());
1179 }
1180 }
1181
1182 let mut result_data: Option<Vec<ColumnBuffer>> = None;
1183 let mut result_names: Vec<Fragment> = Vec::new();
1184
1185 for (row_idx, &selected) in selection.iter().enumerate() {
1186 let branch_results: &[ColumnWithName] = match selected {
1187 NO_BRANCH => &[],
1188 branch => evaluated[branch].as_deref().unwrap(),
1189 };
1190
1191 if branch_results.is_empty() {
1192 if let Some(data) = result_data.as_mut() {
1193 for col_data in data.iter_mut() {
1194 col_data.push_value(Value::none());
1195 }
1196 }
1197 continue;
1198 }
1199
1200 if result_data.is_none() {
1201 let mut data: Vec<ColumnBuffer> = branch_results
1202 .iter()
1203 .map(|col| ColumnBuffer::with_capacity(col.data().get_type(), ctx.row_count))
1204 .collect();
1205 for _ in 0..row_idx {
1206 for col_data in data.iter_mut() {
1207 col_data.push_value(Value::none());
1208 }
1209 }
1210 result_data = Some(data);
1211 result_names = branch_results.iter().map(|col| col.name.clone()).collect();
1212 }
1213
1214 let data = result_data.as_mut().unwrap();
1215 for (slot, branch_col) in data.iter_mut().zip(branch_results.iter()) {
1216 slot.push_value(branch_col.data().get_value(row_idx));
1217 }
1218 }
1219
1220 let result_data = result_data.unwrap_or_default();
1221 let result: Vec<ColumnWithName> = result_data
1222 .into_iter()
1223 .enumerate()
1224 .map(|(i, data)| ColumnWithName {
1225 name: result_names.get(i).cloned().unwrap_or_else(|| Fragment::internal("column")),
1226 data,
1227 })
1228 .collect();
1229
1230 if result.is_empty() {
1231 Ok(vec![ColumnWithName {
1232 name: Fragment::internal("none"),
1233 data: ColumnBuffer::none_typed(ValueType::Boolean, ctx.row_count),
1234 }])
1235 } else {
1236 Ok(result)
1237 }
1238}
1239
1240fn execute_multi_exprs(ctx: &EvalContext, exprs: &[CompiledExpr]) -> Result<Vec<ColumnWithName>> {
1241 let mut result = Vec::new();
1242 for expr in exprs {
1243 result.extend(expr.execute_multi(ctx)?);
1244 }
1245 Ok(result)
1246}
1247
1248fn execute_projection_multi(ctx: &EvalContext, expressions: &[CompiledExpr]) -> Result<Vec<ColumnWithName>> {
1249 let mut result = Vec::with_capacity(expressions.len());
1250
1251 for expr in expressions {
1252 let column = expr.execute(ctx)?;
1253 let name = column.name.text().to_string();
1254 result.push(ColumnWithName::new(Fragment::internal(name), column.data));
1255 }
1256
1257 Ok(result)
1258}
1259
1260fn apply_cast(ctx: &EvalContext, data: &ColumnBuffer, target: &ValueType, fragment: &Fragment) -> Result<ColumnBuffer> {
1261 cast_column_data(ctx, data, target.clone(), &|| fragment.clone())
1262 .map_err(|e| wrap_cast_error(e, fragment.clone(), target))
1263}
1264
1265fn wrap_cast_error(err: Error, fragment: Fragment, target: &ValueType) -> Error {
1266 if err.0.code.starts_with("CAST_") {
1267 return err;
1268 }
1269 let cause = err.diagnostic();
1270 let wrapped = if target.is_bool() {
1271 CastError::InvalidBoolean {
1272 fragment,
1273 cause,
1274 }
1275 } else if target.is_temporal() {
1276 CastError::InvalidTemporal {
1277 fragment,
1278 target: target.clone(),
1279 cause,
1280 }
1281 } else if target.is_uuid() || *target == ValueType::IdentityId {
1282 CastError::InvalidUuid {
1283 fragment,
1284 target: target.clone(),
1285 cause,
1286 }
1287 } else {
1288 CastError::InvalidNumber {
1289 fragment,
1290 target: target.clone(),
1291 cause,
1292 }
1293 };
1294 Error::from(wrapped)
1295}
1296
1297#[cfg(test)]
1298mod tests {
1299 use reifydb_core::{
1300 interface::identifier::ColumnIdentifier,
1301 value::column::{ColumnWithName, buffer::ColumnBuffer, columns::Columns},
1302 };
1303 use reifydb_rql::expression::{
1304 CastExpression, ColumnExpression, ConstantExpression, ElseIfExpression, Expression, IfExpression,
1305 MapExpression, TypeExpression,
1306 };
1307 use reifydb_value::{
1308 fragment::Fragment,
1309 value::{Value, value_type::ValueType},
1310 };
1311
1312 use crate::expression::{context::EvalContext, eval::evaluate};
1313
1314 fn column(name: &str) -> Expression {
1315 Expression::Column(ColumnExpression(ColumnIdentifier::with_alias(
1316 Fragment::internal("t"),
1317 Fragment::internal(name),
1318 )))
1319 }
1320
1321 fn uncastable() -> Expression {
1322 Expression::Cast(CastExpression {
1323 fragment: Fragment::testing_empty(),
1324 expression: Box::new(Expression::Constant(ConstantExpression::Text {
1325 fragment: Fragment::internal("not-a-number"),
1326 })),
1327 to: TypeExpression {
1328 fragment: Fragment::testing_empty(),
1329 ty: ValueType::Int4,
1330 },
1331 })
1332 }
1333
1334 fn conditional(
1335 condition: Expression,
1336 then_expr: Expression,
1337 else_ifs: Vec<(Expression, Expression)>,
1338 else_expr: Option<Expression>,
1339 ) -> Expression {
1340 Expression::If(IfExpression {
1341 condition: Box::new(condition),
1342 then_expr: Box::new(then_expr),
1343 else_ifs: else_ifs
1344 .into_iter()
1345 .map(|(condition, then_expr)| ElseIfExpression {
1346 condition: Box::new(condition),
1347 then_expr: Box::new(then_expr),
1348 fragment: Fragment::testing_empty(),
1349 })
1350 .collect(),
1351 else_expr: else_expr.map(Box::new),
1352 fragment: Fragment::testing_empty(),
1353 })
1354 }
1355
1356 fn bools(name: &str, data: [bool; 4]) -> ColumnWithName {
1357 ColumnWithName::new(Fragment::internal(name), ColumnBuffer::bool(data))
1358 }
1359
1360 fn ints(name: &str, data: [i32; 4]) -> ColumnWithName {
1361 ColumnWithName::new(Fragment::internal(name), ColumnBuffer::int4(data))
1362 }
1363
1364 #[test]
1365 fn every_row_reads_its_own_index_from_the_branch_it_selected() {
1366 let base = EvalContext::testing();
1369 let ctx = base.with_eval(
1370 Columns::new(vec![
1371 bools("flag", [true, false, false, true]),
1372 ints("hi", [100, 200, 300, 400]),
1373 ints("lo", [1, 2, 3, 4]),
1374 ]),
1375 4,
1376 );
1377
1378 let result =
1379 evaluate(&ctx, &conditional(column("flag"), column("hi"), vec![], Some(column("lo")))).unwrap();
1380
1381 assert_eq!(*result.data(), ColumnBuffer::int4([100, 2, 3, 400]));
1382 }
1383
1384 #[test]
1385 fn an_else_if_chain_gives_each_row_its_first_matching_branch() {
1386 let base = EvalContext::testing();
1389 let ctx = base.with_eval(
1390 Columns::new(vec![
1391 bools("first", [true, false, false, false]),
1392 bools("second", [true, true, true, false]),
1393 ints("a", [10, 20, 30, 40]),
1394 ints("b", [1, 2, 3, 4]),
1395 ints("c", [-1, -2, -3, -4]),
1396 ]),
1397 4,
1398 );
1399
1400 let result = evaluate(
1401 &ctx,
1402 &conditional(
1403 column("first"),
1404 column("a"),
1405 vec![(column("second"), column("b"))],
1406 Some(column("c")),
1407 ),
1408 )
1409 .unwrap();
1410
1411 assert_eq!(*result.data(), ColumnBuffer::int4([10, 2, 3, -4]));
1412 }
1413
1414 #[test]
1415 fn a_row_that_matches_no_branch_becomes_none() {
1416 let base = EvalContext::testing();
1419 let ctx = base.with_eval(
1420 Columns::new(vec![bools("flag", [true, false, false, true]), ints("hi", [7, 8, 9, 10])]),
1421 4,
1422 );
1423
1424 let result = evaluate(&ctx, &conditional(column("flag"), column("hi"), vec![], None)).unwrap();
1425
1426 assert_eq!(result.data().len(), 4);
1427 assert_eq!(result.data().get_value(0), Value::Int4(7));
1428 assert!(matches!(result.data().get_value(1), Value::None { .. }));
1429 assert!(matches!(result.data().get_value(2), Value::None { .. }));
1430 assert_eq!(result.data().get_value(3), Value::Int4(10));
1431 }
1432
1433 #[test]
1434 fn a_branch_that_no_row_selects_is_never_evaluated() {
1435 let base = EvalContext::testing();
1438 let all_true = base.with_eval(
1439 Columns::new(vec![bools("flag", [true, true, true, true]), ints("hi", [1, 2, 3, 4])]),
1440 4,
1441 );
1442
1443 let skipped =
1444 evaluate(&all_true, &conditional(column("flag"), column("hi"), vec![], Some(uncastable())));
1445
1446 assert!(skipped.is_ok(), "an else branch no row selects must not be evaluated");
1447
1448 let one_false = base.with_eval(
1449 Columns::new(vec![bools("flag", [true, true, false, true]), ints("hi", [1, 2, 3, 4])]),
1450 4,
1451 );
1452
1453 let taken =
1454 evaluate(&one_false, &conditional(column("flag"), column("hi"), vec![], Some(uncastable())));
1455
1456 assert!(
1457 taken.is_err(),
1458 "the branch must really fail when a row selects it, or the case above is vacuous"
1459 );
1460 }
1461
1462 fn multi(names: [&str; 2]) -> Expression {
1463 Expression::Map(MapExpression {
1464 expressions: names.iter().map(|name| column(name)).collect(),
1465 fragment: Fragment::testing_empty(),
1466 })
1467 }
1468
1469 fn none_literal() -> Expression {
1470 Expression::Constant(ConstantExpression::None {
1471 fragment: Fragment::testing_empty(),
1472 })
1473 }
1474
1475 fn four_row_ctx(extra: Vec<ColumnWithName>) -> Columns {
1476 let mut cols = vec![bools("flag", [true, false, false, true])];
1477 cols.extend(extra);
1478 Columns::new(cols)
1479 }
1480
1481 #[test]
1482 fn branches_of_different_types_are_rejected_instead_of_aborting() {
1483 let base = EvalContext::testing();
1486 let ctx = base.with_eval(
1487 four_row_ctx(vec![
1488 ints("small", [1, 2, 3, 4]),
1489 ColumnWithName::new(Fragment::internal("wide"), ColumnBuffer::int8([5i64, 6, 7, 8])),
1490 ]),
1491 4,
1492 );
1493
1494 let err = evaluate(&ctx, &conditional(column("flag"), column("small"), vec![], Some(column("wide"))))
1495 .expect_err("int4 and int8 branches must not be accepted");
1496
1497 assert_eq!(err.0.code, "RUNTIME_012");
1498 }
1499
1500 #[test]
1501 fn an_optional_branch_still_pairs_with_its_bare_type() {
1502 let base = EvalContext::testing();
1505 let ctx = base.with_eval(
1506 four_row_ctx(vec![
1507 ints("bare", [1, 2, 3, 4]),
1508 ColumnWithName::new(
1509 Fragment::internal("opt"),
1510 ColumnBuffer::int4_with_bitvec([9, 8, 7, 6], vec![true, false, true, true]),
1511 ),
1512 ]),
1513 4,
1514 );
1515
1516 let result = evaluate(&ctx, &conditional(column("flag"), column("bare"), vec![], Some(column("opt"))))
1517 .expect("a bare and an optional branch of one base type must agree");
1518
1519 assert_eq!(result.data().len(), 4);
1520 }
1521
1522 #[test]
1523 fn a_none_branch_widens_to_the_other_branch_type() {
1524 let base = EvalContext::testing();
1527 let ctx = base.with_eval(four_row_ctx(vec![ints("hi", [7, 8, 9, 10])]), 4);
1528
1529 let result = evaluate(&ctx, &conditional(column("flag"), column("hi"), vec![], Some(none_literal())))
1530 .expect("a none branch must widen to the other branch type");
1531
1532 assert_eq!(result.data().get_value(0), Value::Int4(7));
1533 assert!(matches!(result.data().get_value(1), Value::None { .. }));
1534 }
1535
1536 #[test]
1537 fn branches_of_different_widths_are_rejected() {
1538 let base = EvalContext::testing();
1541 let ctx = base.with_eval(four_row_ctx(vec![ints("a", [1, 2, 3, 4]), ints("b", [10, 20, 30, 40])]), 4);
1542
1543 let wide_then =
1544 evaluate(&ctx, &conditional(column("flag"), multi(["a", "b"]), vec![], Some(column("a"))))
1545 .expect_err("a two column branch must not pair with a one column branch");
1546 assert_eq!(wide_then.0.code, "RUNTIME_012");
1547
1548 let wide_else =
1549 evaluate(&ctx, &conditional(column("flag"), column("a"), vec![], Some(multi(["a", "b"]))))
1550 .expect_err("a one column branch must not pair with a two column branch");
1551 assert_eq!(wide_else.0.code, "RUNTIME_012");
1552 }
1553
1554 #[test]
1555 fn a_mismatched_branch_no_row_selects_is_still_not_rejected() {
1556 let base = EvalContext::testing();
1559 let ctx = base.with_eval(
1560 Columns::new(vec![
1561 bools("flag", [true, true, true, true]),
1562 ints("small", [1, 2, 3, 4]),
1563 ColumnWithName::new(Fragment::internal("wide"), ColumnBuffer::int8([5i64, 6, 7, 8])),
1564 ]),
1565 4,
1566 );
1567
1568 let result =
1569 evaluate(&ctx, &conditional(column("flag"), column("small"), vec![], Some(column("wide"))))
1570 .expect("an unselected branch must not be validated");
1571
1572 assert_eq!(result.data().len(), 4);
1573 }
1574
1575 #[test]
1576 fn a_multi_column_value_in_a_single_column_slot_is_rejected() {
1577 let base = EvalContext::testing();
1580 let ctx = base.with_eval(four_row_ctx(vec![ints("a", [1, 2, 3, 4]), ints("b", [10, 20, 30, 40])]), 4);
1581
1582 let err = evaluate(&ctx, &multi(["a", "b"])).expect_err("a two column value must not be truncated");
1583
1584 assert_eq!(err.0.code, "RUNTIME_010");
1585 }
1586}