use crate::{
db::access::{
AccessPathKind, ExecutableAccessPathDispatch, dispatch_executable_access_path,
execution_contract::{
AccessPathExecutionKind, ExecutionBounds, ExecutionDistinctMode, ExecutionOrdering,
ExecutionPathPayload,
},
},
model::index::IndexModel,
value::Value,
};
use std::ops::Bound;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::db) struct ExecutableAccessPath<'a, K> {
execution_kind: AccessPathExecutionKind,
ordering: ExecutionOrdering,
bounds: ExecutionBounds,
distinct: ExecutionDistinctMode,
requires_decoded_id: bool,
payload: ExecutionPathPayload<'a, K>,
}
impl<'a, K> ExecutableAccessPath<'a, K> {
#[must_use]
pub(in crate::db) const fn new(
execution_kind: AccessPathExecutionKind,
ordering: ExecutionOrdering,
bounds: ExecutionBounds,
distinct: ExecutionDistinctMode,
requires_decoded_id: bool,
payload: ExecutionPathPayload<'a, K>,
) -> Self {
Self {
execution_kind,
ordering,
bounds,
distinct,
requires_decoded_id,
payload,
}
}
#[must_use]
pub(in crate::db) const fn payload(&self) -> &ExecutionPathPayload<'a, K> {
&self.payload
}
#[must_use]
pub(in crate::db) const fn kind(&self) -> AccessPathKind {
match dispatch_executable_access_path(self) {
ExecutableAccessPathDispatch::ByKey(_) => AccessPathKind::ByKey,
ExecutableAccessPathDispatch::ByKeys(_) => AccessPathKind::ByKeys,
ExecutableAccessPathDispatch::KeyRange { .. } => AccessPathKind::KeyRange,
ExecutableAccessPathDispatch::IndexPrefix => AccessPathKind::IndexPrefix,
ExecutableAccessPathDispatch::IndexMultiLookup { .. } => {
AccessPathKind::IndexMultiLookup
}
ExecutableAccessPathDispatch::IndexRange => AccessPathKind::IndexRange,
ExecutableAccessPathDispatch::FullScan => AccessPathKind::FullScan,
}
}
#[must_use]
pub(in crate::db) const fn execution_kind(&self) -> AccessPathExecutionKind {
self.execution_kind
}
#[must_use]
pub(in crate::db) const fn ordering(&self) -> ExecutionOrdering {
self.ordering
}
#[must_use]
pub(in crate::db) const fn bounds(&self) -> ExecutionBounds {
self.bounds
}
#[must_use]
pub(in crate::db) const fn distinct(&self) -> ExecutionDistinctMode {
self.distinct
}
#[must_use]
pub(in crate::db) const fn requires_decoded_id(&self) -> bool {
self.requires_decoded_id
}
#[must_use]
pub(in crate::db) const fn index_range_semantic_bounds(
&self,
) -> Option<(&'a [Value], &'a Bound<Value>, &'a Bound<Value>)> {
match self.payload {
ExecutionPathPayload::IndexRange {
prefix_values,
lower,
upper,
} => Some((prefix_values, lower, upper)),
ExecutionPathPayload::ByKey(_)
| ExecutionPathPayload::ByKeys(_)
| ExecutionPathPayload::KeyRange { .. }
| ExecutionPathPayload::IndexPrefix
| ExecutionPathPayload::IndexMultiLookup { .. }
| ExecutionPathPayload::FullScan => None,
}
}
#[must_use]
pub(in crate::db) const fn index_prefix_details(&self) -> Option<(IndexModel, usize)> {
match self.bounds {
ExecutionBounds::IndexPrefix { index, prefix_len } => Some((index, prefix_len)),
ExecutionBounds::Unbounded
| ExecutionBounds::PrimaryKeyRange
| ExecutionBounds::IndexRange { .. } => None,
}
}
#[must_use]
pub(in crate::db) const fn index_range_details(&self) -> Option<(IndexModel, usize)> {
match self.bounds {
ExecutionBounds::IndexRange { index, prefix_len } => Some((index, prefix_len)),
ExecutionBounds::Unbounded
| ExecutionBounds::PrimaryKeyRange
| ExecutionBounds::IndexPrefix { .. } => None,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) enum ExecutableAccessNode<'a, K> {
Path(ExecutableAccessPath<'a, K>),
Union(Vec<ExecutableAccessPlan<'a, K>>),
Intersection(Vec<ExecutableAccessPlan<'a, K>>),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) struct ExecutableAccessPlan<'a, K> {
pub(in crate::db) execution_kind: AccessPathExecutionKind,
pub(in crate::db) ordering: ExecutionOrdering,
pub(in crate::db) bounds: ExecutionBounds,
pub(in crate::db) distinct: ExecutionDistinctMode,
pub(in crate::db) requires_decoded_id: bool,
node: ExecutableAccessNode<'a, K>,
}
impl<'a, K> ExecutableAccessPlan<'a, K> {
#[must_use]
pub(in crate::db) const fn for_path(path: ExecutableAccessPath<'a, K>) -> Self {
Self {
execution_kind: path.execution_kind(),
ordering: path.ordering(),
bounds: path.bounds(),
distinct: path.distinct(),
requires_decoded_id: path.requires_decoded_id(),
node: ExecutableAccessNode::Path(path),
}
}
#[must_use]
pub(in crate::db) fn union(children: Vec<Self>) -> Self {
Self {
execution_kind: AccessPathExecutionKind::Composite,
ordering: ExecutionOrdering::Natural,
bounds: ExecutionBounds::Unbounded,
distinct: ExecutionDistinctMode::RequiresMaterialization,
requires_decoded_id: children.iter().any(|child| child.requires_decoded_id),
node: ExecutableAccessNode::Union(children),
}
}
#[must_use]
pub(in crate::db) fn intersection(children: Vec<Self>) -> Self {
Self {
execution_kind: AccessPathExecutionKind::Intersect,
ordering: ExecutionOrdering::Natural,
bounds: ExecutionBounds::Unbounded,
distinct: ExecutionDistinctMode::RequiresMaterialization,
requires_decoded_id: children.iter().any(|child| child.requires_decoded_id),
node: ExecutableAccessNode::Intersection(children),
}
}
#[must_use]
pub(in crate::db) const fn node(&self) -> &ExecutableAccessNode<'a, K> {
&self.node
}
#[must_use]
pub(in crate::db) const fn as_path(&self) -> Option<&ExecutableAccessPath<'a, K>> {
match &self.node {
ExecutableAccessNode::Path(path) => Some(path),
ExecutableAccessNode::Union(_) | ExecutableAccessNode::Intersection(_) => None,
}
}
}