diesel/
result.rs

1//! Errors, type aliases, and functions related to working with `Result`.
2
3use std::error::Error as StdError;
4use std::ffi::NulError;
5use std::fmt::{self, Display};
6
7#[derive(Debug)]
8#[allow(clippy::enum_variant_names)]
9/// Represents all the ways that a query can fail.
10///
11/// This type is not intended to be exhaustively matched, and new variants may
12/// be added in the future without a major version bump.
13#[non_exhaustive]
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`]: crate::query_dsl::RunQueryDsl::get_result()
38    /// [`first`]: crate::query_dsl::RunQueryDsl::first()
39    /// [`load`]: crate::query_dsl::RunQueryDsl::load()
40    /// [`optional`]: OptionalExtension::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    /// An error occurred when attempting rollback of a transaction subsequently to a failed
65    /// commit attempt.
66    ///
67    /// When a commit attempt fails and Diesel believes that it can attempt a rollback to return
68    /// the connection back in a usable state (out of that transaction), it attempts it then
69    /// returns the original error.
70    ///
71    /// If that fails, you get this.
72    RollbackErrorOnCommit {
73        /// The error that was encountered when attempting the rollback
74        rollback_error: Box<Error>,
75        /// The error that was encountered during the failed commit attempt
76        commit_error: Box<Error>,
77    },
78
79    /// Roll back the current transaction.
80    ///
81    /// You can return this variant inside of a transaction when you want to
82    /// roll it back, but have no actual error to return. Diesel will never
83    /// return this variant unless you gave it to us, and it can be safely
84    /// ignored in error handling.
85    RollbackTransaction,
86
87    /// Attempted to perform an operation that cannot be done inside a transaction
88    /// when a transaction was already open.
89    AlreadyInTransaction,
90
91    /// Attempted to perform an operation that can only be done inside a transaction
92    /// when no transaction was open
93    NotInTransaction,
94
95    /// Transaction manager broken, likely due to a broken connection. No other operations are possible.
96    BrokenTransactionManager,
97}
98
99#[derive(Debug, Clone, Copy)]
100/// The kind of database error that occurred.
101///
102/// This is not meant to exhaustively cover all possible errors, but is used to
103/// identify errors which are commonly recovered from programmatically. This enum
104/// is not intended to be exhaustively matched, and new variants may be added in
105/// the future without a major version bump.
106#[non_exhaustive]
107pub enum DatabaseErrorKind {
108    /// A unique constraint was violated.
109    UniqueViolation = 0,
110
111    /// A foreign key constraint was violated.
112    ForeignKeyViolation = 1,
113
114    /// The query could not be sent to the database due to a protocol violation.
115    ///
116    /// An example of a case where this would occur is if you attempted to send
117    /// a query with more than 65000 bind parameters using PostgreSQL.
118    UnableToSendCommand = 2,
119
120    /// A serializable transaction failed to commit due to a read/write
121    /// dependency on a concurrent transaction.
122    ///
123    /// Corresponds to SQLSTATE code 40001
124    ///
125    /// This error is only detected for PostgreSQL, as we do not yet support
126    /// transaction isolation levels for other backends.
127    SerializationFailure = 3,
128
129    /// The command could not be completed because the transaction was read
130    /// only.
131    ///
132    /// This error will also be returned for `SELECT` statements which attempted
133    /// to lock the rows.
134    ReadOnlyTransaction = 4,
135
136    /// A restrict constraint was violated.
137    RestrictViolation = 9,
138
139    /// A not null constraint was violated.
140    NotNullViolation = 5,
141
142    /// A check constraint was violated.
143    CheckViolation = 6,
144
145    /// An exclusion constraint was violated.
146    ExclusionViolation = 10,
147
148    /// The connection to the server was unexpectedly closed.
149    ///
150    /// This error is only detected for PostgreSQL and is emitted on a best-effort basis
151    /// and may be missed.
152    ClosedConnection = 7,
153
154    #[doc(hidden)]
155    Unknown = 8, // Match against _ instead, more variants may be added in the future
156}
157
158/// Information about an error that was returned by the database.
159pub trait DatabaseErrorInformation {
160    /// The primary human-readable error message. Typically one line.
161    fn message(&self) -> &str;
162
163    /// An optional secondary error message providing more details about the
164    /// problem, if it was provided by the database. Might span multiple lines.
165    fn details(&self) -> Option<&str>;
166
167    /// An optional suggestion of what to do about the problem, if one was
168    /// provided by the database.
169    fn hint(&self) -> Option<&str>;
170
171    /// The name of the table the error was associated with, if the error was
172    /// associated with a specific table and the backend supports retrieving
173    /// that information.
174    ///
175    /// Currently this method will return `None` for all backends other than
176    /// PostgreSQL.
177    fn table_name(&self) -> Option<&str>;
178
179    /// The name of the column the error was associated with, if the error was
180    /// associated with a specific column and the backend supports retrieving
181    /// that information.
182    ///
183    /// Currently this method will return `None` for all backends other than
184    /// PostgreSQL.
185    fn column_name(&self) -> Option<&str>;
186
187    /// The constraint that was violated if this error is a constraint violation
188    /// and the backend supports retrieving that information.
189    ///
190    /// Currently this method will return `None` for all backends other than
191    /// PostgreSQL.
192    fn constraint_name(&self) -> Option<&str>;
193
194    /// An optional integer indicating an error cursor position as an index into
195    /// the original statement string.
196    fn statement_position(&self) -> Option<i32>;
197}
198
199impl fmt::Debug for dyn DatabaseErrorInformation + Send + Sync {
200    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
201        fmt::Debug::fmt(&self.message(), f)
202    }
203}
204
205impl DatabaseErrorInformation for String {
206    fn message(&self) -> &str {
207        self
208    }
209    fn details(&self) -> Option<&str> {
210        None
211    }
212    fn hint(&self) -> Option<&str> {
213        None
214    }
215    fn table_name(&self) -> Option<&str> {
216        None
217    }
218    fn column_name(&self) -> Option<&str> {
219        None
220    }
221    fn constraint_name(&self) -> Option<&str> {
222        None
223    }
224    fn statement_position(&self) -> Option<i32> {
225        None
226    }
227}
228
229/// Errors which can occur during [`Connection::establish`]
230///
231/// [`Connection::establish`]: crate::connection::Connection::establish
232#[derive(Debug, PartialEq)]
233#[non_exhaustive]
234pub enum ConnectionError {
235    /// The connection URL contained a `NUL` byte.
236    InvalidCString(NulError),
237    /// The database returned an error.
238    BadConnection(String),
239    /// The connection URL could not be parsed.
240    InvalidConnectionUrl(String),
241    /// Diesel could not configure the database connection.
242    ///
243    /// Diesel may try to automatically set session specific configuration
244    /// values, such as UTF8 encoding, or enabling the `||` operator on MySQL.
245    /// This variant is returned if an error occurred executing the query to set
246    /// those options. Diesel will never affect global configuration.
247    CouldntSetupConfiguration(Error),
248}
249
250/// A specialized result type for queries.
251///
252/// This type is exported by `diesel::prelude`, and is generally used by any
253/// code which is interacting with Diesel. This type exists to avoid writing out
254/// `diesel::result::Error`, and is otherwise a direct mapping to `Result`.
255pub type QueryResult<T> = Result<T, Error>;
256
257/// A specialized result type for establishing connections.
258///
259/// This type exists to avoid writing out `diesel::result::ConnectionError`, and
260/// is otherwise a direct mapping to `Result`.
261pub type ConnectionResult<T> = Result<T, ConnectionError>;
262
263/// See the [method documentation](OptionalExtension::optional).
264pub trait OptionalExtension<T> {
265    /// Converts a `QueryResult<T>` into a `QueryResult<Option<T>>`.
266    ///
267    /// By default, Diesel treats 0 rows being returned from a query that is expected to return 1
268    /// row as an error (e.g. the return value of [`get_result`] or [`first`]). This method will
269    /// handle that error, and give you back an `Option<T>` instead.
270    ///
271    /// [`get_result`]: crate::query_dsl::RunQueryDsl::get_result()
272    /// [`first`]: crate::query_dsl::RunQueryDsl::first()
273    ///
274    /// # Example
275    ///
276    /// ```rust
277    /// use diesel::{NotFound, OptionalExtension, QueryResult};
278    ///
279    /// let result: QueryResult<i32> = Ok(1);
280    /// assert_eq!(Ok(Some(1)), result.optional());
281    ///
282    /// let result: QueryResult<i32> = Err(NotFound);
283    /// assert_eq!(Ok(None), result.optional());
284    /// ```
285    fn optional(self) -> Result<Option<T>, Error>;
286}
287
288impl<T> OptionalExtension<T> for QueryResult<T> {
289    fn optional(self) -> Result<Option<T>, Error> {
290        match self {
291            Ok(value) => Ok(Some(value)),
292            Err(Error::NotFound) => Ok(None),
293            Err(e) => Err(e),
294        }
295    }
296}
297
298/// See the [method documentation](OptionalEmptyChangesetExtension::optional_empty_changeset).
299pub trait OptionalEmptyChangesetExtension<T> {
300    /// By default, Diesel treats an empty update as a `QueryBuilderError`. This method will
301    /// convert that error into `None`.
302    ///
303    /// # Example
304    ///
305    /// ```rust
306    /// use diesel::{
307    ///     result::EmptyChangeset, result::Error::QueryBuilderError, OptionalEmptyChangesetExtension,
308    ///     QueryResult,
309    /// };
310    /// let result: QueryResult<i32> = Err(QueryBuilderError(Box::new(EmptyChangeset)));
311    /// assert_eq!(Ok(None), result.optional_empty_changeset());
312    /// ```
313    fn optional_empty_changeset(self) -> Result<Option<T>, Error>;
314}
315
316impl<T> OptionalEmptyChangesetExtension<T> for QueryResult<T> {
317    fn optional_empty_changeset(self) -> Result<Option<T>, Error> {
318        match self {
319            Ok(value) => Ok(Some(value)),
320            Err(Error::QueryBuilderError(e)) if e.is::<EmptyChangeset>() => Ok(None),
321            Err(e) => Err(e),
322        }
323    }
324}
325
326impl From<NulError> for ConnectionError {
327    fn from(e: NulError) -> Self {
328        ConnectionError::InvalidCString(e)
329    }
330}
331
332impl From<NulError> for Error {
333    fn from(e: NulError) -> Self {
334        Error::InvalidCString(e)
335    }
336}
337
338impl Display for Error {
339    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
340        match *self {
341            Error::InvalidCString(ref nul_err) => write!(f, "{nul_err}"),
342            Error::DatabaseError(_, ref e) => write!(f, "{}", e.message()),
343            Error::NotFound => f.write_str("Record not found"),
344            Error::QueryBuilderError(ref e) => e.fmt(f),
345            Error::DeserializationError(ref e) => e.fmt(f),
346            Error::SerializationError(ref e) => e.fmt(f),
347            Error::RollbackErrorOnCommit {
348                ref rollback_error,
349                ref commit_error,
350            } => {
351                write!(
352                    f,
353                    "Transaction rollback failed: {} \
354                        (rollback attempted because of failure to commit: {})",
355                    &**rollback_error, &**commit_error
356                )?;
357                Ok(())
358            }
359            Error::RollbackTransaction => {
360                write!(f, "You have asked diesel to rollback the transaction")
361            }
362            Error::BrokenTransactionManager => write!(f, "The transaction manager is broken"),
363            Error::AlreadyInTransaction => write!(
364                f,
365                "Cannot perform this operation while a transaction is open",
366            ),
367            Error::NotInTransaction => {
368                write!(f, "Cannot perform this operation outside of a transaction",)
369            }
370        }
371    }
372}
373
374impl StdError for Error {
375    fn cause(&self) -> Option<&dyn StdError> {
376        match *self {
377            Error::InvalidCString(ref e) => Some(e),
378            Error::QueryBuilderError(ref e) => Some(&**e),
379            Error::DeserializationError(ref e) => Some(&**e),
380            Error::SerializationError(ref e) => Some(&**e),
381            _ => None,
382        }
383    }
384}
385
386impl Display for ConnectionError {
387    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
388        match *self {
389            ConnectionError::InvalidCString(ref nul_err) => nul_err.fmt(f),
390            ConnectionError::BadConnection(ref s) => write!(f, "{s}"),
391            ConnectionError::InvalidConnectionUrl(ref s) => write!(f, "{s}"),
392            ConnectionError::CouldntSetupConfiguration(ref e) => e.fmt(f),
393        }
394    }
395}
396
397impl StdError for ConnectionError {
398    fn cause(&self) -> Option<&dyn StdError> {
399        match *self {
400            ConnectionError::InvalidCString(ref e) => Some(e),
401            ConnectionError::CouldntSetupConfiguration(ref e) => Some(e),
402            _ => None,
403        }
404    }
405}
406
407impl PartialEq for Error {
408    fn eq(&self, other: &Error) -> bool {
409        match (self, other) {
410            (Error::InvalidCString(a), Error::InvalidCString(b)) => a == b,
411            (Error::DatabaseError(_, a), Error::DatabaseError(_, b)) => a.message() == b.message(),
412            (&Error::NotFound, &Error::NotFound) => true,
413            (&Error::RollbackTransaction, &Error::RollbackTransaction) => true,
414            (&Error::AlreadyInTransaction, &Error::AlreadyInTransaction) => true,
415            _ => false,
416        }
417    }
418}
419
420#[cfg(test)]
421#[allow(warnings)]
422fn error_impls_send() {
423    let err: Error = unimplemented!();
424    let x: &dyn Send = &err;
425}
426
427/// An unexpected `NULL` was encountered during deserialization
428#[derive(Debug, Clone, Copy)]
429pub struct UnexpectedNullError;
430
431impl fmt::Display for UnexpectedNullError {
432    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
433        write!(f, "Unexpected null for non-null column")
434    }
435}
436
437impl StdError for UnexpectedNullError {}
438
439/// Expected more fields then present in the current row while deserializing results
440#[derive(Debug, Clone, Copy)]
441pub struct UnexpectedEndOfRow;
442
443impl fmt::Display for UnexpectedEndOfRow {
444    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
445        write!(f, "Unexpected end of row")
446    }
447}
448
449impl StdError for UnexpectedEndOfRow {}
450
451/// Expected when an update has no changes to save.
452///
453/// When using `optional_empty_changeset`, this error is turned into `None`.
454#[derive(Debug, Clone, Copy)]
455pub struct EmptyChangeset;
456
457impl fmt::Display for EmptyChangeset {
458    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
459        write!(
460            f,
461            "There are no changes to save. This query cannot be built"
462        )
463    }
464}
465
466impl StdError for EmptyChangeset {}
467
468/// Expected when you try to execute an empty query
469#[derive(Debug, Clone, Copy)]
470pub struct EmptyQuery;
471
472impl fmt::Display for EmptyQuery {
473    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
474        write!(
475            f,
476            "Detected an empty query. These are not supported by your database system"
477        )
478    }
479}
480
481impl StdError for EmptyQuery {}
482
483/// An error occurred while deserializing a field
484#[derive(Debug)]
485#[non_exhaustive]
486pub struct DeserializeFieldError {
487    /// The name of the field that failed to deserialize
488    pub field_name: Option<String>,
489    /// The error that occurred while deserializing the field
490    pub error: Box<dyn StdError + Send + Sync>,
491}
492
493impl DeserializeFieldError {
494    #[cold]
495    pub(crate) fn new<'a, F, DB>(field: F, error: Box<dyn std::error::Error + Send + Sync>) -> Self
496    where
497        DB: crate::backend::Backend,
498        F: crate::row::Field<'a, DB>,
499    {
500        DeserializeFieldError {
501            field_name: field.field_name().map(|s| s.to_string()),
502            error,
503        }
504    }
505}
506
507impl StdError for DeserializeFieldError {
508    fn source(&self) -> Option<&(dyn StdError + 'static)> {
509        Some(&*self.error)
510    }
511}
512
513impl fmt::Display for DeserializeFieldError {
514    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
515        if let Some(ref field_name) = self.field_name {
516            write!(
517                f,
518                "Error deserializing field '{}': {}",
519                field_name, self.error
520            )
521        } else {
522            write!(f, "Error deserializing field: {}", self.error)
523        }
524    }
525}