1use std::any::type_name;
4use std::borrow::Cow;
5use std::error::Error as StdError;
6use std::fmt::Display;
7use std::io;
8use std::result::Result as StdResult;
9
10use crate::database::Database;
11use crate::type_info::TypeInfo;
12use crate::types::Type;
13
14pub type Result<T> = StdResult<T, Error>;
16
17pub type BoxDynError = Box<dyn StdError + 'static + Send + Sync>;
20
21#[derive(thiserror::Error, Debug)]
26#[error("unexpected null; try decoding as an `Option`")]
27pub struct UnexpectedNullError;
28
29#[derive(Debug, thiserror::Error)]
31#[non_exhaustive]
32pub enum Error {
33 #[error("error with configuration: {0}")]
35 Configuration(#[source] BoxDynError),
36
37 #[error("error returned from database: {0}")]
39 Database(#[source] Box<dyn DatabaseError>),
40
41 #[error("error communicating with database: {0}")]
43 Io(#[from] io::Error),
44
45 #[error("error occurred while attempting to establish a TLS connection: {0}")]
47 Tls(#[source] BoxDynError),
48
49 #[error("encountered unexpected or invalid data: {0}")]
54 Protocol(String),
55
56 #[error("no rows returned by a query that expected to return at least one row")]
58 RowNotFound,
59
60 #[error("type named {type_name} not found")]
62 TypeNotFound { type_name: String },
63
64 #[error("column index out of bounds: the len is {len}, but the index is {index}")]
66 ColumnIndexOutOfBounds { index: usize, len: usize },
67
68 #[error("no column found for name: {0}")]
70 ColumnNotFound(String),
71
72 #[error("error occurred while decoding column {index}: {source}")]
74 ColumnDecode {
75 index: String,
76
77 #[source]
78 source: BoxDynError,
79 },
80
81 #[error("error occurred while decoding: {0}")]
83 Decode(#[source] BoxDynError),
84
85 #[error("pool timed out while waiting for an open connection")]
90 PoolTimedOut,
91
92 #[error("attempted to acquire a connection on a closed pool")]
97 PoolClosed,
98
99 #[error("attempted to communicate with a crashed background worker")]
101 WorkerCrashed,
102
103 #[cfg(feature = "migrate")]
104 #[error("{0}")]
105 Migrate(#[source] Box<crate::migrate::MigrateError>),
106}
107
108impl StdError for Box<dyn DatabaseError> {}
109
110impl Error {
111 pub fn into_database_error(self) -> Option<Box<dyn DatabaseError + 'static>> {
112 match self {
113 Error::Database(err) => Some(err),
114 _ => None,
115 }
116 }
117
118 pub fn as_database_error(&self) -> Option<&(dyn DatabaseError + 'static)> {
119 match self {
120 Error::Database(err) => Some(&**err),
121 _ => None,
122 }
123 }
124
125 #[allow(dead_code)]
126 #[inline]
127 pub(crate) fn protocol(err: impl Display) -> Self {
128 Error::Protocol(err.to_string())
129 }
130
131 #[allow(dead_code)]
132 #[inline]
133 pub(crate) fn config(err: impl StdError + Send + Sync + 'static) -> Self {
134 Error::Configuration(err.into())
135 }
136}
137
138pub(crate) fn mismatched_types<DB: Database, T: Type<DB>>(ty: &DB::TypeInfo) -> BoxDynError {
139 format!(
141 "mismatched types; Rust type `{}` (as SQL type `{}`) is not compatible with SQL type `{}`",
142 type_name::<T>(),
143 T::type_info().name(),
144 ty.name()
145 )
146 .into()
147}
148
149pub trait DatabaseError: 'static + Send + Sync + StdError {
151 fn message(&self) -> &str;
153
154 fn code(&self) -> Option<Cow<'_, str>> {
156 None
157 }
158
159 #[doc(hidden)]
160 fn as_error(&self) -> &(dyn StdError + Send + Sync + 'static);
161
162 #[doc(hidden)]
163 fn as_error_mut(&mut self) -> &mut (dyn StdError + Send + Sync + 'static);
164
165 #[doc(hidden)]
166 fn into_error(self: Box<Self>) -> Box<dyn StdError + Send + Sync + 'static>;
167
168 #[doc(hidden)]
169 fn is_transient_in_connect_phase(&self) -> bool {
170 false
171 }
172
173 fn constraint(&self) -> Option<&str> {
179 None
180 }
181}
182
183impl dyn DatabaseError {
184 pub fn downcast_ref<E: DatabaseError>(&self) -> &E {
193 self.try_downcast_ref().unwrap_or_else(|| {
194 panic!(
195 "downcast to wrong DatabaseError type; original error: {}",
196 self
197 )
198 })
199 }
200
201 pub fn downcast<E: DatabaseError>(self: Box<Self>) -> Box<E> {
209 self.try_downcast().unwrap_or_else(|e| {
210 panic!(
211 "downcast to wrong DatabaseError type; original error: {}",
212 e
213 )
214 })
215 }
216
217 #[inline]
220 pub fn try_downcast_ref<E: DatabaseError>(&self) -> Option<&E> {
221 self.as_error().downcast_ref()
222 }
223
224 #[inline]
226 pub fn try_downcast<E: DatabaseError>(self: Box<Self>) -> StdResult<Box<E>, Box<Self>> {
227 if self.as_error().is::<E>() {
228 Ok(self.into_error().downcast().unwrap())
229 } else {
230 Err(self)
231 }
232 }
233}
234
235impl<E> From<E> for Error
236where
237 E: DatabaseError,
238{
239 #[inline]
240 fn from(error: E) -> Self {
241 Error::Database(Box::new(error))
242 }
243}
244
245#[cfg(feature = "migrate")]
246impl From<crate::migrate::MigrateError> for Error {
247 #[inline]
248 fn from(error: crate::migrate::MigrateError) -> Self {
249 Error::Migrate(Box::new(error))
250 }
251}
252
253#[cfg(feature = "_tls-native-tls")]
254impl From<sqlx_rt::native_tls::Error> for Error {
255 #[inline]
256 fn from(error: sqlx_rt::native_tls::Error) -> Self {
257 Error::Tls(Box::new(error))
258 }
259}
260
261macro_rules! err_protocol {
263 ($expr:expr) => {
264 $crate::error::Error::Protocol($expr.into())
265 };
266
267 ($fmt:expr, $($arg:tt)*) => {
268 $crate::error::Error::Protocol(format!($fmt, $($arg)*))
269 };
270}