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}