use crate::db::{
cursor::CursorPlanError,
predicate::CompareOp,
query::plan::{
GroupPlan,
expr::{BinaryOp, Expr, truth_condition_binary_compare_op},
},
};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::db) enum GroupedCursorPolicyViolation {
ContinuationRequiresLimit,
GlobalDistinctContinuationUnsupported,
}
impl GroupedCursorPolicyViolation {
#[must_use]
pub(in crate::db) const fn into_cursor_plan_error(self) -> CursorPlanError {
let _ = self;
CursorPlanError::continuation_cursor_invariant()
}
}
#[must_use]
pub(in crate::db) const fn grouped_having_compare_op_supported(op: CompareOp) -> bool {
grouped_having_compare_kind(op).is_some()
}
#[must_use]
pub(in crate::db) const fn grouped_having_binary_compare_op(op: BinaryOp) -> Option<CompareOp> {
truth_condition_binary_compare_op(op)
}
#[must_use]
pub(in crate::db) fn grouped_cursor_policy_violation(
grouped: &GroupPlan,
cursor_present: bool,
) -> Option<GroupedCursorPolicyViolation> {
if !cursor_present {
return None;
}
if grouped
.scalar
.page
.as_ref()
.and_then(|page| page.limit)
.is_none()
{
return Some(GroupedCursorPolicyViolation::ContinuationRequiresLimit);
}
if grouped.is_global_distinct_aggregate_without_group_keys() {
return Some(GroupedCursorPolicyViolation::GlobalDistinctContinuationUnsupported);
}
None
}
pub(in crate::db::query::plan::semantics) fn grouped_having_streaming_compatible(
having_expr: Option<&Expr>,
) -> bool {
having_expr.is_none_or(grouped_having_expr_streaming_compatible)
}
fn grouped_having_expr_streaming_compatible(expr: &Expr) -> bool {
expr.all_tree_expr(&mut |node| match node {
Expr::Field(_) | Expr::FieldPath(_) | Expr::Literal(_) | Expr::Aggregate(_) => true,
Expr::FunctionCall { .. } | Expr::Unary { .. } | Expr::Case { .. } => true,
Expr::Binary { op, .. } => {
if let Some(compare_op) = grouped_having_binary_compare_op(*op) {
grouped_having_compare_op_supported(compare_op)
} else {
matches!(op, BinaryOp::And)
}
}
#[cfg(test)]
Expr::Alias { .. } => true,
})
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum GroupedHavingCompareKind {
Eq,
Ne,
Lt,
Lte,
Gt,
Gte,
}
const fn grouped_having_compare_kind(op: CompareOp) -> Option<GroupedHavingCompareKind> {
match op {
CompareOp::Eq => Some(GroupedHavingCompareKind::Eq),
CompareOp::Ne => Some(GroupedHavingCompareKind::Ne),
CompareOp::Lt => Some(GroupedHavingCompareKind::Lt),
CompareOp::Lte => Some(GroupedHavingCompareKind::Lte),
CompareOp::Gt => Some(GroupedHavingCompareKind::Gt),
CompareOp::Gte => Some(GroupedHavingCompareKind::Gte),
CompareOp::In
| CompareOp::NotIn
| CompareOp::Contains
| CompareOp::StartsWith
| CompareOp::EndsWith => None,
}
}