pub struct PlanCache {
pub hits: u64,
pub misses: u64,
/* private fields */
}Expand description
LRU-ish plan cache keyed by canonical query hash.
Mission F: uses FxHashMap. The keys are u64 hashes (already pre-hashed
by canonicalize), so SipHash is pure overhead — Fx is much cheaper for
the integer-keyed lookup.
Fields§
§hits: u64§misses: u64Implementations§
Source§impl PlanCache
impl PlanCache
pub fn new(capacity: usize) -> Self
Sourcepub fn insert(&mut self, hash: u64, plan: PlanNode, source_literal_count: usize)
pub fn insert(&mut self, hash: u64, plan: PlanNode, source_literal_count: usize)
Store a planned query under its canonical hash. The plan can have any literal values inside it — those will be overwritten on hit.
source_literal_count is the number of literals
crate::canonicalize::canonicalize collected from the query text.
The cache only works because, on a hit, every collected literal maps
1:1 to a substitutable slot the substitute_plan walk can reach.
If those counts disagree, the plan has literals the walk cannot
re-bind — the one case today is a subquery (in (<subquery>) /
exists (...)), whose inner literals canonicalize collects at the
token level but which live in an un-walked QueryExpr AST inside the
predicate. Caching such a plan would serve the first call’s inner
literal to every later same-shape call: silent wrong rows in release,
a substitution-count assert in debug (issue #137). We refuse to cache
it; the engine then plans from source on every call, which is always
correct. This check runs only on the populating miss, so the hot
hit-path pays nothing.
Sourcepub fn get_with_substitution(
&mut self,
hash: u64,
literals: &[Literal],
) -> Option<PlanNode>
pub fn get_with_substitution( &mut self, hash: u64, literals: &[Literal], ) -> Option<PlanNode>
Look up a plan by canonical hash and return a clone with the new
literals substituted into every Expr::Literal slot in source
order.
Returns Some(plan) on a hit and bumps self.hits. Returns None
on a miss and bumps self.misses. Returning None instead of
reaching for the planner here keeps this module dependency-free
from planner — the engine handles the miss path.
The substitution is done on a clone of the cached plan, not the stored copy. The cached plan stays pristine for the next call.