use crate::{
db::{
access::AccessPlan,
cursor::IndexScanContinuationInput,
direction::Direction,
executor::{
ExecutableAccessPlan, LoweredIndexPrefixSpec, LoweredIndexRangeSpec, LoweredKey,
traversal::IndexRangeTraversalContract,
},
index::predicate::IndexPredicateExecution,
},
error::InternalError,
};
#[expect(clippy::struct_field_names)]
pub(in crate::db::executor) struct AccessSpecCursor<'a> {
index_prefix_specs: &'a [LoweredIndexPrefixSpec],
index_range_specs: &'a [LoweredIndexRangeSpec],
index_prefix_offset: usize,
index_range_offset: usize,
}
impl<'a> AccessSpecCursor<'a> {
#[must_use]
pub(in crate::db::executor) const fn new(
index_prefix_specs: &'a [LoweredIndexPrefixSpec],
index_range_specs: &'a [LoweredIndexRangeSpec],
) -> Self {
Self {
index_prefix_specs,
index_range_specs,
index_prefix_offset: 0,
index_range_offset: 0,
}
}
pub(in crate::db::executor) fn next_index_prefix_specs(
&mut self,
count: usize,
) -> Option<&'a [LoweredIndexPrefixSpec]> {
let start = self.index_prefix_offset;
let end = start.saturating_add(count);
let slice = self.index_prefix_specs.get(start..end)?;
self.index_prefix_offset = end;
Some(slice)
}
pub(in crate::db::executor) fn require_next_index_prefix_specs(
&mut self,
count: usize,
) -> Result<&'a [LoweredIndexPrefixSpec], InternalError> {
self.next_index_prefix_specs(count).ok_or_else(|| {
InternalError::query_executor_invariant(
"index-prefix execution requires pre-lowered specs",
)
})
}
pub(in crate::db::executor) fn next_index_range_spec(
&mut self,
) -> Option<&'a LoweredIndexRangeSpec> {
let spec = self.index_range_specs.get(self.index_range_offset);
if spec.is_some() {
self.index_range_offset = self.index_range_offset.saturating_add(1);
}
spec
}
pub(in crate::db::executor) fn require_next_index_range_spec(
&mut self,
) -> Result<&'a LoweredIndexRangeSpec, InternalError> {
IndexRangeTraversalContract::require_spec(self.next_index_range_spec())
}
pub(in crate::db::executor) fn validate_consumed(&self) -> Result<(), InternalError> {
if self.index_prefix_offset < self.index_prefix_specs.len() {
return Err(InternalError::query_executor_invariant(
"unused index-prefix executable specs after access-plan traversal",
));
}
validate_index_range_specs_consumed(self.index_range_offset, self.index_range_specs.len())?;
Ok(())
}
}
fn validate_index_range_specs_consumed(
consumed: usize,
available: usize,
) -> Result<(), InternalError> {
IndexRangeTraversalContract::validate_specs_consumed(consumed, available)
}
#[derive(Clone, Copy)]
pub(in crate::db::executor) struct AccessStreamBindings<'a> {
pub(in crate::db::executor) index_prefix_specs: &'a [LoweredIndexPrefixSpec],
pub(in crate::db::executor) index_range_specs: &'a [LoweredIndexRangeSpec],
pub(in crate::db::executor) continuation: AccessScanContinuationInput<'a>,
}
impl<'a> AccessStreamBindings<'a> {
#[must_use]
pub(in crate::db::executor) const fn new(
index_prefix_specs: &'a [LoweredIndexPrefixSpec],
index_range_specs: &'a [LoweredIndexRangeSpec],
continuation: AccessScanContinuationInput<'a>,
) -> Self {
Self {
index_prefix_specs,
index_range_specs,
continuation,
}
}
#[must_use]
pub(in crate::db::executor) const fn no_index(direction: Direction) -> Self {
Self::new(&[], &[], AccessScanContinuationInput::new(None, direction))
}
#[must_use]
pub(in crate::db::executor) const fn with_index_prefix(
index_prefix_spec: &'a LoweredIndexPrefixSpec,
direction: Direction,
) -> Self {
Self::new(
std::slice::from_ref(index_prefix_spec),
&[],
AccessScanContinuationInput::new(None, direction),
)
}
#[must_use]
pub(in crate::db::executor) const fn with_index_range_continuation(
index_range_spec: &'a LoweredIndexRangeSpec,
continuation: AccessScanContinuationInput<'a>,
) -> Self {
Self::new(&[], std::slice::from_ref(index_range_spec), continuation)
}
#[must_use]
pub(in crate::db::executor) const fn direction(&self) -> Direction {
self.continuation.direction()
}
}
pub(in crate::db::executor) struct ExecutableAccess<'a, K> {
pub(in crate::db::executor) plan: ExecutableAccessPlan<'a, K>,
pub(in crate::db::executor) bindings: AccessStreamBindings<'a>,
pub(in crate::db::executor) physical_fetch_hint: Option<usize>,
pub(in crate::db::executor) index_predicate_execution: Option<IndexPredicateExecution<'a>>,
pub(in crate::db::executor) preserve_leaf_index_order: bool,
}
impl<'a, K> ExecutableAccess<'a, K> {
#[must_use]
pub(in crate::db::executor) fn new(
access: &'a AccessPlan<K>,
bindings: AccessStreamBindings<'a>,
physical_fetch_hint: Option<usize>,
index_predicate_execution: Option<IndexPredicateExecution<'a>>,
) -> Self {
Self::from_executable_plan(
access.resolve_strategy().into_executable(),
bindings,
physical_fetch_hint,
index_predicate_execution,
)
}
#[must_use]
pub(in crate::db::executor) fn new_with_preserved_leaf_index_order(
access: &'a AccessPlan<K>,
bindings: AccessStreamBindings<'a>,
physical_fetch_hint: Option<usize>,
index_predicate_execution: Option<IndexPredicateExecution<'a>>,
) -> Self {
Self::new(
access,
bindings,
physical_fetch_hint,
index_predicate_execution,
)
.with_preserved_leaf_index_order()
}
#[must_use]
pub(in crate::db::executor) const fn from_executable_plan(
plan: ExecutableAccessPlan<'a, K>,
bindings: AccessStreamBindings<'a>,
physical_fetch_hint: Option<usize>,
index_predicate_execution: Option<IndexPredicateExecution<'a>>,
) -> Self {
Self {
plan,
bindings,
physical_fetch_hint,
index_predicate_execution,
preserve_leaf_index_order: false,
}
}
#[must_use]
pub(in crate::db::executor) const fn with_preserved_leaf_index_order(mut self) -> Self {
self.preserve_leaf_index_order = true;
self
}
}
pub(in crate::db) struct IndexStreamConstraints<'a> {
pub(in crate::db) prefixes: &'a [LoweredIndexPrefixSpec],
pub(in crate::db) range: Option<&'a LoweredIndexRangeSpec>,
}
#[derive(Clone, Copy)]
pub(in crate::db) struct AccessScanContinuationInput<'a> {
index_scan_continuation: IndexScanContinuationInput<'a>,
}
impl<'a> AccessScanContinuationInput<'a> {
#[must_use]
pub(in crate::db) const fn new(anchor: Option<&'a LoweredKey>, direction: Direction) -> Self {
Self {
index_scan_continuation: IndexScanContinuationInput::new(anchor, direction),
}
}
#[must_use]
pub(in crate::db) const fn initial_asc() -> Self {
Self::new(None, Direction::Asc)
}
#[must_use]
pub(in crate::db) const fn direction(&self) -> Direction {
self.index_scan_continuation.direction()
}
#[must_use]
pub(in crate::db) const fn index_scan_continuation(&self) -> IndexScanContinuationInput<'a> {
self.index_scan_continuation
}
}
pub(in crate::db) struct StreamExecutionHints<'a> {
pub(in crate::db) physical_fetch_hint: Option<usize>,
pub(in crate::db) predicate_execution: Option<IndexPredicateExecution<'a>>,
}