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::{Column, data::ColumnData},
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 = ColumnData::$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 = ColumnData::$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 = ColumnData::$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(column: &Column, operator: &PrefixOperator, fragment: &Fragment) -> Result<Column> {
117	unary_op_unwrap_option(column, |column| match column.data() {
118		ColumnData::Bool(container) => match operator {
119			PrefixOperator::Not(_) => {
120				let mut result = Vec::with_capacity(container.data().len());
121				for (idx, val) in container.data().iter().enumerate() {
122					if container.is_defined(idx) {
123						result.push(!val);
124					} else {
125						result.push(false);
126					}
127				}
128
129				let new_data = ColumnData::bool(result);
130				Ok(column.with_new_data(new_data))
131			}
132			_ => Err(CoreError::FrameError {
133				message: "Cannot apply arithmetic prefix operator to bool".to_string(),
134			}
135			.into()),
136		},
137
138		ColumnData::Float4(container) => {
139			prefix_float!(column, container, operator, fragment.clone(), 0.0f32, float4)
140		}
141
142		ColumnData::Float8(container) => {
143			prefix_float!(column, container, operator, fragment.clone(), 0.0f64, float8)
144		}
145
146		ColumnData::Int1(container) => {
147			prefix_signed_int!(column, container, operator, fragment.clone(), int1)
148		}
149
150		ColumnData::Int2(container) => {
151			prefix_signed_int!(column, container, operator, fragment.clone(), int2)
152		}
153
154		ColumnData::Int4(container) => {
155			prefix_signed_int!(column, container, operator, fragment.clone(), int4)
156		}
157
158		ColumnData::Int8(container) => {
159			prefix_signed_int!(column, container, operator, fragment.clone(), int8)
160		}
161
162		ColumnData::Int16(container) => {
163			prefix_signed_int!(column, container, operator, fragment.clone(), int16)
164		}
165
166		ColumnData::Utf8 {
167			container: _,
168			..
169		} => match operator {
170			PrefixOperator::Not(_) => Err(TypeError::LogicalOperatorNotApplicable {
171				operator: LogicalOp::Not,
172				operand_category: OperandCategory::Text,
173				fragment: fragment.clone(),
174			}
175			.into()),
176			_ => Err(CoreError::FrameError {
177				message: "Cannot apply arithmetic prefix operator to text".to_string(),
178			}
179			.into()),
180		},
181
182		ColumnData::Uint1(container) => {
183			prefix_unsigned_int!(column, container, operator, fragment.clone(), i8, int1)
184		}
185
186		ColumnData::Uint2(container) => {
187			prefix_unsigned_int!(column, container, operator, fragment.clone(), i16, int2)
188		}
189
190		ColumnData::Uint4(container) => {
191			prefix_unsigned_int!(column, container, operator, fragment.clone(), i32, int4)
192		}
193
194		ColumnData::Uint8(container) => {
195			prefix_unsigned_int!(column, container, operator, fragment.clone(), i64, int8)
196		}
197
198		ColumnData::Uint16(container) => {
199			prefix_unsigned_int!(column, container, operator, fragment.clone(), i128, int16)
200		}
201
202		ColumnData::Date(_) => {
203			prefix_not_error!(operator, fragment.clone(), OperandCategory::Temporal)
204		}
205		ColumnData::DateTime(_) => {
206			prefix_not_error!(operator, fragment.clone(), OperandCategory::Temporal)
207		}
208		ColumnData::Time(_) => {
209			prefix_not_error!(operator, fragment.clone(), OperandCategory::Temporal)
210		}
211		ColumnData::Duration(_) => {
212			prefix_not_error!(operator, fragment.clone(), OperandCategory::Temporal)
213		}
214		ColumnData::IdentityId(_) => {
215			prefix_not_error!(operator, fragment.clone(), OperandCategory::Uuid)
216		}
217		ColumnData::Uuid4(_) => {
218			prefix_not_error!(operator, fragment.clone(), OperandCategory::Uuid)
219		}
220		ColumnData::Uuid7(_) => {
221			prefix_not_error!(operator, fragment.clone(), OperandCategory::Uuid)
222		}
223
224		ColumnData::Blob {
225			container: _,
226			..
227		} => match operator {
228			PrefixOperator::Not(_) => Err(CoreError::FrameError {
229				message: "Cannot apply NOT operator to BLOB".to_string(),
230			}
231			.into()),
232			_ => Err(CoreError::FrameError {
233				message: "Cannot apply arithmetic prefix operator to BLOB".to_string(),
234			}
235			.into()),
236		},
237		ColumnData::Int {
238			container,
239			..
240		} => {
241			let mut result = Vec::with_capacity(container.data().len());
242			for (idx, val) in container.data().iter().enumerate() {
243				if container.is_defined(idx) {
244					result.push(match operator {
245						PrefixOperator::Minus(_) => Int(-val.0.clone()),
246						PrefixOperator::Plus(_) => val.clone(),
247						PrefixOperator::Not(_) => {
248							return Err(TypeError::LogicalOperatorNotApplicable {
249								operator: LogicalOp::Not,
250								operand_category: OperandCategory::Number,
251								fragment: fragment.clone(),
252							}
253							.into());
254						}
255					});
256				} else {
257					result.push(Int::zero());
258				}
259			}
260			let new_data = ColumnData::int(result);
261			Ok(column.with_new_data(new_data))
262		}
263		ColumnData::Uint {
264			container,
265			..
266		} => match operator {
267			PrefixOperator::Minus(_) => {
268				let mut result = Vec::with_capacity(container.data().len());
269				for (idx, val) in container.data().iter().enumerate() {
270					if container.is_defined(idx) {
271						let negated = -val.0.clone();
272						result.push(Int::from(negated));
273					} else {
274						result.push(Int::zero());
275					}
276				}
277				let new_data = ColumnData::int(result);
278				Ok(column.with_new_data(new_data))
279			}
280			PrefixOperator::Plus(_) => {
281				let mut result = Vec::with_capacity(container.data().len());
282				for (idx, val) in container.data().iter().enumerate() {
283					if container.is_defined(idx) {
284						result.push(val.clone());
285					} else {
286						result.push(Uint::zero());
287					}
288				}
289				let new_data = ColumnData::uint(result);
290				Ok(column.with_new_data(new_data))
291			}
292			PrefixOperator::Not(_) => Err(TypeError::LogicalOperatorNotApplicable {
293				operator: LogicalOp::Not,
294				operand_category: OperandCategory::Number,
295				fragment: fragment.clone(),
296			}
297			.into()),
298		},
299		ColumnData::Decimal {
300			container,
301			..
302		} => {
303			let mut result = Vec::with_capacity(container.data().len());
304			for (idx, val) in container.data().iter().enumerate() {
305				if container.is_defined(idx) {
306					result.push(match operator {
307						PrefixOperator::Minus(_) => val.clone().negate(),
308						PrefixOperator::Plus(_) => val.clone(),
309						PrefixOperator::Not(_) => {
310							return Err(TypeError::LogicalOperatorNotApplicable {
311								operator: LogicalOp::Not,
312								operand_category: OperandCategory::Number,
313								fragment: fragment.clone(),
314							}
315							.into());
316						}
317					});
318				} else {
319					result.push(Decimal::from(0));
320				}
321			}
322			let new_data = ColumnData::decimal(result);
323			Ok(column.with_new_data(new_data))
324		}
325		ColumnData::DictionaryId(_) => match operator {
326			PrefixOperator::Not(_) => Err(CoreError::FrameError {
327				message: "Cannot apply NOT operator to DictionaryId type".to_string(),
328			}
329			.into()),
330			_ => Err(CoreError::FrameError {
331				message: "Cannot apply arithmetic prefix operator to DictionaryId type".to_string(),
332			}
333			.into()),
334		},
335		ColumnData::Any(_) => match operator {
336			PrefixOperator::Not(_) => Err(CoreError::FrameError {
337				message: "Cannot apply NOT operator to Any type".to_string(),
338			}
339			.into()),
340			_ => Err(CoreError::FrameError {
341				message: "Cannot apply arithmetic prefix operator to Any type".to_string(),
342			}
343			.into()),
344		},
345		ColumnData::Option {
346			..
347		} => unreachable!("nested Option after unwrap"),
348	})
349}