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}