#[cfg(test)]
mod tests;
use crate::{
db::{
access::AccessPlan,
query::{
construction::ConstructionBudget,
fingerprint::hash_sections::{
ACCESS_TAG_BY_KEY, ACCESS_TAG_BY_KEYS, ACCESS_TAG_FULL_SCAN,
ACCESS_TAG_INDEX_BRANCH_SET, ACCESS_TAG_INDEX_MULTI_LOOKUP,
ACCESS_TAG_INDEX_PREFIX, ACCESS_TAG_INDEX_RANGE, ACCESS_TAG_INTERSECTION,
ACCESS_TAG_KEY_RANGE, ACCESS_TAG_UNION, write_str, write_tag, write_u32,
write_value_bound,
},
plan::{AccessPlanProjection, project_access_plan},
},
},
error::InternalError,
value::Value,
};
use icydb_diagnostic_code::DiagnosticExecutionBudgetResource as Resource;
use sha2::{Digest, Sha256};
use std::ops::Bound;
struct AccessFingerprintVisitor<'a> {
hasher: &'a mut Sha256,
budget: &'a dyn ConstructionBudget,
}
pub(in crate::db::query::fingerprint::hash_sections) fn hash_access_plan(
hasher: &mut Sha256,
access: &AccessPlan<Value>,
budget: &dyn ConstructionBudget,
) -> Result<(), InternalError> {
let mut visitor = AccessFingerprintVisitor { hasher, budget };
project_access_plan(access, &mut visitor)?;
Ok(())
}
fn write_access_fields<'a>(
hasher: &mut Sha256,
tag: u8,
name: &str,
fields: impl ExactSizeIterator<Item = &'a str> + Clone,
budget: &dyn ConstructionBudget,
) -> Result<(), InternalError> {
budget.charge(Resource::PredicateExpressionSteps, name.len() as u64)?;
write_tag(hasher, tag);
write_str(hasher, name);
write_u32(hasher, fields.len() as u32);
for field in fields {
budget.charge(Resource::PredicateExpressionSteps, 1 + field.len() as u64)?;
write_str(hasher, field);
}
Ok(())
}
fn write_values(
hasher: &mut Sha256,
values: &[Value],
budget: &dyn ConstructionBudget,
) -> Result<(), InternalError> {
write_u32(hasher, values.len() as u32);
for value in values {
hasher.update(budget.hash_value(value)?);
}
Ok(())
}
impl AccessPlanProjection<Value> for AccessFingerprintVisitor<'_> {
type Output = Result<(), InternalError>;
fn by_key(&mut self, key: &Value) -> Self::Output {
self.budget.charge(Resource::PredicateExpressionSteps, 1)?;
write_tag(self.hasher, ACCESS_TAG_BY_KEY);
self.hasher.update(self.budget.hash_value(key)?);
Ok(())
}
fn by_keys(&mut self, keys: &[Value]) -> Self::Output {
self.budget.charge(Resource::PredicateExpressionSteps, 1)?;
write_tag(self.hasher, ACCESS_TAG_BY_KEYS);
write_values(self.hasher, keys, self.budget)?;
Ok(())
}
fn key_range(&mut self, start: &Value, end: &Value) -> Self::Output {
self.budget.charge(Resource::PredicateExpressionSteps, 1)?;
write_tag(self.hasher, ACCESS_TAG_KEY_RANGE);
self.hasher.update(self.budget.hash_value(start)?);
self.hasher.update(self.budget.hash_value(end)?);
Ok(())
}
fn index_prefix<'a>(
&mut self,
name: &str,
fields: impl ExactSizeIterator<Item = &'a str> + Clone,
prefix_len: usize,
values: &[Value],
) -> Self::Output {
self.budget.charge(Resource::PredicateExpressionSteps, 1)?;
write_access_fields(
self.hasher,
ACCESS_TAG_INDEX_PREFIX,
name,
fields,
self.budget,
)?;
write_u32(self.hasher, prefix_len as u32);
write_values(self.hasher, values, self.budget)?;
Ok(())
}
fn index_multi_lookup<'a>(
&mut self,
name: &str,
fields: impl ExactSizeIterator<Item = &'a str> + Clone,
values: &[Value],
) -> Self::Output {
self.budget.charge(Resource::PredicateExpressionSteps, 1)?;
write_access_fields(
self.hasher,
ACCESS_TAG_INDEX_MULTI_LOOKUP,
name,
fields,
self.budget,
)?;
write_values(self.hasher, values, self.budget)?;
Ok(())
}
fn index_branch_set<'a>(
&mut self,
name: &str,
fields: impl ExactSizeIterator<Item = &'a str> + Clone,
fixed_values: &[Value],
branch_values: &[Value],
) -> Self::Output {
self.budget.charge(Resource::PredicateExpressionSteps, 1)?;
write_access_fields(
self.hasher,
ACCESS_TAG_INDEX_BRANCH_SET,
name,
fields,
self.budget,
)?;
write_values(self.hasher, fixed_values, self.budget)?;
write_values(self.hasher, branch_values, self.budget)?;
Ok(())
}
fn index_range<'a>(
&mut self,
name: &str,
fields: impl ExactSizeIterator<Item = &'a str> + Clone,
prefix_len: usize,
prefix: &[Value],
lower: &Bound<Value>,
upper: &Bound<Value>,
) -> Self::Output {
self.budget.charge(Resource::PredicateExpressionSteps, 1)?;
write_access_fields(
self.hasher,
ACCESS_TAG_INDEX_RANGE,
name,
fields,
self.budget,
)?;
write_u32(self.hasher, prefix_len as u32);
write_values(self.hasher, prefix, self.budget)?;
write_value_bound(self.hasher, lower, self.budget)?;
write_value_bound(self.hasher, upper, self.budget)?;
Ok(())
}
fn full_scan(&mut self) -> Self::Output {
self.budget.charge(Resource::PredicateExpressionSteps, 1)?;
write_tag(self.hasher, ACCESS_TAG_FULL_SCAN);
Ok(())
}
fn union<T>(
&mut self,
children: &[T],
project: impl Fn(&T, &mut Self) -> Self::Output,
) -> Self::Output {
self.budget.charge(Resource::PredicateExpressionSteps, 1)?;
for child in children {
project(child, self)?;
}
write_tag(self.hasher, ACCESS_TAG_UNION);
write_u32(self.hasher, children.len() as u32);
Ok(())
}
fn intersection<T>(
&mut self,
children: &[T],
project: impl Fn(&T, &mut Self) -> Self::Output,
) -> Self::Output {
self.budget.charge(Resource::PredicateExpressionSteps, 1)?;
for child in children {
project(child, self)?;
}
write_tag(self.hasher, ACCESS_TAG_INTERSECTION);
write_u32(self.hasher, children.len() as u32);
Ok(())
}
}