Skip to main content

oxgraph_db/
error.rs

1//! Database error surface.
2
3use std::{fmt, io};
4
5use crate::{
6    ElementId, IncidenceId, IndexId, LabelId, ProjectionId, PropertyKeyId, RelationId,
7    RelationTypeId, RoleId, catalog::PropertyFamily, value::PropertyType,
8};
9
10/// Errors raised by the `OxGraph` database product.
11///
12/// # Performance
13///
14/// Formatting is `O(message length)`.
15#[derive(Debug)]
16#[non_exhaustive]
17pub enum DbError {
18    /// Database files already exist.
19    AlreadyExists,
20    /// Database files do not exist.
21    NotFound,
22    /// The single-writer lock is already held by another writer.
23    WriterLockHeld,
24    /// Canonical ID space is exhausted.
25    IdOverflow,
26    /// Transaction ID space is exhausted.
27    TransactionIdOverflow,
28    /// Commit sequence space is exhausted.
29    CommitSeqOverflow,
30    /// Duplicate catalog name or ID.
31    DuplicateCatalogName,
32    /// Duplicate canonical ID.
33    DuplicateId,
34    /// Unknown element ID.
35    UnknownElement {
36        /// Missing element ID.
37        id: ElementId,
38    },
39    /// Unknown relation ID.
40    UnknownRelation {
41        /// Missing relation ID.
42        id: RelationId,
43    },
44    /// Unknown incidence ID.
45    UnknownIncidence {
46        /// Missing incidence ID.
47        id: IncidenceId,
48    },
49    /// Unknown role ID.
50    UnknownRole {
51        /// Missing role ID.
52        id: RoleId,
53    },
54    /// Unknown label ID.
55    UnknownLabel {
56        /// Missing label ID.
57        id: LabelId,
58    },
59    /// Unknown relation type ID.
60    UnknownRelationType {
61        /// Missing relation type ID.
62        id: RelationTypeId,
63    },
64    /// Unknown property key ID.
65    UnknownPropertyKey {
66        /// Missing property key ID.
67        id: PropertyKeyId,
68    },
69    /// Unknown projection ID.
70    UnknownProjection {
71        /// Missing projection ID.
72        id: ProjectionId,
73    },
74    /// Unknown index ID.
75    UnknownIndex {
76        /// Missing index ID.
77        id: IndexId,
78    },
79    /// Property value type mismatched the catalog schema.
80    PropertyTypeMismatch {
81        /// Expected property type.
82        expected: PropertyType,
83        /// Actual property type.
84        actual: PropertyType,
85    },
86    /// Property subject family mismatched the catalog schema.
87    WrongPropertyFamily {
88        /// Expected subject family.
89        expected: PropertyFamily,
90        /// Actual subject family.
91        actual: PropertyFamily,
92    },
93    /// Projection cannot be materialized as requested.
94    InvalidProjection {
95        /// Deterministic validation message.
96        message: String,
97    },
98    /// Query text is empty.
99    EmptyQuery,
100    /// Query text is outside the pinned profile.
101    UnsupportedQuery {
102        /// Deterministic explanation.
103        message: String,
104    },
105    /// Storage bytes are invalid.
106    InvalidStore {
107        /// Deterministic validation message.
108        message: String,
109    },
110    /// Wraps an IO error with operation context.
111    Io {
112        /// Operation that failed.
113        operation: &'static str,
114        /// Underlying IO error.
115        source: io::Error,
116    },
117    /// Wraps a substrate bounded-BFS traversal failure.
118    Traversal {
119        /// Underlying bounded-BFS error.
120        source: oxgraph_algo::BfsError,
121    },
122    /// A delta-log record is corrupt beyond the recoverable torn tail.
123    LogCorrupt {
124        /// Log sequence number of the offending record.
125        lsn: u64,
126        /// Deterministic reason the record was rejected.
127        reason: &'static str,
128    },
129    /// A delta-log record names a different base generation than the superblock.
130    BaseGenerationMismatch {
131        /// Base generation named by the superblock.
132        expected: u64,
133        /// Base generation found in the record.
134        found: u64,
135    },
136}
137
138impl DbError {
139    /// Creates an IO error with operation context.
140    ///
141    /// # Performance
142    ///
143    /// This function is `O(1)`.
144    pub(crate) const fn io(operation: &'static str, source: io::Error) -> Self {
145        Self::Io { operation, source }
146    }
147
148    /// Creates an unsupported-query error.
149    ///
150    /// # Performance
151    ///
152    /// This function is `O(message.len())`.
153    pub(crate) fn unsupported(message: impl Into<String>) -> Self {
154        Self::UnsupportedQuery {
155            message: message.into(),
156        }
157    }
158
159    /// Creates an invalid-projection error.
160    ///
161    /// # Performance
162    ///
163    /// This function is `O(message.len())`.
164    pub(crate) fn invalid_projection(message: impl Into<String>) -> Self {
165        Self::InvalidProjection {
166            message: message.into(),
167        }
168    }
169
170    /// Creates an invalid-store error.
171    ///
172    /// # Performance
173    ///
174    /// This function is `O(message.len())`.
175    pub(crate) fn invalid_store(message: impl Into<String>) -> Self {
176        Self::InvalidStore {
177            message: message.into(),
178        }
179    }
180
181    /// Wraps a substrate bounded-BFS traversal failure.
182    ///
183    /// # Performance
184    ///
185    /// This function is `O(1)`.
186    pub(crate) const fn traversal(source: oxgraph_algo::BfsError) -> Self {
187        Self::Traversal { source }
188    }
189}
190
191impl fmt::Display for DbError {
192    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
193        match self {
194            Self::AlreadyExists => formatter.write_str("database already exists"),
195            Self::NotFound => formatter.write_str("database not found"),
196            Self::WriterLockHeld => formatter.write_str("database writer lock is held"),
197            Self::IdOverflow => formatter.write_str("database ID overflow"),
198            Self::TransactionIdOverflow => formatter.write_str("transaction ID overflow"),
199            Self::CommitSeqOverflow => formatter.write_str("commit sequence overflow"),
200            Self::DuplicateCatalogName => formatter.write_str("duplicate catalog name"),
201            Self::DuplicateId => formatter.write_str("duplicate ID"),
202            Self::UnknownElement { id } => write!(formatter, "unknown element {}", id.get()),
203            Self::UnknownRelation { id } => write!(formatter, "unknown relation {}", id.get()),
204            Self::UnknownIncidence { id } => write!(formatter, "unknown incidence {}", id.get()),
205            Self::UnknownRole { id } => write!(formatter, "unknown role {}", id.get()),
206            Self::UnknownLabel { id } => write!(formatter, "unknown label {}", id.get()),
207            Self::UnknownRelationType { id } => {
208                write!(formatter, "unknown relation type {}", id.get())
209            }
210            Self::UnknownPropertyKey { id } => {
211                write!(formatter, "unknown property key {}", id.get())
212            }
213            Self::UnknownProjection { id } => write!(formatter, "unknown projection {}", id.get()),
214            Self::UnknownIndex { id } => write!(formatter, "unknown index {}", id.get()),
215            Self::PropertyTypeMismatch { expected, actual } => {
216                write!(
217                    formatter,
218                    "property type mismatch: expected {expected:?}, got {actual:?}"
219                )
220            }
221            Self::WrongPropertyFamily { expected, actual } => {
222                write!(
223                    formatter,
224                    "property family mismatch: expected {expected:?}, got {actual:?}"
225                )
226            }
227            Self::InvalidProjection { message } => {
228                write!(formatter, "invalid projection: {message}")
229            }
230            Self::EmptyQuery => formatter.write_str("empty query"),
231            Self::UnsupportedQuery { message } => write!(formatter, "unsupported query: {message}"),
232            Self::InvalidStore { message } => write!(formatter, "invalid store: {message}"),
233            Self::Io { operation, source } => write!(formatter, "{operation} failed: {source}"),
234            Self::Traversal { source } => write!(formatter, "traversal error: {source}"),
235            Self::LogCorrupt { lsn, reason } => {
236                write!(formatter, "delta-log corrupt at lsn {lsn}: {reason}")
237            }
238            Self::BaseGenerationMismatch { expected, found } => write!(
239                formatter,
240                "base generation mismatch: superblock names {expected}, record has {found}"
241            ),
242        }
243    }
244}
245
246impl std::error::Error for DbError {
247    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
248        match self {
249            Self::Io { source, .. } => Some(source),
250            Self::Traversal { source } => Some(source),
251            Self::AlreadyExists
252            | Self::NotFound
253            | Self::WriterLockHeld
254            | Self::IdOverflow
255            | Self::TransactionIdOverflow
256            | Self::CommitSeqOverflow
257            | Self::DuplicateCatalogName
258            | Self::DuplicateId
259            | Self::UnknownElement { .. }
260            | Self::UnknownRelation { .. }
261            | Self::UnknownIncidence { .. }
262            | Self::UnknownRole { .. }
263            | Self::UnknownLabel { .. }
264            | Self::UnknownRelationType { .. }
265            | Self::UnknownPropertyKey { .. }
266            | Self::UnknownProjection { .. }
267            | Self::UnknownIndex { .. }
268            | Self::PropertyTypeMismatch { .. }
269            | Self::WrongPropertyFamily { .. }
270            | Self::InvalidProjection { .. }
271            | Self::EmptyQuery
272            | Self::UnsupportedQuery { .. }
273            | Self::InvalidStore { .. }
274            | Self::LogCorrupt { .. }
275            | Self::BaseGenerationMismatch { .. } => None,
276        }
277    }
278}