redb/
error.rs

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