1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
use std::{
    marker::PhantomData,
    ops::{Deref, DerefMut},
};

use sea_query::{Alias, Expr, Func};

use crate::{
    ast::Joins,
    value::{Db, Field, IsNotNull, MyAlias, MyIdenT, UnwrapOr, Value},
    Query,
};

/// This is the query type used in sub-queries.
/// It can only produce one result (for each outer result).
/// This type dereferences to [Query].
pub struct GroupQuery<'outer, 'inner> {
    pub(crate) query: Query<'inner>,
    pub(crate) joins: &'outer Joins,
    pub(crate) phantom2: PhantomData<dyn Fn(&'outer ()) -> &'outer ()>,
}

impl<'outer, 'inner> Deref for GroupQuery<'outer, 'inner> {
    type Target = Query<'inner>;

    fn deref(&self) -> &Self::Target {
        &self.query
    }
}

impl<'outer, 'inner> DerefMut for GroupQuery<'outer, 'inner> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.query
    }
}

impl<'outer, 'inner> GroupQuery<'outer, 'inner> {
    /// Filter the rows of this sub-query based on a value from the outer query.
    pub fn filter_on<T: MyIdenT>(
        &mut self,
        val: impl Value<'inner, Typ = T>,
        on: impl Value<'outer, Typ = T>,
    ) {
        let alias = MyAlias::new();
        self.ast
            .filter_on
            .push(Box::new((val.build_expr(), alias, on.build_expr())))
    }

    /// Return the average value in a column, this is [None] if there are zero rows.
    pub fn avg<V: Value<'inner, Typ = i64>>(&'inner self, val: V) -> Db<'outer, Option<i64>> {
        let expr = Func::cast_as(Func::avg(val.build_expr()), Alias::new("integer"));
        let alias = self.ast.select.get_or_init(expr.into(), Field::new);
        Option::iden_any(self.joins, *alias)
    }

    /// Return the maximum value in a column, this is [None] if there are zero rows.
    pub fn max<V: Value<'inner, Typ = i64>>(&'inner self, val: V) -> Db<'outer, Option<i64>> {
        let expr = Func::max(val.build_expr());
        let alias = self.ast.select.get_or_init(expr.into(), Field::new);
        Option::iden_any(self.joins, *alias)
    }

    /// Return the sum of a column.
    pub fn sum_float<V: Value<'inner, Typ = f64>>(
        &'inner self,
        val: V,
    ) -> UnwrapOr<Db<'outer, Option<f64>>, f64> {
        let expr = Func::cast_as(Func::sum(val.build_expr()), Alias::new("integer"));
        let alias = self.ast.select.get_or_init(expr.into(), Field::new);
        UnwrapOr(Option::iden_any(self.joins, *alias), 0.)
    }

    /// Return the number of distinct values in a column.
    pub fn count_distinct<V: Value<'inner>>(
        &'inner self,
        val: V,
    ) -> UnwrapOr<Db<'outer, Option<i64>>, i64> {
        let expr = Func::count_distinct(val.build_expr());
        let alias = self.ast.select.get_or_init(expr.into(), Field::new);
        UnwrapOr(Option::iden_any(self.joins, *alias), 0)
    }

    /// Return whether there are any rows.
    pub fn exists(&'inner self) -> IsNotNull<Db<'outer, i64>> {
        let expr = Expr::val(1);
        let alias = self.ast.select.get_or_init(expr.into(), Field::new);
        IsNotNull(i64::iden_any(self.joins, *alias))
    }
}