pub(crate) mod compile;
#[cfg(test)]
mod tests;
use crate::{
db::index::{EncodedValue, IndexKey},
db::predicate::Predicate,
error::InternalError,
model::index::IndexModel,
value::Value,
};
use std::cell::Cell;
pub(crate) use compile::{
IndexCompilePolicy, compile_index_program, compile_index_program_for_targets,
};
pub(in crate::db) fn canonical_index_predicate(index: &IndexModel) -> Option<&'static Predicate> {
index.predicate_semantics()
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) enum IndexPredicateProgram {
True,
False,
And(Vec<Self>),
Or(Vec<Self>),
Not(Box<Self>),
Compare {
component_index: usize,
op: IndexCompareOp,
literal: IndexLiteral,
},
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum IndexCompareOp {
Eq,
Ne,
Lt,
Lte,
Gt,
Gte,
In,
NotIn,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) enum IndexLiteral {
One(Vec<u8>),
Many(Vec<Vec<u8>>),
}
pub(crate) fn eval_index_compare(
component: &[u8],
op: IndexCompareOp,
literal: &IndexLiteral,
) -> bool {
match (op, literal) {
(IndexCompareOp::Eq, IndexLiteral::One(expected)) => component == expected.as_slice(),
(IndexCompareOp::Ne, IndexLiteral::One(expected)) => component != expected.as_slice(),
(IndexCompareOp::Lt, IndexLiteral::One(expected)) => component < expected.as_slice(),
(IndexCompareOp::Lte, IndexLiteral::One(expected)) => component <= expected.as_slice(),
(IndexCompareOp::Gt, IndexLiteral::One(expected)) => component > expected.as_slice(),
(IndexCompareOp::Gte, IndexLiteral::One(expected)) => component >= expected.as_slice(),
(IndexCompareOp::In, IndexLiteral::Many(candidates)) => {
candidates.iter().any(|candidate| component == candidate)
}
(IndexCompareOp::NotIn, IndexLiteral::Many(candidates)) => {
candidates.iter().all(|candidate| component != candidate)
}
(
IndexCompareOp::Eq
| IndexCompareOp::Ne
| IndexCompareOp::Lt
| IndexCompareOp::Lte
| IndexCompareOp::Gt
| IndexCompareOp::Gte,
IndexLiteral::Many(_),
)
| (IndexCompareOp::In | IndexCompareOp::NotIn, IndexLiteral::One(_)) => false,
}
}
#[derive(Clone, Copy)]
pub(in crate::db) struct IndexPredicateExecution<'a> {
pub(in crate::db) program: &'a IndexPredicateProgram,
pub(in crate::db) rejected_keys_counter: Option<&'a Cell<u64>>,
}
pub(in crate::db) fn eval_index_program_on_decoded_key(
key: &IndexKey,
program: &IndexPredicateProgram,
) -> Result<bool, InternalError> {
match program {
IndexPredicateProgram::True => Ok(true),
IndexPredicateProgram::False => Ok(false),
IndexPredicateProgram::And(children) => {
for child in children {
if !eval_index_program_on_decoded_key(key, child)? {
return Ok(false);
}
}
Ok(true)
}
IndexPredicateProgram::Or(children) => {
for child in children {
if eval_index_program_on_decoded_key(key, child)? {
return Ok(true);
}
}
Ok(false)
}
IndexPredicateProgram::Not(inner) => Ok(!eval_index_program_on_decoded_key(key, inner)?),
IndexPredicateProgram::Compare {
component_index,
op,
literal,
} => {
let Some(component) = key.component(*component_index) else {
return Err(InternalError::index_only_predicate_component_required());
};
Ok(eval_index_compare(component, *op, literal))
}
}
}
pub(in crate::db) fn eval_index_execution_on_decoded_key(
key: &IndexKey,
execution: IndexPredicateExecution<'_>,
) -> Result<bool, InternalError> {
let passed = eval_index_program_on_decoded_key(key, execution.program)?;
if !passed && let Some(counter) = execution.rejected_keys_counter {
counter.set(counter.get().saturating_add(1));
}
Ok(passed)
}
#[must_use]
pub(in crate::db) fn literal_index_component_bytes(value: &Value) -> Option<Vec<u8>> {
let encoded = EncodedValue::try_from_ref(value).ok()?;
Some(encoded.encoded().to_vec())
}