use std::{
marker::PhantomData,
ops::{Deref, DerefMut},
rc::Rc,
};
use crate::{
Expr, IntoExpr, lower,
rows::Rows,
value::{EqTyp, NumTyp},
};
pub struct Aggregate<'outer, 'inner, S> {
pub(crate) query: Rows<'inner, S>,
_p: PhantomData<&'inner &'outer ()>,
}
impl<'inner, S> Deref for Aggregate<'_, 'inner, S> {
type Target = Rows<'inner, S>;
fn deref(&self) -> &Self::Target {
&self.query
}
}
impl<S> DerefMut for Aggregate<'_, '_, S> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.query
}
}
impl<'outer, 'inner, S: 'static> Aggregate<'outer, 'inner, S> {
fn select_func(&self, agg_func: &'static str, val: Rc<lower::Expr>) -> Rc<lower::Expr> {
let expr = Rc::new(lower::Expr::Func(agg_func, Box::new([val])));
Rc::new(lower::Expr::AggrIndex(self.ast.clone(), expr))
}
pub fn avg(&self, val: impl IntoExpr<'inner, S, Typ = f64>) -> Expr<'outer, S, Option<f64>> {
let val = val.into_expr().inner;
Expr::new(self.select_func("avg", val))
}
pub fn max<T>(&self, val: impl IntoExpr<'inner, S, Typ = T>) -> Expr<'outer, S, Option<T>>
where
T: EqTyp,
{
let val = val.into_expr().inner;
Expr::new(self.select_func("max", val))
}
pub fn min<T>(&self, val: impl IntoExpr<'inner, S, Typ = T>) -> Expr<'outer, S, Option<T>>
where
T: EqTyp,
{
let val = val.into_expr().inner;
Expr::new(self.select_func("min", val))
}
pub fn sum<T>(&self, val: impl IntoExpr<'inner, S, Typ = T>) -> Expr<'outer, S, T>
where
T: NumTyp,
{
let val = val.into_expr().inner;
let val = self.select_func("sum", val);
Expr::adhoc(lower::Expr::Func(
"IFNULL",
Box::new([val, Rc::new(lower::Expr::Constant(T::ZERO))]),
))
}
pub fn count_distinct<T: EqTyp + 'static>(
&self,
val: impl IntoExpr<'inner, S, Typ = T>,
) -> Expr<'outer, S, i64> {
let val = val.into_expr().inner;
let val = self.select_func("COUNT", Rc::new(lower::Expr::Prefix("DISTINCT ", val)));
Expr::adhoc(lower::Expr::Func(
"IFNULL",
Box::new([val, Rc::new(lower::Expr::Constant(i64::ZERO))]),
))
}
pub fn exists(&self) -> Expr<'outer, S, bool> {
let zero_expr = Expr::<_, i64>::adhoc(lower::CONST_0);
self.count_distinct(zero_expr.clone()).neq(zero_expr)
}
}
pub fn aggregate<'outer, S, F, R>(f: F) -> R
where
F: for<'inner> FnOnce(&mut Aggregate<'outer, 'inner, S>) -> R,
{
let inner = Rows {
phantom: PhantomData,
ast: Default::default(),
_p: PhantomData,
};
let mut group = Aggregate {
query: inner,
_p: PhantomData,
};
f(&mut group)
}