1use std::any::type_name;
4use std::borrow::Cow;
5use std::error::Error as StdError;
6use std::fmt::Display;
7use std::io;
8
9use crate::database::Database;
10
11use crate::type_info::TypeInfo;
12use crate::types::Type;
13
14pub type Result<T, E = Error> = ::std::result::Result<T, E>;
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("{0}")]
41 InvalidArgument(String),
42
43 #[error("error returned from database: {0}")]
45 Database(#[source] Box<dyn DatabaseError>),
46
47 #[error("error communicating with database: {0}")]
49 Io(#[from] io::Error),
50
51 #[error("error occurred while attempting to establish a TLS connection: {0}")]
53 Tls(#[source] BoxDynError),
54
55 #[error("encountered unexpected or invalid data: {0}")]
60 Protocol(String),
61
62 #[error("no rows returned by a query that expected to return at least one row")]
64 RowNotFound,
65
66 #[error("type named {type_name} not found")]
68 TypeNotFound { type_name: String },
69
70 #[error("column index out of bounds: the len is {len}, but the index is {index}")]
72 ColumnIndexOutOfBounds { index: usize, len: usize },
73
74 #[error("no column found for name: {0}")]
76 ColumnNotFound(String),
77
78 #[error("error occurred while decoding column {index}: {source}")]
80 ColumnDecode {
81 index: String,
82
83 #[source]
84 source: BoxDynError,
85 },
86
87 #[error("error occurred while encoding a value: {0}")]
89 Encode(#[source] BoxDynError),
90
91 #[error("error occurred while decoding: {0}")]
93 Decode(#[source] BoxDynError),
94
95 #[error("error in Any driver mapping: {0}")]
97 AnyDriverError(#[source] BoxDynError),
98
99 #[error("pool timed out while waiting for an open connection")]
104 PoolTimedOut,
105
106 #[error("attempted to acquire a connection on a closed pool")]
111 PoolClosed,
112
113 #[error("attempted to communicate with a crashed background worker")]
115 WorkerCrashed,
116
117 #[cfg(feature = "migrate")]
118 #[error("{0}")]
119 Migrate(#[source] Box<crate::migrate::MigrateError>),
120
121 #[error("attempted to call begin_with at non-zero transaction depth")]
122 InvalidSavePointStatement,
123
124 #[error("got unexpected connection status after attempting to begin transaction")]
125 BeginFailed,
126
127 #[doc(hidden)]
130 #[error("error reading configuration file: {0}")]
131 ConfigFile(#[from] crate::config::ConfigError),
132}
133
134impl StdError for Box<dyn DatabaseError> {}
135
136impl Error {
137 pub fn into_database_error(self) -> Option<Box<dyn DatabaseError + 'static>> {
138 match self {
139 Error::Database(err) => Some(err),
140 _ => None,
141 }
142 }
143
144 pub fn as_database_error(&self) -> Option<&(dyn DatabaseError + 'static)> {
145 match self {
146 Error::Database(err) => Some(&**err),
147 _ => None,
148 }
149 }
150
151 #[doc(hidden)]
152 #[inline]
153 pub fn protocol(err: impl Display) -> Self {
154 Error::Protocol(err.to_string())
155 }
156
157 #[doc(hidden)]
158 #[inline]
159 pub fn database(err: impl DatabaseError) -> Self {
160 Error::Database(Box::new(err))
161 }
162
163 #[doc(hidden)]
164 #[inline]
165 pub fn config(err: impl StdError + Send + Sync + 'static) -> Self {
166 Error::Configuration(err.into())
167 }
168
169 pub(crate) fn tls(err: impl Into<Box<dyn StdError + Send + Sync + 'static>>) -> Self {
170 Error::Tls(err.into())
171 }
172
173 #[doc(hidden)]
174 #[inline]
175 pub fn decode(err: impl Into<Box<dyn StdError + Send + Sync + 'static>>) -> Self {
176 Error::Decode(err.into())
177 }
178}
179
180pub fn mismatched_types<DB: Database, T: Type<DB>>(ty: &DB::TypeInfo) -> BoxDynError {
181 format!(
183 "mismatched types; Rust type `{}` (as SQL type `{}`) is not compatible with SQL type `{}`",
184 type_name::<T>(),
185 T::type_info().name(),
186 ty.name()
187 )
188 .into()
189}
190
191#[derive(Debug, PartialEq, Eq)]
196#[non_exhaustive]
197pub enum ErrorKind {
198 UniqueViolation,
200 ForeignKeyViolation,
202 NotNullViolation,
204 CheckViolation,
206 ExclusionViolation,
208 Other,
210}
211
212pub trait DatabaseError: 'static + Send + Sync + StdError {
214 fn message(&self) -> &str;
216
217 fn code(&self) -> Option<Cow<'_, str>> {
219 None
220 }
221
222 #[doc(hidden)]
223 fn as_error(&self) -> &(dyn StdError + Send + Sync + 'static);
224
225 #[doc(hidden)]
226 fn as_error_mut(&mut self) -> &mut (dyn StdError + Send + Sync + 'static);
227
228 #[doc(hidden)]
229 fn into_error(self: Box<Self>) -> Box<dyn StdError + Send + Sync + 'static>;
230
231 #[doc(hidden)]
232 fn is_transient_in_connect_phase(&self) -> bool {
233 false
234 }
235
236 fn constraint(&self) -> Option<&str> {
242 None
243 }
244
245 fn table(&self) -> Option<&str> {
250 None
251 }
252
253 fn kind(&self) -> ErrorKind;
258
259 fn is_unique_violation(&self) -> bool {
261 matches!(self.kind(), ErrorKind::UniqueViolation)
262 }
263
264 fn is_foreign_key_violation(&self) -> bool {
266 matches!(self.kind(), ErrorKind::ForeignKeyViolation)
267 }
268
269 fn is_check_violation(&self) -> bool {
271 matches!(self.kind(), ErrorKind::CheckViolation)
272 }
273}
274
275impl dyn DatabaseError {
276 pub fn downcast_ref<E: DatabaseError>(&self) -> &E {
285 self.try_downcast_ref().unwrap_or_else(|| {
286 panic!("downcast to wrong DatabaseError type; original error: {self}")
287 })
288 }
289
290 pub fn downcast<E: DatabaseError>(self: Box<Self>) -> Box<E> {
298 self.try_downcast()
299 .unwrap_or_else(|e| panic!("downcast to wrong DatabaseError type; original error: {e}"))
300 }
301
302 #[inline]
305 pub fn try_downcast_ref<E: DatabaseError>(&self) -> Option<&E> {
306 self.as_error().downcast_ref()
307 }
308
309 #[inline]
311 pub fn try_downcast<E: DatabaseError>(self: Box<Self>) -> Result<Box<E>, Box<Self>> {
312 if self.as_error().is::<E>() {
313 Ok(self.into_error().downcast().unwrap())
314 } else {
315 Err(self)
316 }
317 }
318}
319
320impl<E> From<E> for Error
321where
322 E: DatabaseError,
323{
324 #[inline]
325 fn from(error: E) -> Self {
326 Error::Database(Box::new(error))
327 }
328}
329
330#[cfg(feature = "migrate")]
331impl From<crate::migrate::MigrateError> for Error {
332 #[inline]
333 fn from(error: crate::migrate::MigrateError) -> Self {
334 Error::Migrate(Box::new(error))
335 }
336}
337
338#[macro_export]
340macro_rules! err_protocol {
341 ($($fmt_args:tt)*) => {
342 $crate::error::Error::Protocol(
343 format!(
344 "{} ({}:{})",
345 format_args!($($fmt_args)*),
348 module_path!(),
349 line!(),
350 )
351 )
352 };
353}