1use core::marker::PhantomData;
7
8use crate::ValueTypeForDialect;
9use crate::sql::{SQL, Token};
10use crate::traits::{SQLParam, ToSQL};
11use crate::types::{AlwaysNullable, ArithmeticOutput, NegOutput, Numeric, PropagateNullability};
12
13use super::{AggOr, AggregateKind, Expr, NonNull, NullOr, Nullability};
14
15#[derive(Debug, Clone, Copy)]
19pub struct ColumnBinOp<Lhs, Rhs, Op, D, SQLType, Nullable> {
20 lhs: Lhs,
21 rhs: Rhs,
22 _type: PhantomData<(Op, D, SQLType, Nullable)>,
23}
24
25impl<Lhs, Rhs, Op, D, SQLType, Nullable> ColumnBinOp<Lhs, Rhs, Op, D, SQLType, Nullable> {
26 #[inline]
27 pub const fn new(lhs: Lhs, rhs: Rhs) -> Self {
28 Self {
29 lhs,
30 rhs,
31 _type: PhantomData,
32 }
33 }
34}
35
36#[doc(hidden)]
37pub use crate::types::{
38 AddOp as OpAdd, DivOp as OpDiv, MulOp as OpMul, RemOp as OpRem, SubOp as OpSub,
39};
40
41pub trait BinOpToken {
43 const TOKEN: Token;
44}
45
46#[doc(hidden)]
47pub trait ResolveArithmeticNullability<Lhs, Rhs> {
48 type Output: Nullability;
49}
50
51#[doc(hidden)]
54pub trait ArithmeticRhs<D> {
55 type SQLType: Numeric;
56 type Nullable: Nullability;
57}
58
59#[doc(hidden)]
62pub trait BuildColumnArithmetic<Lhs, Rhs, Op, D, LhsSQLType, LhsNullable> {
63 type Output;
64
65 fn build(lhs: Lhs, rhs: Rhs) -> Self::Output;
66}
67
68impl<Lhs, Rhs, Op, D, LhsSQLType, LhsNullable>
69 BuildColumnArithmetic<Lhs, Rhs, Op, D, LhsSQLType, LhsNullable> for ()
70where
71 Rhs: ArithmeticRhs<D>,
72 LhsSQLType: ArithmeticOutput<Rhs::SQLType, Op>,
73 <LhsSQLType as ArithmeticOutput<Rhs::SQLType, Op>>::Nullability:
74 ResolveArithmeticNullability<LhsNullable, Rhs::Nullable>,
75{
76 type Output = ColumnBinOp<
77 Lhs,
78 Rhs,
79 Op,
80 D,
81 <LhsSQLType as ArithmeticOutput<Rhs::SQLType, Op>>::Output,
82 <<LhsSQLType as ArithmeticOutput<Rhs::SQLType, Op>>::Nullability as ResolveArithmeticNullability<
83 LhsNullable,
84 Rhs::Nullable,
85 >>::Output,
86 >;
87
88 fn build(lhs: Lhs, rhs: Rhs) -> Self::Output {
89 ColumnBinOp::new(lhs, rhs)
90 }
91}
92
93macro_rules! arithmetic_value_rhs {
94 ($dialect:ty; $($value:ty),+ $(,)?) => {
95 $(
96 impl ArithmeticRhs<$dialect> for $value
97 where
98 <$value as ValueTypeForDialect<$dialect>>::SQLType: Numeric,
99 {
100 type SQLType = <$value as ValueTypeForDialect<$dialect>>::SQLType;
101 type Nullable = NonNull;
102 }
103 )+
104 };
105}
106
107arithmetic_value_rhs!(
108 crate::SQLiteDialect;
109 i8, i16, i32, i64, isize, u8, u16, u32, u64, usize, bool, f32, f64
110);
111arithmetic_value_rhs!(
112 crate::PostgresDialect;
113 i8, i16, i32, i64, isize, u8, u16, u32, u64, usize, f32, f64
114);
115arithmetic_value_rhs!(
116 crate::MySQLDialect;
117 i8, i16, i32, i64, isize, u8, u16, u32, u64, usize, f32, f64
118);
119
120#[cfg(feature = "rust-decimal")]
121arithmetic_value_rhs!(crate::PostgresDialect; rust_decimal::Decimal);
122#[cfg(feature = "rust-decimal")]
123arithmetic_value_rhs!(crate::MySQLDialect; rust_decimal::Decimal);
124
125impl<D, T> ArithmeticRhs<D> for Option<T>
126where
127 T: ValueTypeForDialect<D>,
128 T::SQLType: Numeric,
129{
130 type SQLType = T::SQLType;
131 type Nullable = super::Null;
132}
133
134impl<D, T> ArithmeticRhs<D> for &T
135where
136 T: ArithmeticRhs<D> + ?Sized,
137{
138 type SQLType = T::SQLType;
139 type Nullable = T::Nullable;
140}
141
142impl<D, V, T, N, A> ArithmeticRhs<D> for super::SQLExpr<'_, V, T, N, A>
143where
144 V: SQLParam<DialectMarker = D>,
145 T: Numeric,
146 N: Nullability,
147 A: AggregateKind,
148{
149 type SQLType = T;
150 type Nullable = N;
151}
152
153impl<Lhs, Rhs> ResolveArithmeticNullability<Lhs, Rhs> for PropagateNullability
154where
155 Lhs: Nullability + NullOr<Rhs>,
156 Rhs: Nullability,
157{
158 type Output = <Lhs as NullOr<Rhs>>::Output;
159}
160
161impl<Lhs: Nullability, Rhs: Nullability> ResolveArithmeticNullability<Lhs, Rhs> for AlwaysNullable {
162 type Output = super::Null;
163}
164
165impl BinOpToken for OpAdd {
166 const TOKEN: Token = Token::PLUS;
167}
168
169impl BinOpToken for OpSub {
170 const TOKEN: Token = Token::MINUS;
171}
172
173impl BinOpToken for OpMul {
174 const TOKEN: Token = Token::STAR;
175}
176
177impl BinOpToken for OpDiv {
178 const TOKEN: Token = Token::SLASH;
179}
180
181impl BinOpToken for OpRem {
182 const TOKEN: Token = Token::REM;
183}
184
185impl<'a, V, Lhs, Rhs, Op, D, SQLType, Nullable> ToSQL<'a, V>
186 for ColumnBinOp<Lhs, Rhs, Op, D, SQLType, Nullable>
187where
188 V: SQLParam,
189 Lhs: ToSQL<'a, V>,
190 Rhs: ToSQL<'a, V>,
191 Op: BinOpToken,
192{
193 fn to_sql(&self) -> SQL<'a, V> {
194 super::ops::binary_operator_sql(self.lhs.to_sql(), Op::TOKEN, self.rhs.to_sql())
195 }
196
197 fn into_sql(self) -> SQL<'a, V> {
198 super::ops::binary_operator_sql(self.lhs.into_sql(), Op::TOKEN, self.rhs.into_sql())
199 }
200}
201
202impl<'a, V, Lhs, Rhs, Op, D, SQLType, Nullable> Expr<'a, V>
203 for ColumnBinOp<Lhs, Rhs, Op, D, SQLType, Nullable>
204where
205 V: SQLParam<DialectMarker = D>,
206 Lhs: Expr<'a, V>,
207 Rhs: Expr<'a, V>,
208 Lhs::SQLType: Numeric + ArithmeticOutput<Rhs::SQLType, Op, Output = SQLType>,
209 Rhs::SQLType: Numeric,
210 Rhs::Nullable: Nullability,
211 Lhs::Aggregate: AggOr<Rhs::Aggregate>,
212 Rhs::Aggregate: AggregateKind,
213 Op: BinOpToken,
214 SQLType: crate::types::DataType,
215 Nullable: Nullability,
216 <Lhs::SQLType as ArithmeticOutput<Rhs::SQLType, Op>>::Nullability:
217 ResolveArithmeticNullability<Lhs::Nullable, Rhs::Nullable, Output = Nullable>,
218{
219 type SQLType = SQLType;
220 type Nullable = Nullable;
221 type Aggregate = <Lhs::Aggregate as AggOr<Rhs::Aggregate>>::Output;
222}
223
224impl<Lhs, Rhs, Op, D, SQLType, Nullable> super::HasAggStatus
225 for ColumnBinOp<Lhs, Rhs, Op, D, SQLType, Nullable>
226where
227 Lhs: super::HasAggStatus,
228 Rhs: super::HasAggStatus,
229 Lhs::Status: super::CombineAggStatus<Rhs::Status>,
230{
231 type Status = <Lhs::Status as super::CombineAggStatus<Rhs::Status>>::Output;
232}
233
234impl<Lhs, Rhs, Op, D, SQLType, Nullable> ArithmeticRhs<D>
235 for ColumnBinOp<Lhs, Rhs, Op, D, SQLType, Nullable>
236where
237 SQLType: Numeric,
238 Nullable: Nullability,
239{
240 type SQLType = SQLType;
241 type Nullable = Nullable;
242}
243
244impl<Lhs, Rhs, Op, D, SQLType, Nullable> crate::row::ExprValueType
245 for ColumnBinOp<Lhs, Rhs, Op, D, SQLType, Nullable>
246where
247 SQLType: crate::types::DataType + crate::row::SQLTypeToRust<D>,
248 Nullable:
249 Nullability + crate::row::WrapNullable<<SQLType as crate::row::SQLTypeToRust<D>>::RustType>,
250{
251 type ValueType = <Nullable as crate::row::WrapNullable<
252 <SQLType as crate::row::SQLTypeToRust<D>>::RustType,
253 >>::Output;
254}
255
256impl<Lhs, Rhs, Op, D, SQLType, Nullable> crate::row::IntoSelectTarget
257 for ColumnBinOp<Lhs, Rhs, Op, D, SQLType, Nullable>
258{
259 type Marker = crate::row::SelectCols<(Self,)>;
260}
261
262#[derive(Debug, Clone, Copy)]
264pub struct ColumnNeg<T, D, SQLType, Nullable> {
265 inner: T,
266 _type: PhantomData<(D, SQLType, Nullable)>,
267}
268
269impl<T, D, SQLType, Nullable> ColumnNeg<T, D, SQLType, Nullable> {
270 #[inline]
271 pub const fn new(inner: T) -> Self {
272 Self {
273 inner,
274 _type: PhantomData,
275 }
276 }
277}
278
279impl<'a, V, T, D, SQLType, Nullable> ToSQL<'a, V> for ColumnNeg<T, D, SQLType, Nullable>
280where
281 V: SQLParam,
282 T: ToSQL<'a, V>,
283{
284 fn to_sql(&self) -> SQL<'a, V> {
285 SQL::raw("-").append(self.inner.to_sql())
286 }
287
288 fn into_sql(self) -> SQL<'a, V> {
289 SQL::raw("-").append(self.inner.into_sql())
290 }
291}
292
293impl<'a, V, T, D, SQLType, Nullable> Expr<'a, V> for ColumnNeg<T, D, SQLType, Nullable>
294where
295 V: SQLParam<DialectMarker = D>,
296 T: Expr<'a, V, Nullable = Nullable>,
297 T::SQLType: Numeric + NegOutput<Output = SQLType>,
298 SQLType: crate::types::DataType,
299 Nullable: Nullability,
300{
301 type SQLType = SQLType;
302 type Nullable = Nullable;
303 type Aggregate = T::Aggregate;
304}
305
306impl<T: super::HasAggStatus, D, SQLType, Nullable> super::HasAggStatus
307 for ColumnNeg<T, D, SQLType, Nullable>
308{
309 type Status = T::Status;
310}
311
312impl<T, D, SQLType, Nullable> crate::row::ExprValueType for ColumnNeg<T, D, SQLType, Nullable>
313where
314 SQLType: crate::types::DataType + crate::row::SQLTypeToRust<D>,
315 Nullable:
316 Nullability + crate::row::WrapNullable<<SQLType as crate::row::SQLTypeToRust<D>>::RustType>,
317{
318 type ValueType = <Nullable as crate::row::WrapNullable<
319 <SQLType as crate::row::SQLTypeToRust<D>>::RustType,
320 >>::Output;
321}
322
323impl<T, D, SQLType, Nullable> crate::row::IntoSelectTarget for ColumnNeg<T, D, SQLType, Nullable> {
324 type Marker = crate::row::SelectCols<(Self,)>;
325}
326
327impl<T, D, SQLType, Nullable> ArithmeticRhs<D> for ColumnNeg<T, D, SQLType, Nullable>
328where
329 SQLType: Numeric,
330 Nullable: Nullability,
331{
332 type SQLType = SQLType;
333 type Nullable = Nullable;
334}