drizzle_core/expr/agg.rs
1//! Type-safe aggregate functions.
2//!
3//! These functions return expressions marked as aggregates, which can be used
4//! to enforce GROUP BY rules at compile time.
5//!
6//! # Type Safety
7//!
8//! - `sum`, `avg`: Require `Numeric` types (Int, BigInt, Float, Double)
9//! - `count`: Works with any type
10//! - `min`, `max`: Work with any type (ordered types in SQL)
11
12use crate::sql::SQL;
13use crate::traits::SQLParam;
14use crate::types::{Any, BigInt, Double, Numeric};
15
16use super::{Agg, Expr, NonNull, Null, SQLExpr, Scalar};
17
18// =============================================================================
19// COUNT
20// =============================================================================
21
22/// COUNT(*) - counts all rows.
23///
24/// Returns a BigInt, NonNull (count is never NULL), Aggregate expression.
25///
26/// # Example
27///
28/// ```ignore
29/// use drizzle_core::expr::count_all;
30///
31/// let total = count_all();
32/// // Generates: COUNT(*)
33/// ```
34pub fn count_all<'a, V>() -> SQLExpr<'a, V, BigInt, NonNull, Agg>
35where
36 V: SQLParam + 'a,
37{
38 SQLExpr::new(SQL::raw("COUNT(*)"))
39}
40
41/// COUNT(expr) - counts non-null values.
42///
43/// Returns a BigInt, NonNull (count is never NULL), Aggregate expression.
44/// Works with any expression type.
45///
46/// # Example
47///
48/// ```ignore
49/// use drizzle_core::expr::count;
50///
51/// let count = count(users.email);
52/// // Generates: COUNT("users"."email")
53/// ```
54pub fn count<'a, V, E>(expr: E) -> SQLExpr<'a, V, BigInt, NonNull, Agg>
55where
56 V: SQLParam + 'a,
57 E: Expr<'a, V>,
58{
59 SQLExpr::new(SQL::func("COUNT", expr.into_sql()))
60}
61
62/// COUNT(DISTINCT expr) - counts distinct non-null values.
63///
64/// Returns a BigInt, NonNull, Aggregate expression.
65/// Works with any expression type.
66pub fn count_distinct<'a, V, E>(expr: E) -> SQLExpr<'a, V, BigInt, NonNull, Agg>
67where
68 V: SQLParam + 'a,
69 E: Expr<'a, V>,
70{
71 SQLExpr::new(SQL::func(
72 "COUNT",
73 SQL::raw("DISTINCT").append(expr.into_sql()),
74 ))
75}
76
77// =============================================================================
78// SUM
79// =============================================================================
80
81/// SUM(expr) - sums numeric values.
82///
83/// Requires the expression to be `Numeric` (Int, BigInt, Float, Double).
84/// Preserves the input expression's SQL type.
85/// Returns a nullable expression (empty set returns NULL).
86///
87/// # Type Safety
88///
89/// ```ignore
90/// // ✅ OK: Numeric column
91/// sum(orders.amount);
92/// // Returns the same SQL type as orders.amount
93///
94/// // ❌ Compile error: Text is not Numeric
95/// sum(users.name);
96/// ```
97pub fn sum<'a, V, E>(expr: E) -> SQLExpr<'a, V, E::SQLType, Null, Agg>
98where
99 V: SQLParam + 'a,
100 E: Expr<'a, V>,
101 E::SQLType: Numeric,
102{
103 SQLExpr::new(SQL::func("SUM", expr.into_sql()))
104}
105
106/// SUM(DISTINCT expr) - sums distinct numeric values.
107///
108/// Requires the expression to be `Numeric`.
109/// Preserves the input expression's SQL type.
110pub fn sum_distinct<'a, V, E>(expr: E) -> SQLExpr<'a, V, E::SQLType, Null, Agg>
111where
112 V: SQLParam + 'a,
113 E: Expr<'a, V>,
114 E::SQLType: Numeric,
115{
116 SQLExpr::new(SQL::func(
117 "SUM",
118 SQL::raw("DISTINCT").append(expr.into_sql()),
119 ))
120}
121
122// =============================================================================
123// AVG
124// =============================================================================
125
126/// AVG(expr) - calculates average of numeric values.
127///
128/// Requires the expression to be `Numeric`.
129/// Always returns Double (SQL standard behavior), nullable.
130///
131/// # Type Safety
132///
133/// ```ignore
134/// // ✅ OK: Numeric column
135/// avg(products.price);
136///
137/// // ❌ Compile error: Text is not Numeric
138/// avg(users.name);
139/// ```
140pub fn avg<'a, V, E>(expr: E) -> SQLExpr<'a, V, Double, Null, Agg>
141where
142 V: SQLParam + 'a,
143 E: Expr<'a, V>,
144 E::SQLType: Numeric,
145{
146 SQLExpr::new(SQL::func("AVG", expr.into_sql()))
147}
148
149/// AVG(DISTINCT expr) - calculates average of distinct numeric values.
150///
151/// Requires the expression to be `Numeric`.
152pub fn avg_distinct<'a, V, E>(expr: E) -> SQLExpr<'a, V, Double, Null, Agg>
153where
154 V: SQLParam + 'a,
155 E: Expr<'a, V>,
156 E::SQLType: Numeric,
157{
158 SQLExpr::new(SQL::func(
159 "AVG",
160 SQL::raw("DISTINCT").append(expr.into_sql()),
161 ))
162}
163
164// =============================================================================
165// MIN / MAX
166// =============================================================================
167
168/// MIN(expr) - finds minimum value.
169///
170/// Works with any expression type (ordered types in SQL).
171/// Preserves the input expression's SQL type.
172/// Result is nullable (empty set returns NULL).
173///
174/// # Example
175///
176/// ```ignore
177/// use drizzle_core::expr::min;
178///
179/// let cheapest = min(products.price);
180/// // Generates: MIN("products"."price")
181/// // Returns the same SQL type as products.price
182/// ```
183pub fn min<'a, V, E>(expr: E) -> SQLExpr<'a, V, E::SQLType, Null, Agg>
184where
185 V: SQLParam + 'a,
186 E: Expr<'a, V>,
187{
188 SQLExpr::new(SQL::func("MIN", expr.into_sql()))
189}
190
191/// MAX(expr) - finds maximum value.
192///
193/// Works with any expression type (ordered types in SQL).
194/// Preserves the input expression's SQL type.
195/// Result is nullable (empty set returns NULL).
196///
197/// # Example
198///
199/// ```ignore
200/// use drizzle_core::expr::max;
201///
202/// let most_expensive = max(products.price);
203/// // Generates: MAX("products"."price")
204/// // Returns the same SQL type as products.price
205/// ```
206pub fn max<'a, V, E>(expr: E) -> SQLExpr<'a, V, E::SQLType, Null, Agg>
207where
208 V: SQLParam + 'a,
209 E: Expr<'a, V>,
210{
211 SQLExpr::new(SQL::func("MAX", expr.into_sql()))
212}
213
214// =============================================================================
215// STATISTICAL FUNCTIONS
216// =============================================================================
217
218/// STDDEV_POP - population standard deviation.
219///
220/// Calculates the population standard deviation of numeric values.
221/// Requires the expression to be `Numeric`.
222/// Returns Double, nullable (empty set returns NULL).
223///
224/// Note: This function is available in PostgreSQL. SQLite does not have it built-in.
225///
226/// # Example
227///
228/// ```ignore
229/// use drizzle_core::expr::stddev_pop;
230///
231/// let deviation = stddev_pop(measurements.value);
232/// // Generates: STDDEV_POP("measurements"."value")
233/// ```
234pub fn stddev_pop<'a, V, E>(expr: E) -> SQLExpr<'a, V, Double, Null, Agg>
235where
236 V: SQLParam + 'a,
237 E: Expr<'a, V>,
238 E::SQLType: Numeric,
239{
240 SQLExpr::new(SQL::func("STDDEV_POP", expr.into_sql()))
241}
242
243/// STDDEV_SAMP / STDDEV - sample standard deviation.
244///
245/// Calculates the sample standard deviation of numeric values.
246/// Requires the expression to be `Numeric`.
247/// Returns Double, nullable (empty set returns NULL).
248///
249/// Note: This function is available in PostgreSQL. SQLite does not have it built-in.
250///
251/// # Example
252///
253/// ```ignore
254/// use drizzle_core::expr::stddev_samp;
255///
256/// let deviation = stddev_samp(measurements.value);
257/// // Generates: STDDEV_SAMP("measurements"."value")
258/// ```
259pub fn stddev_samp<'a, V, E>(expr: E) -> SQLExpr<'a, V, Double, Null, Agg>
260where
261 V: SQLParam + 'a,
262 E: Expr<'a, V>,
263 E::SQLType: Numeric,
264{
265 SQLExpr::new(SQL::func("STDDEV_SAMP", expr.into_sql()))
266}
267
268/// VAR_POP - population variance.
269///
270/// Calculates the population variance of numeric values.
271/// Requires the expression to be `Numeric`.
272/// Returns Double, nullable (empty set returns NULL).
273///
274/// Note: This function is available in PostgreSQL. SQLite does not have it built-in.
275///
276/// # Example
277///
278/// ```ignore
279/// use drizzle_core::expr::var_pop;
280///
281/// let variance = var_pop(measurements.value);
282/// // Generates: VAR_POP("measurements"."value")
283/// ```
284pub fn var_pop<'a, V, E>(expr: E) -> SQLExpr<'a, V, Double, Null, Agg>
285where
286 V: SQLParam + 'a,
287 E: Expr<'a, V>,
288 E::SQLType: Numeric,
289{
290 SQLExpr::new(SQL::func("VAR_POP", expr.into_sql()))
291}
292
293/// VAR_SAMP / VARIANCE - sample variance.
294///
295/// Calculates the sample variance of numeric values.
296/// Requires the expression to be `Numeric`.
297/// Returns Double, nullable (empty set returns NULL).
298///
299/// Note: This function is available in PostgreSQL. SQLite does not have it built-in.
300///
301/// # Example
302///
303/// ```ignore
304/// use drizzle_core::expr::var_samp;
305///
306/// let variance = var_samp(measurements.value);
307/// // Generates: VAR_SAMP("measurements"."value")
308/// ```
309pub fn var_samp<'a, V, E>(expr: E) -> SQLExpr<'a, V, Double, Null, Agg>
310where
311 V: SQLParam + 'a,
312 E: Expr<'a, V>,
313 E::SQLType: Numeric,
314{
315 SQLExpr::new(SQL::func("VAR_SAMP", expr.into_sql()))
316}
317
318// =============================================================================
319// GROUP_CONCAT / STRING_AGG
320// =============================================================================
321
322/// GROUP_CONCAT / STRING_AGG - concatenates values into a string.
323///
324/// Note: This is dialect-specific (GROUP_CONCAT in SQLite/MySQL, STRING_AGG in PostgreSQL).
325/// Returns Text type, nullable.
326pub fn group_concat<'a, V, E>(expr: E) -> SQLExpr<'a, V, crate::types::Text, Null, Agg>
327where
328 V: SQLParam + 'a,
329 E: Expr<'a, V>,
330{
331 SQLExpr::new(SQL::func("GROUP_CONCAT", expr.into_sql()))
332}
333
334// =============================================================================
335// Distinct Wrapper
336// =============================================================================
337
338/// DISTINCT - marks an expression as DISTINCT.
339///
340/// Typically used inside aggregate functions.
341pub fn distinct<'a, V, E>(expr: E) -> SQLExpr<'a, V, Any, Null, Scalar>
342where
343 V: SQLParam + 'a,
344 E: Expr<'a, V>,
345{
346 SQLExpr::new(SQL::raw("DISTINCT").append(expr.into_sql()))
347}