quaint_forked/
error.rs

1//! Error module
2use crate::connector::IsolationLevel;
3use std::{borrow::Cow, fmt, io, num};
4use thiserror::Error;
5
6#[cfg(feature = "pooled")]
7use std::time::Duration;
8
9#[derive(Debug, PartialEq, Eq)]
10pub enum DatabaseConstraint {
11    Fields(Vec<String>),
12    Index(String),
13    ForeignKey,
14    CannotParse,
15}
16
17impl DatabaseConstraint {
18    pub(crate) fn fields<I, S>(names: I) -> Self
19    where
20        I: IntoIterator<Item = S>,
21        S: ToString,
22    {
23        let fields = names.into_iter().map(|s| s.to_string()).collect();
24
25        Self::Fields(fields)
26    }
27}
28
29impl fmt::Display for DatabaseConstraint {
30    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31        match self {
32            Self::Fields(fields) => write!(f, "({})", fields.join(",")),
33            Self::Index(index) => index.fmt(f),
34            Self::ForeignKey => "FOREIGN KEY".fmt(f),
35            Self::CannotParse => "".fmt(f),
36        }
37    }
38}
39
40#[derive(Debug, PartialEq, Eq)]
41pub enum Name {
42    Available(String),
43    Unavailable,
44}
45
46impl Name {
47    pub fn available(name: impl ToString) -> Self {
48        Self::Available(name.to_string())
49    }
50}
51
52impl fmt::Display for Name {
53    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54        match self {
55            Self::Available(name) => name.fmt(f),
56            Self::Unavailable => write!(f, "(not available)"),
57        }
58    }
59}
60
61impl<T> From<Option<T>> for Name
62where
63    T: ToString,
64{
65    fn from(name: Option<T>) -> Self {
66        match name {
67            Some(name) => Self::available(name),
68            None => Self::Unavailable,
69        }
70    }
71}
72
73#[derive(Debug, Error)]
74/// The error types for database I/O, connection and query parameter
75/// construction.
76pub struct Error {
77    kind: ErrorKind,
78    original_code: Option<String>,
79    original_message: Option<String>,
80}
81
82pub(crate) struct ErrorBuilder {
83    kind: ErrorKind,
84    original_code: Option<String>,
85    original_message: Option<String>,
86}
87
88impl ErrorBuilder {
89    pub(crate) fn set_original_code(&mut self, code: impl Into<String>) -> &mut Self {
90        self.original_code = Some(code.into());
91        self
92    }
93
94    pub(crate) fn set_original_message(&mut self, message: impl Into<String>) -> &mut Self {
95        self.original_message = Some(message.into());
96        self
97    }
98
99    pub(crate) fn build(self) -> Error {
100        Error {
101            kind: self.kind,
102            original_code: self.original_code,
103            original_message: self.original_message,
104        }
105    }
106}
107
108impl Error {
109    pub(crate) fn builder(kind: ErrorKind) -> ErrorBuilder {
110        ErrorBuilder {
111            kind,
112            original_code: None,
113            original_message: None,
114        }
115    }
116
117    /// The error code sent by the database, if available.
118    pub fn original_code(&self) -> Option<&str> {
119        self.original_code.as_deref()
120    }
121
122    /// The original error message sent by the database, if available.
123    pub fn original_message(&self) -> Option<&str> {
124        self.original_message.as_deref()
125    }
126
127    /// A more specific error type for matching.
128    pub fn kind(&self) -> &ErrorKind {
129        &self.kind
130    }
131
132    /// Determines if the error was associated with closed connection.
133    pub fn is_closed(&self) -> bool {
134        matches!(self.kind, ErrorKind::ConnectionClosed)
135    }
136}
137
138impl fmt::Display for Error {
139    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
140        self.kind.fmt(f)
141    }
142}
143
144#[derive(Debug, Error)]
145pub enum ErrorKind {
146    #[error("Error querying the database: {}", _0)]
147    QueryError(Box<dyn std::error::Error + Send + Sync + 'static>),
148
149    #[error("Invalid input provided to query: {}", _0)]
150    QueryInvalidInput(String),
151
152    #[error("Database does not exist: {}", db_name)]
153    DatabaseDoesNotExist { db_name: Name },
154
155    #[error("Access denied to database {}", db_name)]
156    DatabaseAccessDenied { db_name: Name },
157
158    #[error("Database already exists {}", db_name)]
159    DatabaseAlreadyExists { db_name: Name },
160
161    #[error("Authentication failed for user {}", user)]
162    AuthenticationFailed { user: Name },
163
164    #[error("Query returned no data")]
165    NotFound,
166
167    #[error("No such table: {}", table)]
168    TableDoesNotExist { table: Name },
169
170    #[error("Unique constraint failed: {}", constraint)]
171    UniqueConstraintViolation { constraint: DatabaseConstraint },
172
173    #[error("Null constraint failed: {}", constraint)]
174    NullConstraintViolation { constraint: DatabaseConstraint },
175
176    #[error("Foreign key constraint failed: {}", constraint)]
177    ForeignKeyConstraintViolation { constraint: DatabaseConstraint },
178
179    #[error("Error creating a database connection.")]
180    ConnectionError(Box<dyn std::error::Error + Send + Sync + 'static>),
181
182    #[error("Error reading the column value: {}", _0)]
183    ColumnReadFailure(Box<dyn std::error::Error + Send + Sync + 'static>),
184
185    #[error("Error accessing result set, index out of bounds: {}", _0)]
186    ResultIndexOutOfBounds(usize),
187
188    #[error("Error accessing result set, column not found: {}", column)]
189    ColumnNotFound { column: Name },
190
191    #[error("Error accessing result set, type mismatch, expected: {}", _0)]
192    ResultTypeMismatch(&'static str),
193
194    #[error("Error parsing connection string: {}", _0)]
195    DatabaseUrlIsInvalid(String),
196
197    #[error("Conversion failed: {}", _0)]
198    ConversionError(Cow<'static, str>),
199
200    #[error("The value provided for column {:?} is too long.", column)]
201    LengthMismatch { column: Name },
202
203    #[error("The provided arguments are not supported")]
204    InvalidConnectionArguments,
205
206    #[error("Error in an I/O operation: {0}")]
207    IoError(io::Error),
208
209    #[error("Timed out when connecting to the database.")]
210    ConnectTimeout,
211
212    #[error("The server terminated the connection.")]
213    ConnectionClosed,
214
215    #[error(
216        "Timed out fetching a connection from the pool (connection limit: {}, in use: {}, pool timeout {})",
217        max_open,
218        in_use,
219        timeout
220    )]
221    PoolTimeout { max_open: u64, in_use: u64, timeout: u64 },
222
223    #[error("The connection pool has been closed")]
224    PoolClosed {},
225
226    #[error("Timed out during query execution.")]
227    SocketTimeout,
228
229    #[error("Error opening a TLS connection. {}", message)]
230    TlsError { message: String },
231
232    #[error("Value out of range error. {}", message)]
233    ValueOutOfRange { message: String },
234
235    #[cfg(feature = "serde-support")]
236        #[error("Deserializing a ResultRow {:?}", _0)]
237    FromRowError(serde::de::value::Error),
238
239    #[error(
240        "Incorrect number of parameters given to a statement. Expected {}: got: {}.",
241        expected,
242        actual
243    )]
244    IncorrectNumberOfParameters { expected: usize, actual: usize },
245
246    #[error("Transaction was already closed: {}", _0)]
247    TransactionAlreadyClosed(String),
248
249    #[error("Transaction write conflict")]
250    TransactionWriteConflict,
251
252    #[error("ROLLBACK statement has no corresponding BEGIN statement")]
253    RollbackWithoutBegin,
254
255    #[error("Invalid isolation level: {}", _0)]
256    InvalidIsolationLevel(String),
257
258    #[error("Error creating UUID, {}", _0)]
259    UUIDError(String),
260
261    #[error("Cannot find a FULLTEXT index to use for the search")]
262    MissingFullTextSearchIndex,
263
264    #[error("Column type '{}' could not be deserialized from the database.", column_type)]
265    UnsupportedColumnType { column_type: String },
266}
267
268impl ErrorKind {
269    #[cfg(feature = "mysql")]
270    pub(crate) fn value_out_of_range(msg: impl Into<String>) -> Self {
271        Self::ValueOutOfRange { message: msg.into() }
272    }
273
274    pub(crate) fn conversion(msg: impl Into<Cow<'static, str>>) -> Self {
275        Self::ConversionError(msg.into())
276    }
277
278    #[allow(dead_code)]
279    pub(crate) fn database_url_is_invalid(msg: impl Into<String>) -> Self {
280        Self::DatabaseUrlIsInvalid(msg.into())
281    }
282
283    #[cfg(feature = "pooled")]
284    pub(crate) fn pool_timeout(max_open: u64, in_use: u64, timeout: Duration) -> Self {
285        Self::PoolTimeout {
286            max_open,
287            in_use,
288            timeout: timeout.as_secs(),
289        }
290    }
291
292    pub(crate) fn invalid_isolation_level(isolation_level: &IsolationLevel) -> Self {
293        Self::InvalidIsolationLevel(isolation_level.to_string())
294    }
295}
296
297impl From<Error> for ErrorKind {
298    fn from(e: Error) -> Self {
299        e.kind
300    }
301}
302
303#[cfg(feature = "bigdecimal")]
304impl From<bigdecimal::ParseBigDecimalError> for Error {
305    fn from(e: bigdecimal::ParseBigDecimalError) -> Self {
306        let kind = ErrorKind::conversion(format!("{e}"));
307        Self::builder(kind).build()
308    }
309}
310
311#[cfg(feature = "json")]
312impl From<serde_json::Error> for Error {
313    fn from(_: serde_json::Error) -> Self {
314        Self::builder(ErrorKind::conversion("Malformed JSON data.")).build()
315    }
316}
317
318impl From<std::fmt::Error> for Error {
319    fn from(_: std::fmt::Error) -> Self {
320        Self::builder(ErrorKind::conversion("Problems writing AST into a query string.")).build()
321    }
322}
323
324impl From<num::TryFromIntError> for Error {
325    fn from(_: num::TryFromIntError) -> Self {
326        Self::builder(ErrorKind::conversion(
327            "Couldn't convert an integer (possible overflow).",
328        ))
329        .build()
330    }
331}
332
333impl From<connection_string::Error> for Error {
334    fn from(err: connection_string::Error) -> Error {
335        Self::builder(ErrorKind::DatabaseUrlIsInvalid(err.to_string())).build()
336    }
337}
338
339impl From<url::ParseError> for Error {
340    fn from(e: url::ParseError) -> Error {
341        let kind = ErrorKind::DatabaseUrlIsInvalid(e.to_string());
342        Error::builder(kind).build()
343    }
344}
345
346impl From<io::Error> for Error {
347    fn from(e: io::Error) -> Error {
348        Error::builder(ErrorKind::IoError(e)).build()
349    }
350}
351
352impl From<std::num::ParseIntError> for Error {
353    fn from(_e: std::num::ParseIntError) -> Error {
354        Error::builder(ErrorKind::conversion("Couldn't convert data to an integer")).build()
355    }
356}
357
358impl From<std::str::ParseBoolError> for Error {
359    fn from(_e: std::str::ParseBoolError) -> Error {
360        Error::builder(ErrorKind::conversion("Couldn't convert data to a boolean")).build()
361    }
362}
363
364impl From<std::string::FromUtf8Error> for Error {
365    fn from(_: std::string::FromUtf8Error) -> Error {
366        Error::builder(ErrorKind::conversion("Couldn't convert data to UTF-8")).build()
367    }
368}
369
370impl From<std::net::AddrParseError> for Error {
371    fn from(e: std::net::AddrParseError) -> Self {
372        Error::builder(ErrorKind::conversion(format!(
373            "Couldn't convert data to std::net::IpAddr: {e}"
374        )))
375        .build()
376    }
377}
378
379#[cfg(feature = "uuid")]
380impl From<uuid::Error> for Error {
381    fn from(e: uuid::Error) -> Self {
382        Error::builder(ErrorKind::UUIDError(format!("{e}"))).build()
383    }
384}