matrix_sdk_sqlite/
error.rs

1// Copyright 2023 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use deadpool_sqlite::{CreatePoolError, PoolError};
16#[cfg(feature = "event-cache")]
17use matrix_sdk_base::event_cache::store::EventCacheStoreError;
18#[cfg(feature = "event-cache")]
19use matrix_sdk_base::media::store::MediaStoreError;
20#[cfg(feature = "state-store")]
21use matrix_sdk_base::store::StoreError as StateStoreError;
22#[cfg(feature = "crypto-store")]
23use matrix_sdk_crypto::CryptoStoreError;
24use thiserror::Error;
25use tokio::io;
26
27/// All the errors that can occur when opening an SQLite store.
28#[derive(Error, Debug)]
29#[non_exhaustive]
30pub enum OpenStoreError {
31    /// Failed to create the DB's parent directory.
32    #[error("Failed to create the database's parent directory: {0}")]
33    CreateDir(#[source] io::Error),
34
35    /// Failed to create the DB pool.
36    #[error(transparent)]
37    CreatePool(#[from] CreatePoolError),
38
39    /// Failed to load the database's version.
40    #[error("Failed to load database version: {0}")]
41    LoadVersion(#[source] rusqlite::Error),
42
43    /// The version of the database is missing.
44    #[error("Missing database version")]
45    MissingVersion,
46
47    /// The version of the database is invalid.
48    #[error("Invalid database version")]
49    InvalidVersion,
50
51    /// Failed to apply migrations.
52    #[error("Failed to run migrations: {0}")]
53    Migration(#[from] Error),
54
55    /// Failed to get a DB connection from the pool.
56    #[error(transparent)]
57    Pool(#[from] PoolError),
58
59    /// Failed to initialize the store cipher.
60    #[error("Failed to initialize the store cipher: {0}")]
61    InitCipher(#[from] matrix_sdk_store_encryption::Error),
62
63    /// Failed to load the store cipher from the DB.
64    #[error("Failed to load the store cipher from the DB: {0}")]
65    LoadCipher(#[source] rusqlite::Error),
66
67    /// Failed to save the store cipher to the DB.
68    #[error("Failed to save the store cipher to the DB: {0}")]
69    SaveCipher(#[source] rusqlite::Error),
70}
71
72#[derive(Debug, Error)]
73pub enum Error {
74    #[error(transparent)]
75    Sqlite(rusqlite::Error),
76
77    #[error("Failed to compute the maximum variable number from {0}")]
78    SqliteMaximumVariableNumber(i32),
79
80    #[error(transparent)]
81    Pool(PoolError),
82
83    #[error(transparent)]
84    Encode(rmp_serde::encode::Error),
85
86    #[error(transparent)]
87    Decode(rmp_serde::decode::Error),
88
89    #[error(transparent)]
90    Json(#[from] serde_json::Error),
91
92    #[error(transparent)]
93    Encryption(matrix_sdk_store_encryption::Error),
94
95    #[error("can't save/load sessions or group sessions in the store before an account is stored")]
96    AccountUnset,
97
98    #[error(transparent)]
99    Pickle(#[from] vodozemac::PickleError),
100
101    #[error("An object failed to be decrypted while unpickling")]
102    Unpickle,
103
104    #[error("Redaction failed: {0}")]
105    Redaction(#[source] ruma::canonical_json::RedactionError),
106
107    #[error("An update keyed by unique ID touched more than one entry")]
108    InconsistentUpdate,
109
110    #[error("The store contains invalid data: {details}")]
111    InvalidData { details: String },
112}
113
114macro_rules! impl_from {
115    ( $ty:ty => $enum:ident::$variant:ident ) => {
116        impl From<$ty> for $enum {
117            fn from(value: $ty) -> Self {
118                Self::$variant(value)
119            }
120        }
121    };
122}
123
124impl From<rusqlite::Error> for Error {
125    fn from(error: rusqlite::Error) -> Self {
126        if let rusqlite::Error::SqliteFailure(ffi_error, message) = &error {
127            if ffi_error.code == rusqlite::ErrorCode::DatabaseBusy {
128                // Report to sentry.
129                tracing::error!(
130                    sentry = true,
131                    sqlite_message = message,
132                    "observed database busy error"
133                );
134            }
135        }
136        Error::Sqlite(error)
137    }
138}
139
140impl_from!(PoolError => Error::Pool);
141impl_from!(rmp_serde::encode::Error => Error::Encode);
142impl_from!(rmp_serde::decode::Error => Error::Decode);
143impl_from!(matrix_sdk_store_encryption::Error => Error::Encryption);
144
145#[cfg(feature = "crypto-store")]
146impl From<Error> for CryptoStoreError {
147    fn from(e: Error) -> Self {
148        CryptoStoreError::backend(e)
149    }
150}
151
152#[cfg(feature = "state-store")]
153impl From<Error> for StateStoreError {
154    fn from(e: Error) -> Self {
155        match e {
156            Error::Json(e) => StateStoreError::Json(e),
157            Error::Encryption(e) => StateStoreError::Encryption(e),
158            Error::Redaction(e) => StateStoreError::Redaction(e),
159            e => StateStoreError::backend(e),
160        }
161    }
162}
163
164#[cfg(feature = "event-cache")]
165impl From<Error> for EventCacheStoreError {
166    fn from(e: Error) -> Self {
167        match e {
168            Error::Encryption(e) => EventCacheStoreError::Encryption(e),
169            e => EventCacheStoreError::backend(e),
170        }
171    }
172}
173
174#[cfg(feature = "event-cache")]
175impl From<Error> for MediaStoreError {
176    fn from(e: Error) -> Self {
177        match e {
178            Error::Encryption(e) => MediaStoreError::Encryption(e),
179            e => MediaStoreError::backend(e),
180        }
181    }
182}
183
184pub(crate) type Result<T, E = Error> = std::result::Result<T, E>;