use crate::db::{
access::{AccessPlan, AccessStrategy},
predicate::{IndexCompileTarget, PredicateProgram},
query::plan::{
AccessChoiceExplainSnapshot, GroupPlan, GroupSpec, GroupedAggregateExecutionSpec,
GroupedDistinctExecutionStrategy, LogicalPlan, PlannerRouteProfile,
access_choice::project_access_choice_explain_snapshot_with_indexes,
expr::{
ProjectionSelection, ProjectionSpec, ScalarProjectionExpr,
extend_scalar_projection_referenced_slots,
},
model::OrderDirection,
},
};
use crate::{
model::{entity::EntityModel, index::IndexModel},
traits::FieldValue,
value::Value,
};
#[cfg(test)]
use crate::db::{
access::AccessPath,
predicate::MissingRowPolicy,
query::plan::{
GroupHavingClause, LoadSpec, QueryMode, ScalarPlan, grouped_having_clause_expr_for_group,
},
};
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) enum ResolvedOrderValueSource {
DirectField(usize),
Expression(ScalarProjectionExpr),
}
impl ResolvedOrderValueSource {
#[must_use]
pub(in crate::db) const fn direct_field(slot: usize) -> Self {
Self::DirectField(slot)
}
#[must_use]
pub(in crate::db) const fn expression(expr: ScalarProjectionExpr) -> Self {
Self::Expression(expr)
}
pub(in crate::db) fn extend_referenced_slots(&self, referenced: &mut Vec<usize>) {
match self {
Self::DirectField(slot) => {
if !referenced.contains(slot) {
referenced.push(*slot);
}
}
Self::Expression(expr) => extend_scalar_projection_referenced_slots(expr, referenced),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) struct ResolvedOrderField {
source: ResolvedOrderValueSource,
direction: OrderDirection,
}
impl ResolvedOrderField {
#[must_use]
pub(in crate::db) const fn new(
source: ResolvedOrderValueSource,
direction: OrderDirection,
) -> Self {
Self { source, direction }
}
#[must_use]
pub(in crate::db) const fn source(&self) -> &ResolvedOrderValueSource {
&self.source
}
#[must_use]
pub(in crate::db) const fn direction(&self) -> OrderDirection {
self.direction
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) struct ResolvedOrder {
fields: Vec<ResolvedOrderField>,
}
impl ResolvedOrder {
#[must_use]
pub(in crate::db) const fn new(fields: Vec<ResolvedOrderField>) -> Self {
Self { fields }
}
#[must_use]
pub(in crate::db) const fn fields(&self) -> &[ResolvedOrderField] {
self.fields.as_slice()
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) struct StaticPlanningShape {
pub(in crate::db) primary_key_name: &'static str,
pub(in crate::db) projection_spec: ProjectionSpec,
pub(in crate::db) execution_preparation_compiled_predicate: Option<PredicateProgram>,
pub(in crate::db) effective_runtime_compiled_predicate: Option<PredicateProgram>,
pub(in crate::db) scalar_projection_plan: Option<Vec<ScalarProjectionExpr>>,
pub(in crate::db) grouped_aggregate_execution_specs: Option<Vec<GroupedAggregateExecutionSpec>>,
pub(in crate::db) grouped_distinct_execution_strategy: Option<GroupedDistinctExecutionStrategy>,
pub(in crate::db) projection_direct_slots: Option<Vec<usize>>,
pub(in crate::db) projection_referenced_slots: Vec<usize>,
pub(in crate::db) projected_slot_mask: Vec<bool>,
pub(in crate::db) projection_is_model_identity: bool,
pub(in crate::db) resolved_order: Option<ResolvedOrder>,
pub(in crate::db) order_referenced_slots: Option<Vec<usize>>,
pub(in crate::db) slot_map: Option<Vec<usize>>,
pub(in crate::db) index_compile_targets: Option<Vec<IndexCompileTarget>>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct AccessPlannedQuery {
pub(crate) logical: LogicalPlan,
pub(crate) access: AccessPlan<Value>,
pub(crate) projection_selection: ProjectionSelection,
pub(in crate::db) access_choice: AccessChoiceExplainSnapshot,
pub(in crate::db) planner_route_profile: PlannerRouteProfile,
pub(in crate::db) static_planning_shape: Option<StaticPlanningShape>,
}
impl AccessPlannedQuery {
#[must_use]
#[cfg(test)]
pub(crate) fn new(access: AccessPath<Value>, consistency: MissingRowPolicy) -> Self {
let access = AccessPlan::path(access);
let logical = LogicalPlan::Scalar(ScalarPlan {
mode: QueryMode::Load(LoadSpec::new()),
predicate: None,
order: None,
distinct: false,
delete_limit: None,
page: None,
consistency,
});
Self::seeded_unfinalized(
logical,
access.clone(),
ProjectionSelection::All,
if access.selected_index_model().is_some() {
AccessChoiceExplainSnapshot::selected_index_not_projected()
} else {
AccessChoiceExplainSnapshot::non_index_access()
},
)
}
#[must_use]
#[cfg(test)]
pub(crate) fn full_scan_for_test(consistency: MissingRowPolicy) -> Self {
Self::new(AccessPath::<Value>::FullScan, consistency)
}
const fn seeded_unfinalized(
logical: LogicalPlan,
access: AccessPlan<Value>,
projection_selection: ProjectionSelection,
access_choice: AccessChoiceExplainSnapshot,
) -> Self {
let planner_route_profile =
PlannerRouteProfile::seeded_unfinalized(matches!(logical, LogicalPlan::Grouped(_)));
Self {
logical,
access,
projection_selection,
access_choice,
planner_route_profile,
static_planning_shape: None,
}
}
#[must_use]
#[cfg(test)]
pub(crate) fn from_parts<K>(logical: LogicalPlan, access: AccessPlan<K>) -> Self
where
K: FieldValue,
{
let access = access.into_value_plan();
Self::seeded_unfinalized(
logical,
access.clone(),
ProjectionSelection::All,
if access.selected_index_model().is_some() {
AccessChoiceExplainSnapshot::selected_index_not_projected()
} else {
AccessChoiceExplainSnapshot::non_index_access()
},
)
}
#[must_use]
pub(crate) fn from_parts_with_projection<K>(
logical: LogicalPlan,
access: AccessPlan<K>,
projection_selection: ProjectionSelection,
) -> Self
where
K: FieldValue,
{
let access = access.into_value_plan();
Self::seeded_unfinalized(
logical,
access.clone(),
projection_selection,
if access.selected_index_model().is_some() {
AccessChoiceExplainSnapshot::selected_index_not_projected()
} else {
AccessChoiceExplainSnapshot::non_index_access()
},
)
}
#[must_use]
pub(in crate::db) fn into_grouped(self, group: GroupSpec) -> Self {
self.into_grouped_with_having_expr(group, None)
}
#[cfg(test)]
#[must_use]
pub(in crate::db) fn into_grouped_with_having(
self,
group: GroupSpec,
having_clause: Option<GroupHavingClause>,
) -> Self {
let having_expr = having_clause
.as_ref()
.and_then(|clause| grouped_having_clause_expr_for_group(&group, clause));
self.into_grouped_with_having_expr(group, having_expr)
}
#[must_use]
pub(in crate::db) fn into_grouped_with_having_expr(
self,
group: GroupSpec,
having_expr: Option<crate::db::query::plan::expr::Expr>,
) -> Self {
let Self {
logical,
access,
projection_selection,
access_choice,
planner_route_profile: _planner_route_profile,
static_planning_shape: _static_planning_shape,
} = self;
let scalar = match logical {
LogicalPlan::Scalar(plan) => plan,
LogicalPlan::Grouped(plan) => plan.scalar,
};
Self::seeded_unfinalized(
LogicalPlan::Grouped(GroupPlan {
scalar,
group,
having_expr,
}),
access,
projection_selection,
access_choice,
)
}
#[must_use]
pub(in crate::db) fn access_strategy(&self) -> AccessStrategy<'_, Value> {
self.access.resolve_strategy()
}
#[must_use]
pub(in crate::db) const fn access_choice(&self) -> &AccessChoiceExplainSnapshot {
&self.access_choice
}
pub(in crate::db) fn finalize_access_choice_for_model_with_indexes(
&mut self,
model: &EntityModel,
visible_indexes: &[&'static IndexModel],
) {
self.access_choice =
project_access_choice_explain_snapshot_with_indexes(model, visible_indexes, self);
}
#[must_use]
pub(in crate::db) const fn planner_route_profile(&self) -> &PlannerRouteProfile {
&self.planner_route_profile
}
pub(in crate::db) fn set_planner_route_profile(
&mut self,
planner_route_profile: PlannerRouteProfile,
) {
self.planner_route_profile = planner_route_profile;
}
}