hdbconnect_impl/base/
hdb_error.rs

1use crate::protocol::parts::{ExecutionResults, ServerError};
2// use std::backtrace::Backtrace;
3use thiserror::Error;
4
5/// A list specifying categories of [`HdbError`](crate::HdbError).
6///
7#[derive(Error, Debug)] //
8#[non_exhaustive]
9pub enum HdbError {
10    /// Initialization without TLS failed.
11    #[error("Initialization without TLS failed")]
12    Initialization {
13        /// The causing Error.
14        source: Box<dyn std::error::Error + Send + Sync>,
15        // backtrace: Backtrace,
16    },
17
18    /// Authentication failed.
19    #[error("Authentication failed")]
20    Authentication {
21        /// The causing Error.
22        #[from]
23        source: Box<HdbError>,
24        // backtrace: Backtrace,
25    },
26
27    /// Deserialization of a `ResultSet`, a `Row`, a single `HdbValue`,
28    /// or an `OutputParameter` failed (methods `try_into()`).
29    #[error("Error occured in deserialization")]
30    Deserialization {
31        /// The causing Error.
32        #[from]
33        source: serde_db::de::DeserializationError,
34        // backtrace: Backtrace,
35    },
36
37    /// Serialization of a `ParameterDescriptor` or a `ParameterRow` failed.
38    #[error("Error occured in serialization")]
39    Serialization {
40        /// The causing Error.
41        #[from]
42        source: serde_db::ser::SerializationError,
43        // backtrace: Backtrace,
44    },
45
46    /// Some error occured while decoding CESU-8. This indicates a server issue!
47    #[error("Some error occured while decoding CESU-8")]
48    Cesu8,
49
50    /// Decoding CESU-8 failed, original bytes are available.
51    #[error("Some error occured while decoding CESU-8")]
52    Cesu8AsBytes {
53        bytes: Vec<u8>,
54        // backtrace: Backtrace,
55    },
56
57    /// Erroneous Connection Parameters, e.g. from a malformed connection URL.
58    #[error("Erroneous Connection Parameters")]
59    ConnParams {
60        /// The causing Error.
61        source: Box<dyn std::error::Error + Send + Sync + 'static>,
62        // backtrace: Backtrace,
63    },
64
65    /// Database server responded with an error;
66    /// the contained `ServerError` describes the conrete reason.
67    #[error("Database server responded with an error")]
68    DbError {
69        /// The causing Error.
70        #[from]
71        source: ServerError,
72        // backtrace: Backtrace,
73    },
74
75    /// Decompression
76    #[error("Decompression failed")]
77    Decompression {
78        /// The causing Error.
79        #[from]
80        source: lz4_flex::block::DecompressError,
81        // backtrace: Backtrace,
82    },
83
84    /// TLS set up failed because the server name was not valid.
85    #[error("TLS setup failed because the server name was not valid")]
86    TlsInvalidDnsName {
87        /// The causing Error.
88        #[from]
89        source: rustls::pki_types::InvalidDnsNameError,
90    },
91
92    /// TLS initialization error
93    #[error("Connection setup failed due to failing TLS initialization")]
94    TlsInit {
95        /// The causing Error.
96        source: Box<dyn std::error::Error + Send + Sync>,
97    },
98
99    /// TLS protocol error.
100    #[error(
101        "TLS set up failed, after setting up the TCP connection; is the database prepared for TLS?"
102    )]
103    TlsProtocol {
104        /// The causing Error.
105        #[from]
106        source: rustls::Error,
107    },
108
109    /// Error occured while evaluating an `HdbResponse` or an `HdbReturnValue`.
110    #[error("Error occured while evaluating a HdbResponse or an HdbReturnValue")]
111    Evaluation(&'static str),
112
113    /// Database server responded with at least one error.
114    #[error("Database server responded with at least one error: \n{0}")]
115    ExecutionResults(ExecutionResults),
116
117    /// Implementation error.
118    #[error("Implementation error: {}", _0)]
119    Impl(std::borrow::Cow<'static, str>),
120
121    /// Error occured in thread synchronization.
122    // #[cfg(feature = "sync")]
123    #[error("Error occured in thread synchronization")]
124    Poison,
125
126    /// An error occurred on the server that requires the session to be terminated.
127    #[error("An error occurred on the server that requires the session to be terminated")]
128    SessionClosingTransactionError,
129
130    /// Error occured in communication with the database.
131    #[error(transparent)]
132    Io {
133        /// The causing Error.
134        #[from]
135        source: std::io::Error,
136        // backtrace: Backtrace,
137    },
138
139    /// Error occured
140    #[error("Error occured with a command that was repeated after a reconnect")]
141    ErrorAfterReconnect {
142        source: std::io::Error,
143        second: Box<HdbError>,
144    },
145
146    /// Error caused by wrong usage.
147    #[error("Wrong usage: {}", _0)]
148    Usage(std::borrow::Cow<'static, str>),
149
150    /// Connection is dead
151    #[error("Connection is broken")]
152    ConnectionBroken { source: Option<Box<HdbError>> },
153}
154
155/// Abbreviation of `Result<T, HdbError>`.
156pub type HdbResult<T> = std::result::Result<T, HdbError>;
157
158impl HdbError {
159    /// Returns the contained `ServerError`, if any.
160    ///
161    /// This method helps in case you need programmatic access to e.g. the error code.
162    ///
163    /// Example:
164    ///
165    /// ```rust,no_run
166    /// # use hdbconnect::{Connection, HdbError, HdbResult};
167    /// # use hdbconnect::IntoConnectParams;
168    /// # fn main() -> HdbResult<()> {
169    ///     # let hdb_result: HdbResult<()> = Err(HdbError::Usage("test"));
170    ///     # let mut connection = Connection::new("".into_connect_params()?)?;
171    ///     if let Err(hdberror) = hdb_result {
172    ///         if let Some(server_error) = hdberror.server_error() {
173    ///             let sys_m_error_code: (i32, String, String) = connection
174    ///                 .query(&format!(
175    ///                     "select * from SYS.M_ERROR_CODES where code = {}",
176    ///                     server_error.code()
177    ///                 ))?.try_into()?;
178    ///             println!("sys_m_error_code: {:?}", sys_m_error_code);
179    ///         }
180    ///     }
181    ///     # Ok(())
182    /// # }
183    /// ```
184    #[must_use]
185    pub fn server_error(&self) -> Option<&ServerError> {
186        match self {
187            Self::DbError {
188                source: server_error,
189            } => Some(server_error),
190            _ => None,
191        }
192    }
193
194    /// Reveal the inner error
195    #[must_use]
196    pub fn inner(&self) -> Option<&dyn std::error::Error> {
197        match self {
198            Self::Authentication { source } => Some(source),
199            Self::Deserialization { source } => Some(source),
200            Self::Serialization { source } => Some(source),
201            Self::ConnParams { source } | Self::TlsInit { source } => Some(&**source),
202            Self::DbError { source } => Some(source),
203            Self::Decompression { source } => Some(source),
204            Self::TlsInvalidDnsName { source } => Some(source),
205            Self::Io { source } => Some(source),
206            Self::TlsProtocol { source } => Some(source),
207            _ => None,
208        }
209    }
210
211    pub(crate) fn conversion_error_into_bytes(&self) -> Option<&[u8]> {
212        match self {
213            Self::Cesu8AsBytes { bytes } => Some(bytes),
214            _ => None,
215        }
216    }
217
218    pub(crate) fn conn_params(error: Box<dyn std::error::Error + Send + Sync + 'static>) -> Self {
219        Self::ConnParams { source: error }
220    }
221
222    /// Returns a decently formed and hopefully helpful error description.
223    #[must_use]
224    pub fn display_with_inner(&self) -> String {
225        if let Some(e) = self.inner() {
226            format!("{}, caused by {:?}", &self, e)
227        } else {
228            format!("{}", &self)
229        }
230    }
231}
232
233#[cfg(feature = "sync")]
234impl<G> From<std::sync::PoisonError<G>> for HdbError {
235    fn from(_error: std::sync::PoisonError<G>) -> Self {
236        Self::Poison
237    }
238}
239
240#[macro_export]
241macro_rules! usage_err {
242    ($($arg:tt)*) => {{
243        $crate::HdbError::Usage(std::borrow::Cow::from(format!($($arg)*)))
244    }};
245}
246#[macro_export]
247macro_rules! impl_err {
248    ($($arg:tt)*) => {{
249        $crate::HdbError::Impl(std::borrow::Cow::from(format!($($arg)*)))
250    }};
251}