reifydb_engine/evaluate/row/
mod.rs

1// Copyright (c) reifydb.com 2025
2// This file is licensed under the AGPL-3.0-or-later, see license.md file
3
4use reifydb_core::{
5	Row,
6	interface::ColumnDef,
7	value::{
8		column::{Column, ColumnData, Columns},
9		container::NumberContainer,
10		encoded::EncodedValuesNamedLayout,
11	},
12};
13use reifydb_rql::expression::Expression;
14use reifydb_type::{Error, Fragment, Params, ROW_NUMBER_COLUMN_NAME, Type, Value, internal};
15
16use crate::evaluate::column::{StandardColumnEvaluator, cast};
17pub(crate) use crate::evaluate::{ColumnEvaluationContext, RowEvaluationContext};
18
19pub struct StandardRowEvaluator {
20	evaluator: StandardColumnEvaluator,
21}
22
23impl StandardRowEvaluator {
24	pub fn new() -> Self {
25		Self {
26			evaluator: StandardColumnEvaluator::default(),
27		}
28	}
29}
30
31impl Default for StandardRowEvaluator {
32	fn default() -> Self {
33		Self::new()
34	}
35}
36
37impl StandardRowEvaluator {
38	pub fn evaluate<'a>(&self, ctx: &RowEvaluationContext<'a>, expr: &Expression<'a>) -> crate::Result<Value> {
39		let mut columns = Vec::new();
40
41		let row_number_column = Column {
42			name: Fragment::owned_internal(ROW_NUMBER_COLUMN_NAME),
43			data: ColumnData::Uint8(NumberContainer::from_vec(vec![ctx.row.number.0])),
44		};
45		columns.push(row_number_column);
46
47		for (idx, field) in ctx.row.layout.fields.iter().enumerate() {
48			let value = ctx.row.layout.get_value(&ctx.row.encoded, idx);
49			// FIXME maybe some auto conversion needs to happen here
50			// Allow undefined values in any field type
51			debug_assert!(
52				field.r#type == value.get_type() || value.get_type() == reifydb_type::Type::Undefined,
53				"Type mismatch: field expects {:?}, got {:?}",
54				field.r#type,
55				value.get_type()
56			);
57
58			// Use the field type for the column data, not the value type
59			// This ensures undefined values are handled correctly
60			let column_type = if value.get_type() == reifydb_type::Type::Undefined {
61				field.r#type
62			} else {
63				value.get_type()
64			};
65			let mut data = if column_type == Type::Undefined {
66				ColumnData::undefined(0)
67			} else {
68				ColumnData::with_capacity(column_type, 1)
69			};
70			data.push_value(value);
71
72			let name = ctx.row.layout.get_name(idx).ok_or_else(|| {
73				Error(internal!("EncodedRowNamedLayout missing name for field at index {}", idx))
74			})?;
75
76			columns.push(Column {
77				name: Fragment::owned_internal(name),
78				data,
79			})
80		}
81
82		let ctx = ColumnEvaluationContext {
83			target: ctx.target.clone(),
84			columns: Columns::new(columns),
85			row_count: 1,
86			take: None,
87			params: ctx.params,
88			stack: &crate::stack::Stack::new(),
89			is_aggregate_context: false,
90		};
91
92		let result = self.evaluator.evaluate(&ctx, &expr)?;
93
94		Ok(result.data().get_value(0))
95	}
96}
97
98impl StandardRowEvaluator {
99	pub fn coerce(&self, row: &Row, target_columns: &[ColumnDef]) -> crate::Result<Row> {
100		let mut source_columns = Vec::new();
101
102		for (idx, field) in row.layout.fields.iter().enumerate() {
103			let value = row.layout.get_value(&row.encoded, idx);
104
105			let mut data = if field.r#type == Type::Undefined {
106				ColumnData::undefined(0)
107			} else {
108				ColumnData::with_capacity(field.r#type, 1)
109			};
110			data.push_value(value);
111
112			let name = row.layout.get_name(idx).ok_or_else(|| {
113				Error(internal!("EncodedRowNamedLayout missing name for field at index {}", idx))
114			})?;
115
116			source_columns.push(Column {
117				name: Fragment::owned_internal(name),
118				data,
119			});
120		}
121
122		let ctx = ColumnEvaluationContext {
123			target: None,
124			columns: Columns::new(source_columns),
125			row_count: 1,
126			take: None,
127			params: &Params::None,
128			stack: &crate::stack::Stack::new(),
129			is_aggregate_context: false,
130		};
131
132		let mut values = Vec::with_capacity(target_columns.len());
133		let mut names = Vec::with_capacity(target_columns.len());
134		let mut types = Vec::with_capacity(target_columns.len());
135
136		for target_col in target_columns.iter() {
137			let r#type = target_col.constraint.get_type();
138
139			let value = if let Some(source_column) = ctx.columns.column(&target_col.name) {
140				let lazy_frag = Fragment::owned_internal(&target_col.name);
141				let coerced = cast::cast_column_data(&ctx, source_column.data(), r#type, &lazy_frag)?;
142				coerced.get_value(0)
143			} else {
144				Value::Undefined
145			};
146			values.push(value);
147			names.push(target_col.name.clone());
148			types.push(r#type);
149		}
150
151		let layout = EncodedValuesNamedLayout::new(names.into_iter().zip(types.into_iter()));
152		let mut encoded = layout.allocate_row();
153		layout.set_values(&mut encoded, &values);
154
155		Ok(Row {
156			number: row.number,
157			encoded,
158			layout,
159		})
160	}
161}