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