Skip to main content

reifydb_engine/expression/arith/
sub.rs

1// SPDX-License-Identifier: AGPL-3.0-or-later
2// Copyright (c) 2025 ReifyDB
3
4use reifydb_core::value::column::{Column, data::ColumnData, 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: &Column,
24	right: &Column,
25	fragment: impl LazyFragment + Copy,
26) -> Result<Column> {
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			// Duration - Duration
35			(ColumnData::Duration(l), ColumnData::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(Column {
44					name: fragment.fragment(),
45					data: ColumnData::Duration(container),
46				})
47			}
48
49			_ => return Err(TypeError::BinaryOperatorNotApplicable {
50				operator: BinaryOp::Sub,
51				left: left.get_type(),
52				right: right.get_type(),
53				fragment: fragment.fragment(),
54			}.into()),
55		)
56	})
57}
58
59fn sub_numeric<'a, L, R>(
60	ctx: &EvalContext,
61	l: &NumberContainer<L>,
62	r: &NumberContainer<R>,
63	target: Type,
64	fragment: impl LazyFragment + Copy,
65) -> Result<Column>
66where
67	L: GetType + Promote<R> + IsNumber,
68	R: GetType + IsNumber,
69	<L as Promote<R>>::Output: IsNumber,
70	<L as Promote<R>>::Output: SafeSub,
71	ColumnData: Push<<L as Promote<R>>::Output>,
72{
73	debug_assert_eq!(l.len(), r.len());
74
75	let mut data = ColumnData::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(Column {
86		name: fragment.fragment(),
87		data,
88	})
89}
90
91fn sub_numeric_clone<'a, L, R>(
92	ctx: &EvalContext,
93	l: &NumberContainer<L>,
94	r: &NumberContainer<R>,
95	target: Type,
96	fragment: impl LazyFragment + Copy,
97) -> Result<Column>
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	ColumnData: Push<<L as Promote<R>>::Output>,
104{
105	debug_assert_eq!(l.len(), r.len());
106
107	let mut data = ColumnData::with_capacity(target, l.len());
108	let l_data = l.data();
109	let r_data = r.data();
110	for i in 0..l.len() {
111		let l_clone = l_data[i].clone();
112		let r_clone = r_data[i].clone();
113		if let Some(value) = ctx.sub(&l_clone, &r_clone, fragment)? {
114			data.push(value);
115		} else {
116			data.push_none()
117		}
118	}
119	Ok(Column {
120		name: fragment.fragment(),
121		data,
122	})
123}