Skip to main content

reifydb_engine/expression/
compile.rs

1// SPDX-License-Identifier: AGPL-3.0-or-later
2// Copyright (c) 2025 ReifyDB
3
4use std::slice::from_ref;
5
6use reifydb_core::value::column::{Column, data::ColumnData};
7use reifydb_rql::expression::Expression;
8use reifydb_type::{
9	error::{BinaryOp, Error, IntoDiagnostic, LogicalOp, OperandCategory, RuntimeErrorKind, TypeError},
10	fragment::Fragment,
11	value::{Value, r#type::Type},
12};
13
14use super::{
15	context::CompileContext,
16	option::{binary_op_unwrap_option, unary_op_unwrap_option},
17};
18use crate::{
19	Result,
20	error::CastError,
21	expression::{
22		access::access_lookup,
23		arith::{add::add_columns, div::div_columns, mul::mul_columns, rem::rem_columns, sub::sub_columns},
24		call::call_eval,
25		cast::cast_column_data,
26		compare::{Equal, GreaterThan, GreaterThanEqual, LessThan, LessThanEqual, NotEqual, compare_columns},
27		constant::{constant_value, constant_value_of},
28		context::EvalContext,
29		lookup::column_lookup,
30		parameter::parameter_lookup,
31		prefix::prefix_eval,
32	},
33	vm::stack::Variable,
34};
35
36pub struct CompiledExpr {
37	inner: CompiledExprInner,
38	access_column_name: Option<String>,
39}
40
41enum CompiledExprInner {
42	Single(Box<dyn Fn(&EvalContext) -> Result<Column> + Send + Sync>),
43	Multi(Box<dyn Fn(&EvalContext) -> Result<Vec<Column>> + Send + Sync>),
44}
45
46impl CompiledExpr {
47	pub fn new(f: impl Fn(&EvalContext) -> Result<Column> + Send + Sync + 'static) -> Self {
48		Self {
49			inner: CompiledExprInner::Single(Box::new(f)),
50			access_column_name: None,
51		}
52	}
53
54	pub fn new_multi(f: impl Fn(&EvalContext) -> Result<Vec<Column>> + Send + Sync + 'static) -> Self {
55		Self {
56			inner: CompiledExprInner::Multi(Box::new(f)),
57			access_column_name: None,
58		}
59	}
60
61	pub fn new_access(name: String, f: impl Fn(&EvalContext) -> Result<Column> + Send + Sync + 'static) -> Self {
62		Self {
63			inner: CompiledExprInner::Single(Box::new(f)),
64			access_column_name: Some(name),
65		}
66	}
67
68	pub fn access_column_name(&self) -> Option<&str> {
69		self.access_column_name.as_deref()
70	}
71
72	pub fn execute(&self, ctx: &EvalContext) -> Result<Column> {
73		match &self.inner {
74			CompiledExprInner::Single(f) => f(ctx),
75			CompiledExprInner::Multi(f) => {
76				let columns = f(ctx)?;
77				Ok(columns.into_iter().next().unwrap_or_else(|| Column {
78					name: Fragment::internal("none"),
79					data: ColumnData::with_capacity(Type::Option(Box::new(Type::Boolean)), 0),
80				}))
81			}
82		}
83	}
84
85	pub fn execute_multi(&self, ctx: &EvalContext) -> Result<Vec<Column>> {
86		match &self.inner {
87			CompiledExprInner::Single(f) => Ok(vec![f(ctx)?]),
88			CompiledExprInner::Multi(f) => f(ctx),
89		}
90	}
91}
92
93/// Compile an `Expression` into a `CompiledExpr`.
94///
95/// All execution logic is baked into closures at compile time — no match dispatch at runtime.
96pub fn compile_expression(_ctx: &CompileContext, expr: &Expression) -> Result<CompiledExpr> {
97	Ok(match expr {
98		Expression::Constant(e) => {
99			let expr = e.clone();
100			CompiledExpr::new(move |ctx| {
101				let row_count = ctx.take.unwrap_or(ctx.row_count);
102				Ok(Column {
103					name: expr.full_fragment_owned(),
104					data: constant_value(&expr, row_count)?,
105				})
106			})
107		}
108
109		Expression::Column(e) => {
110			let expr = e.clone();
111			CompiledExpr::new(move |ctx| column_lookup(ctx, &expr))
112		}
113
114		Expression::Variable(e) => {
115			let expr = e.clone();
116			CompiledExpr::new(move |ctx| {
117				let variable_name = expr.name();
118
119				if variable_name == "env" {
120					return Err(TypeError::Runtime {
121						kind: RuntimeErrorKind::VariableIsDataframe {
122							name: variable_name.to_string(),
123						},
124						message: format!(
125							"Variable '{}' contains a dataframe and cannot be used directly in scalar expressions",
126							variable_name
127						),
128					}
129					.into());
130				}
131
132				match ctx.symbol_table.get(variable_name) {
133					Some(Variable::Scalar(columns)) => {
134						let value = columns.scalar_value();
135						let mut data =
136							ColumnData::with_capacity(value.get_type(), ctx.row_count);
137						for _ in 0..ctx.row_count {
138							data.push_value(value.clone());
139						}
140						Ok(Column {
141							name: Fragment::internal(variable_name),
142							data,
143						})
144					}
145					Some(Variable::Columns(_))
146					| Some(Variable::ForIterator {
147						..
148					})
149					| Some(Variable::Closure(_)) => {
150						return Err(TypeError::Runtime {
151							kind: RuntimeErrorKind::VariableIsDataframe {
152								name: variable_name.to_string(),
153							},
154							message: format!(
155								"Variable '{}' contains a dataframe and cannot be used directly in scalar expressions",
156								variable_name
157							),
158						}
159						.into());
160					}
161					None => {
162						return Err(TypeError::Runtime {
163							kind: RuntimeErrorKind::VariableNotFound {
164								name: variable_name.to_string(),
165							},
166							message: format!("Variable '{}' is not defined", variable_name),
167						}
168						.into());
169					}
170				}
171			})
172		}
173
174		Expression::Parameter(e) => {
175			let expr = e.clone();
176			CompiledExpr::new(move |ctx| parameter_lookup(ctx, &expr))
177		}
178
179		Expression::Alias(e) => {
180			let inner = compile_expression(_ctx, &e.expression)?;
181			let alias = e.alias.0.clone();
182			CompiledExpr::new(move |ctx| {
183				let mut column = inner.execute(ctx)?;
184				column.name = alias.clone();
185				Ok(column)
186			})
187		}
188
189		Expression::Add(e) => {
190			let left = compile_expression(_ctx, &e.left)?;
191			let right = compile_expression(_ctx, &e.right)?;
192			let fragment = e.full_fragment_owned();
193			CompiledExpr::new(move |ctx| {
194				let l = left.execute(ctx)?;
195				let r = right.execute(ctx)?;
196				add_columns(ctx, &l, &r, || fragment.clone())
197			})
198		}
199
200		Expression::Sub(e) => {
201			let left = compile_expression(_ctx, &e.left)?;
202			let right = compile_expression(_ctx, &e.right)?;
203			let fragment = e.full_fragment_owned();
204			CompiledExpr::new(move |ctx| {
205				let l = left.execute(ctx)?;
206				let r = right.execute(ctx)?;
207				sub_columns(ctx, &l, &r, || fragment.clone())
208			})
209		}
210
211		Expression::Mul(e) => {
212			let left = compile_expression(_ctx, &e.left)?;
213			let right = compile_expression(_ctx, &e.right)?;
214			let fragment = e.full_fragment_owned();
215			CompiledExpr::new(move |ctx| {
216				let l = left.execute(ctx)?;
217				let r = right.execute(ctx)?;
218				mul_columns(ctx, &l, &r, || fragment.clone())
219			})
220		}
221
222		Expression::Div(e) => {
223			let left = compile_expression(_ctx, &e.left)?;
224			let right = compile_expression(_ctx, &e.right)?;
225			let fragment = e.full_fragment_owned();
226			CompiledExpr::new(move |ctx| {
227				let l = left.execute(ctx)?;
228				let r = right.execute(ctx)?;
229				div_columns(ctx, &l, &r, || fragment.clone())
230			})
231		}
232
233		Expression::Rem(e) => {
234			let left = compile_expression(_ctx, &e.left)?;
235			let right = compile_expression(_ctx, &e.right)?;
236			let fragment = e.full_fragment_owned();
237			CompiledExpr::new(move |ctx| {
238				let l = left.execute(ctx)?;
239				let r = right.execute(ctx)?;
240				rem_columns(ctx, &l, &r, || fragment.clone())
241			})
242		}
243
244		Expression::Equal(e) => {
245			let left = compile_expression(_ctx, &e.left)?;
246			let right = compile_expression(_ctx, &e.right)?;
247			let fragment = e.full_fragment_owned();
248			CompiledExpr::new(move |ctx| {
249				let l = left.execute(ctx)?;
250				let r = right.execute(ctx)?;
251				let result = compare_columns::<Equal>(&l, &r, fragment.clone(), |f, l, r| {
252					TypeError::BinaryOperatorNotApplicable {
253						operator: BinaryOp::Equal,
254						left: l,
255						right: r,
256						fragment: f,
257					}
258					.into_diagnostic()
259				});
260				result
261			})
262		}
263
264		Expression::NotEqual(e) => {
265			let left = compile_expression(_ctx, &e.left)?;
266			let right = compile_expression(_ctx, &e.right)?;
267			let fragment = e.full_fragment_owned();
268			CompiledExpr::new(move |ctx| {
269				let l = left.execute(ctx)?;
270				let r = right.execute(ctx)?;
271				let result = compare_columns::<NotEqual>(&l, &r, fragment.clone(), |f, l, r| {
272					TypeError::BinaryOperatorNotApplicable {
273						operator: BinaryOp::NotEqual,
274						left: l,
275						right: r,
276						fragment: f,
277					}
278					.into_diagnostic()
279				});
280				result
281			})
282		}
283
284		Expression::GreaterThan(e) => {
285			let left = compile_expression(_ctx, &e.left)?;
286			let right = compile_expression(_ctx, &e.right)?;
287			let fragment = e.full_fragment_owned();
288			CompiledExpr::new(move |ctx| {
289				let l = left.execute(ctx)?;
290				let r = right.execute(ctx)?;
291				let result = compare_columns::<GreaterThan>(&l, &r, fragment.clone(), |f, l, r| {
292					TypeError::BinaryOperatorNotApplicable {
293						operator: BinaryOp::GreaterThan,
294						left: l,
295						right: r,
296						fragment: f,
297					}
298					.into_diagnostic()
299				});
300				result
301			})
302		}
303
304		Expression::GreaterThanEqual(e) => {
305			let left = compile_expression(_ctx, &e.left)?;
306			let right = compile_expression(_ctx, &e.right)?;
307			let fragment = e.full_fragment_owned();
308			CompiledExpr::new(move |ctx| {
309				let l = left.execute(ctx)?;
310				let r = right.execute(ctx)?;
311				let result =
312					compare_columns::<GreaterThanEqual>(&l, &r, fragment.clone(), |f, l, r| {
313						TypeError::BinaryOperatorNotApplicable {
314							operator: BinaryOp::GreaterThanEqual,
315							left: l,
316							right: r,
317							fragment: f,
318						}
319						.into_diagnostic()
320					});
321				result
322			})
323		}
324
325		Expression::LessThan(e) => {
326			let left = compile_expression(_ctx, &e.left)?;
327			let right = compile_expression(_ctx, &e.right)?;
328			let fragment = e.full_fragment_owned();
329			CompiledExpr::new(move |ctx| {
330				let l = left.execute(ctx)?;
331				let r = right.execute(ctx)?;
332				let result = compare_columns::<LessThan>(&l, &r, fragment.clone(), |f, l, r| {
333					TypeError::BinaryOperatorNotApplicable {
334						operator: BinaryOp::LessThan,
335						left: l,
336						right: r,
337						fragment: f,
338					}
339					.into_diagnostic()
340				});
341				result
342			})
343		}
344
345		Expression::LessThanEqual(e) => {
346			let left = compile_expression(_ctx, &e.left)?;
347			let right = compile_expression(_ctx, &e.right)?;
348			let fragment = e.full_fragment_owned();
349			CompiledExpr::new(move |ctx| {
350				let l = left.execute(ctx)?;
351				let r = right.execute(ctx)?;
352				let result = compare_columns::<LessThanEqual>(&l, &r, fragment.clone(), |f, l, r| {
353					TypeError::BinaryOperatorNotApplicable {
354						operator: BinaryOp::LessThanEqual,
355						left: l,
356						right: r,
357						fragment: f,
358					}
359					.into_diagnostic()
360				});
361				result
362			})
363		}
364
365		Expression::And(e) => {
366			let left = compile_expression(_ctx, &e.left)?;
367			let right = compile_expression(_ctx, &e.right)?;
368			let fragment = e.full_fragment_owned();
369			CompiledExpr::new(move |ctx| {
370				let l = left.execute(ctx)?;
371				let r = right.execute(ctx)?;
372				let result = execute_and(&l, &r, &fragment);
373				result
374			})
375		}
376
377		Expression::Or(e) => {
378			let left = compile_expression(_ctx, &e.left)?;
379			let right = compile_expression(_ctx, &e.right)?;
380			let fragment = e.full_fragment_owned();
381			CompiledExpr::new(move |ctx| {
382				let l = left.execute(ctx)?;
383				let r = right.execute(ctx)?;
384				let result = execute_or(&l, &r, &fragment);
385				result
386			})
387		}
388
389		Expression::Xor(e) => {
390			let left = compile_expression(_ctx, &e.left)?;
391			let right = compile_expression(_ctx, &e.right)?;
392			let fragment = e.full_fragment_owned();
393			CompiledExpr::new(move |ctx| {
394				let l = left.execute(ctx)?;
395				let r = right.execute(ctx)?;
396				let result = execute_xor(&l, &r, &fragment);
397				result
398			})
399		}
400
401		Expression::Prefix(e) => {
402			let expr = e.clone();
403			CompiledExpr::new(move |ctx| prefix_eval(ctx, &expr, ctx.functions, ctx.clock))
404		}
405
406		Expression::Type(e) => {
407			let ty = e.ty.clone();
408			let fragment = e.fragment.clone();
409			CompiledExpr::new(move |ctx| {
410				let row_count = ctx.take.unwrap_or(ctx.row_count);
411				let values: Vec<Box<Value>> =
412					(0..row_count).map(|_| Box::new(Value::Type(ty.clone()))).collect();
413				Ok(Column::new(fragment.text(), ColumnData::any(values)))
414			})
415		}
416
417		Expression::AccessSource(e) => {
418			let col_name = e.column.name.text().to_string();
419			let expr = e.clone();
420			CompiledExpr::new_access(col_name, move |ctx| access_lookup(ctx, &expr))
421		}
422
423		Expression::Tuple(e) => {
424			if e.expressions.len() == 1 {
425				let inner = compile_expression(_ctx, &e.expressions[0])?;
426				CompiledExpr::new(move |ctx| inner.execute(ctx))
427			} else {
428				let compiled: Vec<CompiledExpr> = e
429					.expressions
430					.iter()
431					.map(|expr| compile_expression(_ctx, expr))
432					.collect::<Result<Vec<_>>>()?;
433				let fragment = e.fragment.clone();
434				CompiledExpr::new(move |ctx| {
435					let columns: Vec<Column> = compiled
436						.iter()
437						.map(|expr| expr.execute(ctx))
438						.collect::<Result<Vec<_>>>()?;
439
440					let len = columns.first().map_or(1, |c| c.data().len());
441					let mut data: Vec<Box<Value>> = Vec::with_capacity(len);
442
443					for i in 0..len {
444						let items: Vec<Value> =
445							columns.iter().map(|col| col.data().get_value(i)).collect();
446						data.push(Box::new(Value::List(items)));
447					}
448
449					Ok(Column {
450						name: fragment.clone(),
451						data: ColumnData::any(data),
452					})
453				})
454			}
455		}
456
457		Expression::Between(e) => {
458			let value = compile_expression(_ctx, &e.value)?;
459			let lower = compile_expression(_ctx, &e.lower)?;
460			let upper = compile_expression(_ctx, &e.upper)?;
461			let fragment = e.fragment.clone();
462			CompiledExpr::new(move |ctx| {
463				let value_col = value.execute(ctx)?;
464				let lower_col = lower.execute(ctx)?;
465				let upper_col = upper.execute(ctx)?;
466
467				let ge_result = compare_columns::<GreaterThanEqual>(
468					&value_col,
469					&lower_col,
470					fragment.clone(),
471					|f, l, r| {
472						TypeError::BinaryOperatorNotApplicable {
473							operator: BinaryOp::GreaterThanEqual,
474							left: l,
475							right: r,
476							fragment: f,
477						}
478						.into_diagnostic()
479					},
480				)?;
481				let le_result = compare_columns::<LessThanEqual>(
482					&value_col,
483					&upper_col,
484					fragment.clone(),
485					|f, l, r| {
486						TypeError::BinaryOperatorNotApplicable {
487							operator: BinaryOp::LessThanEqual,
488							left: l,
489							right: r,
490							fragment: f,
491						}
492						.into_diagnostic()
493					},
494				)?;
495
496				if !matches!(ge_result.data(), ColumnData::Bool(_))
497					|| !matches!(le_result.data(), ColumnData::Bool(_))
498				{
499					return Err(TypeError::BinaryOperatorNotApplicable {
500						operator: BinaryOp::Between,
501						left: value_col.get_type(),
502						right: lower_col.get_type(),
503						fragment: fragment.clone(),
504					}
505					.into());
506				}
507
508				match (ge_result.data(), le_result.data()) {
509					(ColumnData::Bool(ge_container), ColumnData::Bool(le_container)) => {
510						let mut data = Vec::with_capacity(ge_container.len());
511						let mut bitvec = Vec::with_capacity(ge_container.len());
512
513						for i in 0..ge_container.len() {
514							if ge_container.is_defined(i) && le_container.is_defined(i) {
515								data.push(ge_container.data().get(i)
516									&& le_container.data().get(i));
517								bitvec.push(true);
518							} else {
519								data.push(false);
520								bitvec.push(false);
521							}
522						}
523
524						Ok(Column {
525							name: fragment.clone(),
526							data: ColumnData::bool_with_bitvec(data, bitvec),
527						})
528					}
529					_ => unreachable!(
530						"Both comparison results should be boolean after the check above"
531					),
532				}
533			})
534		}
535
536		Expression::In(e) => {
537			let list_expressions = match e.list.as_ref() {
538				Expression::Tuple(tuple) => &tuple.expressions,
539				_ => from_ref(e.list.as_ref()),
540			};
541			let value = compile_expression(_ctx, &e.value)?;
542			let list: Vec<CompiledExpr> = list_expressions
543				.iter()
544				.map(|expr| compile_expression(_ctx, expr))
545				.collect::<Result<Vec<_>>>()?;
546			let negated = e.negated;
547			let fragment = e.fragment.clone();
548			CompiledExpr::new(move |ctx| {
549				if list.is_empty() {
550					let value_col = value.execute(ctx)?;
551					let len = value_col.data().len();
552					let result = vec![negated; len];
553					return Ok(Column {
554						name: fragment.clone(),
555						data: ColumnData::bool(result),
556					});
557				}
558
559				let value_col = value.execute(ctx)?;
560
561				let first_col = list[0].execute(ctx)?;
562				let mut result = compare_columns::<Equal>(
563					&value_col,
564					&first_col,
565					fragment.clone(),
566					|f, l, r| {
567						TypeError::BinaryOperatorNotApplicable {
568							operator: BinaryOp::Equal,
569							left: l,
570							right: r,
571							fragment: f,
572						}
573						.into_diagnostic()
574					},
575				)?;
576
577				for list_expr in list.iter().skip(1) {
578					let list_col = list_expr.execute(ctx)?;
579					let eq_result = compare_columns::<Equal>(
580						&value_col,
581						&list_col,
582						fragment.clone(),
583						|f, l, r| {
584							TypeError::BinaryOperatorNotApplicable {
585								operator: BinaryOp::Equal,
586								left: l,
587								right: r,
588								fragment: f,
589							}
590							.into_diagnostic()
591						},
592					)?;
593					result = or_columns(result, eq_result, fragment.clone())?;
594				}
595
596				if negated {
597					result = negate_column(result, fragment.clone());
598				}
599
600				Ok(result)
601			})
602		}
603
604		Expression::Contains(e) => {
605			let list_expressions = match e.list.as_ref() {
606				Expression::Tuple(tuple) => &tuple.expressions,
607				_ => from_ref(e.list.as_ref()),
608			};
609			let value = compile_expression(_ctx, &e.value)?;
610			let list: Vec<CompiledExpr> = list_expressions
611				.iter()
612				.map(|expr| compile_expression(_ctx, expr))
613				.collect::<Result<Vec<_>>>()?;
614			let fragment = e.fragment.clone();
615			CompiledExpr::new(move |ctx| {
616				let value_col = value.execute(ctx)?;
617
618				// Empty list → vacuous truth (all elements trivially contained)
619				if list.is_empty() {
620					let len = value_col.data().len();
621					let result = vec![true; len];
622					return Ok(Column {
623						name: fragment.clone(),
624						data: ColumnData::bool(result),
625					});
626				}
627
628				// For each list element, check if it's contained in the set value
629				let first_col = list[0].execute(ctx)?;
630				let mut result = list_contains_element(&value_col, &first_col, &fragment)?;
631
632				for list_expr in list.iter().skip(1) {
633					let list_col = list_expr.execute(ctx)?;
634					let element_result = list_contains_element(&value_col, &list_col, &fragment)?;
635					result = and_columns(result, element_result, fragment.clone())?;
636				}
637
638				Ok(result)
639			})
640		}
641
642		Expression::Cast(e) => {
643			if let Expression::Constant(const_expr) = e.expression.as_ref() {
644				let const_expr = const_expr.clone();
645				let target_type = e.to.ty.clone();
646				CompiledExpr::new(move |ctx| {
647					let row_count = ctx.take.unwrap_or(ctx.row_count);
648					let data = constant_value(&const_expr, row_count)?;
649					let casted = if data.get_type() == target_type {
650						data
651					} else {
652						constant_value_of(&const_expr, target_type.clone(), row_count)?
653					};
654					Ok(Column {
655						name: const_expr.full_fragment_owned(),
656						data: casted,
657					})
658				})
659			} else {
660				let inner = compile_expression(_ctx, &e.expression)?;
661				let target_type = e.to.ty.clone();
662				let inner_fragment = e.expression.full_fragment_owned();
663				CompiledExpr::new(move |ctx| {
664					let column = inner.execute(ctx)?;
665					let frag = inner_fragment.clone();
666					let casted =
667						cast_column_data(ctx, &column.data(), target_type.clone(), &|| {
668							inner_fragment.clone()
669						})
670						.map_err(|e| {
671							Error::from(CastError::InvalidNumber {
672								fragment: frag,
673								target: target_type.clone(),
674								cause: e.diagnostic(),
675							})
676						})?;
677					Ok(Column {
678						name: column.name_owned(),
679						data: casted,
680					})
681				})
682			}
683		}
684
685		Expression::If(e) => {
686			let condition = compile_expression(_ctx, &e.condition)?;
687			let then_expr = compile_expressions(_ctx, from_ref(e.then_expr.as_ref()))?;
688			let else_ifs: Vec<(CompiledExpr, Vec<CompiledExpr>)> = e
689				.else_ifs
690				.iter()
691				.map(|ei| {
692					Ok((
693						compile_expression(_ctx, &ei.condition)?,
694						compile_expressions(_ctx, from_ref(ei.then_expr.as_ref()))?,
695					))
696				})
697				.collect::<Result<Vec<_>>>()?;
698			let else_branch: Option<Vec<CompiledExpr>> = match &e.else_expr {
699				Some(expr) => Some(compile_expressions(_ctx, from_ref(expr.as_ref()))?),
700				None => None,
701			};
702			let fragment = e.fragment.clone();
703			CompiledExpr::new_multi(move |ctx| {
704				execute_if_multi(ctx, &condition, &then_expr, &else_ifs, &else_branch, &fragment)
705			})
706		}
707
708		Expression::Map(e) => {
709			let expressions = compile_expressions(_ctx, &e.expressions)?;
710			CompiledExpr::new_multi(move |ctx| execute_map_multi(ctx, &expressions))
711		}
712
713		Expression::Extend(e) => {
714			let expressions = compile_expressions(_ctx, &e.expressions)?;
715			CompiledExpr::new_multi(move |ctx| execute_extend_multi(ctx, &expressions))
716		}
717
718		Expression::Call(e) => {
719			let expr = e.clone();
720			CompiledExpr::new(move |ctx| call_eval(ctx, &expr, ctx.functions, ctx.clock))
721		}
722
723		Expression::SumTypeConstructor(_) => {
724			panic!(
725				"SumTypeConstructor in expression context — constructors should be expanded by InlineDataNode before expression compilation"
726			);
727		}
728
729		Expression::IsVariant(e) => {
730			let col_name = match e.expression.as_ref() {
731				Expression::Column(c) => c.0.name.text().to_string(),
732				other => other.full_fragment_owned().text().to_string(),
733			};
734			let tag_col_name = format!("{}_tag", col_name);
735			let tag = e.tag.expect("IS variant tag must be resolved before compilation");
736			let fragment = e.fragment.clone();
737			CompiledExpr::new(move |ctx| {
738				if let Some(tag_col) =
739					ctx.columns.iter().find(|c| c.name().text() == tag_col_name.as_str())
740				{
741					match tag_col.data() {
742						ColumnData::Uint1(container) => {
743							let results: Vec<bool> = container
744								.iter()
745								.take(ctx.row_count)
746								.map(|v| v == Some(tag))
747								.collect();
748							Ok(Column {
749								name: fragment.clone(),
750								data: ColumnData::bool(results),
751							})
752						}
753						_ => Ok(Column {
754							name: fragment.clone(),
755							data: ColumnData::none_typed(Type::Boolean, ctx.row_count),
756						}),
757					}
758				} else {
759					Ok(Column {
760						name: fragment.clone(),
761						data: ColumnData::none_typed(Type::Boolean, ctx.row_count),
762					})
763				}
764			})
765		}
766
767		Expression::FieldAccess(e) => {
768			let field_name = e.field.text().to_string();
769			// Extract variable name at compile time if the object is a variable
770			let var_name = match e.object.as_ref() {
771				Expression::Variable(var_expr) => Some(var_expr.name().to_string()),
772				_ => None,
773			};
774			let object = compile_expression(_ctx, &e.object)?;
775			CompiledExpr::new(move |ctx| {
776				if let Some(ref variable_name) = var_name {
777					match ctx.symbol_table.get(variable_name) {
778						Some(Variable::Columns(columns)) => {
779							let col = columns
780								.columns
781								.iter()
782								.find(|c| c.name.text() == field_name);
783							match col {
784								Some(col) => {
785									let value = col.data.get_value(0);
786									let row_count =
787										ctx.take.unwrap_or(ctx.row_count);
788									let mut data = ColumnData::with_capacity(
789										value.get_type(),
790										row_count,
791									);
792									for _ in 0..row_count {
793										data.push_value(value.clone());
794									}
795									Ok(Column {
796										name: Fragment::internal(&field_name),
797										data,
798									})
799								}
800								None => {
801									let available: Vec<String> = columns
802										.columns
803										.iter()
804										.map(|c| c.name.text().to_string())
805										.collect();
806									return Err(TypeError::Runtime {
807										kind: RuntimeErrorKind::FieldNotFound {
808											variable: variable_name
809												.to_string(),
810											field: field_name.to_string(),
811											available,
812										},
813										message: format!(
814											"Field '{}' not found on variable '{}'",
815											field_name, variable_name
816										),
817									}
818									.into());
819								}
820							}
821						}
822						Some(Variable::Scalar(_)) | Some(Variable::Closure(_)) => {
823							return Err(TypeError::Runtime {
824								kind: RuntimeErrorKind::FieldNotFound {
825									variable: variable_name.to_string(),
826									field: field_name.to_string(),
827									available: vec![],
828								},
829								message: format!(
830									"Field '{}' not found on variable '{}'",
831									field_name, variable_name
832								),
833							}
834							.into());
835						}
836						Some(Variable::ForIterator {
837							..
838						}) => {
839							return Err(TypeError::Runtime {
840								kind: RuntimeErrorKind::VariableIsDataframe {
841									name: variable_name.to_string(),
842								},
843								message: format!(
844									"Variable '{}' contains a dataframe and cannot be used directly in scalar expressions",
845									variable_name
846								),
847							}
848							.into());
849						}
850						None => {
851							return Err(TypeError::Runtime {
852								kind: RuntimeErrorKind::VariableNotFound {
853									name: variable_name.to_string(),
854								},
855								message: format!(
856									"Variable '{}' is not defined",
857									variable_name
858								),
859							}
860							.into());
861						}
862					}
863				} else {
864					// For non-variable objects, evaluate the object and try to interpret result
865					let _obj_col = object.execute(ctx)?;
866					return Err(TypeError::Runtime {
867						kind: RuntimeErrorKind::FieldNotFound {
868							variable: "<expression>".to_string(),
869							field: field_name.to_string(),
870							available: vec![],
871						},
872						message: format!(
873							"Field '{}' not found on variable '<expression>'",
874							field_name
875						),
876					}
877					.into());
878				}
879			})
880		}
881	})
882}
883
884fn compile_expressions(ctx: &CompileContext, exprs: &[Expression]) -> Result<Vec<CompiledExpr>> {
885	exprs.iter().map(|e| compile_expression(ctx, e)).collect()
886}
887
888// --- Helper functions (moved from execute.rs) ---
889
890fn execute_and(left: &Column, right: &Column, fragment: &Fragment) -> Result<Column> {
891	binary_op_unwrap_option(left, right, fragment.clone(), |left, right| match (&left.data(), &right.data()) {
892		(ColumnData::Bool(l_container), ColumnData::Bool(r_container)) => {
893			let data: Vec<bool> = l_container
894				.data()
895				.iter()
896				.zip(r_container.data().iter())
897				.map(|(l_val, r_val)| l_val && r_val)
898				.collect();
899
900			Ok(Column {
901				name: fragment.clone(),
902				data: ColumnData::bool(data),
903			})
904		}
905		(l, r) => {
906			if l.is_number() || r.is_number() {
907				return Err(TypeError::LogicalOperatorNotApplicable {
908					operator: LogicalOp::And,
909					operand_category: OperandCategory::Number,
910					fragment: fragment.clone(),
911				}
912				.into());
913			} else if l.is_text() || r.is_text() {
914				return Err(TypeError::LogicalOperatorNotApplicable {
915					operator: LogicalOp::And,
916					operand_category: OperandCategory::Text,
917					fragment: fragment.clone(),
918				}
919				.into());
920			} else if l.is_temporal() || r.is_temporal() {
921				return Err(TypeError::LogicalOperatorNotApplicable {
922					operator: LogicalOp::And,
923					operand_category: OperandCategory::Temporal,
924					fragment: fragment.clone(),
925				}
926				.into());
927			} else if l.is_uuid() || r.is_uuid() {
928				return Err(TypeError::LogicalOperatorNotApplicable {
929					operator: LogicalOp::And,
930					operand_category: OperandCategory::Uuid,
931					fragment: fragment.clone(),
932				}
933				.into());
934			} else {
935				unimplemented!("{} and {}", l.get_type(), r.get_type());
936			}
937		}
938	})
939}
940
941fn execute_or(left: &Column, right: &Column, fragment: &Fragment) -> Result<Column> {
942	binary_op_unwrap_option(left, right, fragment.clone(), |left, right| match (&left.data(), &right.data()) {
943		(ColumnData::Bool(l_container), ColumnData::Bool(r_container)) => {
944			let data: Vec<bool> = l_container
945				.data()
946				.iter()
947				.zip(r_container.data().iter())
948				.map(|(l_val, r_val)| l_val || r_val)
949				.collect();
950
951			Ok(Column {
952				name: fragment.clone(),
953				data: ColumnData::bool(data),
954			})
955		}
956		(l, r) => {
957			if l.is_number() || r.is_number() {
958				return Err(TypeError::LogicalOperatorNotApplicable {
959					operator: LogicalOp::Or,
960					operand_category: OperandCategory::Number,
961					fragment: fragment.clone(),
962				}
963				.into());
964			} else if l.is_text() || r.is_text() {
965				return Err(TypeError::LogicalOperatorNotApplicable {
966					operator: LogicalOp::Or,
967					operand_category: OperandCategory::Text,
968					fragment: fragment.clone(),
969				}
970				.into());
971			} else if l.is_temporal() || r.is_temporal() {
972				return Err(TypeError::LogicalOperatorNotApplicable {
973					operator: LogicalOp::Or,
974					operand_category: OperandCategory::Temporal,
975					fragment: fragment.clone(),
976				}
977				.into());
978			} else if l.is_uuid() || r.is_uuid() {
979				return Err(TypeError::LogicalOperatorNotApplicable {
980					operator: LogicalOp::Or,
981					operand_category: OperandCategory::Uuid,
982					fragment: fragment.clone(),
983				}
984				.into());
985			} else {
986				unimplemented!("{} or {}", l.get_type(), r.get_type());
987			}
988		}
989	})
990}
991
992fn execute_xor(left: &Column, right: &Column, fragment: &Fragment) -> Result<Column> {
993	binary_op_unwrap_option(left, right, fragment.clone(), |left, right| match (&left.data(), &right.data()) {
994		(ColumnData::Bool(l_container), ColumnData::Bool(r_container)) => {
995			let data: Vec<bool> = l_container
996				.data()
997				.iter()
998				.zip(r_container.data().iter())
999				.map(|(l_val, r_val)| l_val != r_val)
1000				.collect();
1001
1002			Ok(Column {
1003				name: fragment.clone(),
1004				data: ColumnData::bool(data),
1005			})
1006		}
1007		(l, r) => {
1008			if l.is_number() || r.is_number() {
1009				return Err(TypeError::LogicalOperatorNotApplicable {
1010					operator: LogicalOp::Xor,
1011					operand_category: OperandCategory::Number,
1012					fragment: fragment.clone(),
1013				}
1014				.into());
1015			} else if l.is_text() || r.is_text() {
1016				return Err(TypeError::LogicalOperatorNotApplicable {
1017					operator: LogicalOp::Xor,
1018					operand_category: OperandCategory::Text,
1019					fragment: fragment.clone(),
1020				}
1021				.into());
1022			} else if l.is_temporal() || r.is_temporal() {
1023				return Err(TypeError::LogicalOperatorNotApplicable {
1024					operator: LogicalOp::Xor,
1025					operand_category: OperandCategory::Temporal,
1026					fragment: fragment.clone(),
1027				}
1028				.into());
1029			} else if l.is_uuid() || r.is_uuid() {
1030				return Err(TypeError::LogicalOperatorNotApplicable {
1031					operator: LogicalOp::Xor,
1032					operand_category: OperandCategory::Uuid,
1033					fragment: fragment.clone(),
1034				}
1035				.into());
1036			} else {
1037				unimplemented!("{} xor {}", l.get_type(), r.get_type());
1038			}
1039		}
1040	})
1041}
1042
1043fn or_columns(left: Column, right: Column, fragment: Fragment) -> Result<Column> {
1044	binary_op_unwrap_option(&left, &right, fragment.clone(), |left, right| match (left.data(), right.data()) {
1045		(ColumnData::Bool(l), ColumnData::Bool(r)) => {
1046			let len = l.len();
1047			let mut data = Vec::with_capacity(len);
1048			let mut bitvec = Vec::with_capacity(len);
1049
1050			for i in 0..len {
1051				let l_defined = l.is_defined(i);
1052				let r_defined = r.is_defined(i);
1053				let l_val = l.data().get(i);
1054				let r_val = r.data().get(i);
1055
1056				if l_defined && r_defined {
1057					data.push(l_val || r_val);
1058					bitvec.push(true);
1059				} else {
1060					data.push(false);
1061					bitvec.push(false);
1062				}
1063			}
1064
1065			Ok(Column {
1066				name: fragment.clone(),
1067				data: ColumnData::bool_with_bitvec(data, bitvec),
1068			})
1069		}
1070		_ => {
1071			unreachable!("OR columns should only be called with boolean columns from equality comparisons")
1072		}
1073	})
1074}
1075
1076fn and_columns(left: Column, right: Column, fragment: Fragment) -> Result<Column> {
1077	binary_op_unwrap_option(&left, &right, fragment.clone(), |left, right| match (left.data(), right.data()) {
1078		(ColumnData::Bool(l), ColumnData::Bool(r)) => {
1079			let len = l.len();
1080			let mut data = Vec::with_capacity(len);
1081			let mut bitvec = Vec::with_capacity(len);
1082
1083			for i in 0..len {
1084				let l_defined = l.is_defined(i);
1085				let r_defined = r.is_defined(i);
1086				let l_val = l.data().get(i);
1087				let r_val = r.data().get(i);
1088
1089				if l_defined && r_defined {
1090					data.push(l_val && r_val);
1091					bitvec.push(true);
1092				} else {
1093					data.push(false);
1094					bitvec.push(false);
1095				}
1096			}
1097
1098			Ok(Column {
1099				name: fragment.clone(),
1100				data: ColumnData::bool_with_bitvec(data, bitvec),
1101			})
1102		}
1103		_ => {
1104			unreachable!("AND columns should only be called with boolean columns")
1105		}
1106	})
1107}
1108
1109fn list_items_contain(items: &[Value], element: &Value, fragment: &Fragment) -> bool {
1110	items.iter().any(|item| {
1111		if item == element {
1112			return true;
1113		}
1114		let item_col = Column {
1115			name: fragment.clone(),
1116			data: ColumnData::from(item.clone()),
1117		};
1118		let elem_col = Column {
1119			name: fragment.clone(),
1120			data: ColumnData::from(element.clone()),
1121		};
1122		compare_columns::<Equal>(&item_col, &elem_col, fragment.clone(), |f, l, r| {
1123			TypeError::BinaryOperatorNotApplicable {
1124				operator: BinaryOp::Equal,
1125				left: l,
1126				right: r,
1127				fragment: f,
1128			}
1129			.into_diagnostic()
1130		})
1131		.ok()
1132		.and_then(|c| match c.data() {
1133			ColumnData::Bool(b) => Some(b.data().get(0)),
1134			_ => None,
1135		})
1136		.unwrap_or(false)
1137	})
1138}
1139
1140fn list_contains_element(list_col: &Column, element_col: &Column, fragment: &Fragment) -> Result<Column> {
1141	let len = list_col.data().len();
1142	let mut data = Vec::with_capacity(len);
1143
1144	for i in 0..len {
1145		let list_value = list_col.data().get_value(i);
1146		let element_value = element_col.data().get_value(i);
1147
1148		let contained = match &list_value {
1149			Value::List(items) => list_items_contain(items, &element_value, fragment),
1150			Value::Any(boxed) => match boxed.as_ref() {
1151				Value::List(items) => list_items_contain(items, &element_value, fragment),
1152				_ => false,
1153			},
1154			_ => false,
1155		};
1156		data.push(contained);
1157	}
1158
1159	Ok(Column {
1160		name: fragment.clone(),
1161		data: ColumnData::bool(data),
1162	})
1163}
1164
1165fn negate_column(col: Column, fragment: Fragment) -> Column {
1166	unary_op_unwrap_option(&col, |col| match col.data() {
1167		ColumnData::Bool(container) => {
1168			let len = container.len();
1169			let mut data = Vec::with_capacity(len);
1170			let mut bitvec = Vec::with_capacity(len);
1171
1172			for i in 0..len {
1173				if container.is_defined(i) {
1174					data.push(!container.data().get(i));
1175					bitvec.push(true);
1176				} else {
1177					data.push(false);
1178					bitvec.push(false);
1179				}
1180			}
1181
1182			Ok(Column {
1183				name: fragment.clone(),
1184				data: ColumnData::bool_with_bitvec(data, bitvec),
1185			})
1186		}
1187		_ => unreachable!("negate_column should only be called with boolean columns"),
1188	})
1189	.unwrap()
1190}
1191
1192fn is_truthy(value: &Value) -> bool {
1193	match value {
1194		Value::Boolean(true) => true,
1195		Value::Boolean(false) => false,
1196		Value::None {
1197			..
1198		} => false,
1199		Value::Int1(0) | Value::Int2(0) | Value::Int4(0) | Value::Int8(0) | Value::Int16(0) => false,
1200		Value::Uint1(0) | Value::Uint2(0) | Value::Uint4(0) | Value::Uint8(0) | Value::Uint16(0) => false,
1201		Value::Int1(_) | Value::Int2(_) | Value::Int4(_) | Value::Int8(_) | Value::Int16(_) => true,
1202		Value::Uint1(_) | Value::Uint2(_) | Value::Uint4(_) | Value::Uint8(_) | Value::Uint16(_) => true,
1203		Value::Utf8(s) => !s.is_empty(),
1204		_ => true,
1205	}
1206}
1207
1208fn execute_if_multi(
1209	ctx: &EvalContext,
1210	condition: &CompiledExpr,
1211	then_expr: &[CompiledExpr],
1212	else_ifs: &[(CompiledExpr, Vec<CompiledExpr>)],
1213	else_branch: &Option<Vec<CompiledExpr>>,
1214	_fragment: &Fragment,
1215) -> Result<Vec<Column>> {
1216	let condition_column = condition.execute(ctx)?;
1217
1218	let mut result_data: Option<Vec<ColumnData>> = None;
1219	let mut result_names: Vec<Fragment> = Vec::new();
1220
1221	for row_idx in 0..ctx.row_count {
1222		let condition_value = condition_column.data().get_value(row_idx);
1223
1224		let branch_results = if is_truthy(&condition_value) {
1225			execute_multi_exprs(ctx, then_expr)?
1226		} else {
1227			let mut found_branch = false;
1228			let mut branch_columns = None;
1229
1230			for (else_if_condition, else_if_then) in else_ifs {
1231				let else_if_col = else_if_condition.execute(ctx)?;
1232				let else_if_value = else_if_col.data().get_value(row_idx);
1233
1234				if is_truthy(&else_if_value) {
1235					branch_columns = Some(execute_multi_exprs(ctx, else_if_then)?);
1236					found_branch = true;
1237					break;
1238				}
1239			}
1240
1241			if found_branch {
1242				branch_columns.unwrap()
1243			} else if let Some(else_exprs) = else_branch {
1244				execute_multi_exprs(ctx, else_exprs)?
1245			} else {
1246				vec![]
1247			}
1248		};
1249
1250		// Handle empty branch results (from empty blocks like `{}` or no-branch-taken)
1251		let is_empty_result = branch_results.is_empty();
1252		if is_empty_result {
1253			if let Some(data) = result_data.as_mut() {
1254				for col_data in data.iter_mut() {
1255					col_data.push_value(Value::none());
1256				}
1257			}
1258			continue;
1259		}
1260
1261		// Initialize from first non-empty branch, backfilling previous empty rows
1262		if result_data.is_none() {
1263			let mut data: Vec<ColumnData> = branch_results
1264				.iter()
1265				.map(|col| ColumnData::with_capacity(col.data().get_type(), ctx.row_count))
1266				.collect();
1267			for _ in 0..row_idx {
1268				for col_data in data.iter_mut() {
1269					col_data.push_value(Value::none());
1270				}
1271			}
1272			result_data = Some(data);
1273			result_names = branch_results.iter().map(|col| col.name.clone()).collect();
1274		}
1275
1276		let data = result_data.as_mut().unwrap();
1277		for (i, branch_col) in branch_results.iter().enumerate() {
1278			if i < data.len() {
1279				let branch_value = branch_col.data().get_value(row_idx);
1280				data[i].push_value(branch_value);
1281			}
1282		}
1283	}
1284
1285	let result_data = result_data.unwrap_or_default();
1286	let result: Vec<Column> = result_data
1287		.into_iter()
1288		.enumerate()
1289		.map(|(i, data)| Column {
1290			name: result_names.get(i).cloned().unwrap_or_else(|| Fragment::internal("column")),
1291			data,
1292		})
1293		.collect();
1294
1295	if result.is_empty() {
1296		Ok(vec![Column {
1297			name: Fragment::internal("none"),
1298			data: ColumnData::none_typed(Type::Boolean, ctx.row_count),
1299		}])
1300	} else {
1301		Ok(result)
1302	}
1303}
1304
1305fn execute_multi_exprs(ctx: &EvalContext, exprs: &[CompiledExpr]) -> Result<Vec<Column>> {
1306	let mut result = Vec::new();
1307	for expr in exprs {
1308		result.extend(expr.execute_multi(ctx)?);
1309	}
1310	Ok(result)
1311}
1312
1313fn execute_map_multi(ctx: &EvalContext, expressions: &[CompiledExpr]) -> Result<Vec<Column>> {
1314	let mut result = Vec::with_capacity(expressions.len());
1315
1316	for expr in expressions {
1317		let column = expr.execute(ctx)?;
1318		let name = column.name.text().to_string();
1319		result.push(Column {
1320			name: Fragment::internal(name),
1321			data: column.data,
1322		});
1323	}
1324
1325	Ok(result)
1326}
1327
1328fn execute_extend_multi(ctx: &EvalContext, expressions: &[CompiledExpr]) -> Result<Vec<Column>> {
1329	let mut result = Vec::with_capacity(expressions.len());
1330
1331	for expr in expressions {
1332		let column = expr.execute(ctx)?;
1333		let name = column.name.text().to_string();
1334		result.push(Column {
1335			name: Fragment::internal(name),
1336			data: column.data,
1337		});
1338	}
1339
1340	Ok(result)
1341}