Skip to main content

reifydb_engine/expression/arith/
sub.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 ReifyDB
3
4use reifydb_core::value::column::{ColumnWithName, buffer::ColumnBuffer, push::Push};
5use reifydb_type::{
6	error::{BinaryOp, TypeError},
7	fragment::LazyFragment,
8	value::{
9		container::{number::NumberContainer, temporal::TemporalContainer},
10		is::IsNumber,
11		number::{promote::Promote, safe::sub::SafeSub},
12		r#type::{Type, get::GetType},
13	},
14};
15
16use crate::{
17	Result,
18	expression::{context::EvalContext, option::binary_op_unwrap_option},
19};
20
21pub(crate) fn sub_columns(
22	ctx: &EvalContext,
23	left: &ColumnWithName,
24	right: &ColumnWithName,
25	fragment: impl LazyFragment + Copy,
26) -> Result<ColumnWithName> {
27	binary_op_unwrap_option(left, right, fragment.fragment(), |left, right| {
28		let target = Type::promote(left.get_type(), right.get_type());
29
30		dispatch_arith!(
31			&left.data(), &right.data();
32			fixed: sub_numeric, arb: sub_numeric_clone (ctx, target, fragment);
33
34
35			(ColumnBuffer::Duration(l), ColumnBuffer::Duration(r)) => {
36				let mut container = TemporalContainer::with_capacity(l.len());
37				for i in 0..l.len() {
38					match (l.get(i), r.get(i)) {
39						(Some(lv), Some(rv)) => container.push(*lv - *rv),
40						_ => container.push_default(),
41					}
42				}
43				Ok(ColumnWithName::new(fragment.fragment(), ColumnBuffer::Duration(container)))
44			}
45
46			_ => Err(TypeError::BinaryOperatorNotApplicable {
47				operator: BinaryOp::Sub,
48				left: left.get_type(),
49				right: right.get_type(),
50				fragment: fragment.fragment(),
51			}.into()),
52		)
53	})
54}
55
56fn sub_numeric<L, R>(
57	ctx: &EvalContext,
58	l: &NumberContainer<L>,
59	r: &NumberContainer<R>,
60	target: Type,
61	fragment: impl LazyFragment + Copy,
62) -> Result<ColumnWithName>
63where
64	L: GetType + Promote<R> + IsNumber,
65	R: GetType + IsNumber,
66	<L as Promote<R>>::Output: IsNumber,
67	<L as Promote<R>>::Output: SafeSub,
68	ColumnBuffer: Push<<L as Promote<R>>::Output>,
69{
70	debug_assert_eq!(l.len(), r.len());
71
72	let mut data = ColumnBuffer::with_capacity(target, l.len());
73	let l_data = l.data();
74	let r_data = r.data();
75	for i in 0..l.len() {
76		if let Some(value) = ctx.sub(&l_data[i], &r_data[i], fragment)? {
77			data.push(value);
78		} else {
79			data.push_none()
80		}
81	}
82	Ok(ColumnWithName {
83		name: fragment.fragment(),
84		data,
85	})
86}
87
88fn sub_numeric_clone<L, R>(
89	ctx: &EvalContext,
90	l: &NumberContainer<L>,
91	r: &NumberContainer<R>,
92	target: Type,
93	fragment: impl LazyFragment + Copy,
94) -> Result<ColumnWithName>
95where
96	L: Clone + GetType + Promote<R> + IsNumber,
97	R: Clone + GetType + IsNumber,
98	<L as Promote<R>>::Output: IsNumber,
99	<L as Promote<R>>::Output: SafeSub,
100	ColumnBuffer: Push<<L as Promote<R>>::Output>,
101{
102	debug_assert_eq!(l.len(), r.len());
103
104	let mut data = ColumnBuffer::with_capacity(target, l.len());
105	let l_data = l.data();
106	let r_data = r.data();
107	for i in 0..l.len() {
108		let l_clone = l_data[i].clone();
109		let r_clone = r_data[i].clone();
110		if let Some(value) = ctx.sub(&l_clone, &r_clone, fragment)? {
111			data.push(value);
112		} else {
113			data.push_none()
114		}
115	}
116	Ok(ColumnWithName {
117		name: fragment.fragment(),
118		data,
119	})
120}