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