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