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}