1use std::{fmt, io};
9
10use crate::{PropertyKeyId, catalog::PropertyFamily, value::PropertyType};
11
12#[derive(Clone, Copy, Debug, Eq, PartialEq)]
18pub enum IdFamily {
19 Element,
21 Relation,
23 Incidence,
25 Role,
27 Label,
29 RelationType,
31 PropertyKey,
33 Projection,
35 Index,
37}
38
39impl fmt::Display for IdFamily {
40 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
41 formatter.write_str(match self {
42 Self::Element => "element",
43 Self::Relation => "relation",
44 Self::Incidence => "incidence",
45 Self::Role => "role",
46 Self::Label => "label",
47 Self::RelationType => "relation type",
48 Self::PropertyKey => "property key",
49 Self::Projection => "projection",
50 Self::Index => "index",
51 })
52 }
53}
54
55#[derive(Debug)]
62#[non_exhaustive]
63pub enum StorageError {
64 AlreadyExists,
66 NotFound,
68 Io {
70 operation: &'static str,
72 source: io::Error,
74 },
75 InvalidStore {
77 message: String,
79 },
80 UnsupportedFormat {
84 found: u32,
86 expected: u32,
88 },
89 LogCorrupt {
91 lsn: u64,
93 reason: &'static str,
95 },
96 BaseGenerationMismatch {
98 expected: u64,
100 found: u64,
102 },
103}
104
105impl StorageError {
106 pub(crate) const fn io(operation: &'static str, source: io::Error) -> Self {
112 Self::Io { operation, source }
113 }
114
115 pub(crate) fn invalid_store(message: impl Into<String>) -> Self {
121 Self::InvalidStore {
122 message: message.into(),
123 }
124 }
125}
126
127impl fmt::Display for StorageError {
128 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
129 match self {
130 Self::AlreadyExists => formatter.write_str("database already exists"),
131 Self::NotFound => formatter.write_str("database not found"),
132 Self::Io { operation, source } => write!(formatter, "{operation} failed: {source}"),
133 Self::InvalidStore { message } => write!(formatter, "invalid store: {message}"),
134 Self::UnsupportedFormat { found, expected } => write!(
135 formatter,
136 "unsupported OXGDB format version: found {found}, this build requires {expected}"
137 ),
138 Self::LogCorrupt { lsn, reason } => {
139 write!(formatter, "delta-log corrupt at lsn {lsn}: {reason}")
140 }
141 Self::BaseGenerationMismatch { expected, found } => write!(
142 formatter,
143 "base generation mismatch: superblock names {expected}, record has {found}"
144 ),
145 }
146 }
147}
148
149impl std::error::Error for StorageError {
150 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
151 match self {
152 Self::Io { source, .. } => Some(source),
153 Self::AlreadyExists
154 | Self::NotFound
155 | Self::InvalidStore { .. }
156 | Self::UnsupportedFormat { .. }
157 | Self::LogCorrupt { .. }
158 | Self::BaseGenerationMismatch { .. } => None,
159 }
160 }
161}
162
163#[derive(Debug)]
170#[non_exhaustive]
171pub enum CatalogError {
172 UnknownId {
174 family: IdFamily,
176 id: u64,
178 },
179 UnknownName {
181 kind: &'static str,
183 name: String,
185 },
186 DuplicateName,
188 DuplicateId,
190 SchemaConflict {
192 name: String,
194 reason: &'static str,
196 },
197}
198
199impl fmt::Display for CatalogError {
200 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
201 match self {
202 Self::UnknownId { family, id } => write!(formatter, "unknown {family} {id}"),
203 Self::UnknownName { kind, name } => write!(formatter, "unknown {kind} {name:?}"),
204 Self::DuplicateName => formatter.write_str("duplicate catalog name"),
205 Self::DuplicateId => formatter.write_str("duplicate ID"),
206 Self::SchemaConflict { name, reason } => {
207 write!(formatter, "schema conflict for {name:?}: {reason}")
208 }
209 }
210 }
211}
212
213impl std::error::Error for CatalogError {}
214
215#[derive(Debug)]
222#[non_exhaustive]
223pub enum TxnError {
224 WriterLockHeld,
226 IdOverflow {
228 family: IdFamily,
230 },
231 TransactionIdOverflow,
233 CommitSeqOverflow,
235}
236
237impl fmt::Display for TxnError {
238 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
239 match self {
240 Self::WriterLockHeld => formatter.write_str("database writer lock is held"),
241 Self::IdOverflow { family } => write!(formatter, "database {family} ID overflow"),
242 Self::TransactionIdOverflow => formatter.write_str("transaction ID overflow"),
243 Self::CommitSeqOverflow => formatter.write_str("commit sequence overflow"),
244 }
245 }
246}
247
248impl std::error::Error for TxnError {}
249
250#[derive(Debug)]
257#[non_exhaustive]
258pub enum QueryError {
259 Empty,
261 Unsupported {
263 message: String,
265 },
266 PropertyTypeMismatch {
268 expected: PropertyType,
270 actual: PropertyType,
272 },
273 WrongPropertyFamily {
275 expected: PropertyFamily,
277 actual: PropertyFamily,
279 },
280 InvalidProjection {
282 message: String,
284 },
285 Traversal {
287 reason: &'static str,
289 },
290 MissingProperty {
292 key: PropertyKeyId,
294 },
295 ValueOutOfRange,
297 NoEqualityIndex {
299 key: PropertyKeyId,
301 },
302}
303
304impl fmt::Display for QueryError {
305 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
306 match self {
307 Self::Empty => formatter.write_str("empty query"),
308 Self::Unsupported { message } => write!(formatter, "unsupported query: {message}"),
309 Self::PropertyTypeMismatch { expected, actual } => {
310 write!(
311 formatter,
312 "property type mismatch: expected {expected:?}, got {actual:?}"
313 )
314 }
315 Self::WrongPropertyFamily { expected, actual } => {
316 write!(
317 formatter,
318 "property family mismatch: expected {expected:?}, got {actual:?}"
319 )
320 }
321 Self::InvalidProjection { message } => {
322 write!(formatter, "invalid projection: {message}")
323 }
324 Self::Traversal { reason } => write!(formatter, "traversal error: {reason}"),
325 Self::MissingProperty { key } => {
326 write!(formatter, "missing property {}", key.get())
327 }
328 Self::ValueOutOfRange => formatter.write_str("value out of representable i64 range"),
329 Self::NoEqualityIndex { key } => {
330 write!(
331 formatter,
332 "no equality index for property key {}",
333 key.get()
334 )
335 }
336 }
337 }
338}
339
340impl std::error::Error for QueryError {}
341
342#[derive(Debug)]
348#[non_exhaustive]
349pub enum DbError {
350 Storage(StorageError),
352 Catalog(CatalogError),
354 Txn(TxnError),
356 Query(QueryError),
358}
359
360impl DbError {
361 pub(crate) const fn io(operation: &'static str, source: io::Error) -> Self {
367 Self::Storage(StorageError::Io { operation, source })
368 }
369
370 pub(crate) fn unsupported(message: impl Into<String>) -> Self {
376 Self::Query(QueryError::Unsupported {
377 message: message.into(),
378 })
379 }
380
381 pub(crate) fn invalid_projection(message: impl Into<String>) -> Self {
387 Self::Query(QueryError::InvalidProjection {
388 message: message.into(),
389 })
390 }
391
392 pub(crate) fn invalid_store(message: impl Into<String>) -> Self {
398 Self::Storage(StorageError::InvalidStore {
399 message: message.into(),
400 })
401 }
402
403 pub(crate) const fn traversal(reason: &'static str) -> Self {
409 Self::Query(QueryError::Traversal { reason })
410 }
411
412 pub(crate) fn unknown(id: impl Into<(IdFamily, u64)>) -> Self {
418 let (family, id) = id.into();
419 Self::Catalog(CatalogError::UnknownId { family, id })
420 }
421
422 #[must_use]
428 pub(crate) const fn id_overflow(family: IdFamily) -> Self {
429 Self::Txn(TxnError::IdOverflow { family })
430 }
431}
432
433impl fmt::Display for DbError {
434 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
435 match self {
436 Self::Storage(error) => fmt::Display::fmt(error, formatter),
437 Self::Catalog(error) => fmt::Display::fmt(error, formatter),
438 Self::Txn(error) => fmt::Display::fmt(error, formatter),
439 Self::Query(error) => fmt::Display::fmt(error, formatter),
440 }
441 }
442}
443
444impl std::error::Error for DbError {
445 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
446 match self {
449 Self::Storage(error) => std::error::Error::source(error),
450 Self::Catalog(error) => std::error::Error::source(error),
451 Self::Txn(error) => std::error::Error::source(error),
452 Self::Query(error) => std::error::Error::source(error),
453 }
454 }
455}
456
457impl From<StorageError> for DbError {
458 fn from(error: StorageError) -> Self {
459 Self::Storage(error)
460 }
461}
462
463impl From<CatalogError> for DbError {
464 fn from(error: CatalogError) -> Self {
465 Self::Catalog(error)
466 }
467}
468
469impl From<TxnError> for DbError {
470 fn from(error: TxnError) -> Self {
471 Self::Txn(error)
472 }
473}
474
475impl From<QueryError> for DbError {
476 fn from(error: QueryError) -> Self {
477 Self::Query(error)
478 }
479}