nodedb_sql/catalog.rs
1//! `SqlCatalog` trait + descriptor-resolution error type.
2//!
3//! The SQL planner resolves collection metadata through the
4//! `SqlCatalog` trait. Both Origin (via the host-side
5//! `CredentialStore` + `SystemCatalog`) and Lite (via the embedded
6//! redb catalog) implement it. The trait lives in its own file so
7//! `types.rs` stays under the 500-line limit and so the error
8//! surface has headroom for additional variants.
9
10use thiserror::Error;
11
12use crate::types::CollectionInfo;
13
14/// Errors surfaced by `SqlCatalog` implementations.
15///
16/// Only one variant today — callers pattern-match directly and
17/// map the retryable case to `SqlError::RetryableSchemaChanged`
18/// via the `From` impl in `error.rs`. The enum shape is kept
19/// despite having a single variant so future variants can be
20/// added without a breaking change.
21#[derive(Debug, Clone, Error)]
22pub enum SqlCatalogError {
23 /// A DDL drain is in progress on the descriptor at the
24 /// version the planner wanted to acquire a lease on. Callers
25 /// should retry the whole plan after a short backoff — by
26 /// then either the drain has completed (new descriptor
27 /// version available in the cache) or the retry budget is
28 /// exhausted and a typed error surfaces to the client.
29 #[error("retryable schema change on {descriptor}")]
30 RetryableSchemaChanged {
31 /// Human-readable identifier for the descriptor, e.g.
32 /// `"collection orders"`. Used in log / trace output.
33 descriptor: String,
34 },
35
36 /// Collection is soft-deleted (`DROP COLLECTION` run, retention
37 /// window still active). Distinct from `Ok(None)` = absent so the
38 /// planner can surface an actionable error with an `UNDROP`
39 /// hint rather than a generic "unknown table".
40 #[error(
41 "collection '{name}' was dropped and is within its retention window; \
42 restore with UNDROP COLLECTION before {retention_expires_at_ns} ns"
43 )]
44 CollectionDeactivated {
45 name: String,
46 /// Wall-clock nanoseconds when retention elapses and the
47 /// collection is hard-deleted by the GC sweeper.
48 retention_expires_at_ns: u64,
49 },
50}
51
52/// Trait for looking up collection metadata during planning.
53///
54/// Both Origin (via CredentialStore) and Lite (via the embedded
55/// redb catalog) implement this trait.
56///
57/// The return type is `Result<Option<CollectionInfo>, _>` with
58/// a three-way semantics:
59///
60/// - `Ok(Some(info))` — the collection exists and is usable.
61/// An Origin implementation will have acquired a descriptor
62/// lease at the current version before returning; subsequent
63/// planning against the same collection within the lease
64/// window is drain-safe.
65/// - `Ok(None)` — the collection does not exist. Callers should
66/// surface this as `SqlError::UnknownTable`.
67/// - `Err(SqlCatalogError::RetryableSchemaChanged { .. })` —
68/// the collection exists but a DDL drain is in progress.
69/// Callers propagate this up so the pgwire layer can retry
70/// the whole statement.
71pub trait SqlCatalog {
72 fn get_collection(&self, name: &str) -> Result<Option<CollectionInfo>, SqlCatalogError>;
73}