Skip to main content

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}