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