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.to_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.to_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.to_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("SUM", SQL::raw("DISTINCT").append(expr.to_sql())))
117}
118
119// =============================================================================
120// AVG
121// =============================================================================
122
123/// AVG(expr) - calculates average of numeric values.
124///
125/// Requires the expression to be `Numeric`.
126/// Always returns Double (SQL standard behavior), nullable.
127///
128/// # Type Safety
129///
130/// ```ignore
131/// // ✅ OK: Numeric column
132/// avg(products.price);
133///
134/// // ❌ Compile error: Text is not Numeric
135/// avg(users.name);
136/// ```
137pub fn avg<'a, V, E>(expr: E) -> SQLExpr<'a, V, Double, Null, Agg>
138where
139 V: SQLParam + 'a,
140 E: Expr<'a, V>,
141 E::SQLType: Numeric,
142{
143 SQLExpr::new(SQL::func("AVG", expr.to_sql()))
144}
145
146/// AVG(DISTINCT expr) - calculates average of distinct numeric values.
147///
148/// Requires the expression to be `Numeric`.
149pub fn avg_distinct<'a, V, E>(expr: E) -> SQLExpr<'a, V, Double, Null, Agg>
150where
151 V: SQLParam + 'a,
152 E: Expr<'a, V>,
153 E::SQLType: Numeric,
154{
155 SQLExpr::new(SQL::func("AVG", SQL::raw("DISTINCT").append(expr.to_sql())))
156}
157
158// =============================================================================
159// MIN / MAX
160// =============================================================================
161
162/// MIN(expr) - finds minimum value.
163///
164/// Works with any expression type (ordered types in SQL).
165/// Preserves the input expression's SQL type.
166/// Result is nullable (empty set returns NULL).
167///
168/// # Example
169///
170/// ```ignore
171/// use drizzle_core::expr::min;
172///
173/// let cheapest = min(products.price);
174/// // Generates: MIN("products"."price")
175/// // Returns the same SQL type as products.price
176/// ```
177pub fn min<'a, V, E>(expr: E) -> SQLExpr<'a, V, E::SQLType, Null, Agg>
178where
179 V: SQLParam + 'a,
180 E: Expr<'a, V>,
181{
182 SQLExpr::new(SQL::func("MIN", expr.to_sql()))
183}
184
185/// MAX(expr) - finds maximum value.
186///
187/// Works with any expression type (ordered types in SQL).
188/// Preserves the input expression's SQL type.
189/// Result is nullable (empty set returns NULL).
190///
191/// # Example
192///
193/// ```ignore
194/// use drizzle_core::expr::max;
195///
196/// let most_expensive = max(products.price);
197/// // Generates: MAX("products"."price")
198/// // Returns the same SQL type as products.price
199/// ```
200pub fn max<'a, V, E>(expr: E) -> SQLExpr<'a, V, E::SQLType, Null, Agg>
201where
202 V: SQLParam + 'a,
203 E: Expr<'a, V>,
204{
205 SQLExpr::new(SQL::func("MAX", expr.to_sql()))
206}
207
208// =============================================================================
209// STATISTICAL FUNCTIONS
210// =============================================================================
211
212/// STDDEV_POP - population standard deviation.
213///
214/// Calculates the population standard deviation of numeric values.
215/// Requires the expression to be `Numeric`.
216/// Returns Double, nullable (empty set returns NULL).
217///
218/// Note: This function is available in PostgreSQL. SQLite does not have it built-in.
219///
220/// # Example
221///
222/// ```ignore
223/// use drizzle_core::expr::stddev_pop;
224///
225/// let deviation = stddev_pop(measurements.value);
226/// // Generates: STDDEV_POP("measurements"."value")
227/// ```
228pub fn stddev_pop<'a, V, E>(expr: E) -> SQLExpr<'a, V, Double, Null, Agg>
229where
230 V: SQLParam + 'a,
231 E: Expr<'a, V>,
232 E::SQLType: Numeric,
233{
234 SQLExpr::new(SQL::func("STDDEV_POP", expr.to_sql()))
235}
236
237/// STDDEV_SAMP / STDDEV - sample standard deviation.
238///
239/// Calculates the sample standard deviation of numeric values.
240/// Requires the expression to be `Numeric`.
241/// Returns Double, nullable (empty set returns NULL).
242///
243/// Note: This function is available in PostgreSQL. SQLite does not have it built-in.
244///
245/// # Example
246///
247/// ```ignore
248/// use drizzle_core::expr::stddev_samp;
249///
250/// let deviation = stddev_samp(measurements.value);
251/// // Generates: STDDEV_SAMP("measurements"."value")
252/// ```
253pub fn stddev_samp<'a, V, E>(expr: E) -> SQLExpr<'a, V, Double, Null, Agg>
254where
255 V: SQLParam + 'a,
256 E: Expr<'a, V>,
257 E::SQLType: Numeric,
258{
259 SQLExpr::new(SQL::func("STDDEV_SAMP", expr.to_sql()))
260}
261
262/// VAR_POP - population variance.
263///
264/// Calculates the population variance of numeric values.
265/// Requires the expression to be `Numeric`.
266/// Returns Double, nullable (empty set returns NULL).
267///
268/// Note: This function is available in PostgreSQL. SQLite does not have it built-in.
269///
270/// # Example
271///
272/// ```ignore
273/// use drizzle_core::expr::var_pop;
274///
275/// let variance = var_pop(measurements.value);
276/// // Generates: VAR_POP("measurements"."value")
277/// ```
278pub fn var_pop<'a, V, E>(expr: E) -> SQLExpr<'a, V, Double, Null, Agg>
279where
280 V: SQLParam + 'a,
281 E: Expr<'a, V>,
282 E::SQLType: Numeric,
283{
284 SQLExpr::new(SQL::func("VAR_POP", expr.to_sql()))
285}
286
287/// VAR_SAMP / VARIANCE - sample variance.
288///
289/// Calculates the sample variance of numeric values.
290/// Requires the expression to be `Numeric`.
291/// Returns Double, nullable (empty set returns NULL).
292///
293/// Note: This function is available in PostgreSQL. SQLite does not have it built-in.
294///
295/// # Example
296///
297/// ```ignore
298/// use drizzle_core::expr::var_samp;
299///
300/// let variance = var_samp(measurements.value);
301/// // Generates: VAR_SAMP("measurements"."value")
302/// ```
303pub fn var_samp<'a, V, E>(expr: E) -> SQLExpr<'a, V, Double, Null, Agg>
304where
305 V: SQLParam + 'a,
306 E: Expr<'a, V>,
307 E::SQLType: Numeric,
308{
309 SQLExpr::new(SQL::func("VAR_SAMP", expr.to_sql()))
310}
311
312// =============================================================================
313// GROUP_CONCAT / STRING_AGG
314// =============================================================================
315
316/// GROUP_CONCAT / STRING_AGG - concatenates values into a string.
317///
318/// Note: This is dialect-specific (GROUP_CONCAT in SQLite/MySQL, STRING_AGG in PostgreSQL).
319/// Returns Text type, nullable.
320pub fn group_concat<'a, V, E>(expr: E) -> SQLExpr<'a, V, crate::types::Text, Null, Agg>
321where
322 V: SQLParam + 'a,
323 E: Expr<'a, V>,
324{
325 SQLExpr::new(SQL::func("GROUP_CONCAT", expr.to_sql()))
326}
327
328// =============================================================================
329// Distinct Wrapper
330// =============================================================================
331
332/// DISTINCT - marks an expression as DISTINCT.
333///
334/// Typically used inside aggregate functions.
335pub fn distinct<'a, V, E>(expr: E) -> SQLExpr<'a, V, Any, Null, Scalar>
336where
337 V: SQLParam + 'a,
338 E: Expr<'a, V>,
339{
340 SQLExpr::new(SQL::raw("DISTINCT").append(expr.to_sql()))
341}