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
128impl StdError for Box<dyn DatabaseError> {}
129
130impl Error {
131 pub fn into_database_error(self) -> Option<Box<dyn DatabaseError + 'static>> {
132 match self {
133 Error::Database(err) => Some(err),
134 _ => None,
135 }
136 }
137
138 pub fn as_database_error(&self) -> Option<&(dyn DatabaseError + 'static)> {
139 match self {
140 Error::Database(err) => Some(&**err),
141 _ => None,
142 }
143 }
144
145 #[doc(hidden)]
146 #[inline]
147 pub fn protocol(err: impl Display) -> Self {
148 Error::Protocol(err.to_string())
149 }
150
151 #[doc(hidden)]
152 #[inline]
153 pub fn database(err: impl DatabaseError) -> Self {
154 Error::Database(Box::new(err))
155 }
156
157 #[doc(hidden)]
158 #[inline]
159 pub fn config(err: impl StdError + Send + Sync + 'static) -> Self {
160 Error::Configuration(err.into())
161 }
162
163 pub(crate) fn tls(err: impl Into<Box<dyn StdError + Send + Sync + 'static>>) -> Self {
164 Error::Tls(err.into())
165 }
166
167 #[doc(hidden)]
168 #[inline]
169 pub fn decode(err: impl Into<Box<dyn StdError + Send + Sync + 'static>>) -> Self {
170 Error::Decode(err.into())
171 }
172}
173
174pub fn mismatched_types<DB: Database, T: Type<DB>>(ty: &DB::TypeInfo) -> BoxDynError {
175 format!(
177 "mismatched types; Rust type `{}` (as SQL type `{}`) is not compatible with SQL type `{}`",
178 type_name::<T>(),
179 T::type_info().name(),
180 ty.name()
181 )
182 .into()
183}
184
185#[derive(Debug, PartialEq, Eq)]
190#[non_exhaustive]
191pub enum ErrorKind {
192 UniqueViolation,
194 ForeignKeyViolation,
196 NotNullViolation,
198 CheckViolation,
200 Other,
202}
203
204pub trait DatabaseError: 'static + Send + Sync + StdError {
206 fn message(&self) -> &str;
208
209 fn code(&self) -> Option<Cow<'_, str>> {
211 None
212 }
213
214 #[doc(hidden)]
215 fn as_error(&self) -> &(dyn StdError + Send + Sync + 'static);
216
217 #[doc(hidden)]
218 fn as_error_mut(&mut self) -> &mut (dyn StdError + Send + Sync + 'static);
219
220 #[doc(hidden)]
221 fn into_error(self: Box<Self>) -> Box<dyn StdError + Send + Sync + 'static>;
222
223 #[doc(hidden)]
224 fn is_transient_in_connect_phase(&self) -> bool {
225 false
226 }
227
228 fn constraint(&self) -> Option<&str> {
234 None
235 }
236
237 fn table(&self) -> Option<&str> {
242 None
243 }
244
245 fn kind(&self) -> ErrorKind;
250
251 fn is_unique_violation(&self) -> bool {
253 matches!(self.kind(), ErrorKind::UniqueViolation)
254 }
255
256 fn is_foreign_key_violation(&self) -> bool {
258 matches!(self.kind(), ErrorKind::ForeignKeyViolation)
259 }
260
261 fn is_check_violation(&self) -> bool {
263 matches!(self.kind(), ErrorKind::CheckViolation)
264 }
265}
266
267impl dyn DatabaseError {
268 pub fn downcast_ref<E: DatabaseError>(&self) -> &E {
277 self.try_downcast_ref().unwrap_or_else(|| {
278 panic!("downcast to wrong DatabaseError type; original error: {self}")
279 })
280 }
281
282 pub fn downcast<E: DatabaseError>(self: Box<Self>) -> Box<E> {
290 self.try_downcast()
291 .unwrap_or_else(|e| panic!("downcast to wrong DatabaseError type; original error: {e}"))
292 }
293
294 #[inline]
297 pub fn try_downcast_ref<E: DatabaseError>(&self) -> Option<&E> {
298 self.as_error().downcast_ref()
299 }
300
301 #[inline]
303 pub fn try_downcast<E: DatabaseError>(self: Box<Self>) -> Result<Box<E>, Box<Self>> {
304 if self.as_error().is::<E>() {
305 Ok(self.into_error().downcast().unwrap())
306 } else {
307 Err(self)
308 }
309 }
310}
311
312impl<E> From<E> for Error
313where
314 E: DatabaseError,
315{
316 #[inline]
317 fn from(error: E) -> Self {
318 Error::Database(Box::new(error))
319 }
320}
321
322#[cfg(feature = "migrate")]
323impl From<crate::migrate::MigrateError> for Error {
324 #[inline]
325 fn from(error: crate::migrate::MigrateError) -> Self {
326 Error::Migrate(Box::new(error))
327 }
328}
329
330#[macro_export]
332macro_rules! err_protocol {
333 ($($fmt_args:tt)*) => {
334 $crate::error::Error::Protocol(
335 format!(
336 "{} ({}:{})",
337 format_args!($($fmt_args)*),
340 module_path!(),
341 line!(),
342 )
343 )
344 };
345}