drizzle_core/expr/
math.rs

1//! Type-safe math functions.
2//!
3//! These functions require `Numeric` types (SmallInt, Int, BigInt, Float, Double)
4//! and provide compile-time enforcement of mathematical operations.
5//!
6//! # Type Safety
7//!
8//! - `abs`, `round`, `ceil`, `floor`: Require `Numeric` types
9//! - `sqrt`, `power`, `log`, `exp`: Require `Numeric` types, return Double
10//! - `mod_`: Modulo operation requiring `Numeric` types
11
12use crate::sql::{SQL, Token};
13use crate::traits::SQLParam;
14use crate::types::{Double, Numeric};
15
16use super::{Expr, NullOr, Nullability, SQLExpr, Scalar};
17
18// =============================================================================
19// ABSOLUTE VALUE
20// =============================================================================
21
22/// ABS - returns the absolute value of a number.
23///
24/// Preserves the SQL type and nullability of the input expression.
25///
26/// # Type Safety
27///
28/// ```ignore
29/// // ✅ OK: Int column
30/// abs(users.balance);
31///
32/// // ❌ Compile error: Text is not Numeric
33/// abs(users.name);
34/// ```
35pub fn abs<'a, V, E>(expr: E) -> SQLExpr<'a, V, E::SQLType, E::Nullable, Scalar>
36where
37    V: SQLParam + 'a,
38    E: Expr<'a, V>,
39    E::SQLType: Numeric,
40{
41    SQLExpr::new(SQL::func("ABS", expr.to_sql()))
42}
43
44// =============================================================================
45// ROUNDING FUNCTIONS
46// =============================================================================
47
48/// ROUND - rounds a number to the nearest integer (or specified precision).
49///
50/// Returns Double type, preserves nullability.
51///
52/// # Example
53///
54/// ```ignore
55/// use drizzle_core::expr::round;
56///
57/// // SELECT ROUND(users.price)
58/// let rounded = round(users.price);
59/// ```
60pub fn round<'a, V, E>(expr: E) -> SQLExpr<'a, V, Double, E::Nullable, Scalar>
61where
62    V: SQLParam + 'a,
63    E: Expr<'a, V>,
64    E::SQLType: Numeric,
65{
66    SQLExpr::new(SQL::func("ROUND", expr.to_sql()))
67}
68
69/// ROUND with precision - rounds a number to specified decimal places.
70///
71/// Returns Double type, preserves nullability of the input expression.
72///
73/// # Example
74///
75/// ```ignore
76/// use drizzle_core::expr::round_to;
77///
78/// // SELECT ROUND(users.price, 2)
79/// let rounded = round_to(users.price, 2);
80/// ```
81pub fn round_to<'a, V, E, P>(expr: E, precision: P) -> SQLExpr<'a, V, Double, E::Nullable, Scalar>
82where
83    V: SQLParam + 'a,
84    E: Expr<'a, V>,
85    E::SQLType: Numeric,
86    P: Expr<'a, V>,
87{
88    SQLExpr::new(SQL::func(
89        "ROUND",
90        expr.to_sql().push(Token::COMMA).append(precision.to_sql()),
91    ))
92}
93
94/// CEIL / CEILING - rounds a number up to the nearest integer.
95///
96/// Returns Double type, preserves nullability.
97///
98/// # Example
99///
100/// ```ignore
101/// use drizzle_core::expr::ceil;
102///
103/// // SELECT CEIL(users.price)
104/// let ceiling = ceil(users.price);
105/// ```
106pub fn ceil<'a, V, E>(expr: E) -> SQLExpr<'a, V, Double, E::Nullable, Scalar>
107where
108    V: SQLParam + 'a,
109    E: Expr<'a, V>,
110    E::SQLType: Numeric,
111{
112    SQLExpr::new(SQL::func("CEIL", expr.to_sql()))
113}
114
115/// FLOOR - rounds a number down to the nearest integer.
116///
117/// Returns Double type, preserves nullability.
118///
119/// # Example
120///
121/// ```ignore
122/// use drizzle_core::expr::floor;
123///
124/// // SELECT FLOOR(users.price)
125/// let floored = floor(users.price);
126/// ```
127pub fn floor<'a, V, E>(expr: E) -> SQLExpr<'a, V, Double, E::Nullable, Scalar>
128where
129    V: SQLParam + 'a,
130    E: Expr<'a, V>,
131    E::SQLType: Numeric,
132{
133    SQLExpr::new(SQL::func("FLOOR", expr.to_sql()))
134}
135
136/// TRUNC - truncates a number towards zero.
137///
138/// Returns Double type, preserves nullability.
139///
140/// # Example
141///
142/// ```ignore
143/// use drizzle_core::expr::trunc;
144///
145/// // SELECT TRUNC(users.price)
146/// let truncated = trunc(users.price);
147/// ```
148pub fn trunc<'a, V, E>(expr: E) -> SQLExpr<'a, V, Double, E::Nullable, Scalar>
149where
150    V: SQLParam + 'a,
151    E: Expr<'a, V>,
152    E::SQLType: Numeric,
153{
154    SQLExpr::new(SQL::func("TRUNC", expr.to_sql()))
155}
156
157// =============================================================================
158// POWER AND ROOT FUNCTIONS
159// =============================================================================
160
161/// SQRT - returns the square root of a number.
162///
163/// Returns Double type, preserves nullability.
164///
165/// # Example
166///
167/// ```ignore
168/// use drizzle_core::expr::sqrt;
169///
170/// // SELECT SQRT(users.area)
171/// let root = sqrt(users.area);
172/// ```
173pub fn sqrt<'a, V, E>(expr: E) -> SQLExpr<'a, V, Double, E::Nullable, Scalar>
174where
175    V: SQLParam + 'a,
176    E: Expr<'a, V>,
177    E::SQLType: Numeric,
178{
179    SQLExpr::new(SQL::func("SQRT", expr.to_sql()))
180}
181
182/// POWER - raises a number to a power.
183///
184/// Returns Double type. The result is nullable if either input is nullable.
185///
186/// # Example
187///
188/// ```ignore
189/// use drizzle_core::expr::power;
190///
191/// // SELECT POWER(users.base, 2)
192/// let squared = power(users.base, 2);
193/// ```
194pub fn power<'a, V, E1, E2>(
195    base: E1,
196    exponent: E2,
197) -> SQLExpr<'a, V, Double, <E1::Nullable as NullOr<E2::Nullable>>::Output, Scalar>
198where
199    V: SQLParam + 'a,
200    E1: Expr<'a, V>,
201    E1::SQLType: Numeric,
202    E2: Expr<'a, V>,
203    E2::SQLType: Numeric,
204    E1::Nullable: NullOr<E2::Nullable>,
205    E2::Nullable: Nullability,
206{
207    SQLExpr::new(SQL::func(
208        "POWER",
209        base.to_sql().push(Token::COMMA).append(exponent.to_sql()),
210    ))
211}
212
213// =============================================================================
214// LOGARITHMIC AND EXPONENTIAL FUNCTIONS
215// =============================================================================
216
217/// EXP - returns e raised to the power of the argument.
218///
219/// Returns Double type, preserves nullability.
220///
221/// # Example
222///
223/// ```ignore
224/// use drizzle_core::expr::exp;
225///
226/// // SELECT EXP(users.rate)
227/// let exponential = exp(users.rate);
228/// ```
229pub fn exp<'a, V, E>(expr: E) -> SQLExpr<'a, V, Double, E::Nullable, Scalar>
230where
231    V: SQLParam + 'a,
232    E: Expr<'a, V>,
233    E::SQLType: Numeric,
234{
235    SQLExpr::new(SQL::func("EXP", expr.to_sql()))
236}
237
238/// LN - returns the natural logarithm of a number.
239///
240/// Returns Double type, preserves nullability.
241///
242/// # Example
243///
244/// ```ignore
245/// use drizzle_core::expr::ln;
246///
247/// // SELECT LN(users.value)
248/// let natural_log = ln(users.value);
249/// ```
250pub fn ln<'a, V, E>(expr: E) -> SQLExpr<'a, V, Double, E::Nullable, Scalar>
251where
252    V: SQLParam + 'a,
253    E: Expr<'a, V>,
254    E::SQLType: Numeric,
255{
256    SQLExpr::new(SQL::func("LN", expr.to_sql()))
257}
258
259/// LOG10 - returns the base-10 logarithm of a number.
260///
261/// Returns Double type, preserves nullability.
262///
263/// # Example
264///
265/// ```ignore
266/// use drizzle_core::expr::log10;
267///
268/// // SELECT LOG10(users.value)
269/// let log_base_10 = log10(users.value);
270/// ```
271pub fn log10<'a, V, E>(expr: E) -> SQLExpr<'a, V, Double, E::Nullable, Scalar>
272where
273    V: SQLParam + 'a,
274    E: Expr<'a, V>,
275    E::SQLType: Numeric,
276{
277    SQLExpr::new(SQL::func("LOG10", expr.to_sql()))
278}
279
280/// LOG - returns the logarithm of a number with a specified base.
281///
282/// Returns Double type. The result is nullable if either input is nullable.
283///
284/// # Example
285///
286/// ```ignore
287/// use drizzle_core::expr::log;
288///
289/// // SELECT LOG(2, users.value)
290/// let log_base_2 = log(2, users.value);
291/// ```
292pub fn log<'a, V, E1, E2>(
293    base: E1,
294    value: E2,
295) -> SQLExpr<'a, V, Double, <E1::Nullable as NullOr<E2::Nullable>>::Output, Scalar>
296where
297    V: SQLParam + 'a,
298    E1: Expr<'a, V>,
299    E1::SQLType: Numeric,
300    E2: Expr<'a, V>,
301    E2::SQLType: Numeric,
302    E1::Nullable: NullOr<E2::Nullable>,
303    E2::Nullable: Nullability,
304{
305    SQLExpr::new(SQL::func(
306        "LOG",
307        base.to_sql().push(Token::COMMA).append(value.to_sql()),
308    ))
309}
310
311// =============================================================================
312// SIGN AND MODULO
313// =============================================================================
314
315/// SIGN - returns the sign of a number (-1, 0, or 1).
316///
317/// Returns the same numeric type, preserves nullability.
318///
319/// # Example
320///
321/// ```ignore
322/// use drizzle_core::expr::sign;
323///
324/// // SELECT SIGN(users.balance)
325/// let balance_sign = sign(users.balance);
326/// ```
327pub fn sign<'a, V, E>(expr: E) -> SQLExpr<'a, V, E::SQLType, E::Nullable, Scalar>
328where
329    V: SQLParam + 'a,
330    E: Expr<'a, V>,
331    E::SQLType: Numeric,
332{
333    SQLExpr::new(SQL::func("SIGN", expr.to_sql()))
334}
335
336/// MOD - returns the remainder of division (using % operator).
337///
338/// Returns the same type as the dividend. The result is nullable if either input is nullable.
339/// Named `mod_` to avoid conflict with Rust's `mod` keyword.
340///
341/// Note: Uses the `%` operator which works on both SQLite and PostgreSQL.
342///
343/// # Example
344///
345/// ```ignore
346/// use drizzle_core::expr::mod_;
347///
348/// // SELECT users.value % 3
349/// let remainder = mod_(users.value, 3);
350/// ```
351#[allow(clippy::type_complexity)]
352pub fn mod_<'a, V, E1, E2>(
353    dividend: E1,
354    divisor: E2,
355) -> SQLExpr<'a, V, E1::SQLType, <E1::Nullable as NullOr<E2::Nullable>>::Output, Scalar>
356where
357    V: SQLParam + 'a,
358    E1: Expr<'a, V>,
359    E1::SQLType: Numeric,
360    E2: Expr<'a, V>,
361    E2::SQLType: Numeric,
362    E1::Nullable: NullOr<E2::Nullable>,
363    E2::Nullable: Nullability,
364{
365    SQLExpr::new(dividend.to_sql().push(Token::REM).append(divisor.to_sql()))
366}