Skip to main content

reifydb_engine/expression/
compile.rs

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