redb/
error.rs

1use crate::tree_store::{FILE_FORMAT_VERSION2, MAX_VALUE_LENGTH};
2use crate::{ReadTransaction, TypeName};
3use std::fmt::{Display, Formatter};
4use std::sync::PoisonError;
5use std::{io, panic};
6
7/// General errors directly from the storage layer
8#[derive(Debug)]
9#[non_exhaustive]
10pub enum StorageError {
11    /// The Database is corrupted
12    Corrupted(String),
13    /// The value being inserted exceeds the maximum of 3GiB
14    ValueTooLarge(usize),
15    Io(io::Error),
16    PreviousIo,
17    LockPoisoned(&'static panic::Location<'static>),
18}
19
20impl<T> From<PoisonError<T>> for StorageError {
21    fn from(_: PoisonError<T>) -> StorageError {
22        StorageError::LockPoisoned(panic::Location::caller())
23    }
24}
25
26impl From<io::Error> for StorageError {
27    fn from(err: io::Error) -> StorageError {
28        StorageError::Io(err)
29    }
30}
31
32impl From<StorageError> for Error {
33    fn from(err: StorageError) -> Error {
34        match err {
35            StorageError::Corrupted(msg) => Error::Corrupted(msg),
36            StorageError::ValueTooLarge(x) => Error::ValueTooLarge(x),
37            StorageError::Io(x) => Error::Io(x),
38            StorageError::PreviousIo => Error::PreviousIo,
39            StorageError::LockPoisoned(location) => Error::LockPoisoned(location),
40        }
41    }
42}
43
44impl Display for StorageError {
45    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
46        match self {
47            StorageError::Corrupted(msg) => {
48                write!(f, "DB corrupted: {msg}")
49            }
50            StorageError::ValueTooLarge(len) => {
51                write!(
52                    f,
53                    "The value (length={len}) being inserted exceeds the maximum of {}GiB",
54                    MAX_VALUE_LENGTH / 1024 / 1024 / 1024
55                )
56            }
57            StorageError::Io(err) => {
58                write!(f, "I/O error: {err}")
59            }
60            StorageError::PreviousIo => {
61                write!(
62                    f,
63                    "Previous I/O error occurred. Please close and re-open the database."
64                )
65            }
66            StorageError::LockPoisoned(location) => {
67                write!(f, "Poisoned internal lock: {location}")
68            }
69        }
70    }
71}
72
73impl std::error::Error for StorageError {}
74
75/// Errors related to opening tables
76#[derive(Debug)]
77#[non_exhaustive]
78pub enum TableError {
79    /// Table types didn't match.
80    TableTypeMismatch {
81        table: String,
82        key: TypeName,
83        value: TypeName,
84    },
85    /// The table is a multimap table
86    TableIsMultimap(String),
87    /// The table is not a multimap table
88    TableIsNotMultimap(String),
89    TypeDefinitionChanged {
90        name: TypeName,
91        alignment: usize,
92        width: Option<usize>,
93    },
94    /// Table name does not match any table in database
95    TableDoesNotExist(String),
96    /// Table name already exists in the database
97    TableExists(String),
98    // Tables cannot be opened for writing multiple times, since they could retrieve immutable &
99    // mutable references to the same dirty pages, or multiple mutable references via insert_reserve()
100    TableAlreadyOpen(String, &'static panic::Location<'static>),
101    /// Error from underlying storage
102    Storage(StorageError),
103}
104
105impl TableError {
106    pub(crate) fn into_storage_error_or_corrupted(self, msg: &str) -> StorageError {
107        match self {
108            TableError::TableTypeMismatch { .. }
109            | TableError::TableIsMultimap(_)
110            | TableError::TableIsNotMultimap(_)
111            | TableError::TypeDefinitionChanged { .. }
112            | TableError::TableDoesNotExist(_)
113            | TableError::TableExists(_)
114            | TableError::TableAlreadyOpen(_, _) => {
115                StorageError::Corrupted(format!("{msg}: {self}"))
116            }
117            TableError::Storage(storage) => storage,
118        }
119    }
120}
121
122impl From<TableError> for Error {
123    fn from(err: TableError) -> Error {
124        match err {
125            TableError::TypeDefinitionChanged {
126                name,
127                alignment,
128                width,
129            } => Error::TypeDefinitionChanged {
130                name,
131                alignment,
132                width,
133            },
134            TableError::TableTypeMismatch { table, key, value } => {
135                Error::TableTypeMismatch { table, key, value }
136            }
137            TableError::TableIsMultimap(table) => Error::TableIsMultimap(table),
138            TableError::TableIsNotMultimap(table) => Error::TableIsNotMultimap(table),
139            TableError::TableDoesNotExist(table) => Error::TableDoesNotExist(table),
140            TableError::TableExists(table) => Error::TableExists(table),
141            TableError::TableAlreadyOpen(name, location) => Error::TableAlreadyOpen(name, location),
142            TableError::Storage(storage) => storage.into(),
143        }
144    }
145}
146
147impl From<StorageError> for TableError {
148    fn from(err: StorageError) -> TableError {
149        TableError::Storage(err)
150    }
151}
152
153impl Display for TableError {
154    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
155        match self {
156            TableError::TypeDefinitionChanged {
157                name,
158                alignment,
159                width,
160            } => {
161                write!(
162                    f,
163                    "Current definition of {} does not match stored definition (width={:?}, alignment={})",
164                    name.name(),
165                    width,
166                    alignment,
167                )
168            }
169            TableError::TableTypeMismatch { table, key, value } => {
170                write!(
171                    f,
172                    "{table} is of type Table<{}, {}>",
173                    key.name(),
174                    value.name(),
175                )
176            }
177            TableError::TableIsMultimap(table) => {
178                write!(f, "{table} is a multimap table")
179            }
180            TableError::TableIsNotMultimap(table) => {
181                write!(f, "{table} is not a multimap table")
182            }
183            TableError::TableDoesNotExist(table) => {
184                write!(f, "Table '{table}' does not exist")
185            }
186            TableError::TableExists(table) => {
187                write!(f, "Table '{table}' already exists")
188            }
189            TableError::TableAlreadyOpen(name, location) => {
190                write!(f, "Table '{name}' already opened at: {location}")
191            }
192            TableError::Storage(storage) => storage.fmt(f),
193        }
194    }
195}
196
197impl std::error::Error for TableError {}
198
199/// Errors related to opening a database
200#[derive(Debug)]
201#[non_exhaustive]
202pub enum DatabaseError {
203    /// The Database is already open. Cannot acquire lock.
204    DatabaseAlreadyOpen,
205    /// [`crate::RepairSession::abort`] was called.
206    RepairAborted,
207    /// The database file is in an old file format and must be manually upgraded
208    UpgradeRequired(u8),
209    /// Error from underlying storage
210    Storage(StorageError),
211}
212
213impl From<DatabaseError> for Error {
214    fn from(err: DatabaseError) -> Error {
215        match err {
216            DatabaseError::DatabaseAlreadyOpen => Error::DatabaseAlreadyOpen,
217            DatabaseError::RepairAborted => Error::RepairAborted,
218            DatabaseError::UpgradeRequired(x) => Error::UpgradeRequired(x),
219            DatabaseError::Storage(storage) => storage.into(),
220        }
221    }
222}
223
224impl From<io::Error> for DatabaseError {
225    fn from(err: io::Error) -> DatabaseError {
226        DatabaseError::Storage(StorageError::Io(err))
227    }
228}
229
230impl From<StorageError> for DatabaseError {
231    fn from(err: StorageError) -> DatabaseError {
232        DatabaseError::Storage(err)
233    }
234}
235
236impl Display for DatabaseError {
237    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
238        match self {
239            DatabaseError::UpgradeRequired(actual) => {
240                write!(
241                    f,
242                    "Manual upgrade required. Expected file format version {FILE_FORMAT_VERSION2}, but file is version {actual}"
243                )
244            }
245            DatabaseError::RepairAborted => {
246                write!(f, "Database repair aborted.")
247            }
248            DatabaseError::DatabaseAlreadyOpen => {
249                write!(f, "Database already open. Cannot acquire lock.")
250            }
251            DatabaseError::Storage(storage) => storage.fmt(f),
252        }
253    }
254}
255
256impl std::error::Error for DatabaseError {}
257
258/// Errors related to savepoints
259#[derive(Debug)]
260#[non_exhaustive]
261pub enum SavepointError {
262    /// This savepoint is invalid or cannot be created.
263    ///
264    /// Savepoints become invalid when an older savepoint is restored after it was created,
265    /// and savepoints cannot be created if the transaction is "dirty" (any tables have been opened)
266    InvalidSavepoint,
267    /// Error from underlying storage
268    Storage(StorageError),
269}
270
271impl From<SavepointError> for Error {
272    fn from(err: SavepointError) -> Error {
273        match err {
274            SavepointError::InvalidSavepoint => Error::InvalidSavepoint,
275            SavepointError::Storage(storage) => storage.into(),
276        }
277    }
278}
279
280impl From<StorageError> for SavepointError {
281    fn from(err: StorageError) -> SavepointError {
282        SavepointError::Storage(err)
283    }
284}
285
286impl Display for SavepointError {
287    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
288        match self {
289            SavepointError::InvalidSavepoint => {
290                write!(f, "Savepoint is invalid or cannot be created.")
291            }
292            SavepointError::Storage(storage) => storage.fmt(f),
293        }
294    }
295}
296
297impl std::error::Error for SavepointError {}
298
299/// Errors related to database upgrades
300#[derive(Debug)]
301#[non_exhaustive]
302pub enum UpgradeError {
303    /// A persistent savepoint exists
304    PersistentSavepointExists,
305    /// A ephemeral savepoint exists
306    EphemeralSavepointExists,
307    /// A transaction is still in-progress
308    TransactionInProgress,
309    /// Error from underlying storage
310    Storage(StorageError),
311}
312
313impl From<UpgradeError> for Error {
314    fn from(err: UpgradeError) -> Error {
315        match err {
316            UpgradeError::PersistentSavepointExists => Error::PersistentSavepointExists,
317            UpgradeError::EphemeralSavepointExists => Error::EphemeralSavepointExists,
318            UpgradeError::TransactionInProgress => Error::TransactionInProgress,
319            UpgradeError::Storage(storage) => storage.into(),
320        }
321    }
322}
323
324impl From<StorageError> for UpgradeError {
325    fn from(err: StorageError) -> UpgradeError {
326        UpgradeError::Storage(err)
327    }
328}
329
330impl From<CommitError> for UpgradeError {
331    fn from(err: CommitError) -> UpgradeError {
332        match err {
333            CommitError::Storage(err) => UpgradeError::Storage(err),
334        }
335    }
336}
337
338impl Display for UpgradeError {
339    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
340        match self {
341            UpgradeError::PersistentSavepointExists => {
342                write!(
343                    f,
344                    "Persistent savepoint exists. Operation cannot be performed."
345                )
346            }
347            UpgradeError::EphemeralSavepointExists => {
348                write!(
349                    f,
350                    "Ephemeral savepoint exists. Operation cannot be performed."
351                )
352            }
353            UpgradeError::TransactionInProgress => {
354                write!(
355                    f,
356                    "A transaction is still in progress. Operation cannot be performed."
357                )
358            }
359            UpgradeError::Storage(storage) => storage.fmt(f),
360        }
361    }
362}
363
364impl std::error::Error for UpgradeError {}
365
366/// Errors related to compaction
367#[derive(Debug)]
368#[non_exhaustive]
369pub enum CompactionError {
370    /// A persistent savepoint exists
371    PersistentSavepointExists,
372    /// A ephemeral savepoint exists
373    EphemeralSavepointExists,
374    /// A transaction is still in-progress
375    TransactionInProgress,
376    /// Error from underlying storage
377    Storage(StorageError),
378}
379
380impl From<CompactionError> for Error {
381    fn from(err: CompactionError) -> Error {
382        match err {
383            CompactionError::PersistentSavepointExists => Error::PersistentSavepointExists,
384            CompactionError::EphemeralSavepointExists => Error::EphemeralSavepointExists,
385            CompactionError::TransactionInProgress => Error::TransactionInProgress,
386            CompactionError::Storage(storage) => storage.into(),
387        }
388    }
389}
390
391impl From<StorageError> for CompactionError {
392    fn from(err: StorageError) -> CompactionError {
393        CompactionError::Storage(err)
394    }
395}
396
397impl Display for CompactionError {
398    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
399        match self {
400            CompactionError::PersistentSavepointExists => {
401                write!(
402                    f,
403                    "Persistent savepoint exists. Operation cannot be performed."
404                )
405            }
406            CompactionError::EphemeralSavepointExists => {
407                write!(
408                    f,
409                    "Ephemeral savepoint exists. Operation cannot be performed."
410                )
411            }
412            CompactionError::TransactionInProgress => {
413                write!(
414                    f,
415                    "A transaction is still in progress. Operation cannot be performed."
416                )
417            }
418            CompactionError::Storage(storage) => storage.fmt(f),
419        }
420    }
421}
422
423impl std::error::Error for CompactionError {}
424
425/// Errors related to transactions
426#[derive(Debug)]
427#[non_exhaustive]
428pub enum TransactionError {
429    /// Error from underlying storage
430    Storage(StorageError),
431    /// The transaction is still referenced by a table or other object
432    ReadTransactionStillInUse(ReadTransaction),
433}
434
435impl TransactionError {
436    pub(crate) fn into_storage_error(self) -> StorageError {
437        match self {
438            TransactionError::Storage(storage) => storage,
439            _ => unreachable!(),
440        }
441    }
442}
443
444impl From<TransactionError> for Error {
445    fn from(err: TransactionError) -> Error {
446        match err {
447            TransactionError::Storage(storage) => storage.into(),
448            TransactionError::ReadTransactionStillInUse(txn) => {
449                Error::ReadTransactionStillInUse(txn)
450            }
451        }
452    }
453}
454
455impl From<StorageError> for TransactionError {
456    fn from(err: StorageError) -> TransactionError {
457        TransactionError::Storage(err)
458    }
459}
460
461impl Display for TransactionError {
462    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
463        match self {
464            TransactionError::Storage(storage) => storage.fmt(f),
465            TransactionError::ReadTransactionStillInUse(_) => {
466                write!(f, "Transaction still in use")
467            }
468        }
469    }
470}
471
472impl std::error::Error for TransactionError {}
473
474/// Errors related to committing transactions
475#[derive(Debug)]
476#[non_exhaustive]
477pub enum CommitError {
478    /// Error from underlying storage
479    Storage(StorageError),
480}
481
482impl CommitError {
483    pub(crate) fn into_storage_error(self) -> StorageError {
484        match self {
485            CommitError::Storage(storage) => storage,
486        }
487    }
488}
489
490impl From<CommitError> for Error {
491    fn from(err: CommitError) -> Error {
492        match err {
493            CommitError::Storage(storage) => storage.into(),
494        }
495    }
496}
497
498impl From<StorageError> for CommitError {
499    fn from(err: StorageError) -> CommitError {
500        CommitError::Storage(err)
501    }
502}
503
504impl Display for CommitError {
505    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
506        match self {
507            CommitError::Storage(storage) => storage.fmt(f),
508        }
509    }
510}
511
512impl std::error::Error for CommitError {}
513
514/// Superset of all other errors that can occur. Convenience enum so that users can convert all errors into a single type
515#[derive(Debug)]
516#[non_exhaustive]
517pub enum Error {
518    /// The Database is already open. Cannot acquire lock.
519    DatabaseAlreadyOpen,
520    /// This savepoint is invalid or cannot be created.
521    ///
522    /// Savepoints become invalid when an older savepoint is restored after it was created,
523    /// and savepoints cannot be created if the transaction is "dirty" (any tables have been opened)
524    InvalidSavepoint,
525    /// [`crate::RepairSession::abort`] was called.
526    RepairAborted,
527    /// A persistent savepoint exists
528    PersistentSavepointExists,
529    /// An Ephemeral savepoint exists
530    EphemeralSavepointExists,
531    /// A transaction is still in-progress
532    TransactionInProgress,
533    /// The Database is corrupted
534    Corrupted(String),
535    /// The database file is in an old file format and must be manually upgraded
536    UpgradeRequired(u8),
537    /// The value being inserted exceeds the maximum of 3GiB
538    ValueTooLarge(usize),
539    /// Table types didn't match.
540    TableTypeMismatch {
541        table: String,
542        key: TypeName,
543        value: TypeName,
544    },
545    /// The table is a multimap table
546    TableIsMultimap(String),
547    /// The table is not a multimap table
548    TableIsNotMultimap(String),
549    TypeDefinitionChanged {
550        name: TypeName,
551        alignment: usize,
552        width: Option<usize>,
553    },
554    /// Table name does not match any table in database
555    TableDoesNotExist(String),
556    /// Table name already exists in the database
557    TableExists(String),
558    // Tables cannot be opened for writing multiple times, since they could retrieve immutable &
559    // mutable references to the same dirty pages, or multiple mutable references via insert_reserve()
560    TableAlreadyOpen(String, &'static panic::Location<'static>),
561    Io(io::Error),
562    /// A previous IO error occurred. The database must be closed and re-opened
563    PreviousIo,
564    LockPoisoned(&'static panic::Location<'static>),
565    /// The transaction is still referenced by a table or other object
566    ReadTransactionStillInUse(ReadTransaction),
567}
568
569impl<T> From<PoisonError<T>> for Error {
570    fn from(_: PoisonError<T>) -> Error {
571        Error::LockPoisoned(panic::Location::caller())
572    }
573}
574
575impl From<io::Error> for Error {
576    fn from(err: io::Error) -> Error {
577        Error::Io(err)
578    }
579}
580
581impl Display for Error {
582    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
583        match self {
584            Error::Corrupted(msg) => {
585                write!(f, "DB corrupted: {msg}")
586            }
587            Error::UpgradeRequired(actual) => {
588                write!(
589                    f,
590                    "Manual upgrade required. Expected file format version {FILE_FORMAT_VERSION2}, but file is version {actual}"
591                )
592            }
593            Error::ValueTooLarge(len) => {
594                write!(
595                    f,
596                    "The value (length={len}) being inserted exceeds the maximum of {}GiB",
597                    MAX_VALUE_LENGTH / 1024 / 1024 / 1024
598                )
599            }
600            Error::TypeDefinitionChanged {
601                name,
602                alignment,
603                width,
604            } => {
605                write!(
606                    f,
607                    "Current definition of {} does not match stored definition (width={:?}, alignment={})",
608                    name.name(),
609                    width,
610                    alignment,
611                )
612            }
613            Error::TableTypeMismatch { table, key, value } => {
614                write!(
615                    f,
616                    "{table} is of type Table<{}, {}>",
617                    key.name(),
618                    value.name(),
619                )
620            }
621            Error::TableIsMultimap(table) => {
622                write!(f, "{table} is a multimap table")
623            }
624            Error::TableIsNotMultimap(table) => {
625                write!(f, "{table} is not a multimap table")
626            }
627            Error::TableDoesNotExist(table) => {
628                write!(f, "Table '{table}' does not exist")
629            }
630            Error::TableExists(table) => {
631                write!(f, "Table '{table}' already exists")
632            }
633            Error::TableAlreadyOpen(name, location) => {
634                write!(f, "Table '{name}' already opened at: {location}")
635            }
636            Error::Io(err) => {
637                write!(f, "I/O error: {err}")
638            }
639            Error::PreviousIo => {
640                write!(
641                    f,
642                    "Previous I/O error occurred. Please close and re-open the database."
643                )
644            }
645            Error::LockPoisoned(location) => {
646                write!(f, "Poisoned internal lock: {location}")
647            }
648            Error::DatabaseAlreadyOpen => {
649                write!(f, "Database already open. Cannot acquire lock.")
650            }
651            Error::RepairAborted => {
652                write!(f, "Database repair aborted.")
653            }
654            Error::PersistentSavepointExists => {
655                write!(
656                    f,
657                    "Persistent savepoint exists. Operation cannot be performed."
658                )
659            }
660            Error::EphemeralSavepointExists => {
661                write!(
662                    f,
663                    "Ephemeral savepoint exists. Operation cannot be performed."
664                )
665            }
666            Error::TransactionInProgress => {
667                write!(
668                    f,
669                    "A transaction is still in progress. Operation cannot be performed."
670                )
671            }
672            Error::InvalidSavepoint => {
673                write!(f, "Savepoint is invalid or cannot be created.")
674            }
675            Error::ReadTransactionStillInUse(_) => {
676                write!(f, "Transaction still in use")
677            }
678        }
679    }
680}
681
682impl std::error::Error for Error {}