Skip to main content

redbx/
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    /// Incorrect password provided for encrypted database
217    IncorrectPassword,
218    /// Encryption data is corrupted
219    CorruptedEncryption(String),
220    /// Unsupported encryption algorithm or parameters
221    UnsupportedEncryption(String),
222    /// Key derivation failed
223    KeyDerivationFailed,
224    /// Encryption operation failed
225    EncryptionFailed(String),
226    /// Decryption operation failed
227    DecryptionFailed(String),
228    /// Salt generation failed
229    SaltGenerationFailed,
230    /// Incompatible file format
231    IncompatibleFileFormat,
232}
233
234impl From<DatabaseError> for Error {
235    fn from(err: DatabaseError) -> Error {
236        match err {
237            DatabaseError::DatabaseAlreadyOpen => Error::DatabaseAlreadyOpen,
238            DatabaseError::RepairAborted => Error::RepairAborted,
239            DatabaseError::UpgradeRequired(x) => Error::UpgradeRequired(x),
240            DatabaseError::Storage(storage) => storage.into(),
241            DatabaseError::IncorrectPassword => Error::IncorrectPassword,
242            DatabaseError::CorruptedEncryption(msg) => Error::CorruptedEncryption(msg),
243            DatabaseError::UnsupportedEncryption(msg) => Error::UnsupportedEncryption(msg),
244            DatabaseError::KeyDerivationFailed => Error::KeyDerivationFailed,
245            DatabaseError::EncryptionFailed(msg) => Error::EncryptionFailed(msg),
246            DatabaseError::DecryptionFailed(msg) => Error::DecryptionFailed(msg),
247            DatabaseError::SaltGenerationFailed => Error::SaltGenerationFailed,
248            DatabaseError::IncompatibleFileFormat => Error::IncompatibleFileFormat,
249        }
250    }
251}
252
253impl From<io::Error> for DatabaseError {
254    fn from(err: io::Error) -> DatabaseError {
255        DatabaseError::Storage(StorageError::Io(err))
256    }
257}
258
259impl From<StorageError> for DatabaseError {
260    fn from(err: StorageError) -> DatabaseError {
261        DatabaseError::Storage(err)
262    }
263}
264
265impl Display for DatabaseError {
266    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
267        match self {
268            DatabaseError::UpgradeRequired(actual) => {
269                write!(
270                    f,
271                    "Manual upgrade required. Expected file format version {FILE_FORMAT_VERSION3}, but file is version {actual}"
272                )
273            }
274            DatabaseError::RepairAborted => {
275                write!(f, "Database repair aborted.")
276            }
277            DatabaseError::DatabaseAlreadyOpen => {
278                write!(f, "Database already open. Cannot acquire lock.")
279            }
280            DatabaseError::Storage(storage) => storage.fmt(f),
281            DatabaseError::IncorrectPassword => {
282                write!(f, "Incorrect password provided for encrypted database.")
283            }
284            DatabaseError::CorruptedEncryption(msg) => {
285                write!(f, "Encryption data is corrupted: {msg}")
286            }
287            DatabaseError::UnsupportedEncryption(msg) => {
288                write!(f, "Unsupported encryption algorithm or parameters: {msg}")
289            }
290            DatabaseError::KeyDerivationFailed => {
291                write!(f, "Key derivation failed.")
292            }
293            DatabaseError::EncryptionFailed(msg) => {
294                write!(f, "Encryption operation failed: {msg}")
295            }
296            DatabaseError::DecryptionFailed(msg) => {
297                write!(f, "Decryption operation failed: {msg}")
298            }
299            DatabaseError::SaltGenerationFailed => {
300                write!(f, "Salt generation failed.")
301            }
302            DatabaseError::IncompatibleFileFormat => {
303                write!(f, "Incompatible file format.")
304            }
305        }
306    }
307}
308
309impl std::error::Error for DatabaseError {}
310
311/// Errors related to savepoints
312#[derive(Debug)]
313#[non_exhaustive]
314pub enum SavepointError {
315    /// This savepoint is invalid or cannot be created.
316    ///
317    /// Savepoints become invalid when an older savepoint is restored after it was created,
318    /// and savepoints cannot be created if the transaction is "dirty" (any tables have been opened)
319    InvalidSavepoint,
320    /// Error from underlying storage
321    Storage(StorageError),
322}
323
324impl From<SavepointError> for Error {
325    fn from(err: SavepointError) -> Error {
326        match err {
327            SavepointError::InvalidSavepoint => Error::InvalidSavepoint,
328            SavepointError::Storage(storage) => storage.into(),
329        }
330    }
331}
332
333impl From<StorageError> for SavepointError {
334    fn from(err: StorageError) -> SavepointError {
335        SavepointError::Storage(err)
336    }
337}
338
339impl Display for SavepointError {
340    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
341        match self {
342            SavepointError::InvalidSavepoint => {
343                write!(f, "Savepoint is invalid or cannot be created.")
344            }
345            SavepointError::Storage(storage) => storage.fmt(f),
346        }
347    }
348}
349
350impl std::error::Error for SavepointError {}
351
352/// Errors related to compaction
353#[derive(Debug)]
354#[non_exhaustive]
355pub enum CompactionError {
356    /// A persistent savepoint exists
357    PersistentSavepointExists,
358    /// A ephemeral savepoint exists
359    EphemeralSavepointExists,
360    /// A transaction is still in-progress
361    TransactionInProgress,
362    /// Error from underlying storage
363    Storage(StorageError),
364}
365
366impl From<CompactionError> for Error {
367    fn from(err: CompactionError) -> Error {
368        match err {
369            CompactionError::PersistentSavepointExists => Error::PersistentSavepointExists,
370            CompactionError::EphemeralSavepointExists => Error::EphemeralSavepointExists,
371            CompactionError::TransactionInProgress => Error::TransactionInProgress,
372            CompactionError::Storage(storage) => storage.into(),
373        }
374    }
375}
376
377impl From<StorageError> for CompactionError {
378    fn from(err: StorageError) -> CompactionError {
379        CompactionError::Storage(err)
380    }
381}
382
383impl Display for CompactionError {
384    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
385        match self {
386            CompactionError::PersistentSavepointExists => {
387                write!(
388                    f,
389                    "Persistent savepoint exists. Operation cannot be performed."
390                )
391            }
392            CompactionError::EphemeralSavepointExists => {
393                write!(
394                    f,
395                    "Ephemeral savepoint exists. Operation cannot be performed."
396                )
397            }
398            CompactionError::TransactionInProgress => {
399                write!(
400                    f,
401                    "A transaction is still in progress. Operation cannot be performed."
402                )
403            }
404            CompactionError::Storage(storage) => storage.fmt(f),
405        }
406    }
407}
408
409impl std::error::Error for CompactionError {}
410
411/// Errors related to transactions
412#[derive(Debug)]
413#[non_exhaustive]
414pub enum SetDurabilityError {
415    /// A persistent savepoint was modified
416    PersistentSavepointModified,
417}
418
419impl From<SetDurabilityError> for Error {
420    fn from(err: SetDurabilityError) -> Error {
421        match err {
422            SetDurabilityError::PersistentSavepointModified => Error::PersistentSavepointModified,
423        }
424    }
425}
426
427impl Display for SetDurabilityError {
428    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
429        match self {
430            SetDurabilityError::PersistentSavepointModified => {
431                write!(
432                    f,
433                    "Persistent savepoint modified. Cannot reduce transaction durability"
434                )
435            }
436        }
437    }
438}
439
440impl std::error::Error for SetDurabilityError {}
441
442/// Errors related to transactions
443#[derive(Debug)]
444#[non_exhaustive]
445pub enum TransactionError {
446    /// Error from underlying storage
447    Storage(StorageError),
448    /// The transaction is still referenced by a table or other object
449    ReadTransactionStillInUse(Box<ReadTransaction>),
450}
451
452impl TransactionError {
453    pub(crate) fn into_storage_error(self) -> StorageError {
454        match self {
455            TransactionError::Storage(storage) => storage,
456            _ => unreachable!(),
457        }
458    }
459}
460
461impl From<TransactionError> for Error {
462    fn from(err: TransactionError) -> Error {
463        match err {
464            TransactionError::Storage(storage) => storage.into(),
465            TransactionError::ReadTransactionStillInUse(txn) => {
466                Error::ReadTransactionStillInUse(txn)
467            }
468        }
469    }
470}
471
472impl From<StorageError> for TransactionError {
473    fn from(err: StorageError) -> TransactionError {
474        TransactionError::Storage(err)
475    }
476}
477
478impl Display for TransactionError {
479    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
480        match self {
481            TransactionError::Storage(storage) => storage.fmt(f),
482            TransactionError::ReadTransactionStillInUse(_) => {
483                write!(f, "Transaction still in use")
484            }
485        }
486    }
487}
488
489impl std::error::Error for TransactionError {}
490
491/// Errors related to committing transactions
492#[derive(Debug)]
493#[non_exhaustive]
494pub enum CommitError {
495    /// Error from underlying storage
496    Storage(StorageError),
497}
498
499impl CommitError {
500    pub(crate) fn into_storage_error(self) -> StorageError {
501        match self {
502            CommitError::Storage(storage) => storage,
503        }
504    }
505}
506
507impl From<CommitError> for Error {
508    fn from(err: CommitError) -> Error {
509        match err {
510            CommitError::Storage(storage) => storage.into(),
511        }
512    }
513}
514
515impl From<StorageError> for CommitError {
516    fn from(err: StorageError) -> CommitError {
517        CommitError::Storage(err)
518    }
519}
520
521impl Display for CommitError {
522    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
523        match self {
524            CommitError::Storage(storage) => storage.fmt(f),
525        }
526    }
527}
528
529impl std::error::Error for CommitError {}
530
531/// Superset of all other errors that can occur. Convenience enum so that users can convert all errors into a single type
532#[derive(Debug)]
533#[non_exhaustive]
534pub enum Error {
535    /// The Database is already open. Cannot acquire lock.
536    DatabaseAlreadyOpen,
537    /// This savepoint is invalid or cannot be created.
538    ///
539    /// Savepoints become invalid when an older savepoint is restored after it was created,
540    /// and savepoints cannot be created if the transaction is "dirty" (any tables have been opened)
541    InvalidSavepoint,
542    /// [`crate::RepairSession::abort`] was called.
543    RepairAborted,
544    /// A persistent savepoint was modified
545    PersistentSavepointModified,
546    /// A persistent savepoint exists
547    PersistentSavepointExists,
548    /// An Ephemeral savepoint exists
549    EphemeralSavepointExists,
550    /// A transaction is still in-progress
551    TransactionInProgress,
552    /// The Database is corrupted
553    Corrupted(String),
554    /// The database file is in an old file format and must be manually upgraded
555    UpgradeRequired(u8),
556    /// The value being inserted exceeds the maximum of 3GiB
557    ValueTooLarge(usize),
558    /// Table types didn't match.
559    TableTypeMismatch {
560        table: String,
561        key: TypeName,
562        value: TypeName,
563    },
564    /// The table is a multimap table
565    TableIsMultimap(String),
566    /// The table is not a multimap table
567    TableIsNotMultimap(String),
568    TypeDefinitionChanged {
569        name: TypeName,
570        alignment: usize,
571        width: Option<usize>,
572    },
573    /// Table name does not match any table in database
574    TableDoesNotExist(String),
575    /// Table name already exists in the database
576    TableExists(String),
577    // Tables cannot be opened for writing multiple times, since they could retrieve immutable &
578    // mutable references to the same dirty pages, or multiple mutable references via insert_reserve()
579    TableAlreadyOpen(String, &'static panic::Location<'static>),
580    Io(io::Error),
581    DatabaseClosed,
582    /// A previous IO error occurred. The database must be closed and re-opened
583    PreviousIo,
584    LockPoisoned(&'static panic::Location<'static>),
585    /// The transaction is still referenced by a table or other object
586    ReadTransactionStillInUse(Box<ReadTransaction>),
587    /// Incorrect password provided for encrypted database
588    IncorrectPassword,
589    /// Encryption data is corrupted
590    CorruptedEncryption(String),
591    /// Unsupported encryption algorithm or parameters
592    UnsupportedEncryption(String),
593    /// Key derivation failed
594    KeyDerivationFailed,
595    /// Encryption operation failed
596    EncryptionFailed(String),
597    /// Decryption operation failed
598    DecryptionFailed(String),
599    /// Salt generation failed
600    SaltGenerationFailed,
601    /// Incompatible file format
602    IncompatibleFileFormat,
603}
604
605impl<T> From<PoisonError<T>> for Error {
606    fn from(_: PoisonError<T>) -> Error {
607        Error::LockPoisoned(panic::Location::caller())
608    }
609}
610
611impl From<io::Error> for Error {
612    fn from(err: io::Error) -> Error {
613        Error::Io(err)
614    }
615}
616
617impl Display for Error {
618    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
619        match self {
620            Error::Corrupted(msg) => {
621                write!(f, "DB corrupted: {msg}")
622            }
623            Error::UpgradeRequired(actual) => {
624                write!(
625                    f,
626                    "Manual upgrade required. Expected file format version {FILE_FORMAT_VERSION3}, but file is version {actual}"
627                )
628            }
629            Error::ValueTooLarge(len) => {
630                write!(
631                    f,
632                    "The value (length={len}) being inserted exceeds the maximum of {}GiB",
633                    MAX_VALUE_LENGTH / 1024 / 1024 / 1024
634                )
635            }
636            Error::TypeDefinitionChanged {
637                name,
638                alignment,
639                width,
640            } => {
641                write!(
642                    f,
643                    "Current definition of {} does not match stored definition (width={:?}, alignment={})",
644                    name.name(),
645                    width,
646                    alignment,
647                )
648            }
649            Error::TableTypeMismatch { table, key, value } => {
650                write!(
651                    f,
652                    "{table} is of type Table<{}, {}>",
653                    key.name(),
654                    value.name(),
655                )
656            }
657            Error::TableIsMultimap(table) => {
658                write!(f, "{table} is a multimap table")
659            }
660            Error::TableIsNotMultimap(table) => {
661                write!(f, "{table} is not a multimap table")
662            }
663            Error::TableDoesNotExist(table) => {
664                write!(f, "Table '{table}' does not exist")
665            }
666            Error::TableExists(table) => {
667                write!(f, "Table '{table}' already exists")
668            }
669            Error::TableAlreadyOpen(name, location) => {
670                write!(f, "Table '{name}' already opened at: {location}")
671            }
672            Error::Io(err) => {
673                write!(f, "I/O error: {err}")
674            }
675            Error::DatabaseClosed => {
676                write!(f, "Database has been closed")
677            }
678            Error::PreviousIo => {
679                write!(
680                    f,
681                    "Previous I/O error occurred. Please close and re-open the database."
682                )
683            }
684            Error::LockPoisoned(location) => {
685                write!(f, "Poisoned internal lock: {location}")
686            }
687            Error::DatabaseAlreadyOpen => {
688                write!(f, "Database already open. Cannot acquire lock.")
689            }
690            Error::RepairAborted => {
691                write!(f, "Database repair aborted.")
692            }
693            Error::PersistentSavepointModified => {
694                write!(
695                    f,
696                    "Persistent savepoint modified. Cannot reduce transaction durability"
697                )
698            }
699            Error::PersistentSavepointExists => {
700                write!(
701                    f,
702                    "Persistent savepoint exists. Operation cannot be performed."
703                )
704            }
705            Error::EphemeralSavepointExists => {
706                write!(
707                    f,
708                    "Ephemeral savepoint exists. Operation cannot be performed."
709                )
710            }
711            Error::TransactionInProgress => {
712                write!(
713                    f,
714                    "A transaction is still in progress. Operation cannot be performed."
715                )
716            }
717            Error::InvalidSavepoint => {
718                write!(f, "Savepoint is invalid or cannot be created.")
719            }
720            Error::ReadTransactionStillInUse(_) => {
721                write!(f, "Transaction still in use")
722            }
723            Error::IncorrectPassword => {
724                write!(f, "Incorrect password provided for encrypted database.")
725            }
726            Error::CorruptedEncryption(msg) => {
727                write!(f, "Encryption data is corrupted: {msg}")
728            }
729            Error::UnsupportedEncryption(msg) => {
730                write!(f, "Unsupported encryption algorithm or parameters: {msg}")
731            }
732            Error::KeyDerivationFailed => {
733                write!(f, "Key derivation failed.")
734            }
735            Error::EncryptionFailed(msg) => {
736                write!(f, "Encryption operation failed: {msg}")
737            }
738            Error::DecryptionFailed(msg) => {
739                write!(f, "Decryption operation failed: {msg}")
740            }
741            Error::SaltGenerationFailed => {
742                write!(f, "Salt generation failed.")
743            }
744            Error::IncompatibleFileFormat => {
745                write!(f, "Incompatible file format.")
746            }
747        }
748    }
749}
750
751impl std::error::Error for Error {}