pub(crate) mod compile;
#[cfg(test)]
mod tests;
use crate::{
db::index::{EncodedValue, IndexKey},
db::predicate::{Predicate, SqlParseError, parse_sql_predicate},
error::InternalError,
model::index::IndexModel,
value::Value,
};
use std::{
cell::Cell,
sync::{Mutex, OnceLock},
};
pub(crate) use compile::{
IndexCompilePolicy, compile_index_program, compile_index_program_for_targets,
};
type CachedIndexPredicateResult = Result<&'static Predicate, SqlParseError>;
type CachedIndexPredicateEntries = Vec<(&'static str, CachedIndexPredicateResult)>;
static INDEX_PREDICATE_SQL_CACHE: OnceLock<Mutex<CachedIndexPredicateEntries>> = OnceLock::new();
pub(in crate::db) fn canonical_index_predicate(
index: &IndexModel,
) -> Result<Option<&'static Predicate>, SqlParseError> {
let Some(predicate_sql) = index.predicate() else {
return Ok(None);
};
cached_index_predicate_from_sql(predicate_sql).map(Some)
}
fn cached_index_predicate_from_sql(
predicate_sql: &'static str,
) -> Result<&'static Predicate, SqlParseError> {
let cache =
INDEX_PREDICATE_SQL_CACHE.get_or_init(|| Mutex::new(CachedIndexPredicateEntries::new()));
if let Some(cached) = cache
.lock()
.expect("index predicate cache mutex poisoned")
.iter()
.find(|(cached_sql, _)| *cached_sql == predicate_sql)
.map(|(_, cached)| cached.clone())
{
return cached;
}
let parsed: CachedIndexPredicateResult = parse_sql_predicate(predicate_sql).map(|predicate| {
let predicate: &'static Predicate = Box::leak(Box::new(predicate));
predicate
});
let mut guard = cache.lock().expect("index predicate cache mutex poisoned");
if let Some((_, cached)) = guard
.iter()
.find(|(cached_sql, _)| *cached_sql == predicate_sql)
{
return cached.clone();
}
guard.push((predicate_sql, parsed.clone()));
parsed
}
#[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())
}