use crate::db::query::plan::{AggregateKind, AggregateShape, expr::Expr};
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct AggregateExpr {
shape: AggregateShape,
}
impl AggregateExpr {
const fn terminal(kind: AggregateKind) -> Self {
Self {
shape: AggregateShape::terminal(kind),
}
}
fn field_target(kind: AggregateKind, field: impl Into<String>) -> Self {
Self {
shape: AggregateShape::field_target(kind, field.into()),
}
}
pub(in crate::db) fn from_expression_input(kind: AggregateKind, input_expr: Expr) -> Self {
Self {
shape: AggregateShape::from_expression_input(kind, input_expr),
}
}
#[must_use]
pub(in crate::db) const fn from_shape(shape: AggregateShape) -> Self {
Self { shape }
}
#[must_use]
pub(in crate::db) const fn shape(&self) -> &AggregateShape {
&self.shape
}
#[must_use]
pub(in crate::db) fn with_filter_expr(mut self, filter_expr: Expr) -> Self {
self.shape = self.shape.with_filter_expr(filter_expr);
self
}
#[must_use]
pub const fn distinct(mut self) -> Self {
self.shape.set_raw_distinct(true);
self
}
#[must_use]
pub(in crate::db) const fn kind(&self) -> AggregateKind {
self.shape.kind()
}
#[must_use]
pub(in crate::db) fn input_expr(&self) -> Option<&Expr> {
self.shape.input_expr()
}
#[must_use]
pub(in crate::db) fn filter_expr(&self) -> Option<&Expr> {
self.shape.filter_expr()
}
#[must_use]
pub(in crate::db) fn target_field(&self) -> Option<&str> {
match self.input_expr() {
Some(Expr::Field(field)) => Some(field.as_str()),
_ => None,
}
}
#[must_use]
pub(in crate::db) const fn is_distinct(&self) -> bool {
self.shape.raw_distinct()
}
#[cfg(test)]
#[must_use]
pub(in crate::db) fn terminal_for_kind(kind: AggregateKind) -> Self {
match kind {
AggregateKind::Count => count(),
AggregateKind::Exists => exists(),
AggregateKind::Min => min(),
AggregateKind::Max => max(),
AggregateKind::First => first(),
AggregateKind::Last => last(),
AggregateKind::Sum | AggregateKind::Avg => unreachable!(
"AggregateExpr::terminal_for_kind does not support SUM/AVG field-target kinds"
),
}
}
}
#[must_use]
pub const fn count() -> AggregateExpr {
AggregateExpr::terminal(AggregateKind::Count)
}
#[must_use]
pub fn count_by(field: impl AsRef<str>) -> AggregateExpr {
AggregateExpr::field_target(AggregateKind::Count, field.as_ref().to_string())
}
#[must_use]
pub fn sum(field: impl AsRef<str>) -> AggregateExpr {
AggregateExpr::field_target(AggregateKind::Sum, field.as_ref().to_string())
}
#[must_use]
pub fn avg(field: impl AsRef<str>) -> AggregateExpr {
AggregateExpr::field_target(AggregateKind::Avg, field.as_ref().to_string())
}
#[must_use]
pub const fn exists() -> AggregateExpr {
AggregateExpr::terminal(AggregateKind::Exists)
}
#[must_use]
pub const fn first() -> AggregateExpr {
AggregateExpr::terminal(AggregateKind::First)
}
#[must_use]
pub const fn last() -> AggregateExpr {
AggregateExpr::terminal(AggregateKind::Last)
}
#[must_use]
pub const fn min() -> AggregateExpr {
AggregateExpr::terminal(AggregateKind::Min)
}
#[must_use]
pub fn min_by(field: impl AsRef<str>) -> AggregateExpr {
AggregateExpr::field_target(AggregateKind::Min, field.as_ref().to_string())
}
#[must_use]
pub const fn max() -> AggregateExpr {
AggregateExpr::terminal(AggregateKind::Max)
}
#[must_use]
pub fn max_by(field: impl AsRef<str>) -> AggregateExpr {
AggregateExpr::field_target(AggregateKind::Max, field.as_ref().to_string())
}