use crate::dialect::{Dialect, DialectTypes};
use crate::sql::SQL;
use crate::traits::SQLParam;
use crate::types::{Array, Numeric};
use crate::{MySQLDialect, PostgresDialect, SQLiteDialect};
use drizzle_types::mysql::types::{
BigInt as MyBigInt, BigIntUnsigned as MyBigIntUnsigned, Decimal as MyDecimal,
Double as MyDouble, Float as MyFloat, Int as MyInt, IntUnsigned as MyIntUnsigned,
MediumInt as MyMediumInt, MediumIntUnsigned as MyMediumIntUnsigned, SmallInt as MySmallInt,
SmallIntUnsigned as MySmallIntUnsigned, TinyInt as MyTinyInt,
TinyIntUnsigned as MyTinyIntUnsigned, Year as MyYear,
};
use drizzle_types::postgres::types::{
Boolean as PgBoolean, Float4, Float8, Int2, Int4, Int8, Numeric as PgNumeric,
};
use drizzle_types::sqlite::types::{
Integer as SqliteInteger, Numeric as SqliteNumeric, Real as SqliteReal,
};
use super::math::pg_double;
use super::{Agg, Expr, NonNull, Null, SQLExpr, Scalar};
#[diagnostic::on_unimplemented(
message = "no aggregate policy for `{Self}` on this dialect",
label = "aggregate result type is not defined for this SQL type/dialect"
)]
pub trait AggregatePolicy<D>: Numeric {
type Sum: crate::types::DataType;
type Avg: crate::types::DataType;
}
#[diagnostic::on_unimplemented(
message = "no statistical aggregate policy for `{Self}` on this dialect",
label = "stddev/variance result type is not defined for this SQL type/dialect"
)]
pub trait StatisticalAggregatePolicy<D>: Numeric {
type StddevPop: crate::types::DataType;
type StddevSamp: crate::types::DataType;
type VarPop: crate::types::DataType;
type VarSamp: crate::types::DataType;
}
#[diagnostic::on_unimplemented(
message = "boolean aggregates are not supported for `{Self}` on this dialect",
label = "use a boolean expression with a dialect that supports BOOL_AND/BOOL_OR"
)]
pub trait BooleanAggregatePolicy<D>: crate::types::DataType {}
#[diagnostic::on_unimplemented(
message = "this aggregate is not available for this dialect",
label = "use a dialect-specific alternative"
)]
pub trait PostgresAggregateSupport {}
#[diagnostic::on_unimplemented(
message = "this aggregate is not available for this dialect",
label = "use a dialect-specific alternative"
)]
pub trait SQLiteAggregateSupport {}
#[diagnostic::on_unimplemented(
message = "GROUP_CONCAT is not available for this dialect",
label = "use the dialect's native string aggregate"
)]
pub trait GroupConcatSupport {}
#[diagnostic::on_unimplemented(
message = "no COUNT return type defined for this dialect",
label = "COUNT result type is not configured for this dialect marker"
)]
pub trait CountPolicy {
type Count: crate::types::DataType;
}
mod count_arg_private {
use super::SQLParam;
pub trait Sealed<'a, V: SQLParam> {}
impl<'a, V: SQLParam> Sealed<'a, V> for () {}
impl<'a, V, E> Sealed<'a, V> for E
where
V: SQLParam + 'a,
E: crate::traits::ToSQL<'a, V> + crate::row::ExprValueType,
{
}
}
#[doc(hidden)]
pub trait CountArg<'a, V: SQLParam>: count_arg_private::Sealed<'a, V> {
fn count_sql(self) -> SQL<'a, V>;
}
impl<'a, V: SQLParam + 'a> CountArg<'a, V> for () {
fn count_sql(self) -> SQL<'a, V> {
SQL::raw("COUNT(*)")
}
}
impl<'a, V, E> CountArg<'a, V> for E
where
V: SQLParam + 'a,
E: crate::traits::ToSQL<'a, V> + crate::row::ExprValueType,
{
fn count_sql(self) -> SQL<'a, V> {
SQL::func("COUNT", self.into_sql().parens_if_subquery())
}
}
impl CountPolicy for SQLiteDialect {
type Count = drizzle_types::sqlite::types::Integer;
}
impl CountPolicy for PostgresDialect {
type Count = drizzle_types::postgres::types::Int8;
}
impl CountPolicy for MySQLDialect {
type Count = drizzle_types::mysql::types::BigInt;
}
#[diagnostic::on_unimplemented(
message = "no floating-point return type defined for this dialect",
label = "PERCENT_RANK/CUME_DIST result type is not configured for this dialect marker"
)]
pub trait FloatPolicy {
type Float: crate::types::DataType;
}
impl FloatPolicy for SQLiteDialect {
type Float = drizzle_types::sqlite::types::Real;
}
impl FloatPolicy for PostgresDialect {
type Float = drizzle_types::postgres::types::Float8;
}
impl FloatPolicy for MySQLDialect {
type Float = drizzle_types::mysql::types::Double;
}
macro_rules! mysql_aggregate_policy {
($output:ty; $($ty:ty),+ $(,)?) => {
$(
impl AggregatePolicy<MySQLDialect> for $ty {
type Sum = $output;
type Avg = $output;
}
)+
};
}
mysql_aggregate_policy!(MyDecimal;
MyTinyInt,
MyTinyIntUnsigned,
MySmallInt,
MySmallIntUnsigned,
MyMediumInt,
MyMediumIntUnsigned,
MyInt,
MyIntUnsigned,
MyBigInt,
MyBigIntUnsigned,
MyYear,
MyDecimal,
);
mysql_aggregate_policy!(MyDouble; MyFloat, MyDouble);
macro_rules! mysql_statistical_aggregate_policy {
($($ty:ty),+ $(,)?) => {
$(
impl StatisticalAggregatePolicy<MySQLDialect> for $ty {
type StddevPop = MyDouble;
type StddevSamp = MyDouble;
type VarPop = MyDouble;
type VarSamp = MyDouble;
}
)+
};
}
mysql_statistical_aggregate_policy!(
MyTinyInt,
MyTinyIntUnsigned,
MySmallInt,
MySmallIntUnsigned,
MyMediumInt,
MyMediumIntUnsigned,
MyInt,
MyIntUnsigned,
MyBigInt,
MyBigIntUnsigned,
MyYear,
MyDecimal,
MyFloat,
MyDouble,
);
impl AggregatePolicy<SQLiteDialect> for SqliteInteger {
type Sum = Self;
type Avg = SqliteReal;
}
impl AggregatePolicy<SQLiteDialect> for SqliteReal {
type Sum = Self;
type Avg = Self;
}
impl AggregatePolicy<SQLiteDialect> for SqliteNumeric {
type Sum = Self;
type Avg = SqliteReal;
}
impl AggregatePolicy<SQLiteDialect> for drizzle_types::sqlite::types::Any {
type Sum = Self;
type Avg = SqliteReal;
}
impl StatisticalAggregatePolicy<PostgresDialect> for Int2 {
type StddevPop = Float8;
type StddevSamp = Float8;
type VarPop = Float8;
type VarSamp = Float8;
}
impl StatisticalAggregatePolicy<PostgresDialect> for Int4 {
type StddevPop = Float8;
type StddevSamp = Float8;
type VarPop = Float8;
type VarSamp = Float8;
}
impl StatisticalAggregatePolicy<PostgresDialect> for Int8 {
type StddevPop = Float8;
type StddevSamp = Float8;
type VarPop = Float8;
type VarSamp = Float8;
}
impl StatisticalAggregatePolicy<PostgresDialect> for Float4 {
type StddevPop = Float8;
type StddevSamp = Float8;
type VarPop = Float8;
type VarSamp = Float8;
}
impl StatisticalAggregatePolicy<PostgresDialect> for Float8 {
type StddevPop = Self;
type StddevSamp = Self;
type VarPop = Self;
type VarSamp = Self;
}
impl StatisticalAggregatePolicy<PostgresDialect> for PgNumeric {
type StddevPop = Float8;
type StddevSamp = Float8;
type VarPop = Float8;
type VarSamp = Float8;
}
impl BooleanAggregatePolicy<PostgresDialect> for PgBoolean {}
impl PostgresAggregateSupport for PostgresDialect {}
impl SQLiteAggregateSupport for SQLiteDialect {}
impl GroupConcatSupport for SQLiteDialect {}
impl GroupConcatSupport for MySQLDialect {}
impl AggregatePolicy<PostgresDialect> for Int2 {
type Sum = Int8;
type Avg = Float8;
}
impl AggregatePolicy<PostgresDialect> for Int4 {
type Sum = Int8;
type Avg = Float8;
}
impl AggregatePolicy<PostgresDialect> for Int8 {
type Sum = Self;
type Avg = Float8;
}
impl AggregatePolicy<PostgresDialect> for Float4 {
type Sum = Float8;
type Avg = Float8;
}
impl AggregatePolicy<PostgresDialect> for Float8 {
type Sum = Self;
type Avg = Self;
}
impl AggregatePolicy<PostgresDialect> for PgNumeric {
type Sum = Self;
type Avg = Self;
}
pub fn count<'a, V, A>(
arg: A,
) -> SQLExpr<'a, V, <V::DialectMarker as CountPolicy>::Count, NonNull, Agg>
where
V: SQLParam + 'a,
V::DialectMarker: CountPolicy,
A: CountArg<'a, V>,
{
SQLExpr::new(arg.count_sql())
}
pub fn count_distinct<'a, V, E>(
expr: E,
) -> SQLExpr<'a, V, <V::DialectMarker as CountPolicy>::Count, NonNull, Agg>
where
V: SQLParam + 'a,
V::DialectMarker: CountPolicy,
E: Expr<'a, V>,
{
SQLExpr::new(SQL::func(
"COUNT",
SQL::raw("DISTINCT").append(expr.into_expr_sql()),
))
}
pub fn sum<'a, V, E>(
expr: E,
) -> SQLExpr<'a, V, <E::SQLType as AggregatePolicy<V::DialectMarker>>::Sum, Null, Agg>
where
V: SQLParam + 'a,
E: Expr<'a, V>,
E::SQLType: AggregatePolicy<V::DialectMarker>,
{
SQLExpr::new(SQL::func("SUM", expr.into_expr_sql()))
}
pub fn sum_distinct<'a, V, E>(
expr: E,
) -> SQLExpr<'a, V, <E::SQLType as AggregatePolicy<V::DialectMarker>>::Sum, Null, Agg>
where
V: SQLParam + 'a,
E: Expr<'a, V>,
E::SQLType: AggregatePolicy<V::DialectMarker>,
{
SQLExpr::new(SQL::func(
"SUM",
SQL::raw("DISTINCT").append(expr.into_expr_sql()),
))
}
pub fn avg<'a, V, E>(
expr: E,
) -> SQLExpr<'a, V, <E::SQLType as AggregatePolicy<V::DialectMarker>>::Avg, Null, Agg>
where
V: SQLParam + 'a,
E: Expr<'a, V>,
E::SQLType: AggregatePolicy<V::DialectMarker>,
{
SQLExpr::new(SQL::func("AVG", expr.into_expr_sql()))
}
pub fn avg_distinct<'a, V, E>(
expr: E,
) -> SQLExpr<'a, V, <E::SQLType as AggregatePolicy<V::DialectMarker>>::Avg, Null, Agg>
where
V: SQLParam + 'a,
E: Expr<'a, V>,
E::SQLType: AggregatePolicy<V::DialectMarker>,
{
SQLExpr::new(SQL::func(
"AVG",
SQL::raw("DISTINCT").append(expr.into_expr_sql()),
))
}
pub fn min<'a, V, E>(expr: E) -> SQLExpr<'a, V, E::SQLType, Null, Agg>
where
V: SQLParam + 'a,
E: Expr<'a, V>,
{
SQLExpr::new(SQL::func("MIN", expr.into_expr_sql()))
}
pub fn max<'a, V, E>(expr: E) -> SQLExpr<'a, V, E::SQLType, Null, Agg>
where
V: SQLParam + 'a,
E: Expr<'a, V>,
{
SQLExpr::new(SQL::func("MAX", expr.into_expr_sql()))
}
pub fn stddev_pop<'a, V, E>(
expr: E,
) -> SQLExpr<
'a,
V,
<E::SQLType as StatisticalAggregatePolicy<V::DialectMarker>>::StddevPop,
Null,
Agg,
>
where
V: SQLParam + 'a,
E: Expr<'a, V>,
E::SQLType: StatisticalAggregatePolicy<V::DialectMarker>,
{
SQLExpr::new(pg_double(SQL::func("STDDEV_POP", expr.into_expr_sql())))
}
pub fn stddev_samp<'a, V, E>(
expr: E,
) -> SQLExpr<
'a,
V,
<E::SQLType as StatisticalAggregatePolicy<V::DialectMarker>>::StddevSamp,
Null,
Agg,
>
where
V: SQLParam + 'a,
E: Expr<'a, V>,
E::SQLType: StatisticalAggregatePolicy<V::DialectMarker>,
{
SQLExpr::new(pg_double(SQL::func("STDDEV_SAMP", expr.into_expr_sql())))
}
pub fn var_pop<'a, V, E>(
expr: E,
) -> SQLExpr<'a, V, <E::SQLType as StatisticalAggregatePolicy<V::DialectMarker>>::VarPop, Null, Agg>
where
V: SQLParam + 'a,
E: Expr<'a, V>,
E::SQLType: StatisticalAggregatePolicy<V::DialectMarker>,
{
SQLExpr::new(pg_double(SQL::func("VAR_POP", expr.into_expr_sql())))
}
pub fn var_samp<'a, V, E>(
expr: E,
) -> SQLExpr<'a, V, <E::SQLType as StatisticalAggregatePolicy<V::DialectMarker>>::VarSamp, Null, Agg>
where
V: SQLParam + 'a,
E: Expr<'a, V>,
E::SQLType: StatisticalAggregatePolicy<V::DialectMarker>,
{
SQLExpr::new(pg_double(SQL::func("VAR_SAMP", expr.into_expr_sql())))
}
pub fn variance<'a, V, E>(
expr: E,
) -> SQLExpr<'a, V, <E::SQLType as StatisticalAggregatePolicy<V::DialectMarker>>::VarSamp, Null, Agg>
where
V: SQLParam + 'a,
E: Expr<'a, V>,
E::SQLType: StatisticalAggregatePolicy<V::DialectMarker>,
{
SQLExpr::new(pg_double(SQL::func(
match V::DIALECT {
Dialect::MySQL => "VAR_SAMP",
Dialect::SQLite | Dialect::PostgreSQL => "VARIANCE",
},
expr.into_expr_sql(),
)))
}
pub fn bool_and<'a, V, E>(
expr: E,
) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Bool, Null, Agg>
where
V: SQLParam + 'a,
V::DialectMarker: PostgresAggregateSupport,
E: Expr<'a, V>,
E::SQLType: BooleanAggregatePolicy<V::DialectMarker>,
{
SQLExpr::new(SQL::func("BOOL_AND", expr.into_expr_sql()))
}
pub fn bool_or<'a, V, E>(
expr: E,
) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Bool, Null, Agg>
where
V: SQLParam + 'a,
V::DialectMarker: PostgresAggregateSupport,
E: Expr<'a, V>,
E::SQLType: BooleanAggregatePolicy<V::DialectMarker>,
{
SQLExpr::new(SQL::func("BOOL_OR", expr.into_expr_sql()))
}
pub fn json_agg<'a, V, E>(
expr: E,
) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Json, Null, Agg>
where
V: SQLParam + 'a,
V::DialectMarker: PostgresAggregateSupport,
E: Expr<'a, V>,
{
SQLExpr::new(SQL::func("JSON_AGG", expr.into_expr_sql()))
}
pub fn jsonb_agg<'a, V, E>(
expr: E,
) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Jsonb, Null, Agg>
where
V: SQLParam + 'a,
V::DialectMarker: PostgresAggregateSupport,
E: Expr<'a, V>,
{
SQLExpr::new(SQL::func("JSONB_AGG", expr.into_expr_sql()))
}
pub fn array_agg<'a, V, E>(expr: E) -> SQLExpr<'a, V, Array<E::SQLType>, Null, Agg>
where
V: SQLParam + 'a,
V::DialectMarker: PostgresAggregateSupport,
E: Expr<'a, V>,
{
SQLExpr::new(SQL::func("ARRAY_AGG", expr.into_expr_sql()))
}
pub fn total<'a, V, E>(
expr: E,
) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Double, NonNull, Agg>
where
V: SQLParam + 'a,
V::DialectMarker: SQLiteAggregateSupport,
E: Expr<'a, V>,
E::SQLType: Numeric,
{
SQLExpr::new(SQL::func("TOTAL", expr.into_expr_sql()))
}
pub fn group_concat<'a, V, E>(
expr: E,
) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Text, Null, Agg>
where
V: SQLParam + 'a,
V::DialectMarker: GroupConcatSupport,
E: Expr<'a, V>,
E::SQLType: crate::types::Textual,
{
SQLExpr::new(SQL::func("GROUP_CONCAT", expr.into_expr_sql()))
}
pub fn string_agg<'a, V, E, D>(
expr: E,
delimiter: D,
) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Text, Null, Agg>
where
V: SQLParam + 'a,
V::DialectMarker: PostgresAggregateSupport,
E: Expr<'a, V>,
E::SQLType: crate::types::Textual,
D: Expr<'a, V>,
D::SQLType: crate::types::Textual,
{
SQLExpr::new(SQL::func(
"STRING_AGG",
expr.into_expr_sql()
.push(crate::Token::COMMA)
.append(delimiter.into_expr_sql()),
))
}
pub fn every<'a, V, E>(
expr: E,
) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Bool, Null, Agg>
where
V: SQLParam + 'a,
V::DialectMarker: PostgresAggregateSupport,
E: Expr<'a, V>,
E::SQLType: BooleanAggregatePolicy<V::DialectMarker>,
{
SQLExpr::new(SQL::func("EVERY", expr.into_expr_sql()))
}
pub fn json_object_agg<'a, V, K, Val>(
key: K,
value: Val,
) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Json, Null, Agg>
where
V: SQLParam + 'a,
V::DialectMarker: PostgresAggregateSupport,
K: Expr<'a, V>,
Val: Expr<'a, V>,
{
SQLExpr::new(SQL::func(
"JSON_OBJECT_AGG",
key.into_expr_sql()
.push(crate::Token::COMMA)
.append(value.into_expr_sql()),
))
}
pub fn jsonb_object_agg<'a, V, K, Val>(
key: K,
value: Val,
) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Jsonb, Null, Agg>
where
V: SQLParam + 'a,
V::DialectMarker: PostgresAggregateSupport,
K: Expr<'a, V>,
Val: Expr<'a, V>,
{
SQLExpr::new(SQL::func(
"JSONB_OBJECT_AGG",
key.into_expr_sql()
.push(crate::Token::COMMA)
.append(value.into_expr_sql()),
))
}
pub fn distinct<'a, V, E>(expr: E) -> SQLExpr<'a, V, E::SQLType, E::Nullable, Scalar>
where
V: SQLParam + 'a,
E: Expr<'a, V>,
{
SQLExpr::new(SQL::raw("DISTINCT").append(expr.into_expr_sql()))
}