use crate::{
db::access::{
AccessPath, AccessPathKind, AccessPlan, ExecutableAccessNode, ExecutableAccessPlan,
ExecutionPathPayload,
},
model::index::IndexModel,
};
const fn has_reversible_traversal_shape_for_path_kind(kind: AccessPathKind) -> bool {
!matches!(kind, AccessPathKind::ByKeys)
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::db) struct SinglePathAccessCapabilities {
kind: AccessPathKind,
is_by_keys_empty: bool,
index_prefix_details: Option<IndexShapeDetails>,
index_range_details: Option<IndexShapeDetails>,
index_fields_for_slot_map: Option<&'static [&'static str]>,
index_prefix_spec_count: usize,
consumes_index_range_spec: bool,
}
impl SinglePathAccessCapabilities {
#[must_use]
pub(in crate::db) const fn kind(&self) -> AccessPathKind {
self.kind
}
#[must_use]
pub(in crate::db) const fn has_reversible_traversal_shape(&self) -> bool {
has_reversible_traversal_shape_for_path_kind(self.kind)
}
#[must_use]
pub(in crate::db) const fn is_by_keys_empty(&self) -> bool {
self.is_by_keys_empty
}
#[must_use]
pub(in crate::db) const fn index_prefix_details(&self) -> Option<IndexShapeDetails> {
self.index_prefix_details
}
#[must_use]
pub(in crate::db) const fn index_range_details(&self) -> Option<IndexShapeDetails> {
self.index_range_details
}
#[must_use]
pub(in crate::db) const fn index_fields_for_slot_map(&self) -> Option<&'static [&'static str]> {
self.index_fields_for_slot_map
}
#[must_use]
pub(in crate::db) const fn index_prefix_spec_count(&self) -> usize {
self.index_prefix_spec_count
}
#[must_use]
pub(in crate::db) const fn consumes_index_range_spec(&self) -> bool {
self.consumes_index_range_spec
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::db) struct IndexShapeDetails {
index: IndexModel,
slot_arity: usize,
}
impl IndexShapeDetails {
#[must_use]
pub(in crate::db) const fn new(index: IndexModel, slot_arity: usize) -> Self {
Self { index, slot_arity }
}
#[must_use]
pub(in crate::db) const fn index(self) -> IndexModel {
self.index
}
#[must_use]
pub(in crate::db) const fn slot_arity(self) -> usize {
self.slot_arity
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::db) struct AccessCapabilities {
single_path: Option<SinglePathAccessCapabilities>,
first_index_range_details: Option<IndexShapeDetails>,
all_paths_support_reverse_traversal: bool,
}
impl AccessCapabilities {
#[must_use]
pub(in crate::db) const fn single_path_capabilities(
&self,
) -> Option<SinglePathAccessCapabilities> {
self.single_path
}
#[must_use]
pub(in crate::db) const fn is_single_path(&self) -> bool {
self.single_path.is_some()
}
#[must_use]
pub(in crate::db) const fn first_index_range_details(&self) -> Option<IndexShapeDetails> {
self.first_index_range_details
}
#[must_use]
pub(in crate::db) const fn is_composite(&self) -> bool {
self.single_path.is_none()
}
#[must_use]
pub(in crate::db) const fn all_paths_support_reverse_traversal(&self) -> bool {
self.all_paths_support_reverse_traversal
}
#[must_use]
pub(in crate::db) const fn single_path_index_prefix_details(
&self,
) -> Option<IndexShapeDetails> {
match self.single_path {
Some(path) => path.index_prefix_details(),
None => None,
}
}
#[must_use]
pub(in crate::db) const fn single_path_index_range_details(&self) -> Option<IndexShapeDetails> {
match self.single_path {
Some(path) => path.index_range_details(),
None => None,
}
}
}
const fn is_by_keys_empty_from_payload<K>(payload: &ExecutionPathPayload<'_, K>) -> bool {
matches!(payload, ExecutionPathPayload::ByKeys(keys) if keys.is_empty())
}
const fn index_prefix_spec_count_from_payload<K>(payload: &ExecutionPathPayload<'_, K>) -> usize {
match payload {
ExecutionPathPayload::IndexPrefix { .. } => 1,
ExecutionPathPayload::IndexMultiLookup { value_count, .. } => *value_count,
ExecutionPathPayload::ByKey(_)
| ExecutionPathPayload::ByKeys(_)
| ExecutionPathPayload::KeyRange { .. }
| ExecutionPathPayload::IndexRange { .. }
| ExecutionPathPayload::FullScan => 0,
}
}
const fn derive_capabilities_from_parts(
kind: AccessPathKind,
is_by_keys_empty: bool,
index_prefix_details: Option<IndexShapeDetails>,
index_range_details: Option<IndexShapeDetails>,
index_prefix_spec_count: usize,
) -> SinglePathAccessCapabilities {
let index_fields_for_slot_map = match (index_prefix_details, index_range_details) {
(Some(details), None) | (None, Some(details)) => Some(details.index().fields()),
(None, None) => None,
(Some(prefix_details), Some(_)) => Some(prefix_details.index().fields()),
};
SinglePathAccessCapabilities {
kind,
is_by_keys_empty,
index_prefix_details,
index_range_details,
index_fields_for_slot_map,
index_prefix_spec_count,
consumes_index_range_spec: index_range_details.is_some(),
}
}
#[must_use]
const fn derive_access_path_capabilities<K>(
path: &ExecutionPathPayload<'_, K>,
) -> SinglePathAccessCapabilities {
let kind = path.kind();
let index_prefix_details = path.index_prefix_details();
let index_range_details = path.index_range_details();
derive_capabilities_from_parts(
kind,
is_by_keys_empty_from_payload(path),
index_prefix_details,
index_range_details,
index_prefix_spec_count_from_payload(path),
)
}
#[must_use]
const fn derive_semantic_access_path_capabilities<K>(
path: &AccessPath<K>,
) -> SinglePathAccessCapabilities {
let payload = ExecutionPathPayload::from_access_path(path);
derive_access_path_capabilities(&payload)
}
fn summarize_access_plan_runtime_shape<K>(
access: &ExecutableAccessPlan<'_, K>,
) -> (Option<IndexShapeDetails>, bool) {
match access.node() {
ExecutableAccessNode::Path(path) => {
let capabilities = path.capabilities();
(
capabilities.index_range_details(),
capabilities.has_reversible_traversal_shape(),
)
}
ExecutableAccessNode::Union(children) | ExecutableAccessNode::Intersection(children) => {
let mut first_index_range_details = None;
let mut all_paths_support_reverse_traversal = true;
for child in children {
let (child_index_range_details, child_reverse_supported) =
summarize_access_plan_runtime_shape(child);
if first_index_range_details.is_none() {
first_index_range_details = child_index_range_details;
}
all_paths_support_reverse_traversal &= child_reverse_supported;
}
(
first_index_range_details,
all_paths_support_reverse_traversal,
)
}
}
}
fn summarize_semantic_access_plan_runtime_shape<K>(
access: &AccessPlan<K>,
) -> (Option<IndexShapeDetails>, bool) {
match access {
AccessPlan::Path(path) => {
let capabilities = path.capabilities();
(
capabilities.index_range_details(),
capabilities.has_reversible_traversal_shape(),
)
}
AccessPlan::Union(children) | AccessPlan::Intersection(children) => {
let mut first_index_range_details = None;
let mut all_paths_support_reverse_traversal = true;
for child in children {
let (child_index_range_details, child_reverse_supported) =
summarize_semantic_access_plan_runtime_shape(child);
if first_index_range_details.is_none() {
first_index_range_details = child_index_range_details;
}
all_paths_support_reverse_traversal &= child_reverse_supported;
}
(
first_index_range_details,
all_paths_support_reverse_traversal,
)
}
}
}
#[must_use]
fn derive_access_capabilities<K>(access: &ExecutableAccessPlan<'_, K>) -> AccessCapabilities {
let single_path = match access.node() {
ExecutableAccessNode::Path(path) => Some(path.capabilities()),
ExecutableAccessNode::Union(_) | ExecutableAccessNode::Intersection(_) => None,
};
let (first_index_range_details, all_paths_support_reverse_traversal) =
summarize_access_plan_runtime_shape(access);
AccessCapabilities {
single_path,
first_index_range_details,
all_paths_support_reverse_traversal,
}
}
#[must_use]
fn derive_semantic_access_capabilities<K>(access: &AccessPlan<K>) -> AccessCapabilities {
let single_path = match access {
AccessPlan::Path(path) => Some(path.capabilities()),
AccessPlan::Union(_) | AccessPlan::Intersection(_) => None,
};
let (first_index_range_details, all_paths_support_reverse_traversal) =
summarize_semantic_access_plan_runtime_shape(access);
AccessCapabilities {
single_path,
first_index_range_details,
all_paths_support_reverse_traversal,
}
}
impl<K> AccessPath<K> {
#[must_use]
pub(in crate::db) const fn capabilities(&self) -> SinglePathAccessCapabilities {
derive_semantic_access_path_capabilities(self)
}
}
impl<K> AccessPlan<K> {
#[must_use]
pub(in crate::db) fn capabilities(&self) -> AccessCapabilities {
derive_semantic_access_capabilities(self)
}
}
impl<K> ExecutionPathPayload<'_, K> {
#[must_use]
pub(in crate::db) const fn capabilities(&self) -> SinglePathAccessCapabilities {
derive_access_path_capabilities(self)
}
}
impl<K> ExecutableAccessPlan<'_, K> {
#[must_use]
pub(in crate::db) fn capabilities(&self) -> AccessCapabilities {
derive_access_capabilities(self)
}
}