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(thiserror::Error, Debug)]
31#[error("mismatched types; Rust type `{rust_type}` (as SQL type `{rust_sql_type}`) could not be decoded into SQL type `{sql_type}`")]
32pub struct MismatchedTypeError {
33 pub rust_type: String,
35 pub rust_sql_type: String,
37 pub sql_type: String,
39 #[source]
41 pub source: Option<BoxDynError>,
42}
43
44impl MismatchedTypeError {
45 pub fn new<DB: Database, T: Type<DB>>(ty: &DB::TypeInfo) -> Self {
47 Self {
48 rust_type: type_name::<T>().to_string(),
49 rust_sql_type: T::type_info().name().to_string(),
50 sql_type: ty.name().to_string(),
51 source: None,
52 }
53 }
54
55 pub fn with_source<DB: Database, T: Type<DB>>(ty: &DB::TypeInfo, source: BoxDynError) -> Self {
57 Self {
58 rust_type: type_name::<T>().to_string(),
59 rust_sql_type: T::type_info().name().to_string(),
60 sql_type: ty.name().to_string(),
61 source: Some(source),
62 }
63 }
64}
65
66#[derive(Debug, thiserror::Error)]
68#[non_exhaustive]
69pub enum Error {
70 #[error("error with configuration: {0}")]
72 Configuration(#[source] BoxDynError),
73
74 #[error("error returned from database: {0}")]
76 Database(#[source] Box<dyn DatabaseError>),
77
78 #[error("error communicating with database: {0}")]
80 Io(#[from] io::Error),
81
82 #[error("error occurred while attempting to establish a TLS connection: {0}")]
84 Tls(#[source] BoxDynError),
85
86 #[error("encountered unexpected or invalid data: {0}")]
91 Protocol(String),
92
93 #[error("no rows returned by a query that expected to return at least one row")]
95 RowNotFound,
96
97 #[error("type named {type_name} not found")]
99 TypeNotFound { type_name: String },
100
101 #[error("column index out of bounds: the len is {len}, but the index is {index}")]
103 ColumnIndexOutOfBounds { index: usize, len: usize },
104
105 #[error("no column found for name: {0}")]
107 ColumnNotFound(String),
108
109 #[error("error occurred while decoding column {index}: {source}")]
111 ColumnDecode {
112 index: String,
113
114 #[source]
115 source: BoxDynError,
116 },
117
118 #[error("error occurred while decoding: {0}")]
120 Decode(#[source] BoxDynError),
121
122 #[error("pool timed out while waiting for an open connection")]
127 PoolTimedOut,
128
129 #[error("attempted to acquire a connection on a closed pool")]
134 PoolClosed,
135
136 #[error("attempted to communicate with a crashed background worker")]
138 WorkerCrashed,
139
140 #[cfg(feature = "migrate")]
141 #[error("{0}")]
142 Migrate(#[source] Box<crate::migrate::MigrateError>),
143
144 #[error("integer overflow while converting to target type")]
145 IntegerOverflow(#[source] std::num::TryFromIntError),
146}
147
148impl StdError for Box<dyn DatabaseError> {}
149
150impl Error {
151 pub fn into_database_error(self) -> Option<Box<dyn DatabaseError + 'static>> {
152 match self {
153 Error::Database(err) => Some(err),
154 _ => None,
155 }
156 }
157
158 pub fn as_database_error(&self) -> Option<&(dyn DatabaseError + 'static)> {
159 match self {
160 Error::Database(err) => Some(&**err),
161 _ => None,
162 }
163 }
164
165 #[allow(dead_code)]
166 #[inline]
167 pub(crate) fn protocol(err: impl Display) -> Self {
168 Error::Protocol(err.to_string())
169 }
170
171 #[allow(dead_code)]
172 #[inline]
173 pub(crate) fn config(err: impl StdError + Send + Sync + 'static) -> Self {
174 Error::Configuration(err.into())
175 }
176
177 #[allow(dead_code)]
178 #[inline]
179 pub(crate) fn tls<T: Into<BoxDynError>>(err: T) -> Self {
180 Error::Tls(err.into())
181 }
182}
183
184pub(crate) fn mismatched_types<DB: Database, T: Type<DB>>(ty: &DB::TypeInfo) -> BoxDynError {
185 Box::new(MismatchedTypeError {
186 rust_type: format!(
187 "{} ({}compatible with SQL type `{}`)",
188 type_name::<T>(),
189 if T::compatible(ty) { "" } else { "in" },
190 T::type_info().name()
191 ),
192 rust_sql_type: T::type_info().name().to_string(),
193 sql_type: ty.name().to_string(),
194 source: None,
195 })
196}
197
198pub trait DatabaseError: 'static + Send + Sync + StdError {
200 fn message(&self) -> &str;
202
203 fn code(&self) -> Option<Cow<'_, str>> {
205 None
206 }
207
208 fn offset(&self) -> Option<usize> {
210 None
211 }
212
213 #[doc(hidden)]
214 fn as_error(&self) -> &(dyn StdError + Send + Sync + 'static);
215
216 #[doc(hidden)]
217 fn as_error_mut(&mut self) -> &mut (dyn StdError + Send + Sync + 'static);
218
219 #[doc(hidden)]
220 fn into_error(self: Box<Self>) -> Box<dyn StdError + Send + Sync + 'static>;
221
222 #[doc(hidden)]
223 fn is_transient_in_connect_phase(&self) -> bool {
224 false
225 }
226
227 fn constraint(&self) -> Option<&str> {
233 None
234 }
235}
236
237impl dyn DatabaseError {
238 pub fn downcast_ref<E: DatabaseError>(&self) -> &E {
247 self.try_downcast_ref().unwrap_or_else(|| {
248 panic!(
249 "downcast to wrong DatabaseError type; original error: {}",
250 self
251 )
252 })
253 }
254
255 pub fn downcast<E: DatabaseError>(self: Box<Self>) -> Box<E> {
263 self.try_downcast().unwrap_or_else(|e| {
264 panic!(
265 "downcast to wrong DatabaseError type; original error: {}",
266 e
267 )
268 })
269 }
270
271 #[inline]
274 pub fn try_downcast_ref<E: DatabaseError>(&self) -> Option<&E> {
275 self.as_error().downcast_ref()
276 }
277
278 #[inline]
280 pub fn try_downcast<E: DatabaseError>(self: Box<Self>) -> StdResult<Box<E>, Box<Self>> {
281 if self.as_error().is::<E>() {
282 Ok(self.into_error().downcast().unwrap())
283 } else {
284 Err(self)
285 }
286 }
287}
288
289impl<E> From<E> for Error
290where
291 E: DatabaseError,
292{
293 #[inline]
294 fn from(error: E) -> Self {
295 Error::Database(Box::new(error))
296 }
297}
298
299#[cfg(feature = "migrate")]
300impl From<crate::migrate::MigrateError> for Error {
301 #[inline]
302 fn from(error: crate::migrate::MigrateError) -> Self {
303 Error::Migrate(Box::new(error))
304 }
305}
306
307#[cfg(feature = "_tls-native-tls")]
308impl From<sqlx_rt::native_tls::Error> for Error {
309 #[inline]
310 fn from(error: sqlx_rt::native_tls::Error) -> Self {
311 Error::Tls(Box::new(error))
312 }
313}
314
315macro_rules! err_protocol {
317 ($expr:expr) => {
318 $crate::error::Error::Protocol($expr.into())
319 };
320
321 ($fmt:expr, $($arg:tt)*) => {
322 $crate::error::Error::Protocol(format!($fmt, $($arg)*))
323 };
324}