Skip to main content

reifydb_evaluate/expression/
prefix.rs

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