sqlx_build_trust_core/
error.rs1use 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("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("error in Any driver mapping: {0}")]
87 AnyDriverError(#[source] BoxDynError),
88
89 #[error("pool timed out while waiting for an open connection")]
94 PoolTimedOut,
95
96 #[error("attempted to acquire a connection on a closed pool")]
101 PoolClosed,
102
103 #[error("attempted to communicate with a crashed background worker")]
105 WorkerCrashed,
106
107 #[cfg(feature = "migrate")]
108 #[error("{0}")]
109 Migrate(#[source] Box<crate::migrate::MigrateError>),
110}
111
112impl StdError for Box<dyn DatabaseError> {}
113
114impl Error {
115 pub fn into_database_error(self) -> Option<Box<dyn DatabaseError + 'static>> {
116 match self {
117 Error::Database(err) => Some(err),
118 _ => None,
119 }
120 }
121
122 pub fn as_database_error(&self) -> Option<&(dyn DatabaseError + 'static)> {
123 match self {
124 Error::Database(err) => Some(&**err),
125 _ => None,
126 }
127 }
128
129 #[doc(hidden)]
130 #[inline]
131 pub fn protocol(err: impl Display) -> Self {
132 Error::Protocol(err.to_string())
133 }
134
135 #[doc(hidden)]
136 #[inline]
137 pub fn config(err: impl StdError + Send + Sync + 'static) -> Self {
138 Error::Configuration(err.into())
139 }
140
141 pub(crate) fn tls(err: impl Into<Box<dyn StdError + Send + Sync + 'static>>) -> Self {
142 Error::Tls(err.into())
143 }
144
145 #[doc(hidden)]
146 #[inline]
147 pub fn decode(err: impl Into<Box<dyn StdError + Send + Sync + 'static>>) -> Self {
148 Error::Decode(err.into())
149 }
150}
151
152pub fn mismatched_types<DB: Database, T: Type<DB>>(ty: &DB::TypeInfo) -> BoxDynError {
153 format!(
155 "mismatched types; Rust type `{}` (as SQL type `{}`) is not compatible with SQL type `{}`",
156 type_name::<T>(),
157 T::type_info().name(),
158 ty.name()
159 )
160 .into()
161}
162
163#[derive(Debug, PartialEq, Eq)]
168#[non_exhaustive]
169pub enum ErrorKind {
170 UniqueViolation,
172 ForeignKeyViolation,
174 NotNullViolation,
176 CheckViolation,
178 Other,
180}
181
182pub trait DatabaseError: 'static + Send + Sync + StdError {
184 fn message(&self) -> &str;
186
187 fn code(&self) -> Option<Cow<'_, str>> {
189 None
190 }
191
192 #[doc(hidden)]
193 fn as_error(&self) -> &(dyn StdError + Send + Sync + 'static);
194
195 #[doc(hidden)]
196 fn as_error_mut(&mut self) -> &mut (dyn StdError + Send + Sync + 'static);
197
198 #[doc(hidden)]
199 fn into_error(self: Box<Self>) -> Box<dyn StdError + Send + Sync + 'static>;
200
201 #[doc(hidden)]
202 fn is_transient_in_connect_phase(&self) -> bool {
203 false
204 }
205
206 fn constraint(&self) -> Option<&str> {
212 None
213 }
214
215 fn table(&self) -> Option<&str> {
220 None
221 }
222
223 fn kind(&self) -> ErrorKind;
228
229 fn is_unique_violation(&self) -> bool {
231 matches!(self.kind(), ErrorKind::UniqueViolation)
232 }
233
234 fn is_foreign_key_violation(&self) -> bool {
236 matches!(self.kind(), ErrorKind::ForeignKeyViolation)
237 }
238
239 fn is_check_violation(&self) -> bool {
241 matches!(self.kind(), ErrorKind::CheckViolation)
242 }
243}
244
245impl dyn DatabaseError {
246 pub fn downcast_ref<E: DatabaseError>(&self) -> &E {
255 self.try_downcast_ref().unwrap_or_else(|| {
256 panic!("downcast to wrong DatabaseError type; original error: {self}")
257 })
258 }
259
260 pub fn downcast<E: DatabaseError>(self: Box<Self>) -> Box<E> {
268 self.try_downcast()
269 .unwrap_or_else(|e| panic!("downcast to wrong DatabaseError type; original error: {e}"))
270 }
271
272 #[inline]
275 pub fn try_downcast_ref<E: DatabaseError>(&self) -> Option<&E> {
276 self.as_error().downcast_ref()
277 }
278
279 #[inline]
281 pub fn try_downcast<E: DatabaseError>(self: Box<Self>) -> Result<Box<E>, Box<Self>> {
282 if self.as_error().is::<E>() {
283 Ok(self.into_error().downcast().unwrap())
284 } else {
285 Err(self)
286 }
287 }
288}
289
290impl<E> From<E> for Error
291where
292 E: DatabaseError,
293{
294 #[inline]
295 fn from(error: E) -> Self {
296 Error::Database(Box::new(error))
297 }
298}
299
300#[cfg(feature = "migrate")]
301impl From<crate::migrate::MigrateError> for Error {
302 #[inline]
303 fn from(error: crate::migrate::MigrateError) -> Self {
304 Error::Migrate(Box::new(error))
305 }
306}
307
308#[macro_export]
310macro_rules! err_protocol {
311 ($expr:expr) => {
312 $crate::error::Error::Protocol($expr.into())
313 };
314
315 ($fmt:expr, $($arg:tt)*) => {
316 $crate::error::Error::Protocol(format!($fmt, $($arg)*))
317 };
318}