1use 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)]
74pub 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 pub fn original_code(&self) -> Option<&str> {
119 self.original_code.as_deref()
120 }
121
122 pub fn original_message(&self) -> Option<&str> {
124 self.original_message.as_deref()
125 }
126
127 pub fn kind(&self) -> &ErrorKind {
129 &self.kind
130 }
131
132 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}