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}
123
124impl DbError {
125    /// Creates an IO error with operation context.
126    ///
127    /// # Performance
128    ///
129    /// This function is `O(1)`.
130    pub(crate) const fn io(operation: &'static str, source: io::Error) -> Self {
131        Self::Io { operation, source }
132    }
133
134    /// Creates an unsupported-query error.
135    ///
136    /// # Performance
137    ///
138    /// This function is `O(message.len())`.
139    pub(crate) fn unsupported(message: impl Into<String>) -> Self {
140        Self::UnsupportedQuery {
141            message: message.into(),
142        }
143    }
144
145    /// Creates an invalid-projection error.
146    ///
147    /// # Performance
148    ///
149    /// This function is `O(message.len())`.
150    pub(crate) fn invalid_projection(message: impl Into<String>) -> Self {
151        Self::InvalidProjection {
152            message: message.into(),
153        }
154    }
155
156    /// Creates an invalid-store error.
157    ///
158    /// # Performance
159    ///
160    /// This function is `O(message.len())`.
161    pub(crate) fn invalid_store(message: impl Into<String>) -> Self {
162        Self::InvalidStore {
163            message: message.into(),
164        }
165    }
166
167    /// Wraps a substrate bounded-BFS traversal failure.
168    ///
169    /// # Performance
170    ///
171    /// This function is `O(1)`.
172    pub(crate) const fn traversal(source: oxgraph_algo::BfsError) -> Self {
173        Self::Traversal { source }
174    }
175}
176
177impl fmt::Display for DbError {
178    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
179        match self {
180            Self::AlreadyExists => formatter.write_str("database already exists"),
181            Self::NotFound => formatter.write_str("database not found"),
182            Self::WriterLockHeld => formatter.write_str("database writer lock is held"),
183            Self::IdOverflow => formatter.write_str("database ID overflow"),
184            Self::TransactionIdOverflow => formatter.write_str("transaction ID overflow"),
185            Self::CommitSeqOverflow => formatter.write_str("commit sequence overflow"),
186            Self::DuplicateCatalogName => formatter.write_str("duplicate catalog name"),
187            Self::DuplicateId => formatter.write_str("duplicate ID"),
188            Self::UnknownElement { id } => write!(formatter, "unknown element {}", id.get()),
189            Self::UnknownRelation { id } => write!(formatter, "unknown relation {}", id.get()),
190            Self::UnknownIncidence { id } => write!(formatter, "unknown incidence {}", id.get()),
191            Self::UnknownRole { id } => write!(formatter, "unknown role {}", id.get()),
192            Self::UnknownLabel { id } => write!(formatter, "unknown label {}", id.get()),
193            Self::UnknownRelationType { id } => {
194                write!(formatter, "unknown relation type {}", id.get())
195            }
196            Self::UnknownPropertyKey { id } => {
197                write!(formatter, "unknown property key {}", id.get())
198            }
199            Self::UnknownProjection { id } => write!(formatter, "unknown projection {}", id.get()),
200            Self::UnknownIndex { id } => write!(formatter, "unknown index {}", id.get()),
201            Self::PropertyTypeMismatch { expected, actual } => {
202                write!(
203                    formatter,
204                    "property type mismatch: expected {expected:?}, got {actual:?}"
205                )
206            }
207            Self::WrongPropertyFamily { expected, actual } => {
208                write!(
209                    formatter,
210                    "property family mismatch: expected {expected:?}, got {actual:?}"
211                )
212            }
213            Self::InvalidProjection { message } => {
214                write!(formatter, "invalid projection: {message}")
215            }
216            Self::EmptyQuery => formatter.write_str("empty query"),
217            Self::UnsupportedQuery { message } => write!(formatter, "unsupported query: {message}"),
218            Self::InvalidStore { message } => write!(formatter, "invalid store: {message}"),
219            Self::Io { operation, source } => write!(formatter, "{operation} failed: {source}"),
220            Self::Traversal { source } => write!(formatter, "traversal error: {source}"),
221        }
222    }
223}
224
225impl std::error::Error for DbError {
226    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
227        match self {
228            Self::Io { source, .. } => Some(source),
229            Self::Traversal { source } => Some(source),
230            Self::AlreadyExists
231            | Self::NotFound
232            | Self::WriterLockHeld
233            | Self::IdOverflow
234            | Self::TransactionIdOverflow
235            | Self::CommitSeqOverflow
236            | Self::DuplicateCatalogName
237            | Self::DuplicateId
238            | Self::UnknownElement { .. }
239            | Self::UnknownRelation { .. }
240            | Self::UnknownIncidence { .. }
241            | Self::UnknownRole { .. }
242            | Self::UnknownLabel { .. }
243            | Self::UnknownRelationType { .. }
244            | Self::UnknownPropertyKey { .. }
245            | Self::UnknownProjection { .. }
246            | Self::UnknownIndex { .. }
247            | Self::PropertyTypeMismatch { .. }
248            | Self::WrongPropertyFamily { .. }
249            | Self::InvalidProjection { .. }
250            | Self::EmptyQuery
251            | Self::UnsupportedQuery { .. }
252            | Self::InvalidStore { .. } => None,
253        }
254    }
255}