use crate::types::query::EngineType;
use super::SqlPlan;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PlanCacheEligibility {
Cacheable,
DataDependent,
}
impl PlanCacheEligibility {
pub fn is_cacheable(self) -> bool {
self == Self::Cacheable
}
fn combine(self, other: Self) -> Self {
if self == Self::DataDependent || other == Self::DataDependent {
Self::DataDependent
} else {
Self::Cacheable
}
}
}
impl SqlPlan {
pub fn cache_eligibility(&self) -> PlanCacheEligibility {
use PlanCacheEligibility::{Cacheable, DataDependent};
match self {
Self::PointGet {
engine: EngineType::DocumentSchemaless | EngineType::DocumentStrict,
..
} => DataDependent,
Self::Update {
engine,
target_keys,
..
}
| Self::Delete {
engine,
target_keys,
..
} if !target_keys.is_empty()
&& matches!(
engine,
EngineType::DocumentSchemaless | EngineType::DocumentStrict
) =>
{
DataDependent
}
Self::InsertSelect { source, .. }
| Self::UpdateFrom { source, .. }
| Self::Aggregate { input: source, .. }
| Self::Merge { source, .. } => source.cache_eligibility(),
Self::Join { left, right, .. }
| Self::Intersect { left, right, .. }
| Self::Except { left, right, .. } => {
left.cache_eligibility().combine(right.cache_eligibility())
}
Self::Union { inputs, .. } => inputs.iter().fold(Cacheable, |eligibility, input| {
eligibility.combine(input.cache_eligibility())
}),
Self::Cte { definitions, outer } => definitions
.iter()
.fold(outer.cache_eligibility(), |eligibility, (_, plan)| {
eligibility.combine(plan.cache_eligibility())
}),
Self::LateralTopK { outer, .. } => outer.cache_eligibility(),
Self::LateralLoop { outer, inner, .. } => {
outer.cache_eligibility().combine(inner.cache_eligibility())
}
Self::ConstantResult { .. }
| Self::Scan { .. }
| Self::PointGet { .. }
| Self::DocumentIndexLookup { .. }
| Self::RangeScan { .. }
| Self::Insert { .. }
| Self::KvInsert { .. }
| Self::Upsert { .. }
| Self::Update { .. }
| Self::Delete { .. }
| Self::Truncate { .. }
| Self::TimeseriesScan { .. }
| Self::TimeseriesIngest { .. }
| Self::VectorSearch { .. }
| Self::MultiVectorSearch { .. }
| Self::SparseSearch { .. }
| Self::TextSearch { .. }
| Self::HybridSearch { .. }
| Self::HybridSearchTriple { .. }
| Self::SpatialScan { .. }
| Self::RecursiveScan { .. }
| Self::RecursiveValue { .. }
| Self::CreateArray { .. }
| Self::DropArray { .. }
| Self::AlterArray { .. }
| Self::InsertArray { .. }
| Self::DeleteArray { .. }
| Self::ArraySlice { .. }
| Self::ArrayProject { .. }
| Self::ArrayAgg { .. }
| Self::ArrayElementwise { .. }
| Self::ArrayFlush { .. }
| Self::ArrayCompact { .. }
| Self::VectorPrimaryInsert { .. }
| Self::CreateIndex { .. }
| Self::DropIndex { .. } => Cacheable,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types_expr::SqlValue;
fn point_get(engine: EngineType) -> SqlPlan {
SqlPlan::PointGet {
collection: "docs".into(),
alias: None,
engine,
key_column: "id".into(),
key_value: SqlValue::String("k".into()),
projection: Vec::new(),
}
}
fn update(engine: EngineType, target_keys: Vec<SqlValue>) -> SqlPlan {
SqlPlan::Update {
collection: "docs".into(),
engine,
assignments: Vec::new(),
filters: Vec::new(),
target_keys,
returning: false,
}
}
fn delete(engine: EngineType, target_keys: Vec<SqlValue>) -> SqlPlan {
SqlPlan::Delete {
collection: "docs".into(),
engine,
filters: Vec::new(),
target_keys,
}
}
#[test]
fn document_point_get_is_data_dependent() {
assert_eq!(
point_get(EngineType::DocumentStrict).cache_eligibility(),
PlanCacheEligibility::DataDependent
);
}
#[test]
fn key_value_point_get_is_cacheable() {
assert_eq!(
point_get(EngineType::KeyValue).cache_eligibility(),
PlanCacheEligibility::Cacheable
);
}
#[test]
fn document_point_update_is_data_dependent() {
assert_eq!(
update(
EngineType::DocumentSchemaless,
vec![SqlValue::String("k".into())]
)
.cache_eligibility(),
PlanCacheEligibility::DataDependent
);
}
#[test]
fn document_predicate_update_is_cacheable() {
assert_eq!(
update(EngineType::DocumentStrict, Vec::new()).cache_eligibility(),
PlanCacheEligibility::Cacheable
);
}
#[test]
fn document_point_delete_is_data_dependent() {
assert_eq!(
delete(
EngineType::DocumentStrict,
vec![SqlValue::String("k".into())]
)
.cache_eligibility(),
PlanCacheEligibility::DataDependent
);
}
#[test]
fn nested_point_dependency_propagates() {
let plan = SqlPlan::Cte {
definitions: vec![("selected".into(), point_get(EngineType::DocumentStrict))],
outer: Box::new(point_get(EngineType::KeyValue)),
};
assert_eq!(
plan.cache_eligibility(),
PlanCacheEligibility::DataDependent
);
}
}