query_lang/database.rs
1//! The incremental engine: input storage, memoization, and validation.
2
3use alloc::collections::BTreeMap;
4use alloc::vec::Vec;
5use core::cell::{Cell, RefCell};
6use core::fmt;
7
8use crate::error::QueryError;
9use crate::revision::Revision;
10use crate::stats::Stats;
11use crate::system::System;
12
13/// A stored input: a value a consumer placed into the database directly.
14struct Input<V> {
15 value: V,
16 /// The revision at which this input last took a new value.
17 changed_at: Revision,
18}
19
20/// The memo table: derived key to its cached result and dependency record.
21type MemoMap<S> = BTreeMap<<S as System>::Key, Memo<<S as System>::Key, <S as System>::Value>>;
22
23/// A memoized derived query: its cached value, what it read, and when.
24struct Memo<K, V> {
25 value: V,
26 /// The queries this value was computed from, in read order. Re-examined to
27 /// decide whether a stale memo can be reused.
28 deps: Vec<K>,
29 /// The revision at which `value` last became a *different* value. Early
30 /// cutoff keeps this stamp old when a recomputation produces the same value.
31 changed_at: Revision,
32 /// The revision at which this memo was last confirmed current.
33 verified_at: Revision,
34}
35
36/// An incremental query database: the store of inputs and the cache of derived
37/// results, with automatic dependency tracking and invalidation.
38///
39/// This is the engine. A consumer defines its queries once by implementing
40/// [`System`], hands the system to [`new`](Self::new), seeds the base facts with
41/// [`set`](Self::set), and asks for results with [`get`](Self::get). Everything
42/// between — remembering what each query read, noticing when an input makes a
43/// cached result stale, recomputing only what actually changed — is the
44/// database's job.
45///
46/// # How it stays correct and fast
47///
48/// The database holds a [`Revision`] clock that advances by one each time an
49/// input takes a new value. Every cached query records two stamps: when it was
50/// last *verified* against the clock, and when its value last *changed*. Asking
51/// for a query takes one of three paths, counted in [`Stats`]:
52///
53/// - **Hit** — the query was already verified at the current revision; its value
54/// is returned without touching its dependencies.
55/// - **Validated** — the query is stale, but re-examining its recorded
56/// dependencies shows none of them changed since the query was last verified,
57/// so the cached value is reused. When a dependency *did* recompute but to the
58/// same value, its change stamp stays old and dependents are validated rather
59/// than recomputed. This *early cutoff* is what stops a local edit from
60/// cascading through the whole graph.
61/// - **Computed** — a genuine miss, or a dependency that truly changed; the
62/// query's [`compute`](System::compute) runs and the new value is cached.
63///
64/// Because dependencies are recorded during computation rather than declared up
65/// front, a query that branches on its inputs is tracked exactly: it depends on
66/// what it actually read on the last run, and nothing more.
67///
68/// # Single-threaded by design
69///
70/// A `Database` is not `Sync`: query resolution walks a shared cache and a
71/// dependency stack through interior mutability, which is correct and allocation-
72/// light on one thread and carries no atomic overhead. Drive one database from
73/// one thread; run independent databases on separate threads for parallelism.
74///
75/// # Examples
76///
77/// A three-layer computation — an input, a query over it, and a query over that
78/// — recomputes only along the path an edit touches:
79///
80/// ```
81/// use query_lang::{Database, System, QueryError};
82///
83/// #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
84/// enum Key {
85/// Width, // an input
86/// Doubled, // = Width * 2
87/// Labeled, // = "w=" + Doubled
88/// }
89///
90/// struct Layout;
91/// impl System for Layout {
92/// type Key = Key;
93/// type Value = String;
94/// fn compute(&self, db: &Database<Self>, key: &Key) -> Result<String, QueryError> {
95/// match key {
96/// Key::Width => Ok("0".into()),
97/// Key::Doubled => {
98/// let w: i64 = db.get(&Key::Width)?.parse().unwrap_or(0);
99/// Ok((w * 2).to_string())
100/// }
101/// Key::Labeled => Ok(format!("w={}", db.get(&Key::Doubled)?)),
102/// }
103/// }
104/// }
105///
106/// let mut db = Database::new(Layout);
107/// db.set(Key::Width, "10".into());
108/// assert_eq!(db.get(&Key::Labeled)?, "w=20");
109/// assert_eq!(db.stats().computed, 2); // Width is a set input; Doubled and Labeled ran
110///
111/// // Re-ask without changing anything: a free hit, no recomputation.
112/// assert_eq!(db.get(&Key::Labeled)?, "w=20");
113/// assert_eq!(db.stats().hits, 1);
114/// # Ok::<(), QueryError>(())
115/// ```
116pub struct Database<S: System> {
117 system: S,
118 revision: Revision,
119 inputs: BTreeMap<S::Key, Input<S::Value>>,
120 memos: RefCell<MemoMap<S>>,
121 /// Dependency-collection stack. Each active `compute` pushes a frame; every
122 /// [`get`](Self::get) call appends the key it read to the top frame.
123 frames: RefCell<Vec<Vec<S::Key>>>,
124 /// The keys currently being resolved, for cycle detection and the depth
125 /// limit. A key is on this stack while its `compute` runs and while its
126 /// recorded dependencies are being revalidated.
127 active: RefCell<Vec<S::Key>>,
128 /// The most keys that may be on the `active` stack at once, if limited.
129 max_depth: Option<usize>,
130 computed: Cell<u64>,
131 validated: Cell<u64>,
132 hits: Cell<u64>,
133}
134
135impl<S: System> Database<S> {
136 /// Create an empty database for the given query system.
137 ///
138 /// The database starts at the initial revision with no inputs and an empty
139 /// cache. Seed inputs with [`set`](Self::set) before asking for derived
140 /// queries.
141 ///
142 /// # Examples
143 ///
144 /// ```
145 /// use query_lang::{Database, System, QueryError};
146 ///
147 /// struct S;
148 /// impl System for S {
149 /// type Key = u32;
150 /// type Value = u32;
151 /// fn compute(&self, _db: &Database<Self>, k: &u32) -> Result<u32, QueryError> { Ok(*k) }
152 /// }
153 ///
154 /// let db = Database::new(S);
155 /// assert_eq!(db.stats().total(), 0);
156 /// ```
157 #[must_use]
158 pub fn new(system: S) -> Self {
159 Self {
160 system,
161 revision: Revision::START,
162 inputs: BTreeMap::new(),
163 memos: RefCell::new(BTreeMap::new()),
164 frames: RefCell::new(Vec::new()),
165 active: RefCell::new(Vec::new()),
166 max_depth: None,
167 computed: Cell::new(0),
168 validated: Cell::new(0),
169 hits: Cell::new(0),
170 }
171 }
172
173 /// Limit how deep one resolution may go, and return the database.
174 ///
175 /// Every query that is computing or revalidating its dependencies takes one
176 /// level, and each level uses some native stack. A long chain of queries
177 /// that each read the next one can therefore overflow the thread's stack.
178 /// With a limit set, a [`get`](Self::get) that would go deeper than `depth`
179 /// levels returns [`QueryError::TooDeep`] instead. The database stays usable
180 /// afterwards.
181 ///
182 /// There is no limit by default. Pick one that fits the stack of the thread
183 /// that drives the database and the stack your `compute` uses per call.
184 ///
185 /// # Examples
186 ///
187 /// ```
188 /// use query_lang::{Database, System, QueryError};
189 ///
190 /// struct Chain;
191 /// impl System for Chain {
192 /// type Key = u32;
193 /// type Value = u32;
194 /// fn compute(&self, db: &Database<Self>, k: &u32) -> Result<u32, QueryError> {
195 /// if *k == 0 { Ok(0) } else { Ok(db.get(&(k - 1))? + 1) }
196 /// }
197 /// }
198 ///
199 /// let db = Database::new(Chain).with_max_depth(200);
200 /// assert_eq!(db.get(&100), Ok(100));
201 /// assert_eq!(db.get(&5_000), Err(QueryError::TooDeep));
202 /// ```
203 #[must_use]
204 pub fn with_max_depth(mut self, depth: usize) -> Self {
205 self.max_depth = Some(depth);
206 self
207 }
208
209 /// The depth limit set with [`with_max_depth`](Self::with_max_depth), if any.
210 #[must_use]
211 #[inline]
212 pub const fn max_depth(&self) -> Option<usize> {
213 self.max_depth
214 }
215
216 /// Set an input to a value, advancing the revision if the value changed.
217 ///
218 /// This is the only way a value enters the database from outside. Once set, a
219 /// key is an input: [`get`](Self::get) returns the stored value directly and
220 /// [`compute`](System::compute) is never called for it. Setting the *same*
221 /// value a key already holds is a no-op — the revision does not advance, and
222 /// nothing that depends on the input is invalidated, so re-feeding unchanged
223 /// facts costs nothing downstream. Setting a *different* value advances the
224 /// revision, which is what later marks dependent queries stale.
225 ///
226 /// Setting a key that previously held a derived (computed) value promotes it
227 /// to an input and discards the stale cached result.
228 ///
229 /// Taking `&mut self` is deliberate: mutating an input is the one operation
230 /// that can invalidate cached results, so it is kept distinct from the shared
231 /// `&self` reads that [`get`](Self::get) performs.
232 ///
233 /// # Examples
234 ///
235 /// ```
236 /// use query_lang::{Database, System, QueryError};
237 ///
238 /// struct Echo;
239 /// impl System for Echo {
240 /// type Key = u32;
241 /// type Value = i64;
242 /// fn compute(&self, db: &Database<Self>, k: &u32) -> Result<i64, QueryError> {
243 /// Ok(db.get(&(k + 1))? + 1) // reads input at k+1
244 /// }
245 /// }
246 ///
247 /// let mut db = Database::new(Echo);
248 /// db.set(1, 41);
249 /// let r0 = db.revision();
250 ///
251 /// db.set(1, 41); // same value
252 /// assert_eq!(db.revision(), r0); // no change, clock still
253 ///
254 /// db.set(1, 99); // new value
255 /// assert!(db.revision() > r0); // clock advanced
256 /// ```
257 pub fn set(&mut self, key: S::Key, value: S::Value) {
258 // If this key cached a derived value, it is becoming an input; drop the
259 // stale memo so the input and a leftover cache entry can never disagree.
260 self.memos.get_mut().remove(&key);
261
262 match self.inputs.get_mut(&key) {
263 Some(existing) if existing.value == value => {
264 // Same value: not a real change. Leave the revision untouched so
265 // dependents stay valid.
266 }
267 Some(existing) => {
268 self.revision = self.revision.next();
269 existing.value = value;
270 existing.changed_at = self.revision;
271 }
272 None => {
273 self.revision = self.revision.next();
274 let changed_at = self.revision;
275 self.inputs.insert(key, Input { value, changed_at });
276 }
277 }
278 }
279
280 /// Resolve a query to its value, computing and caching it as needed.
281 ///
282 /// If `key` is a set input, its value is returned directly. Otherwise the
283 /// query is derived: a valid cached value is reused (a hit or an early-cutoff
284 /// validation), and only a real miss or a genuinely changed dependency runs
285 /// [`compute`](System::compute). Call this both from application code and,
286 /// from inside a `compute`, to read the queries a result depends on — reads
287 /// through `get` are exactly what the engine records as dependencies.
288 ///
289 /// # Errors
290 ///
291 /// Returns [`QueryError::Cycle`] if resolving `key` requires a value that is
292 /// still being computed further up the call chain — that is, if the query
293 /// graph has a cycle.
294 ///
295 /// # Examples
296 ///
297 /// ```
298 /// use query_lang::{Database, System, QueryError};
299 ///
300 /// struct Fib;
301 /// impl System for Fib {
302 /// type Key = u64;
303 /// type Value = u64;
304 /// fn compute(&self, db: &Database<Self>, n: &u64) -> Result<u64, QueryError> {
305 /// // Memoized Fibonacci: each fib(n) is computed once and cached.
306 /// if *n < 2 { return Ok(*n); }
307 /// Ok(db.get(&(n - 1))?.wrapping_add(db.get(&(n - 2))?))
308 /// }
309 /// }
310 ///
311 /// let db = Database::new(Fib);
312 /// assert_eq!(db.get(&50)?, 12586269025);
313 /// # Ok::<(), QueryError>(())
314 /// ```
315 pub fn get(&self, key: &S::Key) -> Result<S::Value, QueryError> {
316 // Record this read as a dependency of the query currently computing, if
317 // any. Reads made outside a `compute` (top-level queries) have no frame.
318 if let Some(frame) = self.frames.borrow_mut().last_mut() {
319 frame.push(key.clone());
320 }
321 let (value, _changed_at) = self.eval(key)?;
322 Ok(value)
323 }
324
325 /// The current revision of the database.
326 ///
327 /// Advances by one each time [`set`](Self::set) gives an input a new value.
328 /// Useful for asserting in tests that an operation did (or did not) change any
329 /// input, and for correlating cache behaviour with input edits in logs.
330 #[must_use]
331 #[inline]
332 pub const fn revision(&self) -> Revision {
333 self.revision
334 }
335
336 /// A snapshot of the cumulative resolution counters.
337 ///
338 /// See [`Stats`] for what each counter means. Snapshot before and after an
339 /// operation and subtract to measure exactly what that operation cost.
340 ///
341 /// # Examples
342 ///
343 /// ```
344 /// use query_lang::{Database, System, QueryError};
345 ///
346 /// struct S;
347 /// impl System for S {
348 /// type Key = u32;
349 /// type Value = u32;
350 /// fn compute(&self, _db: &Database<Self>, k: &u32) -> Result<u32, QueryError> { Ok(*k) }
351 /// }
352 ///
353 /// let db = Database::new(S);
354 /// let before = db.stats();
355 /// let _ = db.get(&5)?;
356 /// let after = db.stats();
357 /// assert_eq!(after.computed - before.computed, 1);
358 /// # Ok::<(), QueryError>(())
359 /// ```
360 #[must_use]
361 pub fn stats(&self) -> Stats {
362 Stats {
363 computed: self.computed.get(),
364 validated: self.validated.get(),
365 hits: self.hits.get(),
366 }
367 }
368
369 /// A shared reference to the query system backing this database.
370 #[must_use]
371 #[inline]
372 pub const fn system(&self) -> &S {
373 &self.system
374 }
375
376 /// Ensure `key` is current and return its value together with the revision at
377 /// which that value last changed.
378 ///
379 /// This is the resolution core. Unlike [`get`](Self::get) it records no
380 /// dependency — it is called both for the requested key and, during
381 /// revalidation, for each recorded dependency, and only genuine reads through
382 /// `get` should count as dependencies.
383 fn eval(&self, key: &S::Key) -> Result<(S::Value, Revision), QueryError> {
384 // Inputs are always current at their own change stamp.
385 if let Some(input) = self.inputs.get(key) {
386 return Ok((input.value.clone(), input.changed_at));
387 }
388
389 // Fast path: a memo already verified at the current revision.
390 {
391 let memos = self.memos.borrow();
392 if let Some(memo) = memos.get(key) {
393 if memo.verified_at == self.revision {
394 self.hits.set(self.hits.get().saturating_add(1));
395 return Ok((memo.value.clone(), memo.changed_at));
396 }
397 }
398 }
399
400 // Stale memo: snapshot what revalidation needs, then release the borrow —
401 // checking dependencies recurses back through `eval`, which borrows again.
402 let snapshot = self
403 .memos
404 .borrow()
405 .get(key)
406 .map(|m| (m.deps.clone(), m.verified_at, m.value.clone(), m.changed_at));
407
408 if let Some((deps, verified_at, value, changed_at)) = snapshot {
409 // The key stays on the active stack while its dependencies are
410 // checked. Memos can record dependencies that form a cycle (a query
411 // that caught a `Cycle` error from a dependency and fell back to a
412 // default), and without this mark revalidating them would recurse
413 // forever.
414 let inputs_changed = {
415 let _active = self.enter(key)?;
416 let mut changed = false;
417 for dep in &deps {
418 match self.eval(dep) {
419 Ok((_dep_value, dep_changed_at)) if dep_changed_at <= verified_at => {}
420 // A dependency that changed, or that now fails, means the
421 // cached value cannot be trusted: run `compute` again and
422 // let it decide how to handle the failure.
423 Ok(_) | Err(_) => {
424 changed = true;
425 break;
426 }
427 }
428 }
429 changed
430 };
431 if !inputs_changed {
432 // Early cutoff: nothing this query read actually changed. Reuse
433 // the cached value; only its verification stamp moves forward.
434 if let Some(memo) = self.memos.borrow_mut().get_mut(key) {
435 memo.verified_at = self.revision;
436 }
437 self.validated.set(self.validated.get().saturating_add(1));
438 return Ok((value, changed_at));
439 }
440 return self.recompute(key, Some((value, changed_at)));
441 }
442
443 // No memo at all: compute from scratch.
444 self.recompute(key, None)
445 }
446
447 /// Run `compute` for `key`, tracking the dependencies it reads and caching
448 /// the result. `previous`, when present, is the memo's prior value and change
449 /// stamp, used for early cutoff.
450 fn recompute(
451 &self,
452 key: &S::Key,
453 previous: Option<(S::Value, Revision)>,
454 ) -> Result<(S::Value, Revision), QueryError> {
455 // Cycle detection and the depth limit: `enter` refuses a key already on
456 // the active stack. The guards unwind both bookkeeping stacks whether
457 // `compute` succeeds, fails, or panics.
458 let (result, deps) = {
459 let _active = self.enter(key)?;
460 self.frames.borrow_mut().push(Vec::new());
461 let _frame = FrameGuard(&self.frames);
462 let result = self.system.compute(self, key);
463 let deps = self
464 .frames
465 .borrow_mut()
466 .last_mut()
467 .map(core::mem::take)
468 .unwrap_or_default();
469 (result, deps)
470 };
471
472 let value = result?;
473
474 // Early cutoff: if the recomputed value equals the old one, keep the old
475 // change stamp so dependents see "unchanged" and are not recomputed.
476 let changed_at = match previous {
477 Some((old_value, old_changed_at)) if old_value == value => old_changed_at,
478 _ => self.revision,
479 };
480
481 self.computed.set(self.computed.get().saturating_add(1));
482 let memo = Memo {
483 value: value.clone(),
484 deps,
485 changed_at,
486 verified_at: self.revision,
487 };
488 self.memos.borrow_mut().insert(key.clone(), memo);
489
490 Ok((value, changed_at))
491 }
492}
493
494impl<S: System> Database<S> {
495 /// Put `key` on the active stack, or fail if it is already there (a cycle)
496 /// or the stack is at the depth limit. The returned guard pops it again,
497 /// also when `compute` panics.
498 fn enter(&self, key: &S::Key) -> Result<ActiveGuard<'_, S::Key>, QueryError> {
499 let mut active = self.active.borrow_mut();
500 if active.iter().any(|k| k == key) {
501 return Err(QueryError::Cycle);
502 }
503 if self.max_depth.is_some_and(|max| active.len() >= max) {
504 return Err(QueryError::TooDeep);
505 }
506 active.push(key.clone());
507 Ok(ActiveGuard(&self.active))
508 }
509}
510
511/// Pops the active stack when dropped.
512struct ActiveGuard<'a, K>(&'a RefCell<Vec<K>>);
513
514impl<K> Drop for ActiveGuard<'_, K> {
515 fn drop(&mut self) {
516 self.0.borrow_mut().pop();
517 }
518}
519
520/// Pops the dependency-frame stack when dropped.
521struct FrameGuard<'a, K>(&'a RefCell<Vec<Vec<K>>>);
522
523impl<K> Drop for FrameGuard<'_, K> {
524 fn drop(&mut self) {
525 self.0.borrow_mut().pop();
526 }
527}
528
529impl<S: System> fmt::Debug for Database<S> {
530 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
531 f.debug_struct("Database")
532 .field("revision", &self.revision)
533 .field("inputs", &self.inputs.len())
534 .field("memos", &self.memos.borrow().len())
535 .field("stats", &self.stats())
536 .finish()
537 }
538}
539
540#[cfg(test)]
541#[allow(clippy::unwrap_used)]
542mod tests {
543 use super::*;
544 use alloc::string::{String, ToString};
545 use core::cell::Cell as StdCell;
546
547 /// A system whose `compute` counts how many times each key ran, so tests can
548 /// assert exactly which queries recomputed.
549 struct Counting {
550 // key -> number of times computed
551 runs: StdCell<[u32; 8]>,
552 }
553
554 impl Counting {
555 fn new() -> Self {
556 Self {
557 runs: StdCell::new([0; 8]),
558 }
559 }
560
561 fn runs_of(&self, key: usize) -> u32 {
562 self.runs.get()[key]
563 }
564
565 fn bump(&self, key: usize) {
566 let mut r = self.runs.get();
567 r[key] += 1;
568 self.runs.set(r);
569 }
570 }
571
572 // Key layout: 0 = input A, 1 = input B, 2 = A*10, 3 = (A*10)+(B), 4 = sign of 2
573 impl System for Counting {
574 type Key = usize;
575 type Value = i64;
576 fn compute(&self, db: &Database<Self>, key: &usize) -> Result<i64, QueryError> {
577 self.bump(*key);
578 match key {
579 0 | 1 => Ok(0), // default when the input was never set
580 2 => Ok(db.get(&0)? * 10),
581 3 => Ok(db.get(&2)? + db.get(&1)?),
582 4 => Ok(db.get(&2)?.signum()),
583 _ => Ok(0),
584 }
585 }
586 }
587
588 #[test]
589 fn test_input_get_returns_set_value() {
590 let mut db = Database::new(Counting::new());
591 db.set(0, 7);
592 assert_eq!(db.get(&0).unwrap(), 7);
593 // Inputs never invoke compute.
594 assert_eq!(db.system().runs_of(0), 0);
595 }
596
597 #[test]
598 fn test_first_get_computes_once() {
599 let mut db = Database::new(Counting::new());
600 db.set(0, 3);
601 assert_eq!(db.get(&2).unwrap(), 30);
602 assert_eq!(db.system().runs_of(2), 1);
603 // Only key 2 computed; key 0 is a set input and never runs compute.
604 assert_eq!(db.stats().computed, 1);
605 }
606
607 #[test]
608 fn test_second_get_is_a_hit() {
609 let mut db = Database::new(Counting::new());
610 db.set(0, 3);
611 assert_eq!(db.get(&2).unwrap(), 30);
612 let before = db.stats();
613 assert_eq!(db.get(&2).unwrap(), 30);
614 let after = db.stats();
615 assert_eq!(after.hits - before.hits, 1);
616 assert_eq!(after.computed, before.computed); // no recompute
617 }
618
619 #[test]
620 fn test_input_change_recomputes_dependents() {
621 let mut db = Database::new(Counting::new());
622 db.set(0, 3);
623 assert_eq!(db.get(&2).unwrap(), 30);
624 db.set(0, 4);
625 assert_eq!(db.get(&2).unwrap(), 40);
626 assert_eq!(db.system().runs_of(2), 2);
627 }
628
629 #[test]
630 fn test_unchanged_input_set_does_not_recompute() {
631 let mut db = Database::new(Counting::new());
632 db.set(0, 3);
633 assert_eq!(db.get(&2).unwrap(), 30);
634 db.set(0, 3); // same value -> no revision bump
635 assert_eq!(db.get(&2).unwrap(), 30);
636 // Still verified at the current revision: a hit, not a recompute.
637 assert_eq!(db.system().runs_of(2), 1);
638 }
639
640 #[test]
641 fn test_multilayer_hit_after_no_change() {
642 let mut db = Database::new(Counting::new());
643 db.set(0, 3);
644 // key 4 = signum(A*10); depends on key 2, which depends on input 0.
645 assert_eq!(db.get(&4).unwrap(), 1);
646 assert_eq!(db.system().runs_of(4), 1);
647 assert_eq!(db.system().runs_of(2), 1);
648
649 // Re-setting the input to the same value does not advance the revision,
650 // so the next query is a plain hit and nothing recomputes.
651 db.set(0, 3);
652 assert_eq!(db.get(&4).unwrap(), 1);
653 assert_eq!(db.system().runs_of(4), 1);
654 assert_eq!(db.system().runs_of(2), 1);
655 }
656
657 #[test]
658 fn test_early_cutoff_when_intermediate_value_is_stable() {
659 // A system where an input change leaves an intermediate query's value
660 // unchanged, so the top query is validated, not recomputed.
661 struct AbsChain {
662 top_runs: StdCell<u32>,
663 }
664 #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
665 enum K {
666 In,
667 Abs,
668 Plus1,
669 }
670 impl System for AbsChain {
671 type Key = K;
672 type Value = i64;
673 fn compute(&self, db: &Database<Self>, key: &K) -> Result<i64, QueryError> {
674 match key {
675 K::In => Ok(0),
676 K::Abs => Ok(db.get(&K::In)?.abs()),
677 K::Plus1 => {
678 self.top_runs.set(self.top_runs.get() + 1);
679 Ok(db.get(&K::Abs)? + 1)
680 }
681 }
682 }
683 }
684
685 let mut db = Database::new(AbsChain {
686 top_runs: StdCell::new(0),
687 });
688 db.set(K::In, 5);
689 assert_eq!(db.get(&K::Plus1).unwrap(), 6);
690 assert_eq!(db.system().top_runs.get(), 1);
691
692 // -5 and 5 have the same abs, so Abs recomputes to the same value and
693 // Plus1 is validated (early cutoff), not recomputed.
694 db.set(K::In, -5);
695 assert_eq!(db.get(&K::Plus1).unwrap(), 6);
696 assert_eq!(db.system().top_runs.get(), 1);
697 assert!(db.stats().validated >= 1);
698 }
699
700 #[test]
701 fn test_default_value_when_input_unset() {
702 let db = Database::new(Counting::new());
703 // Input 0 was never set; compute(0) returns its default of 0.
704 assert_eq!(db.get(&2).unwrap(), 0);
705 }
706
707 #[test]
708 fn test_direct_self_cycle_is_error() {
709 struct SelfDep;
710 impl System for SelfDep {
711 type Key = u32;
712 type Value = u32;
713 fn compute(&self, db: &Database<Self>, k: &u32) -> Result<u32, QueryError> {
714 db.get(k)
715 }
716 }
717 let db = Database::new(SelfDep);
718 assert_eq!(db.get(&1), Err(QueryError::Cycle));
719 }
720
721 #[test]
722 fn test_indirect_cycle_is_error() {
723 struct PingPong;
724 impl System for PingPong {
725 type Key = u32;
726 type Value = u32;
727 fn compute(&self, db: &Database<Self>, k: &u32) -> Result<u32, QueryError> {
728 // 0 depends on 1, 1 depends on 0.
729 match k {
730 0 => db.get(&1),
731 _ => db.get(&0),
732 }
733 }
734 }
735 let db = Database::new(PingPong);
736 assert_eq!(db.get(&0), Err(QueryError::Cycle));
737 }
738
739 #[test]
740 fn test_state_is_clean_after_cycle_error() {
741 struct SelfDep;
742 impl System for SelfDep {
743 type Key = u32;
744 type Value = u32;
745 fn compute(&self, db: &Database<Self>, k: &u32) -> Result<u32, QueryError> {
746 if *k == 0 { db.get(&0) } else { Ok(*k) }
747 }
748 }
749 let db = Database::new(SelfDep);
750 assert_eq!(db.get(&0), Err(QueryError::Cycle));
751 // A non-cyclic query still resolves normally afterwards.
752 assert_eq!(db.get(&9).unwrap(), 9);
753 }
754
755 #[test]
756 fn test_set_promotes_derived_key_to_input() {
757 let mut db = Database::new(Counting::new());
758 db.set(0, 3);
759 assert_eq!(db.get(&2).unwrap(), 30); // key 2 derived and cached
760 db.set(2, 999); // promote key 2 to an input
761 assert_eq!(db.get(&2).unwrap(), 999);
762 // It is now an input: compute is never called for it again.
763 let runs_before = db.system().runs_of(2);
764 assert_eq!(db.get(&2).unwrap(), 999);
765 assert_eq!(db.system().runs_of(2), runs_before);
766 }
767
768 #[test]
769 fn test_branching_dependencies_tracked_precisely() {
770 // A query reads one of two inputs depending on a selector input; only the
771 // branch it actually took is a dependency.
772 #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
773 enum K {
774 Select,
775 Left,
776 Right,
777 Picked,
778 }
779 struct Switch {
780 runs: StdCell<u32>,
781 }
782 impl System for Switch {
783 type Key = K;
784 type Value = i64;
785 fn compute(&self, db: &Database<Self>, key: &K) -> Result<i64, QueryError> {
786 match key {
787 K::Select | K::Left | K::Right => Ok(0),
788 K::Picked => {
789 self.runs.set(self.runs.get() + 1);
790 if db.get(&K::Select)? == 0 {
791 db.get(&K::Left)
792 } else {
793 db.get(&K::Right)
794 }
795 }
796 }
797 }
798 }
799 let mut db = Database::new(Switch {
800 runs: StdCell::new(0),
801 });
802 db.set(K::Select, 0);
803 db.set(K::Left, 100);
804 db.set(K::Right, 200);
805 assert_eq!(db.get(&K::Picked).unwrap(), 100);
806 assert_eq!(db.system().runs.get(), 1);
807
808 // Changing Right must NOT recompute Picked: it read Left, not Right.
809 db.set(K::Right, 999);
810 assert_eq!(db.get(&K::Picked).unwrap(), 100);
811 assert_eq!(db.system().runs.get(), 1);
812
813 // Changing Left DOES recompute Picked.
814 db.set(K::Left, 111);
815 assert_eq!(db.get(&K::Picked).unwrap(), 111);
816 assert_eq!(db.system().runs.get(), 2);
817 }
818
819 #[test]
820 fn test_string_values_work() {
821 struct Greeter;
822 impl System for Greeter {
823 type Key = u32;
824 type Value = String;
825 fn compute(&self, db: &Database<Self>, k: &u32) -> Result<String, QueryError> {
826 if *k == 0 {
827 Ok("world".to_string())
828 } else {
829 Ok(alloc::format!("hello, {}", db.get(&0)?))
830 }
831 }
832 }
833 let db = Database::new(Greeter);
834 assert_eq!(db.get(&1).unwrap(), "hello, world");
835 }
836
837 #[test]
838 fn test_debug_reports_shape() {
839 let mut db = Database::new(Counting::new());
840 db.set(0, 1);
841 let _ = db.get(&2).unwrap();
842 let rendered = alloc::format!("{db:?}");
843 assert!(rendered.contains("revision"));
844 assert!(rendered.contains("memos"));
845 }
846
847 #[test]
848 fn test_diamond_recomputes_shared_dep_once() {
849 // top depends on left and right, both of which depend on a shared input.
850 #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
851 enum K {
852 In,
853 Left,
854 Right,
855 Top,
856 }
857 struct Diamond {
858 in_runs: StdCell<u32>,
859 }
860 impl System for Diamond {
861 type Key = K;
862 type Value = i64;
863 fn compute(&self, db: &Database<Self>, key: &K) -> Result<i64, QueryError> {
864 match key {
865 K::In => Ok(0),
866 K::Left => Ok(db.get(&K::In)? + 1),
867 K::Right => Ok(db.get(&K::In)? + 2),
868 K::Top => {
869 self.in_runs.set(self.in_runs.get() + 1);
870 Ok(db.get(&K::Left)? + db.get(&K::Right)?)
871 }
872 }
873 }
874 }
875 let mut db = Database::new(Diamond {
876 in_runs: StdCell::new(0),
877 });
878 db.set(K::In, 10);
879 assert_eq!(db.get(&K::Top).unwrap(), (10 + 1) + (10 + 2));
880 assert_eq!(db.system().in_runs.get(), 1);
881
882 db.set(K::In, 20);
883 assert_eq!(db.get(&K::Top).unwrap(), (20 + 1) + (20 + 2));
884 assert_eq!(db.system().in_runs.get(), 2);
885 }
886
887 #[test]
888 fn test_multiple_deps_each_invalidate() {
889 // Guards against a regression where deps are dropped: a query with two
890 // input deps must be invalidated by either.
891 let mut db = Database::new(Counting::new());
892 db.set(0, 1);
893 db.set(1, 2);
894 // key 3 = (A*10) + B = 10 + 2 = 12
895 assert_eq!(db.get(&3).unwrap(), 12);
896 db.set(1, 5);
897 assert_eq!(db.get(&3).unwrap(), 15);
898 // And the other dependency still invalidates it too.
899 db.set(0, 2); // A*10 becomes 20
900 assert_eq!(db.get(&3).unwrap(), 25);
901 }
902}