use crate::{
db::{
data::{CanonicalSlotReader, StructuralRowContract},
index::{
IndexId, IndexKey, IndexKeyKind, RawIndexStoreKey,
plan::{
accepted_expression_index_key_for_slot_reader_with_membership_structural,
accepted_field_path_index_key_for_slot_reader_with_membership_structural,
},
raw_keys_for_component_prefix_with_kind,
},
key_taxonomy::PrimaryKeyValue,
predicate::{PredicateProgram, normalize, parse_sql_predicate},
schema::{
AcceptedSchemaSnapshot, AcceptedValueCatalogHandle, PersistedIndexKeySnapshot,
SchemaExpressionIndexInfo, SchemaIndexId, SchemaIndexInfo, SchemaInfo,
},
},
error::InternalError,
types::EntityTag,
};
use std::ops::Bound;
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) struct AcceptedIndexInspectionWitness {
schema_index_id: SchemaIndexId,
store_path: String,
raw_key: RawIndexStoreKey,
}
impl AcceptedIndexInspectionWitness {
#[must_use]
pub(in crate::db) const fn schema_index_id(&self) -> SchemaIndexId {
self.schema_index_id
}
#[must_use]
pub(in crate::db) const fn store_path(&self) -> &str {
self.store_path.as_str()
}
#[must_use]
pub(in crate::db) const fn raw_key(&self) -> &RawIndexStoreKey {
&self.raw_key
}
}
#[derive(Clone, Debug)]
pub(in crate::db) struct AcceptedIndexInspectionDomain {
schema_index_id: SchemaIndexId,
store_path: String,
physical_index_id: IndexId,
key_component_count: usize,
unique: bool,
}
impl AcceptedIndexInspectionDomain {
#[must_use]
pub(in crate::db) const fn schema_index_id(&self) -> SchemaIndexId {
self.schema_index_id
}
#[must_use]
pub(in crate::db) const fn store_path(&self) -> &str {
self.store_path.as_str()
}
#[must_use]
pub(in crate::db) const fn unique(&self) -> bool {
self.unique
}
#[must_use]
pub(in crate::db) const fn physical_generation(&self) -> u64 {
self.physical_index_id.generation()
}
pub(in crate::db) fn raw_bounds(
&self,
) -> Result<(Bound<RawIndexStoreKey>, Bound<RawIndexStoreKey>), InternalError> {
let (lower, upper) = raw_keys_for_component_prefix_with_kind::<Vec<u8>>(
&self.physical_index_id,
IndexKeyKind::User,
self.key_component_count,
&[],
)
.map_err(|_| InternalError::store_corruption())?;
Ok((Bound::Included(lower), Bound::Included(upper)))
}
#[must_use]
pub(in crate::db) fn contains_decoded_key(&self, key: &IndexKey) -> bool {
key.key_kind() == IndexKeyKind::User && key.index_id() == &self.physical_index_id
}
}
#[derive(Clone, Debug)]
pub(in crate::db) struct AcceptedIndexInspectionPlan {
indexes: Vec<AcceptedIndexInspectionEntry>,
}
impl AcceptedIndexInspectionPlan {
pub(in crate::db) fn compile(
schema: &AcceptedSchemaSnapshot,
value_catalog: AcceptedValueCatalogHandle,
row_contract: &StructuralRowContract,
) -> Result<Self, InternalError> {
let schema_info =
SchemaInfo::from_accepted_snapshot_and_catalog(schema, value_catalog, true);
let snapshot = schema.persisted_snapshot();
let mut indexes = Vec::with_capacity(snapshot.indexes().len());
for accepted in snapshot.indexes() {
let predicate = compile_predicate(accepted.predicate_sql(), row_contract)?;
let entry = match accepted.key() {
PersistedIndexKeySnapshot::FieldPath(_) => {
let info = schema_info
.field_path_indexes()
.iter()
.find(|info| info.ordinal() == accepted.ordinal())
.cloned()
.ok_or_else(InternalError::store_corruption)?;
validate_projection_identity(
info.ordinal(),
info.physical_generation(),
info.name(),
info.store(),
info.unique(),
accepted,
)?;
AcceptedIndexInspectionEntry::FieldPath {
schema_index_id: accepted.schema_id(),
info,
predicate,
}
}
PersistedIndexKeySnapshot::Items(_) => {
let info = schema_info
.expression_indexes()
.iter()
.find(|info| info.ordinal() == accepted.ordinal())
.cloned()
.ok_or_else(InternalError::store_corruption)?;
validate_projection_identity(
info.ordinal(),
info.physical_generation(),
info.name(),
info.store(),
info.unique(),
accepted,
)?;
AcceptedIndexInspectionEntry::Expression {
schema_index_id: accepted.schema_id(),
info,
predicate,
}
}
};
indexes.push(entry);
}
Ok(Self { indexes })
}
#[must_use]
pub(in crate::db) const fn len(&self) -> usize {
self.indexes.len()
}
pub(in crate::db) fn domain(
&self,
ordinal: usize,
entity_tag: EntityTag,
) -> Result<AcceptedIndexInspectionDomain, InternalError> {
let entry = self
.indexes
.get(ordinal)
.ok_or_else(InternalError::store_invariant)?;
let (schema_index_id, store_path, physical_index_id, key_component_count, unique) =
match entry {
AcceptedIndexInspectionEntry::FieldPath {
schema_index_id,
info,
..
} => (
*schema_index_id,
info.store(),
IndexId::new_with_generation(
entity_tag,
info.ordinal(),
info.physical_generation(),
),
info.fields().len(),
info.unique(),
),
AcceptedIndexInspectionEntry::Expression {
schema_index_id,
info,
..
} => (
*schema_index_id,
info.store(),
IndexId::new_with_generation(
entity_tag,
info.ordinal(),
info.physical_generation(),
),
info.key_items().len(),
info.unique(),
),
};
Ok(AcceptedIndexInspectionDomain {
schema_index_id,
store_path: store_path.to_string(),
physical_index_id,
key_component_count,
unique,
})
}
pub(in crate::db) fn project(
&self,
ordinal: usize,
entity_tag: EntityTag,
primary_key: &PrimaryKeyValue,
row: &dyn CanonicalSlotReader,
) -> Result<Option<AcceptedIndexInspectionWitness>, InternalError> {
let entry = self
.indexes
.get(ordinal)
.ok_or_else(InternalError::store_invariant)?;
let (schema_index_id, store_path, key) = match entry {
AcceptedIndexInspectionEntry::FieldPath {
schema_index_id,
info,
predicate,
} => (
*schema_index_id,
info.store(),
accepted_field_path_index_key_for_slot_reader_with_membership_structural(
entity_tag,
info,
predicate.as_ref(),
primary_key,
row,
)?,
),
AcceptedIndexInspectionEntry::Expression {
schema_index_id,
info,
predicate,
} => (
*schema_index_id,
info.store(),
accepted_expression_index_key_for_slot_reader_with_membership_structural(
entity_tag,
info,
predicate.as_ref(),
primary_key,
row,
)?,
),
};
let raw_key = key
.as_ref()
.map(IndexKey::to_raw)
.transpose()
.map_err(InternalError::from)?;
Ok(raw_key.map(|raw_key| AcceptedIndexInspectionWitness {
schema_index_id,
store_path: store_path.to_string(),
raw_key,
}))
}
}
#[derive(Clone, Debug)]
enum AcceptedIndexInspectionEntry {
FieldPath {
schema_index_id: SchemaIndexId,
info: SchemaIndexInfo,
predicate: Option<PredicateProgram>,
},
Expression {
schema_index_id: SchemaIndexId,
info: SchemaExpressionIndexInfo,
predicate: Option<PredicateProgram>,
},
}
fn compile_predicate(
sql: Option<&str>,
row_contract: &StructuralRowContract,
) -> Result<Option<PredicateProgram>, InternalError> {
sql.map(|sql| {
let predicate = parse_sql_predicate(sql).map_err(|_| InternalError::store_corruption())?;
Ok(PredicateProgram::compile_with_row_contract(
row_contract,
&normalize(&predicate),
))
})
.transpose()
}
fn validate_projection_identity(
ordinal: u16,
physical_generation: u64,
name: &str,
store: &str,
unique: bool,
accepted: &crate::db::schema::PersistedIndexSnapshot,
) -> Result<(), InternalError> {
if ordinal != accepted.ordinal()
|| physical_generation != accepted.physical_generation()
|| name != accepted.name()
|| store != accepted.store()
|| unique != accepted.unique()
{
return Err(InternalError::store_corruption());
}
Ok(())
}