use crate::{
db::{
access::{AccessPath, AccessPlan, ExecutableAccessPlan},
index::{
EncodedValue, IndexBoundsSpec, IndexId, IndexKeyKind, IndexRangeBoundEncodeError,
RawIndexStoreKey, build_index_bounds_for_arity,
raw_keys_for_component_prefix_with_kind,
},
},
error::InternalError,
types::EntityTag,
value::Value,
};
use std::ops::Bound;
pub(in crate::db) type LoweredKey = RawIndexStoreKey;
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) struct LoweredAccess<'a, K> {
executable: ExecutableAccessPlan<'a, K>,
index_prefix_specs: Vec<LoweredIndexPrefixSpec>,
index_range_specs: Vec<LoweredIndexRangeSpec>,
}
impl<'a, K> LoweredAccess<'a, K> {
#[must_use]
pub(in crate::db) const fn executable(&self) -> &ExecutableAccessPlan<'a, K> {
&self.executable
}
#[must_use]
#[cfg(any(test, feature = "sql"))]
pub(in crate::db) const fn index_prefix_specs(&self) -> &[LoweredIndexPrefixSpec] {
self.index_prefix_specs.as_slice()
}
#[must_use]
#[cfg(any(test, feature = "sql"))]
pub(in crate::db) const fn index_range_specs(&self) -> &[LoweredIndexRangeSpec] {
self.index_range_specs.as_slice()
}
#[must_use]
pub(in crate::db) fn into_executable_and_index_specs(
self,
) -> (
ExecutableAccessPlan<'a, K>,
Vec<LoweredIndexPrefixSpec>,
Vec<LoweredIndexRangeSpec>,
) {
(
self.executable,
self.index_prefix_specs,
self.index_range_specs,
)
}
}
#[derive(Debug)]
pub(in crate::db) enum LoweredAccessError {
IndexPrefix,
IndexRange,
}
pub(in crate::db) fn lower_access<K>(
entity_tag: EntityTag,
access: &AccessPlan<K>,
) -> Result<LoweredAccess<'_, K>, LoweredAccessError> {
let mut index_prefix_specs = Vec::new();
let mut index_range_specs = Vec::new();
let executable = lower_access_node(
entity_tag,
access,
&mut index_prefix_specs,
&mut index_range_specs,
)?;
Ok(LoweredAccess {
executable,
index_prefix_specs,
index_range_specs,
})
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) struct LoweredIndexScanContract {
name: String,
store_path: String,
}
impl LoweredIndexScanContract {
#[must_use]
fn from_access_contract(index: crate::db::access::SemanticIndexAccessContract) -> Self {
Self {
name: index.name().to_string(),
store_path: index.store_path().to_string(),
}
}
#[must_use]
pub(in crate::db) const fn name(&self) -> &str {
self.name.as_str()
}
#[must_use]
pub(in crate::db) const fn store_path(&self) -> &str {
self.store_path.as_str()
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) struct LoweredIndexPrefixSpec {
scan_contract: LoweredIndexScanContract,
lower: Bound<LoweredKey>,
upper: Bound<LoweredKey>,
prefix_components: Vec<Vec<u8>>,
}
impl LoweredIndexPrefixSpec {
#[must_use]
fn new(
index: crate::db::access::SemanticIndexAccessContract,
lower: Bound<LoweredKey>,
upper: Bound<LoweredKey>,
prefix_components: Vec<Vec<u8>>,
) -> Self {
Self {
scan_contract: LoweredIndexScanContract::from_access_contract(index),
lower,
upper,
prefix_components,
}
}
pub(in crate::db) fn from_raw_component_prefix(
entity_tag: EntityTag,
index: crate::db::access::SemanticIndexAccessContract,
key_kind: IndexKeyKind,
prefix_components: Vec<Vec<u8>>,
) -> Result<Self, InternalError> {
if prefix_components.is_empty() || prefix_components.len() > index.key_arity() {
return Err(InternalError::query_executor_invariant());
}
let index_id = IndexId::new(entity_tag, index.ordinal());
let (lower, upper) = raw_keys_for_component_prefix_with_kind(
&index_id,
key_kind,
index.key_arity(),
prefix_components.as_slice(),
)
.map_err(validated_spec_not_indexable)?;
Ok(Self::new(
index,
Bound::Included(lower),
Bound::Excluded(upper),
prefix_components,
))
}
#[must_use]
pub(in crate::db) fn scan_contract(&self) -> LoweredIndexScanContract {
self.scan_contract.clone()
}
#[must_use]
pub(in crate::db) const fn lower(&self) -> &Bound<LoweredKey> {
&self.lower
}
#[must_use]
pub(in crate::db) const fn upper(&self) -> &Bound<LoweredKey> {
&self.upper
}
#[must_use]
pub(in crate::db) const fn prefix_components(&self) -> &[Vec<u8>] {
self.prefix_components.as_slice()
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg(feature = "sql")]
pub(in crate::db) struct LoweredIndexPrefixCardinalitySpec {
index_id: IndexId,
prefix_components: Vec<Vec<u8>>,
}
#[cfg(feature = "sql")]
impl LoweredIndexPrefixCardinalitySpec {
#[must_use]
pub(in crate::db) const fn new(index_id: IndexId, prefix_components: Vec<Vec<u8>>) -> Self {
Self {
index_id,
prefix_components,
}
}
#[must_use]
pub(in crate::db) const fn index_id(&self) -> IndexId {
self.index_id
}
#[must_use]
pub(in crate::db) const fn prefix_components(&self) -> &[Vec<u8>] {
self.prefix_components.as_slice()
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) struct LoweredIndexRangeSpec {
scan_contract: LoweredIndexScanContract,
lower: Bound<LoweredKey>,
upper: Bound<LoweredKey>,
prefix_components: Vec<Vec<u8>>,
}
impl LoweredIndexRangeSpec {
#[must_use]
fn new(
index: crate::db::access::SemanticIndexAccessContract,
lower: Bound<LoweredKey>,
upper: Bound<LoweredKey>,
prefix_components: Vec<Vec<u8>>,
) -> Self {
Self {
scan_contract: LoweredIndexScanContract::from_access_contract(index),
lower,
upper,
prefix_components,
}
}
#[must_use]
pub(in crate::db) fn scan_contract(&self) -> LoweredIndexScanContract {
self.scan_contract.clone()
}
#[must_use]
pub(in crate::db) const fn lower(&self) -> &Bound<LoweredKey> {
&self.lower
}
#[must_use]
pub(in crate::db) const fn upper(&self) -> &Bound<LoweredKey> {
&self.upper
}
#[must_use]
pub(in crate::db) const fn prefix_components(&self) -> &[Vec<u8>] {
self.prefix_components.as_slice()
}
}
fn validated_spec_not_indexable(_err: IndexRangeBoundEncodeError) -> InternalError {
InternalError::query_executor_invariant()
}
fn lower_index_range_bounds_for_scope(
entity_tag: EntityTag,
index: crate::db::access::SemanticIndexAccessContract,
prefix: &[Value],
lower: &Bound<Value>,
upper: &Bound<Value>,
) -> Result<(Bound<LoweredKey>, Bound<LoweredKey>), InternalError> {
let index_id = IndexId::new(entity_tag, index.ordinal());
build_index_bounds_for_arity(
&index_id,
index.key_arity(),
IndexBoundsSpec::component_range(prefix, lower, upper),
)
.map_err(validated_spec_not_indexable)
}
fn lower_index_range_prefix_components(prefix: &[Value]) -> Result<Vec<Vec<u8>>, InternalError> {
if prefix.is_empty() {
return Ok(Vec::new());
}
Ok(EncodedValue::try_encode_all(prefix)
.map_err(|_| InternalError::query_executor_invariant())?
.into_iter()
.map(|encoded| encoded.encoded().to_vec())
.collect())
}
fn lower_access_node<'a, K>(
entity_tag: EntityTag,
access: &'a AccessPlan<K>,
index_prefix_specs: &mut Vec<LoweredIndexPrefixSpec>,
index_range_specs: &mut Vec<LoweredIndexRangeSpec>,
) -> Result<ExecutableAccessPlan<'a, K>, LoweredAccessError> {
match access {
AccessPlan::Path(path) => {
let path = path.as_ref();
lower_index_specs_for_path(entity_tag, path, index_prefix_specs, index_range_specs)?;
Ok(ExecutableAccessPlan::from_access_path(path))
}
AccessPlan::Union(children) => {
let mut lowered_children = Vec::with_capacity(children.len());
for child in children {
lowered_children.push(lower_access_node(
entity_tag,
child,
index_prefix_specs,
index_range_specs,
)?);
}
Ok(ExecutableAccessPlan::union(lowered_children))
}
AccessPlan::Intersection(children) => {
let mut lowered_children = Vec::with_capacity(children.len());
for child in children {
lowered_children.push(lower_access_node(
entity_tag,
child,
index_prefix_specs,
index_range_specs,
)?);
}
Ok(ExecutableAccessPlan::intersection(lowered_children))
}
}
}
fn lower_index_specs_for_path<K>(
entity_tag: EntityTag,
path: &AccessPath<K>,
index_prefix_specs: &mut Vec<LoweredIndexPrefixSpec>,
index_range_specs: &mut Vec<LoweredIndexRangeSpec>,
) -> Result<(), LoweredAccessError> {
match path {
AccessPath::IndexPrefix { index, values } => {
lower_index_prefix_values_for_specs(
entity_tag,
index.clone(),
values,
index_prefix_specs,
)
.map_err(|_err| LoweredAccessError::IndexPrefix)?;
}
AccessPath::IndexMultiLookup { index, values } => {
for value in values {
lower_index_prefix_values_for_specs(
entity_tag,
index.clone(),
std::slice::from_ref(value),
index_prefix_specs,
)
.map_err(|_err| LoweredAccessError::IndexPrefix)?;
}
}
AccessPath::IndexBranchSet { spec } => {
for branch_value in spec.branch_values() {
let values = spec.branch_prefix_values(branch_value);
lower_index_prefix_values_for_specs(
entity_tag,
spec.index(),
values.as_slice(),
index_prefix_specs,
)
.map_err(|_err| LoweredAccessError::IndexPrefix)?;
}
}
AccessPath::IndexRange { spec } => {
debug_assert_eq!(
spec.field_slots().len(),
spec.prefix_values().len().saturating_add(1),
"semantic range field-slot arity must remain prefix_len + range slot",
);
let (lower, upper) = lower_index_range_bounds_for_scope(
entity_tag,
spec.index(),
spec.prefix_values(),
spec.lower(),
spec.upper(),
)
.map_err(|_err| LoweredAccessError::IndexRange)?;
let prefix_components = lower_index_range_prefix_components(spec.prefix_values())
.map_err(|_err| LoweredAccessError::IndexRange)?;
index_range_specs.push(LoweredIndexRangeSpec::new(
spec.index(),
lower,
upper,
prefix_components,
));
}
AccessPath::ByKey(_)
| AccessPath::ByKeys(_)
| AccessPath::KeyRange { .. }
| AccessPath::FullScan => {}
}
Ok(())
}
#[cfg(feature = "sql")]
pub(in crate::db) fn lower_exact_index_prefix_cardinality_specs_for_prefix_access(
entity_tag: EntityTag,
access: &crate::db::query::plan::CountCardinalityPrefixAccess<'_>,
) -> Result<Vec<LoweredIndexPrefixCardinalitySpec>, LoweredAccessError> {
let values = access.values();
if values.is_empty() {
return Err(LoweredAccessError::IndexPrefix);
}
match values {
crate::db::query::plan::CountCardinalityPrefixValues::One(value) => {
lower_single_component_index_prefix_cardinality_specs(
entity_tag,
access.index().ordinal(),
std::slice::from_ref(value),
)
.map_err(|_err| LoweredAccessError::IndexPrefix)
}
crate::db::query::plan::CountCardinalityPrefixValues::Many(values) => {
lower_single_component_index_prefix_cardinality_specs(
entity_tag,
access.index().ordinal(),
values,
)
.map_err(|_err| LoweredAccessError::IndexPrefix)
}
}
}
#[cfg(feature = "sql")]
fn lower_single_component_index_prefix_cardinality_specs(
entity_tag: EntityTag,
index_ordinal: u16,
values: &[Value],
) -> Result<Vec<LoweredIndexPrefixCardinalitySpec>, InternalError> {
if values.is_empty() {
return Err(InternalError::query_executor_invariant());
}
let index_id = IndexId::new(entity_tag, index_ordinal);
let encoded = EncodedValue::try_encode_all(values)
.map_err(|_| InternalError::query_executor_invariant())?;
Ok(encoded
.into_iter()
.map(|component| {
LoweredIndexPrefixCardinalitySpec::new(index_id, vec![component.encoded().to_vec()])
})
.collect())
}
fn lower_index_prefix_values_for_specs(
entity_tag: EntityTag,
index: crate::db::access::SemanticIndexAccessContract,
values: &[Value],
specs: &mut Vec<LoweredIndexPrefixSpec>,
) -> Result<(), InternalError> {
let index_id = IndexId::new(entity_tag, index.ordinal());
let (lower, upper) = build_index_bounds_for_arity(
&index_id,
index.key_arity(),
IndexBoundsSpec::Prefix { values },
)
.map_err(|_| InternalError::query_executor_invariant())?;
let prefix_components = EncodedValue::try_encode_all(values)
.map_err(|_| InternalError::query_executor_invariant())?
.into_iter()
.map(|encoded| encoded.encoded().to_vec())
.collect();
specs.push(LoweredIndexPrefixSpec::new(
index,
lower,
upper,
prefix_components,
));
Ok(())
}