Skip to main content

reifydb_engine/expression/
prefix.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 ReifyDB
3
4use reifydb_core::{
5	error::CoreError,
6	value::column::{ColumnWithName, buffer::ColumnBuffer},
7};
8use reifydb_rql::expression::PrefixOperator;
9use reifydb_type::{
10	error::{LogicalOp, OperandCategory, TypeError},
11	fragment::Fragment,
12	value::{decimal::Decimal, int::Int, uint::Uint},
13};
14
15use crate::{Result, expression::option::unary_op_unwrap_option};
16
17/// Macro for signed integer prefix arms (Int1, Int2, Int4, Int8, Int16).
18/// Each arm negates with `-*val`, plus with `*val`, and Not returns an error.
19macro_rules! prefix_signed_int {
20	($column:expr, $container:expr, $operator:expr, $fragment:expr, $variant:ident) => {{
21		let mut result = Vec::with_capacity($container.data().len());
22		for (idx, val) in $container.data().iter().enumerate() {
23			if $container.is_defined(idx) {
24				result.push(match $operator {
25					PrefixOperator::Minus(_) => -*val,
26					PrefixOperator::Plus(_) => *val,
27					PrefixOperator::Not(_) => {
28						return Err(TypeError::LogicalOperatorNotApplicable {
29							operator: LogicalOp::Not,
30							operand_category: OperandCategory::Number,
31							fragment: $fragment,
32						}
33						.into());
34					}
35				});
36			} else {
37				result.push(0);
38			}
39		}
40		let new_data = ColumnBuffer::$variant(result);
41		Ok($column.with_new_data(new_data))
42	}};
43}
44
45/// Macro for unsigned integer prefix arms (Uint1, Uint2, Uint4, Uint8, Uint16).
46/// Each arm converts to a signed type, then negates/plus, and Not returns an error.
47macro_rules! prefix_unsigned_int {
48	($column:expr, $container:expr, $operator:expr, $fragment:expr, $signed_ty:ty, $constructor:ident) => {{
49		let mut result = Vec::with_capacity($container.data().len());
50		for val in $container.data().iter() {
51			let signed = *val as $signed_ty;
52			result.push(match $operator {
53				PrefixOperator::Minus(_) => -signed,
54				PrefixOperator::Plus(_) => signed,
55				PrefixOperator::Not(_) => {
56					return Err(TypeError::LogicalOperatorNotApplicable {
57						operator: LogicalOp::Not,
58						operand_category: OperandCategory::Number,
59						fragment: $fragment,
60					}
61					.into());
62				}
63			});
64		}
65		let new_data = ColumnBuffer::$constructor(result);
66		Ok($column.with_new_data(new_data))
67	}};
68}
69
70/// Macro for float prefix arms (Float4, Float8).
71/// Same pattern as signed ints but with float zero defaults.
72macro_rules! prefix_float {
73	($column:expr, $container:expr, $operator:expr, $fragment:expr, $zero:expr, $constructor:ident) => {{
74		let mut result = Vec::with_capacity($container.data().len());
75		for (idx, val) in $container.data().iter().enumerate() {
76			if $container.is_defined(idx) {
77				result.push(match $operator {
78					PrefixOperator::Minus(_) => -*val,
79					PrefixOperator::Plus(_) => *val,
80					PrefixOperator::Not(_) => {
81						return Err(TypeError::LogicalOperatorNotApplicable {
82							operator: LogicalOp::Not,
83							operand_category: OperandCategory::Number,
84							fragment: $fragment,
85						}
86						.into());
87					}
88				});
89			} else {
90				result.push($zero);
91			}
92		}
93		let new_data = ColumnBuffer::$constructor(result);
94		Ok($column.with_new_data(new_data))
95	}};
96}
97
98/// Macro for types that only support `Not` returning an error and `unimplemented!()` for arithmetic.
99macro_rules! prefix_not_error {
100	($operator:expr, $fragment:expr, $category:expr) => {
101		match $operator {
102			PrefixOperator::Not(_) => Err(TypeError::LogicalOperatorNotApplicable {
103				operator: LogicalOp::Not,
104				operand_category: $category,
105				fragment: $fragment,
106			}
107			.into()),
108			_ => unimplemented!(),
109		}
110	};
111}
112
113/// Applies a prefix operator to an already-evaluated column, without re-evaluating
114/// the inner expression. This avoids recompilation when the column has already been
115/// computed.
116pub(crate) fn prefix_apply(
117	column: &ColumnWithName,
118	operator: &PrefixOperator,
119	fragment: &Fragment,
120) -> Result<ColumnWithName> {
121	unary_op_unwrap_option(column, |column| match column.data() {
122		ColumnBuffer::Bool(container) => match operator {
123			PrefixOperator::Not(_) => {
124				let mut result = Vec::with_capacity(container.data().len());
125				for (idx, val) in container.data().iter().enumerate() {
126					if container.is_defined(idx) {
127						result.push(!val);
128					} else {
129						result.push(false);
130					}
131				}
132
133				let new_data = ColumnBuffer::bool(result);
134				Ok(column.with_new_data(new_data))
135			}
136			_ => Err(CoreError::FrameError {
137				message: "Cannot apply arithmetic prefix operator to bool".to_string(),
138			}
139			.into()),
140		},
141
142		ColumnBuffer::Float4(container) => {
143			prefix_float!(column, container, operator, fragment.clone(), 0.0f32, float4)
144		}
145
146		ColumnBuffer::Float8(container) => {
147			prefix_float!(column, container, operator, fragment.clone(), 0.0f64, float8)
148		}
149
150		ColumnBuffer::Int1(container) => {
151			prefix_signed_int!(column, container, operator, fragment.clone(), int1)
152		}
153
154		ColumnBuffer::Int2(container) => {
155			prefix_signed_int!(column, container, operator, fragment.clone(), int2)
156		}
157
158		ColumnBuffer::Int4(container) => {
159			prefix_signed_int!(column, container, operator, fragment.clone(), int4)
160		}
161
162		ColumnBuffer::Int8(container) => {
163			prefix_signed_int!(column, container, operator, fragment.clone(), int8)
164		}
165
166		ColumnBuffer::Int16(container) => {
167			prefix_signed_int!(column, container, operator, fragment.clone(), int16)
168		}
169
170		ColumnBuffer::Utf8 {
171			container: _,
172			..
173		} => match operator {
174			PrefixOperator::Not(_) => Err(TypeError::LogicalOperatorNotApplicable {
175				operator: LogicalOp::Not,
176				operand_category: OperandCategory::Text,
177				fragment: fragment.clone(),
178			}
179			.into()),
180			_ => Err(CoreError::FrameError {
181				message: "Cannot apply arithmetic prefix operator to text".to_string(),
182			}
183			.into()),
184		},
185
186		ColumnBuffer::Uint1(container) => {
187			prefix_unsigned_int!(column, container, operator, fragment.clone(), i8, int1)
188		}
189
190		ColumnBuffer::Uint2(container) => {
191			prefix_unsigned_int!(column, container, operator, fragment.clone(), i16, int2)
192		}
193
194		ColumnBuffer::Uint4(container) => {
195			prefix_unsigned_int!(column, container, operator, fragment.clone(), i32, int4)
196		}
197
198		ColumnBuffer::Uint8(container) => {
199			prefix_unsigned_int!(column, container, operator, fragment.clone(), i64, int8)
200		}
201
202		ColumnBuffer::Uint16(container) => {
203			prefix_unsigned_int!(column, container, operator, fragment.clone(), i128, int16)
204		}
205
206		ColumnBuffer::Date(_) => {
207			prefix_not_error!(operator, fragment.clone(), OperandCategory::Temporal)
208		}
209		ColumnBuffer::DateTime(_) => {
210			prefix_not_error!(operator, fragment.clone(), OperandCategory::Temporal)
211		}
212		ColumnBuffer::Time(_) => {
213			prefix_not_error!(operator, fragment.clone(), OperandCategory::Temporal)
214		}
215		ColumnBuffer::Duration(_) => {
216			prefix_not_error!(operator, fragment.clone(), OperandCategory::Temporal)
217		}
218		ColumnBuffer::IdentityId(_) => {
219			prefix_not_error!(operator, fragment.clone(), OperandCategory::Uuid)
220		}
221		ColumnBuffer::Uuid4(_) => {
222			prefix_not_error!(operator, fragment.clone(), OperandCategory::Uuid)
223		}
224		ColumnBuffer::Uuid7(_) => {
225			prefix_not_error!(operator, fragment.clone(), OperandCategory::Uuid)
226		}
227
228		ColumnBuffer::Blob {
229			container: _,
230			..
231		} => match operator {
232			PrefixOperator::Not(_) => Err(CoreError::FrameError {
233				message: "Cannot apply NOT operator to BLOB".to_string(),
234			}
235			.into()),
236			_ => Err(CoreError::FrameError {
237				message: "Cannot apply arithmetic prefix operator to BLOB".to_string(),
238			}
239			.into()),
240		},
241		ColumnBuffer::Int {
242			container,
243			..
244		} => {
245			let mut result = Vec::with_capacity(container.data().len());
246			for (idx, val) in container.data().iter().enumerate() {
247				if container.is_defined(idx) {
248					result.push(match operator {
249						PrefixOperator::Minus(_) => Int(-val.0.clone()),
250						PrefixOperator::Plus(_) => val.clone(),
251						PrefixOperator::Not(_) => {
252							return Err(TypeError::LogicalOperatorNotApplicable {
253								operator: LogicalOp::Not,
254								operand_category: OperandCategory::Number,
255								fragment: fragment.clone(),
256							}
257							.into());
258						}
259					});
260				} else {
261					result.push(Int::zero());
262				}
263			}
264			let new_data = ColumnBuffer::int(result);
265			Ok(column.with_new_data(new_data))
266		}
267		ColumnBuffer::Uint {
268			container,
269			..
270		} => match operator {
271			PrefixOperator::Minus(_) => {
272				let mut result = Vec::with_capacity(container.data().len());
273				for (idx, val) in container.data().iter().enumerate() {
274					if container.is_defined(idx) {
275						let negated = -val.0.clone();
276						result.push(Int::from(negated));
277					} else {
278						result.push(Int::zero());
279					}
280				}
281				let new_data = ColumnBuffer::int(result);
282				Ok(column.with_new_data(new_data))
283			}
284			PrefixOperator::Plus(_) => {
285				let mut result = Vec::with_capacity(container.data().len());
286				for (idx, val) in container.data().iter().enumerate() {
287					if container.is_defined(idx) {
288						result.push(val.clone());
289					} else {
290						result.push(Uint::zero());
291					}
292				}
293				let new_data = ColumnBuffer::uint(result);
294				Ok(column.with_new_data(new_data))
295			}
296			PrefixOperator::Not(_) => Err(TypeError::LogicalOperatorNotApplicable {
297				operator: LogicalOp::Not,
298				operand_category: OperandCategory::Number,
299				fragment: fragment.clone(),
300			}
301			.into()),
302		},
303		ColumnBuffer::Decimal {
304			container,
305			..
306		} => {
307			let mut result = Vec::with_capacity(container.data().len());
308			for (idx, val) in container.data().iter().enumerate() {
309				if container.is_defined(idx) {
310					result.push(match operator {
311						PrefixOperator::Minus(_) => val.clone().negate(),
312						PrefixOperator::Plus(_) => val.clone(),
313						PrefixOperator::Not(_) => {
314							return Err(TypeError::LogicalOperatorNotApplicable {
315								operator: LogicalOp::Not,
316								operand_category: OperandCategory::Number,
317								fragment: fragment.clone(),
318							}
319							.into());
320						}
321					});
322				} else {
323					result.push(Decimal::from(0));
324				}
325			}
326			let new_data = ColumnBuffer::decimal(result);
327			Ok(column.with_new_data(new_data))
328		}
329		ColumnBuffer::DictionaryId(_) => match operator {
330			PrefixOperator::Not(_) => Err(CoreError::FrameError {
331				message: "Cannot apply NOT operator to DictionaryId type".to_string(),
332			}
333			.into()),
334			_ => Err(CoreError::FrameError {
335				message: "Cannot apply arithmetic prefix operator to DictionaryId type".to_string(),
336			}
337			.into()),
338		},
339		ColumnBuffer::Any(_) => match operator {
340			PrefixOperator::Not(_) => Err(CoreError::FrameError {
341				message: "Cannot apply NOT operator to Any type".to_string(),
342			}
343			.into()),
344			_ => Err(CoreError::FrameError {
345				message: "Cannot apply arithmetic prefix operator to Any type".to_string(),
346			}
347			.into()),
348		},
349		ColumnBuffer::Option {
350			..
351		} => unreachable!("nested Option after unwrap"),
352	})
353}