Skip to main content

drizzle_core/expr/
column_ops.rs

1//! Result types of arithmetic on table columns (`users.age + 1`).
2//!
3//! The table macros implement the Rust operators on generated columns using
4//! these types. You do not name them directly.
5
6use 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/// The result of `column <op> value` on a generated column, such as
16/// `users.age + 1`.
17#[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    /// Pairs the two operands; the type parameters carry the result type.
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
41/// The SQL operator token of an arithmetic operation marker.
42pub trait BinOpToken {
43    /// The operator token, such as `+`.
44    const TOKEN: Token;
45}
46
47#[doc(hidden)]
48pub trait ResolveArithmeticNullability<Lhs, Rhs> {
49    type Output: Nullability;
50}
51
52/// Lifetime-independent type metadata for the right-hand side of generated
53/// column arithmetic operators.
54#[doc(hidden)]
55pub trait ArithmeticRhs<D> {
56    type SQLType: Numeric;
57    type Nullable: Nullability;
58}
59
60/// Computes and constructs the result of a generated column arithmetic
61/// operator without tying right-hand-side metadata to an expression lifetime.
62#[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/// The result of `-column` on a generated column.
272#[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    /// Wraps the negated operand.
280    #[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}