mas_storage_pg/
errors.rs

1// Copyright 2024 New Vector Ltd.
2// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.
3//
4// SPDX-License-Identifier: AGPL-3.0-only
5// Please see LICENSE in the repository root for full details.
6
7use sqlx::postgres::PgQueryResult;
8use thiserror::Error;
9use ulid::Ulid;
10
11/// Generic error when interacting with the database
12#[derive(Debug, Error)]
13#[error(transparent)]
14pub enum DatabaseError {
15    /// An error which came from the database itself
16    Driver {
17        /// The underlying error from the database driver
18        #[from]
19        source: sqlx::Error,
20    },
21
22    /// An error which occured while converting the data from the database
23    Inconsistency(#[from] DatabaseInconsistencyError),
24
25    /// An error which happened because the requested database operation is
26    /// invalid
27    #[error("Invalid database operation")]
28    InvalidOperation {
29        /// The source of the error, if any
30        #[source]
31        source: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
32    },
33
34    /// An error which happens when an operation affects not enough or too many
35    /// rows
36    #[error("Expected {expected} rows to be affected, but {actual} rows were affected")]
37    RowsAffected {
38        /// How many rows were expected to be affected
39        expected: u64,
40
41        /// How many rows were actually affected
42        actual: u64,
43    },
44}
45
46impl DatabaseError {
47    pub(crate) fn ensure_affected_rows(
48        result: &PgQueryResult,
49        expected: u64,
50    ) -> Result<(), DatabaseError> {
51        let actual = result.rows_affected();
52        if actual == expected {
53            Ok(())
54        } else {
55            Err(DatabaseError::RowsAffected { expected, actual })
56        }
57    }
58
59    pub(crate) fn to_invalid_operation<E: std::error::Error + Send + Sync + 'static>(e: E) -> Self {
60        Self::InvalidOperation {
61            source: Some(Box::new(e)),
62        }
63    }
64
65    pub(crate) const fn invalid_operation() -> Self {
66        Self::InvalidOperation { source: None }
67    }
68}
69
70/// An error which occured while converting the data from the database
71#[derive(Debug, Error)]
72pub struct DatabaseInconsistencyError {
73    /// The table which was being queried
74    table: &'static str,
75
76    /// The column which was being queried
77    column: Option<&'static str>,
78
79    /// The row which was being queried
80    row: Option<Ulid>,
81
82    /// The source of the error
83    #[source]
84    source: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
85}
86
87impl std::fmt::Display for DatabaseInconsistencyError {
88    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89        write!(f, "Database inconsistency on table {}", self.table)?;
90        if let Some(column) = self.column {
91            write!(f, " column {column}")?;
92        }
93        if let Some(row) = self.row {
94            write!(f, " row {row}")?;
95        }
96
97        Ok(())
98    }
99}
100
101impl DatabaseInconsistencyError {
102    /// Create a new [`DatabaseInconsistencyError`] for the given table
103    #[must_use]
104    pub(crate) const fn on(table: &'static str) -> Self {
105        Self {
106            table,
107            column: None,
108            row: None,
109            source: None,
110        }
111    }
112
113    /// Set the column which was being queried
114    #[must_use]
115    pub(crate) const fn column(mut self, column: &'static str) -> Self {
116        self.column = Some(column);
117        self
118    }
119
120    /// Set the row which was being queried
121    #[must_use]
122    pub(crate) const fn row(mut self, row: Ulid) -> Self {
123        self.row = Some(row);
124        self
125    }
126
127    /// Give the source of the error
128    #[must_use]
129    pub(crate) fn source<E: std::error::Error + Send + Sync + 'static>(
130        mut self,
131        source: E,
132    ) -> Self {
133        self.source = Some(Box::new(source));
134        self
135    }
136}