Skip to main content

reifydb_evaluate/expression/arith/
sub.rs

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