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.into_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.into_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.into_sql()
91 .push(Token::COMMA)
92 .append(precision.into_sql()),
93 ))
94}
95
96/// CEIL / CEILING - rounds a number up to the nearest integer.
97///
98/// Returns Double type, preserves nullability.
99///
100/// # Example
101///
102/// ```ignore
103/// use drizzle_core::expr::ceil;
104///
105/// // SELECT CEIL(users.price)
106/// let ceiling = ceil(users.price);
107/// ```
108pub fn ceil<'a, V, E>(expr: E) -> SQLExpr<'a, V, Double, E::Nullable, Scalar>
109where
110 V: SQLParam + 'a,
111 E: Expr<'a, V>,
112 E::SQLType: Numeric,
113{
114 SQLExpr::new(SQL::func("CEIL", expr.into_sql()))
115}
116
117/// FLOOR - rounds a number down to the nearest integer.
118///
119/// Returns Double type, preserves nullability.
120///
121/// # Example
122///
123/// ```ignore
124/// use drizzle_core::expr::floor;
125///
126/// // SELECT FLOOR(users.price)
127/// let floored = floor(users.price);
128/// ```
129pub fn floor<'a, V, E>(expr: E) -> SQLExpr<'a, V, Double, E::Nullable, Scalar>
130where
131 V: SQLParam + 'a,
132 E: Expr<'a, V>,
133 E::SQLType: Numeric,
134{
135 SQLExpr::new(SQL::func("FLOOR", expr.into_sql()))
136}
137
138/// TRUNC - truncates a number towards zero.
139///
140/// Returns Double type, preserves nullability.
141///
142/// # Example
143///
144/// ```ignore
145/// use drizzle_core::expr::trunc;
146///
147/// // SELECT TRUNC(users.price)
148/// let truncated = trunc(users.price);
149/// ```
150pub fn trunc<'a, V, E>(expr: E) -> SQLExpr<'a, V, Double, E::Nullable, Scalar>
151where
152 V: SQLParam + 'a,
153 E: Expr<'a, V>,
154 E::SQLType: Numeric,
155{
156 SQLExpr::new(SQL::func("TRUNC", expr.into_sql()))
157}
158
159// =============================================================================
160// POWER AND ROOT FUNCTIONS
161// =============================================================================
162
163/// SQRT - returns the square root of a number.
164///
165/// Returns Double type, preserves nullability.
166///
167/// # Example
168///
169/// ```ignore
170/// use drizzle_core::expr::sqrt;
171///
172/// // SELECT SQRT(users.area)
173/// let root = sqrt(users.area);
174/// ```
175pub fn sqrt<'a, V, E>(expr: E) -> SQLExpr<'a, V, Double, E::Nullable, Scalar>
176where
177 V: SQLParam + 'a,
178 E: Expr<'a, V>,
179 E::SQLType: Numeric,
180{
181 SQLExpr::new(SQL::func("SQRT", expr.into_sql()))
182}
183
184/// POWER - raises a number to a power.
185///
186/// Returns Double type. The result is nullable if either input is nullable.
187///
188/// # Example
189///
190/// ```ignore
191/// use drizzle_core::expr::power;
192///
193/// // SELECT POWER(users.base, 2)
194/// let squared = power(users.base, 2);
195/// ```
196pub fn power<'a, V, E1, E2>(
197 base: E1,
198 exponent: E2,
199) -> SQLExpr<'a, V, Double, <E1::Nullable as NullOr<E2::Nullable>>::Output, Scalar>
200where
201 V: SQLParam + 'a,
202 E1: Expr<'a, V>,
203 E1::SQLType: Numeric,
204 E2: Expr<'a, V>,
205 E2::SQLType: Numeric,
206 E1::Nullable: NullOr<E2::Nullable>,
207 E2::Nullable: Nullability,
208{
209 SQLExpr::new(SQL::func(
210 "POWER",
211 base.into_sql()
212 .push(Token::COMMA)
213 .append(exponent.into_sql()),
214 ))
215}
216
217// =============================================================================
218// LOGARITHMIC AND EXPONENTIAL FUNCTIONS
219// =============================================================================
220
221/// EXP - returns e raised to the power of the argument.
222///
223/// Returns Double type, preserves nullability.
224///
225/// # Example
226///
227/// ```ignore
228/// use drizzle_core::expr::exp;
229///
230/// // SELECT EXP(users.rate)
231/// let exponential = exp(users.rate);
232/// ```
233pub fn exp<'a, V, E>(expr: E) -> SQLExpr<'a, V, Double, E::Nullable, Scalar>
234where
235 V: SQLParam + 'a,
236 E: Expr<'a, V>,
237 E::SQLType: Numeric,
238{
239 SQLExpr::new(SQL::func("EXP", expr.into_sql()))
240}
241
242/// LN - returns the natural logarithm of a number.
243///
244/// Returns Double type, preserves nullability.
245///
246/// # Example
247///
248/// ```ignore
249/// use drizzle_core::expr::ln;
250///
251/// // SELECT LN(users.value)
252/// let natural_log = ln(users.value);
253/// ```
254pub fn ln<'a, V, E>(expr: E) -> SQLExpr<'a, V, Double, E::Nullable, Scalar>
255where
256 V: SQLParam + 'a,
257 E: Expr<'a, V>,
258 E::SQLType: Numeric,
259{
260 SQLExpr::new(SQL::func("LN", expr.into_sql()))
261}
262
263/// LOG10 - returns the base-10 logarithm of a number.
264///
265/// Returns Double type, preserves nullability.
266///
267/// # Example
268///
269/// ```ignore
270/// use drizzle_core::expr::log10;
271///
272/// // SELECT LOG10(users.value)
273/// let log_base_10 = log10(users.value);
274/// ```
275pub fn log10<'a, V, E>(expr: E) -> SQLExpr<'a, V, Double, E::Nullable, Scalar>
276where
277 V: SQLParam + 'a,
278 E: Expr<'a, V>,
279 E::SQLType: Numeric,
280{
281 SQLExpr::new(SQL::func("LOG10", expr.into_sql()))
282}
283
284/// LOG - returns the logarithm of a number with a specified base.
285///
286/// Returns Double type. The result is nullable if either input is nullable.
287///
288/// # Example
289///
290/// ```ignore
291/// use drizzle_core::expr::log;
292///
293/// // SELECT LOG(2, users.value)
294/// let log_base_2 = log(2, users.value);
295/// ```
296pub fn log<'a, V, E1, E2>(
297 base: E1,
298 value: E2,
299) -> SQLExpr<'a, V, Double, <E1::Nullable as NullOr<E2::Nullable>>::Output, Scalar>
300where
301 V: SQLParam + 'a,
302 E1: Expr<'a, V>,
303 E1::SQLType: Numeric,
304 E2: Expr<'a, V>,
305 E2::SQLType: Numeric,
306 E1::Nullable: NullOr<E2::Nullable>,
307 E2::Nullable: Nullability,
308{
309 SQLExpr::new(SQL::func(
310 "LOG",
311 base.into_sql().push(Token::COMMA).append(value.into_sql()),
312 ))
313}
314
315// =============================================================================
316// SIGN AND MODULO
317// =============================================================================
318
319/// SIGN - returns the sign of a number (-1, 0, or 1).
320///
321/// Returns the same numeric type, preserves nullability.
322///
323/// # Example
324///
325/// ```ignore
326/// use drizzle_core::expr::sign;
327///
328/// // SELECT SIGN(users.balance)
329/// let balance_sign = sign(users.balance);
330/// ```
331pub fn sign<'a, V, E>(expr: E) -> SQLExpr<'a, V, E::SQLType, E::Nullable, Scalar>
332where
333 V: SQLParam + 'a,
334 E: Expr<'a, V>,
335 E::SQLType: Numeric,
336{
337 SQLExpr::new(SQL::func("SIGN", expr.into_sql()))
338}
339
340/// MOD - returns the remainder of division (using % operator).
341///
342/// Returns the same type as the dividend. The result is nullable if either input is nullable.
343/// Named `mod_` to avoid conflict with Rust's `mod` keyword.
344///
345/// Note: Uses the `%` operator which works on both SQLite and PostgreSQL.
346///
347/// # Example
348///
349/// ```ignore
350/// use drizzle_core::expr::mod_;
351///
352/// // SELECT users.value % 3
353/// let remainder = mod_(users.value, 3);
354/// ```
355#[allow(clippy::type_complexity)]
356pub fn mod_<'a, V, E1, E2>(
357 dividend: E1,
358 divisor: E2,
359) -> SQLExpr<'a, V, E1::SQLType, <E1::Nullable as NullOr<E2::Nullable>>::Output, Scalar>
360where
361 V: SQLParam + 'a,
362 E1: Expr<'a, V>,
363 E1::SQLType: Numeric,
364 E2: Expr<'a, V>,
365 E2::SQLType: Numeric,
366 E1::Nullable: NullOr<E2::Nullable>,
367 E2::Nullable: Nullability,
368{
369 SQLExpr::new(
370 dividend
371 .into_sql()
372 .push(Token::REM)
373 .append(divisor.into_sql()),
374 )
375}