diesel/
result.rs

1//! Errors, type aliases, and functions related to working with `Result`.
2
3use std::convert::From;
4use std::error::Error as StdError;
5use std::ffi::NulError;
6use std::fmt::{self, Display};
7
8#[derive(Debug)]
9#[allow(clippy::enum_variant_names)]
10/// Represents all the ways that a query can fail.
11///
12/// This type is not intended to be exhaustively matched, and new variants may
13/// be added in the future without a major version bump.
14pub enum Error {
15    /// The query contained a nul byte.
16    ///
17    /// This should never occur in normal usage.
18    InvalidCString(NulError),
19
20    /// The database returned an error.
21    ///
22    /// While Diesel prevents almost all sources of runtime errors at compile
23    /// time, it does not attempt to prevent 100% of them. Typically this error
24    /// will occur from insert or update statements due to a constraint
25    /// violation.
26    DatabaseError(
27        DatabaseErrorKind,
28        Box<dyn DatabaseErrorInformation + Send + Sync>,
29    ),
30
31    /// No rows were returned by a query expected to return at least one row.
32    ///
33    /// This variant is only returned by [`get_result`] and [`first`]. [`load`]
34    /// does not treat 0 rows as an error. If you would like to allow either 0
35    /// or 1 rows, call [`optional`] on the result.
36    ///
37    /// [`get_result`]: ../query_dsl/trait.RunQueryDsl.html#method.get_result
38    /// [`first`]: ../query_dsl/trait.RunQueryDsl.html#method.first
39    /// [`load`]: ../query_dsl/trait.RunQueryDsl.html#method.load
40    /// [`optional`]: trait.OptionalExtension.html#tymethod.optional
41    NotFound,
42
43    /// The query could not be constructed
44    ///
45    /// An example of when this error could occur is if you are attempting to
46    /// construct an update statement with no changes (e.g. all fields on the
47    /// struct are `None`).
48    QueryBuilderError(Box<dyn StdError + Send + Sync>),
49
50    /// An error occurred deserializing the data being sent to the database.
51    ///
52    /// Typically this error means that the stated type of the query is
53    /// incorrect. An example of when this error might occur in normal usage is
54    /// attempting to deserialize an infinite date into chrono.
55    DeserializationError(Box<dyn StdError + Send + Sync>),
56
57    /// An error occurred serializing the data being sent to the database.
58    ///
59    /// An example of when this error would be returned is if you attempted to
60    /// serialize a `chrono::NaiveDate` earlier than the earliest date supported
61    /// by PostgreSQL.
62    SerializationError(Box<dyn StdError + Send + Sync>),
63
64    /// Roll back the current transaction.
65    ///
66    /// You can return this variant inside of a transaction when you want to
67    /// roll it back, but have no actual error to return. Diesel will never
68    /// return this variant unless you gave it to us, and it can be safely
69    /// ignored in error handling.
70    RollbackTransaction,
71
72    /// Attempted to perform an operation that cannot be done inside a transaction
73    /// when a transaction was already open.
74    AlreadyInTransaction,
75
76    #[doc(hidden)]
77    __Nonexhaustive,
78}
79
80#[derive(Debug, Clone, Copy)]
81/// The kind of database error that occurred.
82///
83/// This is not meant to exhaustively cover all possible errors, but is used to
84/// identify errors which are commonly recovered from programmatically. This enum
85/// is not intended to be exhaustively matched, and new variants may be added in
86/// the future without a major version bump.
87pub enum DatabaseErrorKind {
88    /// A unique constraint was violated.
89    UniqueViolation,
90
91    /// A foreign key constraint was violated.
92    ForeignKeyViolation,
93
94    /// The query could not be sent to the database due to a protocol violation.
95    ///
96    /// An example of a case where this would occur is if you attempted to send
97    /// a query with more than 65000 bind parameters using PostgreSQL.
98    UnableToSendCommand,
99
100    /// A serializable transaction failed to commit due to a read/write
101    /// dependency on a concurrent transaction.
102    ///
103    /// Corresponds to SQLSTATE code 40001
104    ///
105    /// This error is only detected for PostgreSQL, as we do not yet support
106    /// transaction isolation levels for other backends.
107    SerializationFailure,
108
109    #[doc(hidden)]
110    __Unknown, // Match against _ instead, more variants may be added in the future
111}
112
113/// Information about an error that was returned by the database.
114pub trait DatabaseErrorInformation {
115    /// The primary human-readable error message. Typically one line.
116    fn message(&self) -> &str;
117
118    /// An optional secondary error message providing more details about the
119    /// problem, if it was provided by the database. Might span multiple lines.
120    fn details(&self) -> Option<&str>;
121
122    /// An optional suggestion of what to do about the problem, if one was
123    /// provided by the database.
124    fn hint(&self) -> Option<&str>;
125
126    /// The name of the table the error was associated with, if the error was
127    /// associated with a specific table and the backend supports retrieving
128    /// that information.
129    ///
130    /// Currently this method will return `None` for all backends other than
131    /// PostgreSQL.
132    fn table_name(&self) -> Option<&str>;
133
134    /// The name of the column the error was associated with, if the error was
135    /// associated with a specific column and the backend supports retrieving
136    /// that information.
137    ///
138    /// Currently this method will return `None` for all backends other than
139    /// PostgreSQL.
140    fn column_name(&self) -> Option<&str>;
141
142    /// The constraint that was violated if this error is a constraint violation
143    /// and the backend supports retrieving that information.
144    ///
145    /// Currently this method will return `None` for all backends other than
146    /// PostgreSQL.
147    fn constraint_name(&self) -> Option<&str>;
148}
149
150impl fmt::Debug for dyn DatabaseErrorInformation + Send + Sync {
151    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
152        fmt::Debug::fmt(&self.message(), f)
153    }
154}
155
156impl DatabaseErrorInformation for String {
157    fn message(&self) -> &str {
158        self
159    }
160
161    fn details(&self) -> Option<&str> {
162        None
163    }
164    fn hint(&self) -> Option<&str> {
165        None
166    }
167    fn table_name(&self) -> Option<&str> {
168        None
169    }
170    fn column_name(&self) -> Option<&str> {
171        None
172    }
173    fn constraint_name(&self) -> Option<&str> {
174        None
175    }
176}
177
178/// Errors which can occur during [`Connection::establish`]
179///
180/// [`Connection::establish`]: ../connection/trait.Connection.html#tymethod.establish
181#[derive(Debug, PartialEq)]
182pub enum ConnectionError {
183    /// The connection URL contained a `NUL` byte.
184    InvalidCString(NulError),
185    /// The database returned an error.
186    BadConnection(String),
187    /// The connection URL could not be parsed.
188    InvalidConnectionUrl(String),
189    /// Diesel could not configure the database connection.
190    ///
191    /// Diesel may try to automatically set session specific configuration
192    /// values, such as UTF8 encoding, or enabling the `||` operator on MySQL.
193    /// This variant is returned if an error occurred executing the query to set
194    /// those options. Diesel will never affect global configuration.
195    CouldntSetupConfiguration(Error),
196    #[doc(hidden)]
197    __Nonexhaustive, // Match against _ instead, more variants may be added in the future
198}
199
200/// A specialized result type for queries.
201///
202/// This type is exported by `diesel::prelude`, and is generally used by any
203/// code which is interacting with Diesel. This type exists to avoid writing out
204/// `diesel::result::Error`, and is otherwise a direct mapping to `Result`.
205pub type QueryResult<T> = Result<T, Error>;
206
207/// A specialized result type for establishing connections.
208///
209/// This type exists to avoid writing out `diesel::result::ConnectionError`, and
210/// is otherwise a direct mapping to `Result`.
211pub type ConnectionResult<T> = Result<T, ConnectionError>;
212
213/// See the [method documentation](#tymethod.optional).
214pub trait OptionalExtension<T> {
215    /// Converts a `QueryResult<T>` into a `QueryResult<Option<T>>`.
216    ///
217    /// By default, Diesel treats 0 rows being returned from a query that is expected to return 1
218    /// row as an error (e.g. the return value of [`get_result`] or [`first`]). This method will
219    /// handle that error, and give you back an `Option<T>` instead.
220    ///
221    /// [`get_result`]: ../query_dsl/trait.RunQueryDsl.html#method.get_result
222    /// [`first`]: ../query_dsl/trait.RunQueryDsl.html#method.first
223    ///
224    /// # Example
225    ///
226    /// ```rust
227    /// use diesel::{QueryResult, NotFound, OptionalExtension};
228    ///
229    /// let result: QueryResult<i32> = Ok(1);
230    /// assert_eq!(Ok(Some(1)), result.optional());
231    ///
232    /// let result: QueryResult<i32> = Err(NotFound);
233    /// assert_eq!(Ok(None), result.optional());
234    /// ```
235    fn optional(self) -> Result<Option<T>, Error>;
236}
237
238impl<T> OptionalExtension<T> for QueryResult<T> {
239    fn optional(self) -> Result<Option<T>, Error> {
240        match self {
241            Ok(value) => Ok(Some(value)),
242            Err(Error::NotFound) => Ok(None),
243            Err(e) => Err(e),
244        }
245    }
246}
247
248impl From<NulError> for ConnectionError {
249    fn from(e: NulError) -> Self {
250        ConnectionError::InvalidCString(e)
251    }
252}
253
254impl From<NulError> for Error {
255    fn from(e: NulError) -> Self {
256        Error::InvalidCString(e)
257    }
258}
259
260impl Display for Error {
261    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
262        match *self {
263            Error::InvalidCString(ref nul_err) => nul_err.fmt(f),
264            Error::DatabaseError(_, ref e) => write!(f, "{}", e.message()),
265            Error::NotFound => f.write_str("NotFound"),
266            Error::QueryBuilderError(ref e) => e.fmt(f),
267            Error::DeserializationError(ref e) => e.fmt(f),
268            Error::SerializationError(ref e) => e.fmt(f),
269            Error::RollbackTransaction => write!(f, "{}", self.description()),
270            Error::AlreadyInTransaction => write!(f, "{}", self.description()),
271            Error::__Nonexhaustive => unreachable!(),
272        }
273    }
274}
275
276impl StdError for Error {
277    fn description(&self) -> &str {
278        match *self {
279            Error::InvalidCString(ref nul_err) => nul_err.description(),
280            Error::DatabaseError(_, ref e) => e.message(),
281            Error::NotFound => "Record not found",
282            Error::QueryBuilderError(ref e) => e.description(),
283            Error::DeserializationError(ref e) => e.description(),
284            Error::SerializationError(ref e) => e.description(),
285            Error::RollbackTransaction => "The current transaction was aborted",
286            Error::AlreadyInTransaction => {
287                "Cannot perform this operation while a transaction is open"
288            }
289            Error::__Nonexhaustive => unreachable!(),
290        }
291    }
292
293    fn cause(&self) -> Option<&dyn StdError> {
294        match *self {
295            Error::InvalidCString(ref e) => Some(e),
296            Error::QueryBuilderError(ref e) => Some(&**e),
297            Error::DeserializationError(ref e) => Some(&**e),
298            Error::SerializationError(ref e) => Some(&**e),
299            _ => None,
300        }
301    }
302}
303
304impl Display for ConnectionError {
305    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
306        match *self {
307            ConnectionError::InvalidCString(ref nul_err) => nul_err.fmt(f),
308            ConnectionError::BadConnection(ref s) => write!(f, "{}", s),
309            ConnectionError::InvalidConnectionUrl(ref s) => write!(f, "{}", s),
310            ConnectionError::CouldntSetupConfiguration(ref e) => e.fmt(f),
311            ConnectionError::__Nonexhaustive => unreachable!(),
312        }
313    }
314}
315
316impl StdError for ConnectionError {
317    fn description(&self) -> &str {
318        match *self {
319            ConnectionError::InvalidCString(ref nul_err) => nul_err.description(),
320            ConnectionError::BadConnection(ref s) => s,
321            ConnectionError::InvalidConnectionUrl(ref s) => s,
322            ConnectionError::CouldntSetupConfiguration(ref e) => e.description(),
323            ConnectionError::__Nonexhaustive => unreachable!(),
324        }
325    }
326
327    fn cause(&self) -> Option<&dyn StdError> {
328        match *self {
329            ConnectionError::InvalidCString(ref e) => Some(e),
330            ConnectionError::CouldntSetupConfiguration(ref e) => Some(e),
331            _ => None,
332        }
333    }
334}
335
336impl PartialEq for Error {
337    fn eq(&self, other: &Error) -> bool {
338        match (self, other) {
339            (&Error::InvalidCString(ref a), &Error::InvalidCString(ref b)) => a == b,
340            (&Error::DatabaseError(_, ref a), &Error::DatabaseError(_, ref b)) => {
341                a.message() == b.message()
342            }
343            (&Error::NotFound, &Error::NotFound) => true,
344            (&Error::RollbackTransaction, &Error::RollbackTransaction) => true,
345            (&Error::AlreadyInTransaction, &Error::AlreadyInTransaction) => true,
346            _ => false,
347        }
348    }
349}
350
351#[cfg(test)]
352#[allow(warnings)]
353fn error_impls_send() {
354    let err: Error = unimplemented!();
355    let x: &Send = &err;
356}
357
358pub(crate) fn first_or_not_found<T>(records: QueryResult<Vec<T>>) -> QueryResult<T> {
359    records?.into_iter().next().ok_or(Error::NotFound)
360}
361
362/// An unexpected `NULL` was encountered during deserialization
363#[derive(Debug, Clone, Copy)]
364pub struct UnexpectedNullError;
365
366impl fmt::Display for UnexpectedNullError {
367    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
368        write!(f, "{}", self.description())
369    }
370}
371
372impl StdError for UnexpectedNullError {
373    fn description(&self) -> &str {
374        "Unexpected null for non-null column"
375    }
376}