1//! Types for working with errors produced by SQLx.
23use std::any::type_name;
4use std::borrow::Cow;
5use std::error::Error as StdError;
6use std::fmt::Display;
7use std::io;
89use crate::database::Database;
1011use crate::type_info::TypeInfo;
12use crate::types::Type;
1314/// A specialized `Result` type for SQLx.
15pub type Result<T, E = Error> = ::std::result::Result<T, E>;
1617// Convenience type alias for usage within SQLx.
18// Do not make this type public.
19pub type BoxDynError = Box<dyn StdError + 'static + Send + Sync>;
2021/// An unexpected `NULL` was encountered during decoding.
22///
23/// Returned from [`Row::get`](crate::row::Row::get) if the value from the database is `NULL`,
24/// and you are not decoding into an `Option`.
25#[derive(thiserror::Error, Debug)]
26#[error("unexpected null; try decoding as an `Option`")]
27pub struct UnexpectedNullError;
2829/// Represents all the ways a method can fail within SQLx.
30#[derive(Debug, thiserror::Error)]
31#[non_exhaustive]
32pub enum Error {
33/// Error occurred while parsing a connection string.
34#[error("error with configuration: {0}")]
35Configuration(#[source] BoxDynError),
3637/// Error returned from the database.
38#[error("error returned from database: {0}")]
39Database(#[source] Box<dyn DatabaseError>),
4041/// Error communicating with the database backend.
42#[error("error communicating with database: {0}")]
43Io(#[from] io::Error),
4445/// Error occurred while attempting to establish a TLS connection.
46#[error("error occurred while attempting to establish a TLS connection: {0}")]
47Tls(#[source] BoxDynError),
4849/// Unexpected or invalid data encountered while communicating with the database.
50 ///
51 /// This should indicate there is a programming error in a SQLx driver or there
52 /// is something corrupted with the connection to the database itself.
53#[error("encountered unexpected or invalid data: {0}")]
54Protocol(String),
5556/// No rows returned by a query that expected to return at least one row.
57#[error("no rows returned by a query that expected to return at least one row")]
58RowNotFound,
5960/// Type in query doesn't exist. Likely due to typo or missing user type.
61#[error("type named {type_name} not found")]
62TypeNotFound { type_name: String },
6364/// Column index was out of bounds.
65#[error("column index out of bounds: the len is {len}, but the index is {index}")]
66ColumnIndexOutOfBounds { index: usize, len: usize },
6768/// No column found for the given name.
69#[error("no column found for name: {0}")]
70ColumnNotFound(String),
7172/// Error occurred while decoding a value from a specific column.
73#[error("error occurred while decoding column {index}: {source}")]
74ColumnDecode {
75 index: String,
7677#[source]
78source: BoxDynError,
79 },
8081/// Error occured while encoding a value.
82#[error("error occured while encoding a value: {0}")]
83Encode(#[source] BoxDynError),
8485/// Error occurred while decoding a value.
86#[error("error occurred while decoding: {0}")]
87Decode(#[source] BoxDynError),
8889/// Error occurred within the `Any` driver mapping to/from the native driver.
90#[error("error in Any driver mapping: {0}")]
91AnyDriverError(#[source] BoxDynError),
9293/// A [`Pool::acquire`] timed out due to connections not becoming available or
94 /// because another task encountered too many errors while trying to open a new connection.
95 ///
96 /// [`Pool::acquire`]: crate::pool::Pool::acquire
97#[error("pool timed out while waiting for an open connection")]
98PoolTimedOut,
99100/// [`Pool::close`] was called while we were waiting in [`Pool::acquire`].
101 ///
102 /// [`Pool::acquire`]: crate::pool::Pool::acquire
103 /// [`Pool::close`]: crate::pool::Pool::close
104#[error("attempted to acquire a connection on a closed pool")]
105PoolClosed,
106107/// A background worker has crashed.
108#[error("attempted to communicate with a crashed background worker")]
109WorkerCrashed,
110111#[cfg(feature = "migrate")]
112 #[error("{0}")]
113Migrate(#[source] Box<crate::migrate::MigrateError>),
114}
115116impl StdError for Box<dyn DatabaseError> {}
117118impl Error {
119pub fn into_database_error(self) -> Option<Box<dyn DatabaseError + 'static>> {
120match self {
121 Error::Database(err) => Some(err),
122_ => None,
123 }
124 }
125126pub fn as_database_error(&self) -> Option<&(dyn DatabaseError + 'static)> {
127match self {
128 Error::Database(err) => Some(&**err),
129_ => None,
130 }
131 }
132133#[doc(hidden)]
134 #[inline]
135pub fn protocol(err: impl Display) -> Self {
136 Error::Protocol(err.to_string())
137 }
138139#[doc(hidden)]
140 #[inline]
141pub fn config(err: impl StdError + Send + Sync + 'static) -> Self {
142 Error::Configuration(err.into())
143 }
144145pub(crate) fn tls(err: impl Into<Box<dyn StdError + Send + Sync + 'static>>) -> Self {
146 Error::Tls(err.into())
147 }
148149#[doc(hidden)]
150 #[inline]
151pub fn decode(err: impl Into<Box<dyn StdError + Send + Sync + 'static>>) -> Self {
152 Error::Decode(err.into())
153 }
154}
155156pub fn mismatched_types<DB: Database, T: Type<DB>>(ty: &DB::TypeInfo) -> BoxDynError {
157// TODO: `#name` only produces `TINYINT` but perhaps we want to show `TINYINT(1)`
158format!(
159"mismatched types; Rust type `{}` (as SQL type `{}`) is not compatible with SQL type `{}`",
160 type_name::<T>(),
161 T::type_info().name(),
162 ty.name()
163 )
164 .into()
165}
166167/// The error kind.
168///
169/// This enum is to be used to identify frequent errors that can be handled by the program.
170/// Although it currently only supports constraint violations, the type may grow in the future.
171#[derive(Debug, PartialEq, Eq)]
172#[non_exhaustive]
173pub enum ErrorKind {
174/// Unique/primary key constraint violation.
175UniqueViolation,
176/// Foreign key constraint violation.
177ForeignKeyViolation,
178/// Not-null constraint violation.
179NotNullViolation,
180/// Check constraint violation.
181CheckViolation,
182/// An unmapped error.
183Other,
184}
185186/// An error that was returned from the database.
187pub trait DatabaseError: 'static + Send + Sync + StdError {
188/// The primary, human-readable error message.
189fn message(&self) -> &str;
190191/// The (SQLSTATE) code for the error.
192fn code(&self) -> Option<Cow<'_, str>> {
193None
194}
195196#[doc(hidden)]
197fn as_error(&self) -> &(dyn StdError + Send + Sync + 'static);
198199#[doc(hidden)]
200fn as_error_mut(&mut self) -> &mut (dyn StdError + Send + Sync + 'static);
201202#[doc(hidden)]
203fn into_error(self: Box<Self>) -> Box<dyn StdError + Send + Sync + 'static>;
204205#[doc(hidden)]
206fn is_transient_in_connect_phase(&self) -> bool {
207false
208}
209210/// Returns the name of the constraint that triggered the error, if applicable.
211 /// If the error was caused by a conflict of a unique index, this will be the index name.
212 ///
213 /// ### Note
214 /// Currently only populated by the Postgres driver.
215fn constraint(&self) -> Option<&str> {
216None
217}
218219/// Returns the name of the table that was affected by the error, if applicable.
220 ///
221 /// ### Note
222 /// Currently only populated by the Postgres driver.
223fn table(&self) -> Option<&str> {
224None
225}
226227/// Returns the kind of the error, if supported.
228 ///
229 /// ### Note
230 /// Not all back-ends behave the same when reporting the error code.
231fn kind(&self) -> ErrorKind;
232233/// Returns whether the error kind is a violation of a unique/primary key constraint.
234fn is_unique_violation(&self) -> bool {
235matches!(self.kind(), ErrorKind::UniqueViolation)
236 }
237238/// Returns whether the error kind is a violation of a foreign key.
239fn is_foreign_key_violation(&self) -> bool {
240matches!(self.kind(), ErrorKind::ForeignKeyViolation)
241 }
242243/// Returns whether the error kind is a violation of a check.
244fn is_check_violation(&self) -> bool {
245matches!(self.kind(), ErrorKind::CheckViolation)
246 }
247}
248249impl dyn DatabaseError {
250/// Downcast a reference to this generic database error to a specific
251 /// database error type.
252 ///
253 /// # Panics
254 ///
255 /// Panics if the database error type is not `E`. This is a deliberate contrast from
256 /// `Error::downcast_ref` which returns `Option<&E>`. In normal usage, you should know the
257 /// specific error type. In other cases, use `try_downcast_ref`.
258pub fn downcast_ref<E: DatabaseError>(&self) -> &E {
259self.try_downcast_ref().unwrap_or_else(|| {
260panic!("downcast to wrong DatabaseError type; original error: {self}")
261 })
262 }
263264/// Downcast this generic database error to a specific database error type.
265 ///
266 /// # Panics
267 ///
268 /// Panics if the database error type is not `E`. This is a deliberate contrast from
269 /// `Error::downcast` which returns `Option<E>`. In normal usage, you should know the
270 /// specific error type. In other cases, use `try_downcast`.
271pub fn downcast<E: DatabaseError>(self: Box<Self>) -> Box<E> {
272self.try_downcast()
273 .unwrap_or_else(|e| panic!("downcast to wrong DatabaseError type; original error: {e}"))
274 }
275276/// Downcast a reference to this generic database error to a specific
277 /// database error type.
278#[inline]
279pub fn try_downcast_ref<E: DatabaseError>(&self) -> Option<&E> {
280self.as_error().downcast_ref()
281 }
282283/// Downcast this generic database error to a specific database error type.
284#[inline]
285pub fn try_downcast<E: DatabaseError>(self: Box<Self>) -> Result<Box<E>, Box<Self>> {
286if self.as_error().is::<E>() {
287Ok(self.into_error().downcast().unwrap())
288 } else {
289Err(self)
290 }
291 }
292}
293294impl<E> From<E> for Error
295where
296E: DatabaseError,
297{
298#[inline]
299fn from(error: E) -> Self {
300 Error::Database(Box::new(error))
301 }
302}
303304#[cfg(feature = "migrate")]
305impl From<crate::migrate::MigrateError> for Error {
306#[inline]
307fn from(error: crate::migrate::MigrateError) -> Self {
308 Error::Migrate(Box::new(error))
309 }
310}
311312/// Format an error message as a `Protocol` error
313#[macro_export]
314macro_rules! err_protocol {
315 ($($fmt_args:tt)*) => {
316$crate::error::Error::Protocol(
317format!(
318"{} ({}:{})",
319// Note: the format string needs to be unmodified (e.g. by `concat!()`)
320 // for implicit formatting arguments to work
321format_args!($($fmt_args)*),
322module_path!(),
323line!(),
324 )
325 )
326 };
327}