use crate::{
db::access::{
AccessPathKind, ExecutableAccessNode, ExecutableAccessPath, ExecutableAccessPlan,
ExecutionPathPayload,
},
metrics::sink::PlanKind,
model::index::IndexModel,
};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::db) enum AccessScanKind {
Keys,
Range,
Index,
FullScan,
Composite,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::db) enum AccessPlanKind {
Path(AccessPathKind),
Union,
Intersection,
}
impl AccessPathKind {
#[must_use]
pub(in crate::db) const fn scan_kind(self) -> AccessScanKind {
match self {
Self::ByKey | Self::ByKeys => AccessScanKind::Keys,
Self::KeyRange => AccessScanKind::Range,
Self::IndexPrefix | Self::IndexMultiLookup | Self::IndexRange => AccessScanKind::Index,
Self::FullScan => AccessScanKind::FullScan,
}
}
#[must_use]
pub(in crate::db) const fn supports_streaming_numeric_fold(self) -> bool {
matches!(
self,
Self::ByKey
| Self::ByKeys
| Self::FullScan
| Self::KeyRange
| Self::IndexPrefix
| Self::IndexRange
)
}
#[must_use]
pub(in crate::db) const fn supports_streaming_numeric_fold_for_paged_primary_key_window(
self,
) -> bool {
matches!(
self,
Self::ByKey | Self::ByKeys | Self::FullScan | Self::KeyRange
)
}
}
impl AccessPlanKind {
#[must_use]
pub(in crate::db) const fn scan_kind(self) -> AccessScanKind {
match self {
Self::Path(kind) => kind.scan_kind(),
Self::Union | Self::Intersection => AccessScanKind::Composite,
}
}
#[must_use]
pub(in crate::db) const fn metrics_kind(self) -> PlanKind {
match self.scan_kind() {
AccessScanKind::Keys => PlanKind::Keys,
AccessScanKind::Range => PlanKind::Range,
AccessScanKind::Index => PlanKind::Index,
AccessScanKind::FullScan | AccessScanKind::Composite => PlanKind::FullScan,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[expect(clippy::struct_excessive_bools)]
pub(in crate::db) struct SinglePathAccessCapabilities {
kind: AccessPathKind,
stream: SinglePathStreamCapabilities,
pushdown: SinglePathPushdownCapabilities,
supports_primary_scan_fetch_hint: bool,
is_key_direct_access: bool,
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,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct SinglePathStreamCapabilities {
supports_pk_stream_access: bool,
supports_reverse_traversal: bool,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct SinglePathPushdownCapabilities {
supports_count_pushdown_shape: bool,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct StaticAccessPathCapabilities {
stream: SinglePathStreamCapabilities,
pushdown: SinglePathPushdownCapabilities,
supports_primary_scan_fetch_hint: bool,
is_key_direct_access: bool,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct PayloadAccessPathMetadata {
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 supports_bytes_terminal_primary_key_window(&self) -> bool {
self.kind.supports_bytes_terminal_primary_key_window()
}
#[must_use]
pub(in crate::db) const fn supports_bytes_terminal_ordered_key_stream_window(&self) -> bool {
self.kind
.supports_bytes_terminal_ordered_key_stream_window()
}
#[must_use]
pub(in crate::db) const fn supports_count_terminal_primary_key_cardinality(&self) -> bool {
self.supports_bytes_terminal_primary_key_window()
}
#[must_use]
pub(in crate::db) const fn supports_count_terminal_primary_key_existing_rows(&self) -> bool {
self.kind
.supports_count_terminal_primary_key_existing_rows()
}
#[must_use]
pub(in crate::db) const fn requires_top_n_seek_lookahead(&self) -> bool {
self.kind.requires_top_n_seek_lookahead()
}
#[must_use]
pub(in crate::db) const fn supports_pk_stream_access(&self) -> bool {
self.stream.supports_pk_stream_access
}
#[must_use]
pub(in crate::db) const fn supports_count_pushdown_shape(&self) -> bool {
self.pushdown.supports_count_pushdown_shape
}
#[must_use]
pub(in crate::db) const fn supports_primary_scan_fetch_hint(&self) -> bool {
self.supports_primary_scan_fetch_hint
}
#[must_use]
pub(in crate::db) const fn supports_reverse_traversal(&self) -> bool {
self.stream.supports_reverse_traversal
}
#[must_use]
pub(in crate::db) const fn is_key_direct_access(&self) -> bool {
self.is_key_direct_access
}
#[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_prefix_model(&self) -> Option<IndexModel> {
match self.index_prefix_details {
Some(details) => Some(details.index()),
None => None,
}
}
#[must_use]
pub(in crate::db) const fn index_range_model(&self) -> Option<IndexModel> {
match self.index_range_details {
Some(details) => Some(details.index()),
None => None,
}
}
#[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 {
plan_kind: AccessPlanKind,
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(&self) -> Option<SinglePathAccessCapabilities> {
self.single_path
}
#[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 {
matches!(
self.plan_kind,
AccessPlanKind::Union | AccessPlanKind::Intersection
)
}
#[must_use]
pub(in crate::db) const fn all_paths_support_reverse_traversal(&self) -> bool {
self.all_paths_support_reverse_traversal
}
}
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_static_access_path_capabilities(
kind: AccessPathKind,
) -> StaticAccessPathCapabilities {
StaticAccessPathCapabilities {
stream: SinglePathStreamCapabilities {
supports_pk_stream_access: kind.supports_pk_stream_access(),
supports_reverse_traversal: kind.supports_reverse_traversal(),
},
pushdown: SinglePathPushdownCapabilities {
supports_count_pushdown_shape: kind.supports_count_pushdown_shape(),
},
supports_primary_scan_fetch_hint: kind.supports_primary_scan_fetch_hint(),
is_key_direct_access: kind.is_key_direct_access(),
}
}
const fn derive_payload_access_path_metadata<K>(
path: &ExecutableAccessPath<'_, K>,
) -> PayloadAccessPathMetadata {
let index_prefix_details = match path.index_prefix_details() {
Some((index, slot_arity)) => Some(IndexShapeDetails::new(index, slot_arity)),
None => None,
};
let index_range_details = match path.index_range_details() {
Some((index, slot_arity)) => Some(IndexShapeDetails::new(index, slot_arity)),
None => None,
};
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()),
};
PayloadAccessPathMetadata {
is_by_keys_empty: is_by_keys_empty_from_payload(path.payload()),
index_prefix_details,
index_range_details,
index_fields_for_slot_map,
index_prefix_spec_count: index_prefix_spec_count_from_payload(path.payload()),
consumes_index_range_spec: index_range_details.is_some(),
}
}
#[must_use]
const fn derive_access_path_capabilities<K>(
path: &ExecutableAccessPath<'_, K>,
) -> SinglePathAccessCapabilities {
let kind = path.kind();
let static_capabilities = derive_static_access_path_capabilities(kind);
let payload_metadata = derive_payload_access_path_metadata(path);
SinglePathAccessCapabilities {
kind,
stream: static_capabilities.stream,
pushdown: static_capabilities.pushdown,
supports_primary_scan_fetch_hint: static_capabilities.supports_primary_scan_fetch_hint,
is_key_direct_access: static_capabilities.is_key_direct_access,
is_by_keys_empty: payload_metadata.is_by_keys_empty,
index_prefix_details: payload_metadata.index_prefix_details,
index_range_details: payload_metadata.index_range_details,
index_fields_for_slot_map: payload_metadata.index_fields_for_slot_map,
index_prefix_spec_count: payload_metadata.index_prefix_spec_count,
consumes_index_range_spec: payload_metadata.consumes_index_range_spec,
}
}
fn summarize_access_plan_runtime_shape<K>(
access: &ExecutableAccessPlan<'_, K>,
) -> (Option<IndexShapeDetails>, bool) {
match access.node() {
ExecutableAccessNode::Path(path) => (
path.capabilities().index_range_details(),
path.capabilities().supports_reverse_traversal(),
),
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,
)
}
}
}
#[must_use]
fn derive_access_capabilities<K>(access: &ExecutableAccessPlan<'_, K>) -> AccessCapabilities {
let plan_kind = dispatch_access_plan_kind(access);
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 {
plan_kind,
single_path,
first_index_range_details,
all_paths_support_reverse_traversal,
}
}
impl<K> ExecutableAccessPath<'_, K> {
#[must_use]
pub(in crate::db) const fn capabilities(&self) -> SinglePathAccessCapabilities {
derive_access_path_capabilities(self)
}
}
#[must_use]
pub(in crate::db) const fn single_path_capabilities<K>(
path: &ExecutableAccessPath<'_, K>,
) -> SinglePathAccessCapabilities {
path.capabilities()
}
impl<K> ExecutableAccessPlan<'_, K> {
#[must_use]
pub(in crate::db) fn capabilities(&self) -> AccessCapabilities {
derive_access_capabilities(self)
}
#[must_use]
pub(in crate::db) const fn metrics_kind(&self) -> PlanKind {
dispatch_access_plan_kind(self).metrics_kind()
}
}
#[must_use]
pub(in crate::db) const fn dispatch_access_plan_kind<K>(
access: &ExecutableAccessPlan<'_, K>,
) -> AccessPlanKind {
match access.node() {
ExecutableAccessNode::Path(path) => AccessPlanKind::Path(path.capabilities().kind()),
ExecutableAccessNode::Union(_) => AccessPlanKind::Union,
ExecutableAccessNode::Intersection(_) => AccessPlanKind::Intersection,
}
}