Skip to main content

reifydb_evaluate/expression/
prefix.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use arrow_arith::boolean::not;
5use arrow_array::ArrayRef;
6use arrow_schema::FieldRef;
7use reifydb_core::{
8	error::CoreError,
9	expression::PrefixOperator,
10	value::column::{arith::option::unary_op_unwrap_option, factory},
11};
12use reifydb_value::{
13	error::{LogicalOp, OperandCategory, TypeError},
14	fragment::Fragment,
15	value::{
16		column_view::{ColumnView, ViewData},
17		container::{decimal_array::decimals, wide_int_array::wides},
18		decimal::Decimal,
19		value_type::ValueType,
20	},
21};
22
23use crate::{Result, expression::logic::bool_column};
24
25macro_rules! prefix_signed_int {
26	($column:expr, $values:expr, $operator:expr, $fragment:expr, $variant:ident, $value_type:expr) => {{
27		let values: &[_] = $values;
28		let mut result = Vec::with_capacity(values.len());
29		for val in values.iter() {
30			result.push(match $operator {
31				PrefixOperator::Minus(_) => {
32					val.checked_neg().ok_or_else(|| TypeError::NumberOutOfRange {
33						target: $value_type,
34						fragment: $fragment,
35						descriptor: None,
36					})?
37				}
38				PrefixOperator::Plus(_) => *val,
39				PrefixOperator::Not(_) => {
40					return Err(TypeError::LogicalOperatorNotApplicable {
41						operator: LogicalOp::Not,
42						operand_category: OperandCategory::Number,
43						fragment: $fragment,
44					}
45					.into());
46				}
47			});
48		}
49		Ok(factory::$variant($column.0.name(), result))
50	}};
51}
52
53macro_rules! prefix_unsigned_int {
54	($column:expr, $values:expr, $operator:expr, $fragment:expr, $signed_ty:ty, $constructor:ident, $value_type:expr) => {{
55		match $operator {
56			PrefixOperator::Plus(_) => Ok($column.clone()),
57			PrefixOperator::Not(_) => Err(TypeError::LogicalOperatorNotApplicable {
58				operator: LogicalOp::Not,
59				operand_category: OperandCategory::Number,
60				fragment: $fragment,
61			}
62			.into()),
63			PrefixOperator::Minus(_) => {
64				let values: &[_] = $values;
65				let magnitude = <$signed_ty>::MIN.unsigned_abs();
66				let mut result = Vec::with_capacity(values.len());
67				for val in values.iter() {
68					if *val > magnitude {
69						return Err(TypeError::NumberOutOfRange {
70							target: $value_type,
71							fragment: $fragment,
72							descriptor: None,
73						}
74						.into());
75					}
76					result.push((*val as $signed_ty).wrapping_neg());
77				}
78				Ok(factory::$constructor($column.0.name(), result))
79			}
80		}
81	}};
82}
83
84macro_rules! prefix_float {
85	($column:expr, $container:expr, $operator:expr, $fragment:expr, $zero:expr, $constructor:ident) => {{
86		let values: &[_] = $container.values();
87		let mut result = Vec::with_capacity(values.len());
88		for (idx, val) in values.iter().enumerate() {
89			if idx < values.len() {
90				result.push(match $operator {
91					PrefixOperator::Minus(_) => -*val,
92					PrefixOperator::Plus(_) => *val,
93					PrefixOperator::Not(_) => {
94						return Err(TypeError::LogicalOperatorNotApplicable {
95							operator: LogicalOp::Not,
96							operand_category: OperandCategory::Number,
97							fragment: $fragment,
98						}
99						.into());
100					}
101				});
102			} else {
103				result.push($zero);
104			}
105		}
106		Ok(factory::$constructor($column.0.name(), result))
107	}};
108}
109
110macro_rules! prefix_not_error {
111	($operator:expr, $fragment:expr, $category:expr, $what:expr) => {
112		match $operator {
113			PrefixOperator::Not(_) => Err(TypeError::LogicalOperatorNotApplicable {
114				operator: LogicalOp::Not,
115				operand_category: $category,
116				fragment: $fragment,
117			}
118			.into()),
119			_ => Err(CoreError::FrameError {
120				message: format!("Cannot apply arithmetic prefix operator to {}", $what),
121			}
122			.into()),
123		}
124	};
125}
126
127pub fn prefix_apply(
128	column: &(FieldRef, ArrayRef),
129	operator: &PrefixOperator,
130	fragment: &Fragment,
131) -> Result<(FieldRef, ArrayRef)> {
132	if ColumnView::try_from(column)?.is_untyped_none() && !matches!(operator, PrefixOperator::Not(_)) {
133		return Ok(column.clone());
134	}
135	unary_op_unwrap_option(column, |column| match &ColumnView::try_from(column)?.data {
136		ViewData::Bool(container) => match operator {
137			PrefixOperator::Not(_) => {
138				let negated = not(container).map_err(|err| CoreError::FrameError {
139					message: err.to_string(),
140				})?;
141				Ok(bool_column(column.0.name(), negated, column.0.is_nullable()))
142			}
143			_ => Err(CoreError::FrameError {
144				message: "Cannot apply arithmetic prefix operator to bool".to_string(),
145			}
146			.into()),
147		},
148
149		ViewData::Float4(container) => {
150			prefix_float!(column, container, operator, fragment.clone(), 0.0f32, float4)
151		}
152
153		ViewData::Float8(container) => {
154			prefix_float!(column, container, operator, fragment.clone(), 0.0f64, float8)
155		}
156
157		ViewData::Int1(container) => {
158			prefix_signed_int!(
159				column,
160				container.values(),
161				operator,
162				fragment.clone(),
163				int1,
164				ValueType::Int1
165			)
166		}
167
168		ViewData::Int2(container) => {
169			prefix_signed_int!(
170				column,
171				container.values(),
172				operator,
173				fragment.clone(),
174				int2,
175				ValueType::Int2
176			)
177		}
178
179		ViewData::Int4(container) => {
180			prefix_signed_int!(
181				column,
182				container.values(),
183				operator,
184				fragment.clone(),
185				int4,
186				ValueType::Int4
187			)
188		}
189
190		ViewData::Int8(container) => {
191			prefix_signed_int!(
192				column,
193				container.values(),
194				operator,
195				fragment.clone(),
196				int8,
197				ValueType::Int8
198			)
199		}
200
201		ViewData::Int16(container) => {
202			prefix_signed_int!(
203				column,
204				&wides::<i128>(container),
205				operator,
206				fragment.clone(),
207				int16,
208				ValueType::Int16
209			)
210		}
211
212		ViewData::Utf8 {
213			container: _,
214			..
215		} => match operator {
216			PrefixOperator::Not(_) => Err(TypeError::LogicalOperatorNotApplicable {
217				operator: LogicalOp::Not,
218				operand_category: OperandCategory::Text,
219				fragment: fragment.clone(),
220			}
221			.into()),
222			_ => Err(CoreError::FrameError {
223				message: "Cannot apply arithmetic prefix operator to text".to_string(),
224			}
225			.into()),
226		},
227
228		ViewData::Uint1(container) => {
229			prefix_unsigned_int!(
230				column,
231				container.values(),
232				operator,
233				fragment.clone(),
234				i8,
235				int1,
236				ValueType::Int1
237			)
238		}
239
240		ViewData::Uint2(container) => {
241			prefix_unsigned_int!(
242				column,
243				container.values(),
244				operator,
245				fragment.clone(),
246				i16,
247				int2,
248				ValueType::Int2
249			)
250		}
251
252		ViewData::Uint4(container) => {
253			prefix_unsigned_int!(
254				column,
255				container.values(),
256				operator,
257				fragment.clone(),
258				i32,
259				int4,
260				ValueType::Int4
261			)
262		}
263
264		ViewData::Uint8(container) => {
265			prefix_unsigned_int!(
266				column,
267				container.values(),
268				operator,
269				fragment.clone(),
270				i64,
271				int8,
272				ValueType::Int8
273			)
274		}
275
276		ViewData::Uint16(container) => {
277			prefix_unsigned_int!(
278				column,
279				&wides::<u128>(container),
280				operator,
281				fragment.clone(),
282				i128,
283				int16,
284				ValueType::Int16
285			)
286		}
287
288		ViewData::Date(_) => {
289			prefix_not_error!(operator, fragment.clone(), OperandCategory::Temporal, "date")
290		}
291		ViewData::DateTime(_) => {
292			prefix_not_error!(operator, fragment.clone(), OperandCategory::Temporal, "datetime")
293		}
294		ViewData::Time(_) => {
295			prefix_not_error!(operator, fragment.clone(), OperandCategory::Temporal, "time")
296		}
297		ViewData::Duration(_) => {
298			prefix_not_error!(operator, fragment.clone(), OperandCategory::Temporal, "duration")
299		}
300		ViewData::IdentityId(_) => {
301			prefix_not_error!(operator, fragment.clone(), OperandCategory::Uuid, "identity id")
302		}
303		ViewData::Uuid4(_) => {
304			prefix_not_error!(operator, fragment.clone(), OperandCategory::Uuid, "uuid4")
305		}
306		ViewData::Uuid7(_) => {
307			prefix_not_error!(operator, fragment.clone(), OperandCategory::Uuid, "uuid7")
308		}
309
310		ViewData::None {
311			..
312		} => Ok(column.clone()),
313
314		ViewData::Blob {
315			container: _,
316			..
317		} => match operator {
318			PrefixOperator::Not(_) => Err(CoreError::FrameError {
319				message: "Cannot apply NOT operator to BLOB".to_string(),
320			}
321			.into()),
322			_ => Err(CoreError::FrameError {
323				message: "Cannot apply arithmetic prefix operator to BLOB".to_string(),
324			}
325			.into()),
326		},
327		ViewData::Decimal(container) => match operator {
328			PrefixOperator::Minus(_) => {
329				let result = decimals(container).iter().map(Decimal::negate).collect::<Vec<_>>();
330				Ok(factory::decimal(column.0.name(), container.precision(), container.scale(), result))
331			}
332			PrefixOperator::Plus(_) => Ok(column.clone()),
333			PrefixOperator::Not(_) => Err(TypeError::LogicalOperatorNotApplicable {
334				operator: LogicalOp::Not,
335				operand_category: OperandCategory::Number,
336				fragment: fragment.clone(),
337			}
338			.into()),
339		},
340		ViewData::DictionaryId {
341			..
342		} => match operator {
343			PrefixOperator::Not(_) => Err(CoreError::FrameError {
344				message: "Cannot apply NOT operator to DictionaryId type".to_string(),
345			}
346			.into()),
347			_ => Err(CoreError::FrameError {
348				message: "Cannot apply arithmetic prefix operator to DictionaryId type".to_string(),
349			}
350			.into()),
351		},
352		ViewData::Any {
353			..
354		} => match operator {
355			PrefixOperator::Not(_) => Err(CoreError::FrameError {
356				message: "Cannot apply NOT operator to Any type".to_string(),
357			}
358			.into()),
359			_ => Err(CoreError::FrameError {
360				message: "Cannot apply arithmetic prefix operator to Any type".to_string(),
361			}
362			.into()),
363		},
364		ViewData::Digest {
365			..
366		} => match operator {
367			PrefixOperator::Not(_) => Err(CoreError::FrameError {
368				message: "Cannot apply NOT operator to Digest type".to_string(),
369			}
370			.into()),
371			_ => Err(CoreError::FrameError {
372				message: "Cannot apply arithmetic prefix operator to Digest type".to_string(),
373			}
374			.into()),
375		},
376	})
377}