sqlx_core_oldapi/
error.rs1use 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 #[error("integer overflow while converting to target type")]
108 IntegerOverflow(#[source] std::num::TryFromIntError),
109}
110
111impl StdError for Box<dyn DatabaseError> {}
112
113impl Error {
114 pub fn into_database_error(self) -> Option<Box<dyn DatabaseError + 'static>> {
115 match self {
116 Error::Database(err) => Some(err),
117 _ => None,
118 }
119 }
120
121 pub fn as_database_error(&self) -> Option<&(dyn DatabaseError + 'static)> {
122 match self {
123 Error::Database(err) => Some(&**err),
124 _ => None,
125 }
126 }
127
128 #[allow(dead_code)]
129 #[inline]
130 pub(crate) fn protocol(err: impl Display) -> Self {
131 Error::Protocol(err.to_string())
132 }
133
134 #[allow(dead_code)]
135 #[inline]
136 pub(crate) fn config(err: impl StdError + Send + Sync + 'static) -> Self {
137 Error::Configuration(err.into())
138 }
139
140 #[allow(dead_code)]
141 #[inline]
142 pub(crate) fn tls<T: Into<BoxDynError>>(err: T) -> Self {
143 Error::Tls(err.into())
144 }
145}
146
147pub(crate) fn mismatched_types<DB: Database, T: Type<DB>>(ty: &DB::TypeInfo) -> BoxDynError {
148 format!(
150 "mismatched types; Rust type `{}` (as SQL type `{}`) is not compatible with SQL type `{}`",
151 type_name::<T>(),
152 T::type_info().name(),
153 ty.name()
154 )
155 .into()
156}
157
158pub trait DatabaseError: 'static + Send + Sync + StdError {
160 fn message(&self) -> &str;
162
163 fn code(&self) -> Option<Cow<'_, str>> {
165 None
166 }
167
168 fn offset(&self) -> Option<usize> {
170 None
171 }
172
173 #[doc(hidden)]
174 fn as_error(&self) -> &(dyn StdError + Send + Sync + 'static);
175
176 #[doc(hidden)]
177 fn as_error_mut(&mut self) -> &mut (dyn StdError + Send + Sync + 'static);
178
179 #[doc(hidden)]
180 fn into_error(self: Box<Self>) -> Box<dyn StdError + Send + Sync + 'static>;
181
182 #[doc(hidden)]
183 fn is_transient_in_connect_phase(&self) -> bool {
184 false
185 }
186
187 fn constraint(&self) -> Option<&str> {
193 None
194 }
195}
196
197impl dyn DatabaseError {
198 pub fn downcast_ref<E: DatabaseError>(&self) -> &E {
207 self.try_downcast_ref().unwrap_or_else(|| {
208 panic!(
209 "downcast to wrong DatabaseError type; original error: {}",
210 self
211 )
212 })
213 }
214
215 pub fn downcast<E: DatabaseError>(self: Box<Self>) -> Box<E> {
223 self.try_downcast().unwrap_or_else(|e| {
224 panic!(
225 "downcast to wrong DatabaseError type; original error: {}",
226 e
227 )
228 })
229 }
230
231 #[inline]
234 pub fn try_downcast_ref<E: DatabaseError>(&self) -> Option<&E> {
235 self.as_error().downcast_ref()
236 }
237
238 #[inline]
240 pub fn try_downcast<E: DatabaseError>(self: Box<Self>) -> StdResult<Box<E>, Box<Self>> {
241 if self.as_error().is::<E>() {
242 Ok(self.into_error().downcast().unwrap())
243 } else {
244 Err(self)
245 }
246 }
247}
248
249impl<E> From<E> for Error
250where
251 E: DatabaseError,
252{
253 #[inline]
254 fn from(error: E) -> Self {
255 Error::Database(Box::new(error))
256 }
257}
258
259#[cfg(feature = "migrate")]
260impl From<crate::migrate::MigrateError> for Error {
261 #[inline]
262 fn from(error: crate::migrate::MigrateError) -> Self {
263 Error::Migrate(Box::new(error))
264 }
265}
266
267#[cfg(feature = "_tls-native-tls")]
268impl From<sqlx_rt::native_tls::Error> for Error {
269 #[inline]
270 fn from(error: sqlx_rt::native_tls::Error) -> Self {
271 Error::Tls(Box::new(error))
272 }
273}
274
275macro_rules! err_protocol {
277 ($expr:expr) => {
278 $crate::error::Error::Protocol($expr.into())
279 };
280
281 ($fmt:expr, $($arg:tt)*) => {
282 $crate::error::Error::Protocol(format!($fmt, $($arg)*))
283 };
284}