mod evaluator;
mod model;
#[cfg(test)]
mod tests;
use crate::{
db::{
query::{
explain::ExplainAccessPath,
plan::{
AccessPlannedQuery,
access_choice::{
evaluator::{
chosen_access_shape_projection, chosen_selection_reason,
evaluate_index_candidate, ranked_rejection_reason, sorted_indexes,
},
model::{AccessChoiceFamily, AccessChoiceSelectedReason},
},
},
},
schema::SchemaInfo,
},
model::entity::EntityModel,
};
pub(in crate::db) use self::model::AccessChoiceExplainSnapshot;
#[must_use]
pub(in crate::db) fn project_access_choice_explain_snapshot(
model: &EntityModel,
plan: &AccessPlannedQuery,
access: &ExplainAccessPath,
) -> AccessChoiceExplainSnapshot {
let (family, chosen_index_name, chosen_score_hint) = chosen_access_shape_projection(access);
if matches!(family, AccessChoiceFamily::NonIndex) {
return AccessChoiceExplainSnapshot {
chosen_reason: AccessChoiceSelectedReason::NonIndexAccess,
alternatives: Vec::new(),
rejected: Vec::new(),
};
}
let Some(chosen_index_name) = chosen_index_name else {
return AccessChoiceExplainSnapshot {
chosen_reason: AccessChoiceSelectedReason::SelectedIndexUnavailable,
alternatives: Vec::new(),
rejected: Vec::new(),
};
};
let Ok(schema_info) = SchemaInfo::from_entity_model(model) else {
return AccessChoiceExplainSnapshot {
chosen_reason: AccessChoiceSelectedReason::SchemaUnavailable,
alternatives: Vec::new(),
rejected: Vec::new(),
};
};
let predicate = plan.scalar_plan().predicate.as_ref();
let mut chosen_score = chosen_score_hint;
let mut alternatives = Vec::new();
let mut rejected = Vec::new();
let mut eligible_other_scores = Vec::new();
for index in sorted_indexes(model) {
let index_name = index.name();
match evaluate_index_candidate(family, index, &schema_info, predicate) {
self::model::CandidateEvaluation::Eligible(score)
if index_name == chosen_index_name =>
{
chosen_score = score;
}
self::model::CandidateEvaluation::Eligible(score) => {
alternatives.push(index_name);
eligible_other_scores.push(score);
rejected.push(
ranked_rejection_reason(family, score, chosen_score)
.render_for_index(index_name),
);
}
self::model::CandidateEvaluation::Rejected(reason) => {
rejected.push(reason.render_for_index(index_name));
}
}
}
AccessChoiceExplainSnapshot {
chosen_reason: chosen_selection_reason(family, chosen_score, &eligible_other_scores),
alternatives,
rejected,
}
}