reifydb-engine 0.4.10

Query execution and processing engine for ReifyDB
Documentation
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2025 ReifyDB

use reifydb_core::interface::catalog::property::ColumnSaturationStrategy;
use reifydb_type::{
	Result,
	error::TypeError,
	fragment::LazyFragment,
	value::{
		is::IsNumber,
		number::{
			promote::Promote,
			safe::{add::SafeAdd, div::SafeDiv, mul::SafeMul, remainder::SafeRemainder, sub::SafeSub},
		},
		r#type::get::GetType,
	},
};

use crate::expression::context::EvalContext;

macro_rules! impl_scalar_op {
	($method:ident, $safe_trait:ident, $checked_method:ident) => {
		impl EvalContext<'_> {
			pub fn $method<L, R>(
				&self,
				l: &L,
				r: &R,
				fragment: impl LazyFragment + Copy,
			) -> Result<Option<<L as Promote<R>>::Output>>
			where
				L: Promote<R>,
				R: IsNumber,
				<L as Promote<R>>::Output: IsNumber,
				<L as Promote<R>>::Output: $safe_trait,
			{
				match &self.saturation_policy() {
					ColumnSaturationStrategy::Error => {
						let Some((lp, rp)) = l.checked_promote(r) else {
							let descriptor = self
								.target
								.as_ref()
								.and_then(|c| c.to_number_descriptor());
							return Err(TypeError::NumberOutOfRange {
								target: <L as Promote<R>>::Output::get_type(),
								fragment: fragment.fragment(),
								descriptor,
							}
							.into());
						};

						lp.$checked_method(&rp)
							.ok_or_else(|| {
								let descriptor = self
									.target
									.as_ref()
									.and_then(|c| c.to_number_descriptor());
								TypeError::NumberOutOfRange {
									target: <L as Promote<R>>::Output::get_type(),
									fragment: fragment.fragment(),
									descriptor,
								}
								.into()
							})
							.map(Some)
					}
					ColumnSaturationStrategy::None => {
						let Some((lp, rp)) = l.checked_promote(r) else {
							return Ok(None);
						};

						match lp.$checked_method(&rp) {
							None => Ok(None),
							Some(value) => Ok(Some(value)),
						}
					}
				}
			}
		}
	};
}

impl_scalar_op!(add, SafeAdd, checked_add);
impl_scalar_op!(sub, SafeSub, checked_sub);
impl_scalar_op!(mul, SafeMul, checked_mul);
impl_scalar_op!(div, SafeDiv, checked_div);
impl_scalar_op!(remainder, SafeRemainder, checked_rem);

#[cfg(test)]
pub mod tests {
	use reifydb_type::fragment::Fragment;

	use crate::expression::context::EvalContext;

	#[test]
	fn test_add() {
		let test_instance = EvalContext::testing();
		let result = test_instance.add(&1i8, &255i16, || Fragment::testing_empty());
		assert_eq!(result, Ok(Some(256i128)));
	}

	#[test]
	fn test_sub() {
		let test_instance = EvalContext::testing();
		let result = test_instance.sub(&1i8, &255i16, || Fragment::testing_empty());
		assert_eq!(result, Ok(Some(-254i128)));
	}

	#[test]
	fn test_mul() {
		let test_instance = EvalContext::testing();
		let result = test_instance.mul(&23i8, &255i16, || Fragment::testing_empty());
		assert_eq!(result, Ok(Some(5865i128)));
	}

	#[test]
	fn test_div() {
		let test_instance = EvalContext::testing();
		let result = test_instance.div(&120i8, &20i16, || Fragment::testing_empty());
		assert_eq!(result, Ok(Some(6i128)));
	}

	#[test]
	fn test_remainder() {
		let test_instance = EvalContext::testing();
		let result = test_instance.remainder(&120i8, &21i16, || Fragment::testing_empty());
		assert_eq!(result, Ok(Some(15i128)));
	}
}