icydb_core/db/query/builder/aggregate/
expr.rs1use crate::{
2 db::query::{
3 construction::ConstructionBudget,
4 plan::{AggregateKind, AggregateShape, expr::Expr},
5 },
6 error::InternalError,
7};
8
9#[derive(Clone, Debug, Eq, PartialEq)]
19pub struct AggregateExpr {
20 shape: AggregateShape,
21}
22
23impl AggregateExpr {
24 pub(in crate::db) fn copy_for_preparation(
26 &self,
27 work: &dyn ConstructionBudget,
28 ) -> Result<Self, InternalError> {
29 Ok(Self::from_shape(self.shape.copy_for_preparation(work)?))
30 }
31
32 pub(in crate::db) const fn take_expressions(&mut self) -> [Option<Box<Expr>>; 2] {
34 self.shape.take_expressions()
35 }
36
37 const fn terminal(kind: AggregateKind) -> Self {
39 Self {
40 shape: AggregateShape::terminal(kind),
41 }
42 }
43
44 fn field_target(kind: AggregateKind, field: impl Into<String>) -> Self {
46 Self {
47 shape: AggregateShape::field_target(kind, field.into()),
48 }
49 }
50
51 pub(in crate::db) fn from_expression_input(kind: AggregateKind, input_expr: Expr) -> Self {
53 Self {
54 shape: AggregateShape::from_expression_input(kind, input_expr),
55 }
56 }
57
58 #[must_use]
60 pub(in crate::db) const fn from_shape(shape: AggregateShape) -> Self {
61 Self { shape }
62 }
63
64 #[must_use]
66 pub(in crate::db) fn into_shape(self) -> AggregateShape {
67 self.shape
68 }
69
70 #[must_use]
72 pub(in crate::db) fn with_filter_expr(mut self, filter_expr: Expr) -> Self {
73 self.shape = self.shape.with_filter_expr(filter_expr);
74 self
75 }
76
77 #[must_use]
79 pub const fn distinct(mut self) -> Self {
80 self.shape.set_raw_distinct(true);
81 self
82 }
83
84 #[must_use]
86 pub(in crate::db) const fn kind(&self) -> AggregateKind {
87 self.shape.kind()
88 }
89
90 #[must_use]
92 pub(in crate::db) fn input_expr(&self) -> Option<&Expr> {
93 self.shape.input_expr()
94 }
95
96 #[must_use]
98 pub(in crate::db) fn filter_expr(&self) -> Option<&Expr> {
99 self.shape.filter_expr()
100 }
101
102 #[must_use]
104 pub(in crate::db) fn target_field(&self) -> Option<&str> {
105 match self.input_expr() {
106 Some(Expr::Field(field)) => Some(field.as_str()),
107 _ => None,
108 }
109 }
110
111 #[must_use]
113 pub(in crate::db) const fn is_distinct(&self) -> bool {
114 self.shape.raw_distinct()
115 }
116
117 #[cfg(test)]
119 #[must_use]
120 pub(in crate::db) fn terminal_for_kind(kind: AggregateKind) -> Self {
121 match kind {
122 AggregateKind::Count => count(),
123 AggregateKind::Exists => exists(),
124 AggregateKind::Min => min(),
125 AggregateKind::Max => max(),
126 AggregateKind::First => first(),
127 AggregateKind::Last => last(),
128 AggregateKind::Sum | AggregateKind::Avg => unreachable!(
129 "AggregateExpr::terminal_for_kind does not support SUM/AVG field-target kinds"
130 ),
131 }
132 }
133}
134
135#[must_use]
137pub const fn count() -> AggregateExpr {
138 AggregateExpr::terminal(AggregateKind::Count)
139}
140
141#[must_use]
143pub fn count_by(field: impl AsRef<str>) -> AggregateExpr {
144 AggregateExpr::field_target(AggregateKind::Count, field.as_ref().to_string())
145}
146
147#[must_use]
149pub fn sum(field: impl AsRef<str>) -> AggregateExpr {
150 AggregateExpr::field_target(AggregateKind::Sum, field.as_ref().to_string())
151}
152
153#[must_use]
155pub fn avg(field: impl AsRef<str>) -> AggregateExpr {
156 AggregateExpr::field_target(AggregateKind::Avg, field.as_ref().to_string())
157}
158
159#[must_use]
161pub const fn exists() -> AggregateExpr {
162 AggregateExpr::terminal(AggregateKind::Exists)
163}
164
165#[must_use]
167pub const fn first() -> AggregateExpr {
168 AggregateExpr::terminal(AggregateKind::First)
169}
170
171#[must_use]
173pub const fn last() -> AggregateExpr {
174 AggregateExpr::terminal(AggregateKind::Last)
175}
176
177#[must_use]
179pub const fn min() -> AggregateExpr {
180 AggregateExpr::terminal(AggregateKind::Min)
181}
182
183#[must_use]
185pub fn min_by(field: impl AsRef<str>) -> AggregateExpr {
186 AggregateExpr::field_target(AggregateKind::Min, field.as_ref().to_string())
187}
188
189#[must_use]
191pub const fn max() -> AggregateExpr {
192 AggregateExpr::terminal(AggregateKind::Max)
193}
194
195#[must_use]
197pub fn max_by(field: impl AsRef<str>) -> AggregateExpr {
198 AggregateExpr::field_target(AggregateKind::Max, field.as_ref().to_string())
199}
200
201crate::retained::retained_fields!(AggregateExpr {
203Self{shape} => [shape],
204});