Skip to main content

alopex_sql/planner/
aggregate_expr.rs

1use crate::planner::typed_expr::TypedExpr;
2use crate::planner::types::ResolvedType;
3
4/// Supported aggregate function types.
5#[derive(Debug, Clone, PartialEq)]
6pub enum AggregateFunction {
7    Count,
8    Sum,
9    Total,
10    Avg,
11    Min,
12    Max,
13    GroupConcat { separator: Option<String> },
14    StringAgg { separator: Option<String> },
15}
16
17/// Aggregate expression definition.
18#[derive(Debug, Clone)]
19pub struct AggregateExpr {
20    pub function: AggregateFunction,
21    pub arg: Option<TypedExpr>,
22    pub distinct: bool,
23    pub result_type: ResolvedType,
24}
25
26impl AggregateExpr {
27    pub fn count_star() -> Self {
28        Self {
29            function: AggregateFunction::Count,
30            arg: None,
31            distinct: false,
32            result_type: ResolvedType::BigInt,
33        }
34    }
35
36    pub fn count(arg: TypedExpr, distinct: bool) -> Self {
37        Self {
38            function: AggregateFunction::Count,
39            arg: Some(arg),
40            distinct,
41            result_type: ResolvedType::BigInt,
42        }
43    }
44
45    pub fn sum(arg: TypedExpr) -> Self {
46        let result_type = sum_result_type(&arg.resolved_type);
47        Self {
48            function: AggregateFunction::Sum,
49            arg: Some(arg),
50            distinct: false,
51            result_type,
52        }
53    }
54
55    pub fn total(arg: TypedExpr) -> Self {
56        Self {
57            function: AggregateFunction::Total,
58            arg: Some(arg),
59            distinct: false,
60            result_type: ResolvedType::Double,
61        }
62    }
63
64    pub fn avg(arg: TypedExpr) -> Self {
65        Self {
66            function: AggregateFunction::Avg,
67            arg: Some(arg),
68            distinct: false,
69            result_type: ResolvedType::Double,
70        }
71    }
72
73    pub fn min(arg: TypedExpr) -> Self {
74        let result_type = arg.resolved_type.clone();
75        Self {
76            function: AggregateFunction::Min,
77            arg: Some(arg),
78            distinct: false,
79            result_type,
80        }
81    }
82
83    pub fn max(arg: TypedExpr) -> Self {
84        let result_type = arg.resolved_type.clone();
85        Self {
86            function: AggregateFunction::Max,
87            arg: Some(arg),
88            distinct: false,
89            result_type,
90        }
91    }
92}
93
94/// Return the SQL result type for `SUM` over a value of `input_type`.
95///
96/// Fixed-width integral inputs retain their integral type. All other numeric
97/// inputs accumulate and return DOUBLE, matching the historical floating-point
98/// behaviour and keeping `TOTAL`/`AVG` semantics distinct.
99/// `SUM` keeps integer inputs exact, but accumulates them in a wider type: a
100/// 32-bit accumulator overflows on ordinary data, so summing INTEGER yields
101/// BIGINT. PostgreSQL sums int4 into int8 for the same reason, and DuckDB
102/// widens further to hugeint.
103pub fn sum_result_type(input_type: &ResolvedType) -> ResolvedType {
104    match input_type {
105        ResolvedType::Integer | ResolvedType::BigInt => ResolvedType::BigInt,
106        _ => ResolvedType::Double,
107    }
108}