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 compaction
300#[derive(Debug)]
301#[non_exhaustive]
302pub enum CompactionError {
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<CompactionError> for Error {
314    fn from(err: CompactionError) -> Error {
315        match err {
316            CompactionError::PersistentSavepointExists => Error::PersistentSavepointExists,
317            CompactionError::EphemeralSavepointExists => Error::EphemeralSavepointExists,
318            CompactionError::TransactionInProgress => Error::TransactionInProgress,
319            CompactionError::Storage(storage) => storage.into(),
320        }
321    }
322}
323
324impl From<StorageError> for CompactionError {
325    fn from(err: StorageError) -> CompactionError {
326        CompactionError::Storage(err)
327    }
328}
329
330impl Display for CompactionError {
331    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
332        match self {
333            CompactionError::PersistentSavepointExists => {
334                write!(
335                    f,
336                    "Persistent savepoint exists. Operation cannot be performed."
337                )
338            }
339            CompactionError::EphemeralSavepointExists => {
340                write!(
341                    f,
342                    "Ephemeral savepoint exists. Operation cannot be performed."
343                )
344            }
345            CompactionError::TransactionInProgress => {
346                write!(
347                    f,
348                    "A transaction is still in progress. Operation cannot be performed."
349                )
350            }
351            CompactionError::Storage(storage) => storage.fmt(f),
352        }
353    }
354}
355
356impl std::error::Error for CompactionError {}
357
358/// Errors related to transactions
359#[derive(Debug)]
360#[non_exhaustive]
361pub enum TransactionError {
362    /// Error from underlying storage
363    Storage(StorageError),
364    /// The transaction is still referenced by a table or other object
365    ReadTransactionStillInUse(ReadTransaction),
366}
367
368impl TransactionError {
369    pub(crate) fn into_storage_error(self) -> StorageError {
370        match self {
371            TransactionError::Storage(storage) => storage,
372            _ => unreachable!(),
373        }
374    }
375}
376
377impl From<TransactionError> for Error {
378    fn from(err: TransactionError) -> Error {
379        match err {
380            TransactionError::Storage(storage) => storage.into(),
381            TransactionError::ReadTransactionStillInUse(txn) => {
382                Error::ReadTransactionStillInUse(txn)
383            }
384        }
385    }
386}
387
388impl From<StorageError> for TransactionError {
389    fn from(err: StorageError) -> TransactionError {
390        TransactionError::Storage(err)
391    }
392}
393
394impl Display for TransactionError {
395    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
396        match self {
397            TransactionError::Storage(storage) => storage.fmt(f),
398            TransactionError::ReadTransactionStillInUse(_) => {
399                write!(f, "Transaction still in use")
400            }
401        }
402    }
403}
404
405impl std::error::Error for TransactionError {}
406
407/// Errors related to committing transactions
408#[derive(Debug)]
409#[non_exhaustive]
410pub enum CommitError {
411    /// Error from underlying storage
412    Storage(StorageError),
413}
414
415impl CommitError {
416    pub(crate) fn into_storage_error(self) -> StorageError {
417        match self {
418            CommitError::Storage(storage) => storage,
419        }
420    }
421}
422
423impl From<CommitError> for Error {
424    fn from(err: CommitError) -> Error {
425        match err {
426            CommitError::Storage(storage) => storage.into(),
427        }
428    }
429}
430
431impl From<StorageError> for CommitError {
432    fn from(err: StorageError) -> CommitError {
433        CommitError::Storage(err)
434    }
435}
436
437impl Display for CommitError {
438    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
439        match self {
440            CommitError::Storage(storage) => storage.fmt(f),
441        }
442    }
443}
444
445impl std::error::Error for CommitError {}
446
447/// Superset of all other errors that can occur. Convenience enum so that users can convert all errors into a single type
448#[derive(Debug)]
449#[non_exhaustive]
450pub enum Error {
451    /// The Database is already open. Cannot acquire lock.
452    DatabaseAlreadyOpen,
453    /// This savepoint is invalid or cannot be created.
454    ///
455    /// Savepoints become invalid when an older savepoint is restored after it was created,
456    /// and savepoints cannot be created if the transaction is "dirty" (any tables have been opened)
457    InvalidSavepoint,
458    /// [`crate::RepairSession::abort`] was called.
459    RepairAborted,
460    /// A persistent savepoint exists
461    PersistentSavepointExists,
462    /// An Ephemeral savepoint exists
463    EphemeralSavepointExists,
464    /// A transaction is still in-progress
465    TransactionInProgress,
466    /// The Database is corrupted
467    Corrupted(String),
468    /// The database file is in an old file format and must be manually upgraded
469    UpgradeRequired(u8),
470    /// The value being inserted exceeds the maximum of 3GiB
471    ValueTooLarge(usize),
472    /// Table types didn't match.
473    TableTypeMismatch {
474        table: String,
475        key: TypeName,
476        value: TypeName,
477    },
478    /// The table is a multimap table
479    TableIsMultimap(String),
480    /// The table is not a multimap table
481    TableIsNotMultimap(String),
482    TypeDefinitionChanged {
483        name: TypeName,
484        alignment: usize,
485        width: Option<usize>,
486    },
487    /// Table name does not match any table in database
488    TableDoesNotExist(String),
489    /// Table name already exists in the database
490    TableExists(String),
491    // Tables cannot be opened for writing multiple times, since they could retrieve immutable &
492    // mutable references to the same dirty pages, or multiple mutable references via insert_reserve()
493    TableAlreadyOpen(String, &'static panic::Location<'static>),
494    Io(io::Error),
495    /// A previous IO error occurred. The database must be closed and re-opened
496    PreviousIo,
497    LockPoisoned(&'static panic::Location<'static>),
498    /// The transaction is still referenced by a table or other object
499    ReadTransactionStillInUse(ReadTransaction),
500}
501
502impl<T> From<PoisonError<T>> for Error {
503    fn from(_: PoisonError<T>) -> Error {
504        Error::LockPoisoned(panic::Location::caller())
505    }
506}
507
508impl From<io::Error> for Error {
509    fn from(err: io::Error) -> Error {
510        Error::Io(err)
511    }
512}
513
514impl Display for Error {
515    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
516        match self {
517            Error::Corrupted(msg) => {
518                write!(f, "DB corrupted: {msg}")
519            }
520            Error::UpgradeRequired(actual) => {
521                write!(
522                    f,
523                    "Manual upgrade required. Expected file format version {FILE_FORMAT_VERSION2}, but file is version {actual}"
524                )
525            }
526            Error::ValueTooLarge(len) => {
527                write!(
528                    f,
529                    "The value (length={len}) being inserted exceeds the maximum of {}GiB",
530                    MAX_VALUE_LENGTH / 1024 / 1024 / 1024
531                )
532            }
533            Error::TypeDefinitionChanged {
534                name,
535                alignment,
536                width,
537            } => {
538                write!(
539                    f,
540                    "Current definition of {} does not match stored definition (width={:?}, alignment={})",
541                    name.name(),
542                    width,
543                    alignment,
544                )
545            }
546            Error::TableTypeMismatch { table, key, value } => {
547                write!(
548                    f,
549                    "{table} is of type Table<{}, {}>",
550                    key.name(),
551                    value.name(),
552                )
553            }
554            Error::TableIsMultimap(table) => {
555                write!(f, "{table} is a multimap table")
556            }
557            Error::TableIsNotMultimap(table) => {
558                write!(f, "{table} is not a multimap table")
559            }
560            Error::TableDoesNotExist(table) => {
561                write!(f, "Table '{table}' does not exist")
562            }
563            Error::TableExists(table) => {
564                write!(f, "Table '{table}' already exists")
565            }
566            Error::TableAlreadyOpen(name, location) => {
567                write!(f, "Table '{name}' already opened at: {location}")
568            }
569            Error::Io(err) => {
570                write!(f, "I/O error: {err}")
571            }
572            Error::PreviousIo => {
573                write!(
574                    f,
575                    "Previous I/O error occurred. Please close and re-open the database."
576                )
577            }
578            Error::LockPoisoned(location) => {
579                write!(f, "Poisoned internal lock: {location}")
580            }
581            Error::DatabaseAlreadyOpen => {
582                write!(f, "Database already open. Cannot acquire lock.")
583            }
584            Error::RepairAborted => {
585                write!(f, "Database repair aborted.")
586            }
587            Error::PersistentSavepointExists => {
588                write!(
589                    f,
590                    "Persistent savepoint exists. Operation cannot be performed."
591                )
592            }
593            Error::EphemeralSavepointExists => {
594                write!(
595                    f,
596                    "Ephemeral savepoint exists. Operation cannot be performed."
597                )
598            }
599            Error::TransactionInProgress => {
600                write!(
601                    f,
602                    "A transaction is still in progress. Operation cannot be performed."
603                )
604            }
605            Error::InvalidSavepoint => {
606                write!(f, "Savepoint is invalid or cannot be created.")
607            }
608            Error::ReadTransactionStillInUse(_) => {
609                write!(f, "Transaction still in use")
610            }
611        }
612    }
613}
614
615impl std::error::Error for Error {}