Skip to main content

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::dialect::{Dialect, DialectTypes};
13use crate::sql::SQL;
14use crate::traits::SQLParam;
15use crate::types::{Array, Numeric};
16use crate::{MySQLDialect, PostgresDialect, SQLiteDialect};
17use drizzle_types::mysql::types::{
18    BigInt as MyBigInt, BigIntUnsigned as MyBigIntUnsigned, Decimal as MyDecimal,
19    Double as MyDouble, Float as MyFloat, Int as MyInt, IntUnsigned as MyIntUnsigned,
20    MediumInt as MyMediumInt, MediumIntUnsigned as MyMediumIntUnsigned, SmallInt as MySmallInt,
21    SmallIntUnsigned as MySmallIntUnsigned, TinyInt as MyTinyInt,
22    TinyIntUnsigned as MyTinyIntUnsigned, Year as MyYear,
23};
24use drizzle_types::postgres::types::{
25    Boolean as PgBoolean, Float4, Float8, Int2, Int4, Int8, Numeric as PgNumeric,
26};
27use drizzle_types::sqlite::types::{
28    Integer as SqliteInteger, Numeric as SqliteNumeric, Real as SqliteReal,
29};
30
31use super::math::pg_double;
32use super::{Agg, Expr, NonNull, Null, SQLExpr, Scalar};
33
34// =============================================================================
35// Dialect Aggregate Policy
36// =============================================================================
37
38/// Dialect-specific aggregate output mapping.
39///
40/// Keeps aggregate output typing in one place so all aggregate functions
41/// follow the same per-dialect policy.
42#[diagnostic::on_unimplemented(
43    message = "no aggregate policy for `{Self}` on this dialect",
44    label = "aggregate result type is not defined for this SQL type/dialect"
45)]
46pub trait AggregatePolicy<D>: Numeric {
47    type Sum: crate::types::DataType;
48    type Avg: crate::types::DataType;
49}
50
51#[diagnostic::on_unimplemented(
52    message = "no statistical aggregate policy for `{Self}` on this dialect",
53    label = "stddev/variance result type is not defined for this SQL type/dialect"
54)]
55pub trait StatisticalAggregatePolicy<D>: Numeric {
56    type StddevPop: crate::types::DataType;
57    type StddevSamp: crate::types::DataType;
58    type VarPop: crate::types::DataType;
59    type VarSamp: crate::types::DataType;
60}
61
62#[diagnostic::on_unimplemented(
63    message = "boolean aggregates are not supported for `{Self}` on this dialect",
64    label = "use a boolean expression with a dialect that supports BOOL_AND/BOOL_OR"
65)]
66pub trait BooleanAggregatePolicy<D>: crate::types::DataType {}
67
68#[diagnostic::on_unimplemented(
69    message = "this aggregate is not available for this dialect",
70    label = "use a dialect-specific alternative"
71)]
72pub trait PostgresAggregateSupport {}
73
74#[diagnostic::on_unimplemented(
75    message = "this aggregate is not available for this dialect",
76    label = "use a dialect-specific alternative"
77)]
78pub trait SQLiteAggregateSupport {}
79
80#[diagnostic::on_unimplemented(
81    message = "GROUP_CONCAT is not available for this dialect",
82    label = "use the dialect's native string aggregate"
83)]
84pub trait GroupConcatSupport {}
85
86#[diagnostic::on_unimplemented(
87    message = "no COUNT return type defined for this dialect",
88    label = "COUNT result type is not configured for this dialect marker"
89)]
90pub trait CountPolicy {
91    /// The integer type returned by COUNT (e.g. Integer on `SQLite`, Int8 on `PostgreSQL`).
92    type Count: crate::types::DataType;
93}
94
95mod count_arg_private {
96    use super::SQLParam;
97
98    pub trait Sealed<'a, V: SQLParam> {}
99
100    impl<'a, V: SQLParam> Sealed<'a, V> for () {}
101
102    impl<'a, V, E> Sealed<'a, V> for E
103    where
104        V: SQLParam + 'a,
105        E: crate::traits::ToSQL<'a, V> + crate::row::ExprValueType,
106    {
107    }
108}
109
110/// Argument accepted by [`count`].
111///
112/// This trait is sealed and exists only to support `count(())` for `COUNT(*)`
113/// and `count(expr)` for `COUNT(expr)` without making `()` a general SQL
114/// expression.
115#[doc(hidden)]
116pub trait CountArg<'a, V: SQLParam>: count_arg_private::Sealed<'a, V> {
117    fn count_sql(self) -> SQL<'a, V>;
118}
119
120impl<'a, V: SQLParam + 'a> CountArg<'a, V> for () {
121    fn count_sql(self) -> SQL<'a, V> {
122        SQL::raw("COUNT(*)")
123    }
124}
125
126impl<'a, V, E> CountArg<'a, V> for E
127where
128    V: SQLParam + 'a,
129    E: crate::traits::ToSQL<'a, V> + crate::row::ExprValueType,
130{
131    fn count_sql(self) -> SQL<'a, V> {
132        SQL::func("COUNT", self.into_sql().parens_if_subquery())
133    }
134}
135
136impl CountPolicy for SQLiteDialect {
137    type Count = drizzle_types::sqlite::types::Integer;
138}
139
140impl CountPolicy for PostgresDialect {
141    type Count = drizzle_types::postgres::types::Int8;
142}
143
144impl CountPolicy for MySQLDialect {
145    type Count = drizzle_types::mysql::types::BigInt;
146}
147
148#[diagnostic::on_unimplemented(
149    message = "no floating-point return type defined for this dialect",
150    label = "PERCENT_RANK/CUME_DIST result type is not configured for this dialect marker"
151)]
152pub trait FloatPolicy {
153    /// The floating-point type returned by distribution window functions
154    /// like `PERCENT_RANK` and `CUME_DIST` (Real on `SQLite`, Float8 on `PostgreSQL`).
155    type Float: crate::types::DataType;
156}
157
158impl FloatPolicy for SQLiteDialect {
159    type Float = drizzle_types::sqlite::types::Real;
160}
161
162impl FloatPolicy for PostgresDialect {
163    type Float = drizzle_types::postgres::types::Float8;
164}
165
166impl FloatPolicy for MySQLDialect {
167    type Float = drizzle_types::mysql::types::Double;
168}
169
170macro_rules! mysql_aggregate_policy {
171    ($output:ty; $($ty:ty),+ $(,)?) => {
172        $(
173            impl AggregatePolicy<MySQLDialect> for $ty {
174                type Sum = $output;
175                type Avg = $output;
176            }
177        )+
178    };
179}
180
181mysql_aggregate_policy!(MyDecimal;
182    MyTinyInt,
183    MyTinyIntUnsigned,
184    MySmallInt,
185    MySmallIntUnsigned,
186    MyMediumInt,
187    MyMediumIntUnsigned,
188    MyInt,
189    MyIntUnsigned,
190    MyBigInt,
191    MyBigIntUnsigned,
192    MyYear,
193    MyDecimal,
194);
195
196mysql_aggregate_policy!(MyDouble; MyFloat, MyDouble);
197
198macro_rules! mysql_statistical_aggregate_policy {
199    ($($ty:ty),+ $(,)?) => {
200        $(
201            impl StatisticalAggregatePolicy<MySQLDialect> for $ty {
202                type StddevPop = MyDouble;
203                type StddevSamp = MyDouble;
204                type VarPop = MyDouble;
205                type VarSamp = MyDouble;
206            }
207        )+
208    };
209}
210
211mysql_statistical_aggregate_policy!(
212    MyTinyInt,
213    MyTinyIntUnsigned,
214    MySmallInt,
215    MySmallIntUnsigned,
216    MyMediumInt,
217    MyMediumIntUnsigned,
218    MyInt,
219    MyIntUnsigned,
220    MyBigInt,
221    MyBigIntUnsigned,
222    MyYear,
223    MyDecimal,
224    MyFloat,
225    MyDouble,
226);
227
228impl AggregatePolicy<SQLiteDialect> for SqliteInteger {
229    type Sum = Self;
230    type Avg = SqliteReal;
231}
232impl AggregatePolicy<SQLiteDialect> for SqliteReal {
233    type Sum = Self;
234    type Avg = Self;
235}
236impl AggregatePolicy<SQLiteDialect> for SqliteNumeric {
237    type Sum = Self;
238    type Avg = SqliteReal;
239}
240impl AggregatePolicy<SQLiteDialect> for drizzle_types::sqlite::types::Any {
241    type Sum = Self;
242    type Avg = SqliteReal;
243}
244
245impl StatisticalAggregatePolicy<PostgresDialect> for Int2 {
246    type StddevPop = Float8;
247    type StddevSamp = Float8;
248    type VarPop = Float8;
249    type VarSamp = Float8;
250}
251impl StatisticalAggregatePolicy<PostgresDialect> for Int4 {
252    type StddevPop = Float8;
253    type StddevSamp = Float8;
254    type VarPop = Float8;
255    type VarSamp = Float8;
256}
257impl StatisticalAggregatePolicy<PostgresDialect> for Int8 {
258    type StddevPop = Float8;
259    type StddevSamp = Float8;
260    type VarPop = Float8;
261    type VarSamp = Float8;
262}
263impl StatisticalAggregatePolicy<PostgresDialect> for Float4 {
264    type StddevPop = Float8;
265    type StddevSamp = Float8;
266    type VarPop = Float8;
267    type VarSamp = Float8;
268}
269impl StatisticalAggregatePolicy<PostgresDialect> for Float8 {
270    type StddevPop = Self;
271    type StddevSamp = Self;
272    type VarPop = Self;
273    type VarSamp = Self;
274}
275impl StatisticalAggregatePolicy<PostgresDialect> for PgNumeric {
276    type StddevPop = Float8;
277    type StddevSamp = Float8;
278    type VarPop = Float8;
279    type VarSamp = Float8;
280}
281
282impl BooleanAggregatePolicy<PostgresDialect> for PgBoolean {}
283
284impl PostgresAggregateSupport for PostgresDialect {}
285impl SQLiteAggregateSupport for SQLiteDialect {}
286impl GroupConcatSupport for SQLiteDialect {}
287impl GroupConcatSupport for MySQLDialect {}
288
289impl AggregatePolicy<PostgresDialect> for Int2 {
290    type Sum = Int8;
291    type Avg = Float8;
292}
293impl AggregatePolicy<PostgresDialect> for Int4 {
294    type Sum = Int8;
295    type Avg = Float8;
296}
297impl AggregatePolicy<PostgresDialect> for Int8 {
298    type Sum = Self;
299    type Avg = Float8;
300}
301impl AggregatePolicy<PostgresDialect> for Float4 {
302    type Sum = Float8;
303    type Avg = Float8;
304}
305impl AggregatePolicy<PostgresDialect> for Float8 {
306    type Sum = Self;
307    type Avg = Self;
308}
309impl AggregatePolicy<PostgresDialect> for PgNumeric {
310    type Sum = Self;
311    type Avg = Self;
312}
313
314// =============================================================================
315// COUNT
316// =============================================================================
317
318/// COUNT aggregate.
319///
320/// Pass `()` for `COUNT(*)`, or a column/expression for `COUNT(expr)`.
321///
322/// Returns a `BigInt`, `NonNull` (count is never NULL), Aggregate expression.
323///
324/// # Example
325///
326/// ```rust
327/// # let _ = r####"
328/// use drizzle_core::expr::count;
329///
330/// let all_rows = count(());
331/// // Generates: COUNT(*)
332///
333/// let email_count = count(users.email);
334/// // Generates: COUNT("users"."email")
335/// # "####;
336/// ```
337pub fn count<'a, V, A>(
338    arg: A,
339) -> SQLExpr<'a, V, <V::DialectMarker as CountPolicy>::Count, NonNull, Agg>
340where
341    V: SQLParam + 'a,
342    V::DialectMarker: CountPolicy,
343    A: CountArg<'a, V>,
344{
345    SQLExpr::new(arg.count_sql())
346}
347
348/// COUNT(DISTINCT expr) - counts distinct non-null values.
349///
350/// Returns a `BigInt`, `NonNull`, Aggregate expression.
351/// Works with any expression type.
352pub fn count_distinct<'a, V, E>(
353    expr: E,
354) -> SQLExpr<'a, V, <V::DialectMarker as CountPolicy>::Count, NonNull, Agg>
355where
356    V: SQLParam + 'a,
357    V::DialectMarker: CountPolicy,
358    E: Expr<'a, V>,
359{
360    SQLExpr::new(SQL::func(
361        "COUNT",
362        SQL::raw("DISTINCT").append(expr.into_expr_sql()),
363    ))
364}
365
366// =============================================================================
367// SUM
368// =============================================================================
369
370/// SUM(expr) - sums numeric values.
371///
372/// Requires the expression to be `Numeric` (Int, `BigInt`, Float, Double).
373/// Result type is dialect-aware.
374/// Returns a nullable expression (empty set returns NULL).
375///
376/// # Type Safety
377///
378/// ```rust
379/// # let _ = r####"
380/// // ✅ OK: Numeric column
381/// sum(orders.amount);
382/// // SQLite: same width for integer sums
383/// // PostgreSQL: Int/SmallInt promote to BigInt
384///
385/// // ❌ Compile error: Text is not Numeric
386/// sum(users.name);
387/// # "####;
388/// ```
389pub fn sum<'a, V, E>(
390    expr: E,
391) -> SQLExpr<'a, V, <E::SQLType as AggregatePolicy<V::DialectMarker>>::Sum, Null, Agg>
392where
393    V: SQLParam + 'a,
394    E: Expr<'a, V>,
395    E::SQLType: AggregatePolicy<V::DialectMarker>,
396{
397    SQLExpr::new(SQL::func("SUM", expr.into_expr_sql()))
398}
399
400/// SUM(DISTINCT expr) - sums distinct numeric values.
401///
402/// Requires the expression to be `Numeric`.
403/// Result type is dialect-aware.
404pub fn sum_distinct<'a, V, E>(
405    expr: E,
406) -> SQLExpr<'a, V, <E::SQLType as AggregatePolicy<V::DialectMarker>>::Sum, Null, Agg>
407where
408    V: SQLParam + 'a,
409    E: Expr<'a, V>,
410    E::SQLType: AggregatePolicy<V::DialectMarker>,
411{
412    SQLExpr::new(SQL::func(
413        "SUM",
414        SQL::raw("DISTINCT").append(expr.into_expr_sql()),
415    ))
416}
417
418// =============================================================================
419// AVG
420// =============================================================================
421
422/// AVG(expr) - calculates average of numeric values.
423///
424/// Requires the expression to be `Numeric`.
425/// Always returns Double (SQL standard behavior), nullable.
426///
427/// # Type Safety
428///
429/// ```rust
430/// # let _ = r####"
431/// // ✅ OK: Numeric column
432/// avg(products.price);
433///
434/// // ❌ Compile error: Text is not Numeric
435/// avg(users.name);
436/// # "####;
437/// ```
438pub fn avg<'a, V, E>(
439    expr: E,
440) -> SQLExpr<'a, V, <E::SQLType as AggregatePolicy<V::DialectMarker>>::Avg, Null, Agg>
441where
442    V: SQLParam + 'a,
443    E: Expr<'a, V>,
444    E::SQLType: AggregatePolicy<V::DialectMarker>,
445{
446    SQLExpr::new(SQL::func("AVG", expr.into_expr_sql()))
447}
448
449/// AVG(DISTINCT expr) - calculates average of distinct numeric values.
450///
451/// Requires the expression to be `Numeric`.
452pub fn avg_distinct<'a, V, E>(
453    expr: E,
454) -> SQLExpr<'a, V, <E::SQLType as AggregatePolicy<V::DialectMarker>>::Avg, Null, Agg>
455where
456    V: SQLParam + 'a,
457    E: Expr<'a, V>,
458    E::SQLType: AggregatePolicy<V::DialectMarker>,
459{
460    SQLExpr::new(SQL::func(
461        "AVG",
462        SQL::raw("DISTINCT").append(expr.into_expr_sql()),
463    ))
464}
465
466// =============================================================================
467// MIN / MAX
468// =============================================================================
469
470/// MIN(expr) - finds minimum value.
471///
472/// Works with any expression type (ordered types in SQL).
473/// Preserves the input expression's SQL type.
474/// Result is nullable (empty set returns NULL).
475///
476/// # Example
477///
478/// ```rust
479/// # let _ = r####"
480/// use drizzle_core::expr::min;
481///
482/// let cheapest = min(products.price);
483/// // Generates: MIN("products"."price")
484/// // Returns the same SQL type as products.price
485/// # "####;
486/// ```
487pub fn min<'a, V, E>(expr: E) -> SQLExpr<'a, V, E::SQLType, Null, Agg>
488where
489    V: SQLParam + 'a,
490    E: Expr<'a, V>,
491{
492    SQLExpr::new(SQL::func("MIN", expr.into_expr_sql()))
493}
494
495/// MAX(expr) - finds maximum value.
496///
497/// Works with any expression type (ordered types in SQL).
498/// Preserves the input expression's SQL type.
499/// Result is nullable (empty set returns NULL).
500///
501/// # Example
502///
503/// ```rust
504/// # let _ = r####"
505/// use drizzle_core::expr::max;
506///
507/// let most_expensive = max(products.price);
508/// // Generates: MAX("products"."price")
509/// // Returns the same SQL type as products.price
510/// # "####;
511/// ```
512pub fn max<'a, V, E>(expr: E) -> SQLExpr<'a, V, E::SQLType, Null, Agg>
513where
514    V: SQLParam + 'a,
515    E: Expr<'a, V>,
516{
517    SQLExpr::new(SQL::func("MAX", expr.into_expr_sql()))
518}
519
520// =============================================================================
521// STATISTICAL FUNCTIONS
522// =============================================================================
523
524/// `STDDEV_POP` - population standard deviation.
525///
526/// Calculates the population standard deviation of numeric values.
527/// Requires the expression to be `Numeric`.
528/// Returns Double, nullable (empty set returns NULL).
529///
530/// Note: This function is available in `PostgreSQL`. `SQLite` does not have it built-in.
531///
532/// # Example
533///
534/// ```rust
535/// # let _ = r####"
536/// use drizzle_core::expr::stddev_pop;
537///
538/// let deviation = stddev_pop(measurements.value);
539/// // Generates: STDDEV_POP("measurements"."value")
540/// # "####;
541/// ```
542pub fn stddev_pop<'a, V, E>(
543    expr: E,
544) -> SQLExpr<
545    'a,
546    V,
547    <E::SQLType as StatisticalAggregatePolicy<V::DialectMarker>>::StddevPop,
548    Null,
549    Agg,
550>
551where
552    V: SQLParam + 'a,
553    E: Expr<'a, V>,
554    E::SQLType: StatisticalAggregatePolicy<V::DialectMarker>,
555{
556    SQLExpr::new(pg_double(SQL::func("STDDEV_POP", expr.into_expr_sql())))
557}
558
559/// `STDDEV_SAMP` / STDDEV - sample standard deviation.
560///
561/// Calculates the sample standard deviation of numeric values.
562/// Requires the expression to be `Numeric`.
563/// Returns Double, nullable (empty set returns NULL).
564///
565/// Note: This function is available in `PostgreSQL`. `SQLite` does not have it built-in.
566///
567/// # Example
568///
569/// ```rust
570/// # let _ = r####"
571/// use drizzle_core::expr::stddev_samp;
572///
573/// let deviation = stddev_samp(measurements.value);
574/// // Generates: STDDEV_SAMP("measurements"."value")
575/// # "####;
576/// ```
577pub fn stddev_samp<'a, V, E>(
578    expr: E,
579) -> SQLExpr<
580    'a,
581    V,
582    <E::SQLType as StatisticalAggregatePolicy<V::DialectMarker>>::StddevSamp,
583    Null,
584    Agg,
585>
586where
587    V: SQLParam + 'a,
588    E: Expr<'a, V>,
589    E::SQLType: StatisticalAggregatePolicy<V::DialectMarker>,
590{
591    SQLExpr::new(pg_double(SQL::func("STDDEV_SAMP", expr.into_expr_sql())))
592}
593
594/// `VAR_POP` - population variance.
595///
596/// Calculates the population variance of numeric values.
597/// Requires the expression to be `Numeric`.
598/// Returns Double, nullable (empty set returns NULL).
599///
600/// Note: This function is available in `PostgreSQL`. `SQLite` does not have it built-in.
601///
602/// # Example
603///
604/// ```rust
605/// # let _ = r####"
606/// use drizzle_core::expr::var_pop;
607///
608/// let variance = var_pop(measurements.value);
609/// // Generates: VAR_POP("measurements"."value")
610/// # "####;
611/// ```
612pub fn var_pop<'a, V, E>(
613    expr: E,
614) -> SQLExpr<'a, V, <E::SQLType as StatisticalAggregatePolicy<V::DialectMarker>>::VarPop, Null, Agg>
615where
616    V: SQLParam + 'a,
617    E: Expr<'a, V>,
618    E::SQLType: StatisticalAggregatePolicy<V::DialectMarker>,
619{
620    SQLExpr::new(pg_double(SQL::func("VAR_POP", expr.into_expr_sql())))
621}
622
623/// `VAR_SAMP` / VARIANCE - sample variance.
624///
625/// Calculates the sample variance of numeric values.
626/// Requires the expression to be `Numeric`.
627/// Returns Double, nullable (empty set returns NULL).
628///
629/// Note: This function is available in `PostgreSQL`. `SQLite` does not have it built-in.
630///
631/// # Example
632///
633/// ```rust
634/// # let _ = r####"
635/// use drizzle_core::expr::var_samp;
636///
637/// let variance = var_samp(measurements.value);
638/// // Generates: VAR_SAMP("measurements"."value")
639/// # "####;
640/// ```
641pub fn var_samp<'a, V, E>(
642    expr: E,
643) -> SQLExpr<'a, V, <E::SQLType as StatisticalAggregatePolicy<V::DialectMarker>>::VarSamp, Null, Agg>
644where
645    V: SQLParam + 'a,
646    E: Expr<'a, V>,
647    E::SQLType: StatisticalAggregatePolicy<V::DialectMarker>,
648{
649    SQLExpr::new(pg_double(SQL::func("VAR_SAMP", expr.into_expr_sql())))
650}
651
652/// Sample variance. Emits `VARIANCE` on PostgreSQL and `VAR_SAMP` on MySQL.
653pub fn variance<'a, V, E>(
654    expr: E,
655) -> SQLExpr<'a, V, <E::SQLType as StatisticalAggregatePolicy<V::DialectMarker>>::VarSamp, Null, Agg>
656where
657    V: SQLParam + 'a,
658    E: Expr<'a, V>,
659    E::SQLType: StatisticalAggregatePolicy<V::DialectMarker>,
660{
661    SQLExpr::new(pg_double(SQL::func(
662        match V::DIALECT {
663            Dialect::MySQL => "VAR_SAMP",
664            Dialect::SQLite | Dialect::PostgreSQL => "VARIANCE",
665        },
666        expr.into_expr_sql(),
667    )))
668}
669
670/// `BOOL_AND` - true if all non-null inputs are true (`PostgreSQL`).
671pub fn bool_and<'a, V, E>(
672    expr: E,
673) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Bool, Null, Agg>
674where
675    V: SQLParam + 'a,
676    V::DialectMarker: PostgresAggregateSupport,
677    E: Expr<'a, V>,
678    E::SQLType: BooleanAggregatePolicy<V::DialectMarker>,
679{
680    SQLExpr::new(SQL::func("BOOL_AND", expr.into_expr_sql()))
681}
682
683/// `BOOL_OR` - true if any non-null input is true (`PostgreSQL`).
684pub fn bool_or<'a, V, E>(
685    expr: E,
686) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Bool, Null, Agg>
687where
688    V: SQLParam + 'a,
689    V::DialectMarker: PostgresAggregateSupport,
690    E: Expr<'a, V>,
691    E::SQLType: BooleanAggregatePolicy<V::DialectMarker>,
692{
693    SQLExpr::new(SQL::func("BOOL_OR", expr.into_expr_sql()))
694}
695
696/// `JSON_AGG` - aggregates values into a JSON array (`PostgreSQL`).
697pub fn json_agg<'a, V, E>(
698    expr: E,
699) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Json, Null, Agg>
700where
701    V: SQLParam + 'a,
702    V::DialectMarker: PostgresAggregateSupport,
703    E: Expr<'a, V>,
704{
705    SQLExpr::new(SQL::func("JSON_AGG", expr.into_expr_sql()))
706}
707
708/// `JSONB_AGG` - aggregates values into a JSONB array (`PostgreSQL`).
709pub fn jsonb_agg<'a, V, E>(
710    expr: E,
711) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Jsonb, Null, Agg>
712where
713    V: SQLParam + 'a,
714    V::DialectMarker: PostgresAggregateSupport,
715    E: Expr<'a, V>,
716{
717    SQLExpr::new(SQL::func("JSONB_AGG", expr.into_expr_sql()))
718}
719
720/// `ARRAY_AGG` - aggregates values into a SQL array (`PostgreSQL`).
721pub fn array_agg<'a, V, E>(expr: E) -> SQLExpr<'a, V, Array<E::SQLType>, Null, Agg>
722where
723    V: SQLParam + 'a,
724    V::DialectMarker: PostgresAggregateSupport,
725    E: Expr<'a, V>,
726{
727    SQLExpr::new(SQL::func("ARRAY_AGG", expr.into_expr_sql()))
728}
729
730// =============================================================================
731// TOTAL (SQLite)
732// =============================================================================
733
734/// TOTAL - sums numeric values, returning 0.0 for empty sets (`SQLite`).
735///
736/// Unlike `SUM`, which returns NULL for an empty result set,
737/// `TOTAL` always returns a floating-point value (0.0 for empty sets).
738///
739/// # Example
740///
741/// ```rust
742/// # let _ = r####"
743/// use drizzle_core::expr::total;
744///
745/// // SELECT TOTAL(orders.amount)
746/// let total_amount = total(orders.amount);
747/// # "####;
748/// ```
749pub fn total<'a, V, E>(
750    expr: E,
751) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Double, NonNull, Agg>
752where
753    V: SQLParam + 'a,
754    V::DialectMarker: SQLiteAggregateSupport,
755    E: Expr<'a, V>,
756    E::SQLType: Numeric,
757{
758    SQLExpr::new(SQL::func("TOTAL", expr.into_expr_sql()))
759}
760
761// =============================================================================
762// GROUP_CONCAT / STRING_AGG
763// =============================================================================
764
765/// `GROUP_CONCAT` - concatenates values into a string (`SQLite` and `MySQL`).
766///
767/// Returns Text type, nullable.
768pub fn group_concat<'a, V, E>(
769    expr: E,
770) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Text, Null, Agg>
771where
772    V: SQLParam + 'a,
773    V::DialectMarker: GroupConcatSupport,
774    E: Expr<'a, V>,
775    E::SQLType: crate::types::Textual,
776{
777    SQLExpr::new(SQL::func("GROUP_CONCAT", expr.into_expr_sql()))
778}
779
780/// `STRING_AGG` - concatenates text values using a delimiter (`PostgreSQL`).
781pub fn string_agg<'a, V, E, D>(
782    expr: E,
783    delimiter: D,
784) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Text, Null, Agg>
785where
786    V: SQLParam + 'a,
787    V::DialectMarker: PostgresAggregateSupport,
788    E: Expr<'a, V>,
789    E::SQLType: crate::types::Textual,
790    D: Expr<'a, V>,
791    D::SQLType: crate::types::Textual,
792{
793    SQLExpr::new(SQL::func(
794        "STRING_AGG",
795        expr.into_expr_sql()
796            .push(crate::Token::COMMA)
797            .append(delimiter.into_expr_sql()),
798    ))
799}
800
801// =============================================================================
802// PostgreSQL Aggregate Functions
803// =============================================================================
804
805/// EVERY - true if all non-null inputs are true (`PostgreSQL`).
806///
807/// SQL standard alias for `BOOL_AND`.
808///
809/// # Example
810///
811/// ```rust
812/// # let _ = r####"
813/// use drizzle_core::expr::every;
814///
815/// // SELECT EVERY(orders.is_paid)
816/// let all_paid = every(orders.is_paid);
817/// # "####;
818/// ```
819pub fn every<'a, V, E>(
820    expr: E,
821) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Bool, Null, Agg>
822where
823    V: SQLParam + 'a,
824    V::DialectMarker: PostgresAggregateSupport,
825    E: Expr<'a, V>,
826    E::SQLType: BooleanAggregatePolicy<V::DialectMarker>,
827{
828    SQLExpr::new(SQL::func("EVERY", expr.into_expr_sql()))
829}
830
831/// `JSON_OBJECT_AGG` - aggregates key/value pairs into a JSON object (`PostgreSQL`).
832///
833/// # Example
834///
835/// ```rust
836/// # let _ = r####"
837/// use drizzle_core::expr::json_object_agg;
838///
839/// // SELECT JSON_OBJECT_AGG(settings.key, settings.value)
840/// let obj = json_object_agg(settings.key, settings.value);
841/// # "####;
842/// ```
843pub fn json_object_agg<'a, V, K, Val>(
844    key: K,
845    value: Val,
846) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Json, Null, Agg>
847where
848    V: SQLParam + 'a,
849    V::DialectMarker: PostgresAggregateSupport,
850    K: Expr<'a, V>,
851    Val: Expr<'a, V>,
852{
853    SQLExpr::new(SQL::func(
854        "JSON_OBJECT_AGG",
855        key.into_expr_sql()
856            .push(crate::Token::COMMA)
857            .append(value.into_expr_sql()),
858    ))
859}
860
861/// `JSONB_OBJECT_AGG` - aggregates key/value pairs into a JSONB object (`PostgreSQL`).
862///
863/// # Example
864///
865/// ```rust
866/// # let _ = r####"
867/// use drizzle_core::expr::jsonb_object_agg;
868///
869/// // SELECT JSONB_OBJECT_AGG(settings.key, settings.value)
870/// let obj = jsonb_object_agg(settings.key, settings.value);
871/// # "####;
872/// ```
873pub fn jsonb_object_agg<'a, V, K, Val>(
874    key: K,
875    value: Val,
876) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Jsonb, Null, Agg>
877where
878    V: SQLParam + 'a,
879    V::DialectMarker: PostgresAggregateSupport,
880    K: Expr<'a, V>,
881    Val: Expr<'a, V>,
882{
883    SQLExpr::new(SQL::func(
884        "JSONB_OBJECT_AGG",
885        key.into_expr_sql()
886            .push(crate::Token::COMMA)
887            .append(value.into_expr_sql()),
888    ))
889}
890
891// =============================================================================
892// Distinct Wrapper
893// =============================================================================
894
895/// DISTINCT - marks an expression as DISTINCT.
896///
897/// Typically used inside aggregate functions.
898pub fn distinct<'a, V, E>(expr: E) -> SQLExpr<'a, V, E::SQLType, E::Nullable, Scalar>
899where
900    V: SQLParam + 'a,
901    E: Expr<'a, V>,
902{
903    SQLExpr::new(SQL::raw("DISTINCT").append(expr.into_expr_sql()))
904}