mod comparison;
use crate::db::query::{
builder::AggregateExpr,
plan::{
AggregateKind,
expr::{Expr, aggregate_count_input_expr_is_non_null_literal},
},
};
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) enum AggregateIdentity {
Count {
input_expr: Option<Expr>,
distinct: bool,
},
Sum {
input_expr: Option<Expr>,
distinct: bool,
},
Avg {
input_expr: Option<Expr>,
distinct: bool,
},
Min {
input_expr: Option<Expr>,
},
Max {
input_expr: Option<Expr>,
},
Exists {
input_expr: Option<Expr>,
distinct: bool,
},
First {
input_expr: Option<Expr>,
distinct: bool,
},
Last {
input_expr: Option<Expr>,
distinct: bool,
},
}
impl AggregateIdentity {
#[must_use]
pub(in crate::db) fn is_count_rows_input(
kind: AggregateKind,
input_expr: Option<&Expr>,
distinct: bool,
) -> bool {
kind == AggregateKind::Count
&& !distinct
&& input_expr.is_none_or(aggregate_count_input_expr_is_non_null_literal)
}
#[must_use]
pub(in crate::db) fn from_kind_input_and_distinct(
kind: AggregateKind,
input_expr: Option<Expr>,
distinct: bool,
) -> Self {
let input_expr = normalize_aggregate_identity_input(kind, input_expr, distinct);
match kind {
AggregateKind::Count => Self::Count {
input_expr,
distinct,
},
AggregateKind::Sum => Self::Sum {
input_expr,
distinct,
},
AggregateKind::Avg => Self::Avg {
input_expr,
distinct,
},
AggregateKind::Min => Self::Min { input_expr },
AggregateKind::Max => Self::Max { input_expr },
AggregateKind::Exists => Self::Exists {
input_expr,
distinct,
},
AggregateKind::First => Self::First {
input_expr,
distinct,
},
AggregateKind::Last => Self::Last {
input_expr,
distinct,
},
}
}
#[must_use]
pub(in crate::db) fn from_aggregate_expr(aggregate: &AggregateExpr) -> Self {
Self::from_kind_input_and_distinct(
aggregate.kind(),
aggregate.input_expr().cloned(),
aggregate.is_distinct(),
)
}
#[must_use]
pub(in crate::db) const fn normalize_distinct_for_kind(
kind: AggregateKind,
distinct: bool,
) -> bool {
match kind {
AggregateKind::Min | AggregateKind::Max => false,
AggregateKind::Count
| AggregateKind::Sum
| AggregateKind::Avg
| AggregateKind::Exists
| AggregateKind::First
| AggregateKind::Last => distinct,
}
}
#[must_use]
pub(in crate::db) const fn kind(&self) -> AggregateKind {
match self {
Self::Count { .. } => AggregateKind::Count,
Self::Sum { .. } => AggregateKind::Sum,
Self::Avg { .. } => AggregateKind::Avg,
Self::Min { .. } => AggregateKind::Min,
Self::Max { .. } => AggregateKind::Max,
Self::Exists { .. } => AggregateKind::Exists,
Self::First { .. } => AggregateKind::First,
Self::Last { .. } => AggregateKind::Last,
}
}
#[must_use]
pub(in crate::db) const fn input_expr(&self) -> Option<&Expr> {
match self {
Self::Count { input_expr, .. }
| Self::Sum { input_expr, .. }
| Self::Avg { input_expr, .. }
| Self::Min { input_expr }
| Self::Max { input_expr }
| Self::Exists { input_expr, .. }
| Self::First { input_expr, .. }
| Self::Last { input_expr, .. } => input_expr.as_ref(),
}
}
#[must_use]
pub(in crate::db) const fn distinct(&self) -> bool {
match self {
Self::Count { distinct, .. }
| Self::Sum { distinct, .. }
| Self::Avg { distinct, .. }
| Self::Exists { distinct, .. }
| Self::First { distinct, .. }
| Self::Last { distinct, .. } => *distinct,
Self::Min { .. } | Self::Max { .. } => false,
}
}
#[must_use]
pub(in crate::db) const fn uses_grouped_distinct_value_dedup(&self) -> bool {
matches!(
self,
Self::Count { distinct: true, .. }
| Self::Sum { distinct: true, .. }
| Self::Avg { distinct: true, .. }
)
}
}
fn normalize_aggregate_identity_input(
kind: AggregateKind,
input_expr: Option<Expr>,
distinct: bool,
) -> Option<Expr> {
if AggregateIdentity::is_count_rows_input(kind, input_expr.as_ref(), distinct) {
return None;
}
input_expr
}
#[cfg(any(feature = "sql", test))]
#[derive(Clone, Debug, Eq)]
pub(in crate::db) struct AggregateSemanticKey {
identity: AggregateIdentity,
filter_expr: Option<Expr>,
}
#[cfg(any(feature = "sql", test))]
impl AggregateSemanticKey {
#[must_use]
pub(in crate::db) fn as_ref(&self) -> AggregateSemanticKeyRef<'_> {
AggregateSemanticKeyRef::new(
self.identity.kind(),
self.identity.input_expr(),
self.filter_expr.as_ref(),
self.identity.distinct(),
)
}
#[must_use]
pub(in crate::db) fn from_aggregate_expr(aggregate: &AggregateExpr) -> Self {
Self {
identity: AggregateIdentity::from_aggregate_expr(aggregate),
filter_expr: aggregate.filter_expr().cloned(),
}
}
#[must_use]
#[cfg(feature = "sql")]
pub(in crate::db) fn into_identity_and_filter(self) -> (AggregateIdentity, Option<Expr>) {
(self.identity, self.filter_expr)
}
}
#[cfg(any(feature = "sql", test))]
impl PartialEq for AggregateSemanticKey {
fn eq(&self, other: &Self) -> bool {
self.as_ref() == other.as_ref()
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::db) struct AggregateSemanticKeyRef<'a> {
kind: AggregateKind,
input_expr: Option<&'a Expr>,
filter_expr: Option<&'a Expr>,
distinct: bool,
}
impl<'a> AggregateSemanticKeyRef<'a> {
#[must_use]
pub(in crate::db) const fn kind(self) -> AggregateKind {
self.kind
}
#[must_use]
pub(in crate::db) const fn input_expr(self) -> Option<&'a Expr> {
self.input_expr
}
#[must_use]
pub(in crate::db) const fn filter_expr(self) -> Option<&'a Expr> {
self.filter_expr
}
#[must_use]
pub(in crate::db) const fn distinct(self) -> bool {
self.distinct
}
#[must_use]
pub(in crate::db) fn new(
kind: AggregateKind,
input_expr: Option<&'a Expr>,
filter_expr: Option<&'a Expr>,
distinct: bool,
) -> Self {
Self {
kind,
input_expr: if AggregateIdentity::is_count_rows_input(kind, input_expr, distinct) {
None
} else {
input_expr
},
filter_expr,
distinct: AggregateIdentity::normalize_distinct_for_kind(kind, distinct),
}
}
#[must_use]
pub(in crate::db) fn from_aggregate_expr(aggregate: &'a AggregateExpr) -> Self {
Self::new(
aggregate.kind(),
aggregate.input_expr(),
aggregate.filter_expr(),
aggregate.is_distinct(),
)
}
}
#[cfg(test)]
mod tests {
use crate::value::Value;
use super::*;
#[test]
fn borrowed_semantic_keys_preserve_owned_identity_and_filter_equivalence() {
use crate::db::query::plan::AggregateShape;
let mut expressions = Vec::new();
for kind in [
AggregateKind::Count,
AggregateKind::Sum,
AggregateKind::Avg,
AggregateKind::Min,
AggregateKind::Max,
AggregateKind::Exists,
AggregateKind::First,
AggregateKind::Last,
] {
for distinct in [false, true] {
for input in [
None,
Some(Expr::Literal(Value::Null)),
Some(Expr::Literal(Value::Nat64(1))),
Some(Expr::Literal(Value::Nat64(2))),
Some(Expr::Field("amount".into())),
Some(Expr::Field("rank".into())),
] {
for filter in [
None,
Some(Expr::Literal(Value::Bool(true))),
Some(Expr::Literal(Value::Bool(false))),
] {
let mut shape = input
.clone()
.map_or_else(
|| AggregateShape::terminal(kind),
|input| AggregateShape::from_expression_input(kind, input),
)
.with_raw_distinct(distinct);
if let Some(filter) = filter {
shape = shape.with_filter_expr(filter);
}
expressions.push(AggregateExpr::from_shape(shape));
}
}
}
}
let owned: Vec<_> = expressions
.iter()
.map(AggregateSemanticKey::from_aggregate_expr)
.collect();
for (left_expr, left) in expressions.iter().zip(&owned) {
let borrowed = AggregateSemanticKeyRef::from_aggregate_expr(left_expr);
assert_eq!(borrowed, left.as_ref());
if let Some(input) = borrowed.input_expr {
assert!(std::ptr::eq(input, left_expr.input_expr().unwrap()));
}
if let Some(filter) = borrowed.filter_expr {
assert!(std::ptr::eq(filter, left_expr.filter_expr().unwrap()));
}
for (right_expr, right) in expressions.iter().zip(&owned) {
let expected =
left.identity == right.identity && left.filter_expr == right.filter_expr;
assert_eq!(
borrowed == AggregateSemanticKeyRef::from_aggregate_expr(right_expr),
expected
);
assert_eq!(left == right, expected);
}
}
}
#[test]
fn aggregate_identity_normalizes_only_non_distinct_count_non_null_literals() {
let literal_count = AggregateIdentity::from_kind_input_and_distinct(
AggregateKind::Count,
Some(Expr::Literal(Value::Nat64(1))),
false,
);
let null_count = AggregateIdentity::from_kind_input_and_distinct(
AggregateKind::Count,
Some(Expr::Literal(Value::Null)),
false,
);
let distinct_literal_count = AggregateIdentity::from_kind_input_and_distinct(
AggregateKind::Count,
Some(Expr::Literal(Value::Nat64(1))),
true,
);
assert!(matches!(
literal_count,
AggregateIdentity::Count {
input_expr: None,
distinct: false,
}
));
assert!(matches!(
null_count.input_expr(),
Some(Expr::Literal(Value::Null))
));
assert!(matches!(
distinct_literal_count,
AggregateIdentity::Count {
input_expr: Some(Expr::Literal(Value::Nat64(1))),
distinct: true
}
));
}
}
crate::retained::retained_fields!(AggregateIdentity {
Self::Count{input_expr,distinct} => [input_expr,distinct],
Self::Sum{input_expr,distinct} => [input_expr,distinct],
Self::Avg{input_expr,distinct} => [input_expr,distinct],
Self::Min{input_expr} => [input_expr],
Self::Max{input_expr} => [input_expr],
Self::Exists{input_expr,distinct} => [input_expr,distinct],
Self::First{input_expr,distinct} => [input_expr,distinct],
Self::Last{input_expr,distinct} => [input_expr,distinct],
});