use crate::{
db::{
cursor::ContinuationSignature,
direction::Direction,
predicate::{CompareOp, MissingRowPolicy, PredicateExecutionModel},
query::plan::{
expr::Expr, order_contract::DeterministicSecondaryOrderContract,
semantics::LogicalPushdownEligibility,
},
},
model::field::FieldKind,
value::Value,
};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum QueryMode {
Load(LoadSpec),
Delete(DeleteSpec),
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct LoadSpec {
pub(crate) limit: Option<u32>,
pub(crate) offset: u32,
}
impl LoadSpec {
#[must_use]
pub const fn limit(&self) -> Option<u32> {
self.limit
}
#[must_use]
pub const fn offset(&self) -> u32 {
self.offset
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct DeleteSpec {
pub(crate) limit: Option<u32>,
pub(crate) offset: u32,
}
impl DeleteSpec {
#[must_use]
pub const fn limit(&self) -> Option<u32> {
self.limit
}
#[must_use]
pub const fn offset(&self) -> u32 {
self.offset
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum OrderDirection {
Asc,
Desc,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct OrderSpec {
pub(crate) fields: Vec<(String, OrderDirection)>,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) struct DeleteLimitSpec {
pub(crate) limit: Option<u32>,
pub(crate) offset: u32,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum DistinctExecutionStrategy {
None,
PreOrdered,
HashMaterialize,
}
impl DistinctExecutionStrategy {
#[must_use]
pub(crate) const fn is_enabled(self) -> bool {
!matches!(self, Self::None)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) struct PlannerRouteProfile {
continuation_policy: ContinuationPolicy,
logical_pushdown_eligibility: LogicalPushdownEligibility,
secondary_order_contract: Option<DeterministicSecondaryOrderContract>,
}
impl PlannerRouteProfile {
#[must_use]
pub(in crate::db) const fn new(
continuation_policy: ContinuationPolicy,
logical_pushdown_eligibility: LogicalPushdownEligibility,
secondary_order_contract: Option<DeterministicSecondaryOrderContract>,
) -> Self {
Self {
continuation_policy,
logical_pushdown_eligibility,
secondary_order_contract,
}
}
#[must_use]
pub(in crate::db) const fn seeded_unfinalized(is_grouped: bool) -> Self {
Self {
continuation_policy: ContinuationPolicy::new(true, true, !is_grouped),
logical_pushdown_eligibility: LogicalPushdownEligibility::new(false, is_grouped, false),
secondary_order_contract: None,
}
}
#[must_use]
pub(in crate::db) const fn continuation_policy(&self) -> &ContinuationPolicy {
&self.continuation_policy
}
#[must_use]
pub(in crate::db) const fn logical_pushdown_eligibility(&self) -> LogicalPushdownEligibility {
self.logical_pushdown_eligibility
}
#[must_use]
pub(in crate::db) const fn secondary_order_contract(
&self,
) -> Option<&DeterministicSecondaryOrderContract> {
self.secondary_order_contract.as_ref()
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::db) struct ContinuationPolicy {
requires_anchor: bool,
requires_strict_advance: bool,
is_grouped_safe: bool,
}
impl ContinuationPolicy {
#[must_use]
pub(in crate::db) const fn new(
requires_anchor: bool,
requires_strict_advance: bool,
is_grouped_safe: bool,
) -> Self {
Self {
requires_anchor,
requires_strict_advance,
is_grouped_safe,
}
}
#[must_use]
pub(in crate::db) const fn requires_anchor(self) -> bool {
self.requires_anchor
}
#[must_use]
pub(in crate::db) const fn requires_strict_advance(self) -> bool {
self.requires_strict_advance
}
#[must_use]
pub(in crate::db) const fn is_grouped_safe(self) -> bool {
self.is_grouped_safe
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::db) struct ExecutionShapeSignature {
continuation_signature: ContinuationSignature,
}
impl ExecutionShapeSignature {
#[must_use]
pub(in crate::db) const fn new(continuation_signature: ContinuationSignature) -> Self {
Self {
continuation_signature,
}
}
#[must_use]
pub(in crate::db) const fn continuation_signature(self) -> ContinuationSignature {
self.continuation_signature
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct PageSpec {
pub(crate) limit: Option<u32>,
pub(crate) offset: u32,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum AggregateKind {
Count,
Sum,
Avg,
Exists,
Min,
Max,
First,
Last,
}
impl AggregateKind {
#[must_use]
pub(in crate::db) const fn sql_label(self) -> &'static str {
match self {
Self::Count => "COUNT",
Self::Sum => "SUM",
Self::Avg => "AVG",
Self::Exists => "EXISTS",
Self::First => "FIRST",
Self::Last => "LAST",
Self::Min => "MIN",
Self::Max => "MAX",
}
}
#[must_use]
pub(crate) const fn is_count(self) -> bool {
matches!(self, Self::Count)
}
#[must_use]
pub(in crate::db) const fn is_sum(self) -> bool {
matches!(self, Self::Sum | Self::Avg)
}
#[must_use]
pub(in crate::db) const fn is_extrema(self) -> bool {
matches!(self, Self::Min | Self::Max)
}
#[must_use]
pub(in crate::db) const fn requires_decoded_id(self) -> bool {
!matches!(self, Self::Count | Self::Sum | Self::Avg | Self::Exists)
}
#[must_use]
pub(in crate::db) const fn supports_grouped_distinct_v1(self) -> bool {
matches!(
self,
Self::Count | Self::Min | Self::Max | Self::Sum | Self::Avg
)
}
#[must_use]
pub(in crate::db) const fn supports_global_distinct_without_group_keys(self) -> bool {
matches!(self, Self::Count | Self::Sum | Self::Avg)
}
#[must_use]
pub(crate) const fn extrema_direction(self) -> Option<Direction> {
match self {
Self::Min => Some(Direction::Asc),
Self::Max => Some(Direction::Desc),
Self::Count | Self::Sum | Self::Avg | Self::Exists | Self::First | Self::Last => None,
}
}
#[must_use]
pub(crate) const fn materialized_fold_direction(self) -> Direction {
match self {
Self::Min => Direction::Desc,
Self::Count
| Self::Sum
| Self::Avg
| Self::Exists
| Self::Max
| Self::First
| Self::Last => Direction::Asc,
}
}
#[must_use]
pub(crate) const fn supports_bounded_probe_hint(self) -> bool {
!self.is_count() && !self.is_sum()
}
#[must_use]
pub(crate) fn bounded_probe_fetch_hint(
self,
direction: Direction,
offset: usize,
page_limit: Option<usize>,
) -> Option<usize> {
match self {
Self::Exists | Self::First => Some(offset.saturating_add(1)),
Self::Min if direction == Direction::Asc => Some(offset.saturating_add(1)),
Self::Max if direction == Direction::Desc => Some(offset.saturating_add(1)),
Self::Last => page_limit.map(|limit| offset.saturating_add(limit)),
Self::Count | Self::Sum | Self::Avg | Self::Min | Self::Max => None,
}
}
#[must_use]
pub(in crate::db) const fn explain_projection_mode_label(
self,
has_projected_field: bool,
covering_projection: bool,
) -> &'static str {
if has_projected_field {
if covering_projection {
"field_idx"
} else {
"field_mat"
}
} else if matches!(self, Self::Min | Self::Max | Self::First | Self::Last) {
"entity_term"
} else {
"scalar_agg"
}
}
#[must_use]
pub(in crate::db) const fn supports_covering_existing_rows_terminal(self) -> bool {
matches!(self, Self::Count | Self::Exists)
}
}
#[derive(Clone, Debug)]
pub(crate) struct GroupAggregateSpec {
pub(crate) kind: AggregateKind,
#[cfg(test)]
#[allow(dead_code)]
#[cfg(test)]
pub(crate) target_field: Option<String>,
pub(crate) input_expr: Option<Box<Expr>>,
pub(crate) filter_expr: Option<Box<Expr>>,
pub(crate) distinct: bool,
}
impl PartialEq for GroupAggregateSpec {
fn eq(&self, other: &Self) -> bool {
self.kind == other.kind
&& self.input_expr == other.input_expr
&& self.filter_expr == other.filter_expr
&& self.distinct == other.distinct
}
}
impl Eq for GroupAggregateSpec {}
#[derive(Clone, Debug)]
pub(crate) struct FieldSlot {
pub(crate) index: usize,
pub(crate) field: String,
pub(crate) kind: Option<FieldKind>,
}
impl PartialEq for FieldSlot {
fn eq(&self, other: &Self) -> bool {
self.index == other.index && self.field == other.field
}
}
impl Eq for FieldSlot {}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) struct GroupedExecutionConfig {
pub(crate) max_groups: u64,
pub(crate) max_group_bytes: u64,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct GroupSpec {
pub(crate) group_fields: Vec<FieldSlot>,
pub(crate) aggregates: Vec<GroupAggregateSpec>,
pub(crate) execution: GroupedExecutionConfig,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) enum GroupHavingSymbol {
GroupField(FieldSlot),
AggregateIndex(usize),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct GroupHavingClause {
pub(crate) symbol: GroupHavingSymbol,
pub(crate) op: CompareOp,
pub(crate) value: Value,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct ScalarPlan {
pub(crate) mode: QueryMode,
pub(crate) predicate: Option<PredicateExecutionModel>,
pub(crate) order: Option<OrderSpec>,
pub(crate) distinct: bool,
pub(crate) delete_limit: Option<DeleteLimitSpec>,
pub(crate) page: Option<PageSpec>,
pub(crate) consistency: MissingRowPolicy,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct GroupPlan {
pub(crate) scalar: ScalarPlan,
pub(crate) group: GroupSpec,
pub(crate) having_expr: Option<Expr>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) enum LogicalPlan {
Scalar(ScalarPlan),
Grouped(GroupPlan),
}