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,
110
111 /// A foreign key constraint was violated.
112 ForeignKeyViolation,
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,
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,
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,
135
136 /// A restrict constraint was violated.
137 RestrictViolation,
138
139 /// A not null constraint was violated.
140 NotNullViolation,
141
142 /// A check constraint was violated.
143 CheckViolation,
144
145 /// An exclusion constraint was violated.
146 ExclusionViolation,
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,
153
154 #[doc(hidden)]
155 Unknown, // 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::{QueryResult, NotFound, OptionalExtension};
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::{QueryResult, OptionalEmptyChangesetExtension, result::Error::QueryBuilderError, result::EmptyChangeset};
307 /// let result: QueryResult<i32> = Err(QueryBuilderError(Box::new(EmptyChangeset)));
308 /// assert_eq!(Ok(None), result.optional_empty_changeset());
309 /// ```
310 fn optional_empty_changeset(self) -> Result<Option<T>, Error>;
311}
312
313impl<T> OptionalEmptyChangesetExtension<T> for QueryResult<T> {
314 fn optional_empty_changeset(self) -> Result<Option<T>, Error> {
315 match self {
316 Ok(value) => Ok(Some(value)),
317 Err(Error::QueryBuilderError(e)) if e.is::<EmptyChangeset>() => Ok(None),
318 Err(e) => Err(e),
319 }
320 }
321}
322
323impl From<NulError> for ConnectionError {
324 fn from(e: NulError) -> Self {
325 ConnectionError::InvalidCString(e)
326 }
327}
328
329impl From<NulError> for Error {
330 fn from(e: NulError) -> Self {
331 Error::InvalidCString(e)
332 }
333}
334
335impl Display for Error {
336 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
337 match *self {
338 Error::InvalidCString(ref nul_err) => write!(f, "{nul_err}"),
339 Error::DatabaseError(_, ref e) => write!(f, "{}", e.message()),
340 Error::NotFound => f.write_str("Record not found"),
341 Error::QueryBuilderError(ref e) => e.fmt(f),
342 Error::DeserializationError(ref e) => e.fmt(f),
343 Error::SerializationError(ref e) => e.fmt(f),
344 Error::RollbackErrorOnCommit {
345 ref rollback_error,
346 ref commit_error,
347 } => {
348 write!(
349 f,
350 "Transaction rollback failed: {} \
351 (rollback attempted because of failure to commit: {})",
352 &**rollback_error, &**commit_error
353 )?;
354 Ok(())
355 }
356 Error::RollbackTransaction => {
357 write!(f, "You have asked diesel to rollback the transaction")
358 }
359 Error::BrokenTransactionManager => write!(f, "The transaction manager is broken"),
360 Error::AlreadyInTransaction => write!(
361 f,
362 "Cannot perform this operation while a transaction is open",
363 ),
364 Error::NotInTransaction => {
365 write!(f, "Cannot perform this operation outside of a transaction",)
366 }
367 }
368 }
369}
370
371impl StdError for Error {
372 fn cause(&self) -> Option<&dyn StdError> {
373 match *self {
374 Error::InvalidCString(ref e) => Some(e),
375 Error::QueryBuilderError(ref e) => Some(&**e),
376 Error::DeserializationError(ref e) => Some(&**e),
377 Error::SerializationError(ref e) => Some(&**e),
378 _ => None,
379 }
380 }
381}
382
383impl Display for ConnectionError {
384 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
385 match *self {
386 ConnectionError::InvalidCString(ref nul_err) => nul_err.fmt(f),
387 ConnectionError::BadConnection(ref s) => write!(f, "{s}"),
388 ConnectionError::InvalidConnectionUrl(ref s) => write!(f, "{s}"),
389 ConnectionError::CouldntSetupConfiguration(ref e) => e.fmt(f),
390 }
391 }
392}
393
394impl StdError for ConnectionError {
395 fn cause(&self) -> Option<&dyn StdError> {
396 match *self {
397 ConnectionError::InvalidCString(ref e) => Some(e),
398 ConnectionError::CouldntSetupConfiguration(ref e) => Some(e),
399 _ => None,
400 }
401 }
402}
403
404impl PartialEq for Error {
405 fn eq(&self, other: &Error) -> bool {
406 match (self, other) {
407 (Error::InvalidCString(a), Error::InvalidCString(b)) => a == b,
408 (Error::DatabaseError(_, a), Error::DatabaseError(_, b)) => a.message() == b.message(),
409 (&Error::NotFound, &Error::NotFound) => true,
410 (&Error::RollbackTransaction, &Error::RollbackTransaction) => true,
411 (&Error::AlreadyInTransaction, &Error::AlreadyInTransaction) => true,
412 _ => false,
413 }
414 }
415}
416
417#[cfg(test)]
418#[allow(warnings)]
419fn error_impls_send() {
420 let err: Error = unimplemented!();
421 let x: &dyn Send = &err;
422}
423
424/// An unexpected `NULL` was encountered during deserialization
425#[derive(Debug, Clone, Copy)]
426pub struct UnexpectedNullError;
427
428impl fmt::Display for UnexpectedNullError {
429 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
430 write!(f, "Unexpected null for non-null column")
431 }
432}
433
434impl StdError for UnexpectedNullError {}
435
436/// Expected more fields then present in the current row while deserializing results
437#[derive(Debug, Clone, Copy)]
438pub struct UnexpectedEndOfRow;
439
440impl fmt::Display for UnexpectedEndOfRow {
441 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
442 write!(f, "Unexpected end of row")
443 }
444}
445
446impl StdError for UnexpectedEndOfRow {}
447
448/// Expected when an update has no changes to save.
449///
450/// When using `optional_empty_changeset`, this error is turned into `None`.
451#[derive(Debug, Clone, Copy)]
452pub struct EmptyChangeset;
453
454impl fmt::Display for EmptyChangeset {
455 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
456 write!(
457 f,
458 "There are no changes to save. This query cannot be built"
459 )
460 }
461}
462
463impl StdError for EmptyChangeset {}
464
465/// Expected when you try to execute an empty query
466#[derive(Debug, Clone, Copy)]
467pub struct EmptyQuery;
468
469impl fmt::Display for EmptyQuery {
470 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
471 write!(
472 f,
473 "Detected an empty query. These are not supported by your database system"
474 )
475 }
476}
477
478impl StdError for EmptyQuery {}
479
480/// An error occurred while deserializing a field
481#[derive(Debug)]
482#[non_exhaustive]
483pub struct DeserializeFieldError {
484 /// The name of the field that failed to deserialize
485 pub field_name: Option<String>,
486 /// The error that occurred while deserializing the field
487 pub error: Box<dyn StdError + Send + Sync>,
488}
489
490impl DeserializeFieldError {
491 #[cold]
492 pub(crate) fn new<'a, F, DB>(field: F, error: Box<dyn std::error::Error + Send + Sync>) -> Self
493 where
494 DB: crate::backend::Backend,
495 F: crate::row::Field<'a, DB>,
496 {
497 DeserializeFieldError {
498 field_name: field.field_name().map(|s| s.to_string()),
499 error,
500 }
501 }
502}
503
504impl StdError for DeserializeFieldError {
505 fn source(&self) -> Option<&(dyn StdError + 'static)> {
506 Some(&*self.error)
507 }
508}
509
510impl fmt::Display for DeserializeFieldError {
511 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
512 if let Some(ref field_name) = self.field_name {
513 write!(
514 f,
515 "Error deserializing field '{}': {}",
516 field_name, self.error
517 )
518 } else {
519 write!(f, "Error deserializing field: {}", self.error)
520 }
521 }
522}