mysql_async/error/
mod.rs

1// Copyright (c) 2016 Anatoly Ikorsky
2//
3// Licensed under the Apache License, Version 2.0
4// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
5// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. All files in the project carrying such notice may not be copied,
7// modified, or distributed except according to those terms.
8
9pub use url::ParseError;
10
11pub mod tls;
12
13use mysql_common::{
14    named_params::MixedParamsError, params::MissingNamedParameterError,
15    proto::codec::error::PacketCodecError, row::Row, value::Value,
16};
17use thiserror::Error;
18
19use std::{io, result};
20
21/// Result type alias for this library.
22pub type Result<T> = result::Result<T, Error>;
23
24/// The maximum number of bind variables supported by MySQL.
25/// https://stackoverflow.com/questions/4922345/how-many-bind-variables-can-i-use-in-a-sql-query-in-mysql-5#comment136409462_11131824
26pub(crate) const MAX_STATEMENT_PARAMS: usize = u16::MAX as usize;
27
28/// This type enumerates library errors.
29#[derive(Debug, Error)]
30pub enum Error {
31    #[error("Driver error: `{}'", _0)]
32    Driver(#[source] DriverError),
33
34    #[error("Input/output error: {}", _0)]
35    Io(#[source] IoError),
36
37    #[error("Other error: {}", _0)]
38    Other(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
39
40    #[error("Server error: `{}'", _0)]
41    Server(#[source] ServerError),
42
43    #[error("URL error: `{}'", _0)]
44    Url(#[source] UrlError),
45}
46
47impl Error {
48    /// Returns true if the error means that connection is broken.
49    pub fn is_fatal(&self) -> bool {
50        match self {
51            Error::Driver(_) | Error::Io(_) | Error::Other(_) | Error::Url(_) => true,
52            Error::Server(_) => false,
53        }
54    }
55}
56
57/// This type enumerates IO errors.
58#[derive(Debug, Error)]
59pub enum IoError {
60    #[error("Input/output error: {}", _0)]
61    Io(#[source] io::Error),
62    #[cfg(any(feature = "native-tls-tls", feature = "rustls-tls"))]
63    #[error("TLS error: `{}'", _0)]
64    Tls(#[source] tls::TlsError),
65}
66
67/// This type represents MySql server error.
68#[derive(Debug, Error, Clone, Eq, PartialEq)]
69#[error("ERROR {} ({}): {}", state, code, message)]
70pub struct ServerError {
71    pub code: u16,
72    pub message: String,
73    pub state: String,
74}
75
76/// This type enumerates connection URL errors.
77#[derive(Debug, Error, Clone, Eq, PartialEq)]
78pub enum UrlError {
79    #[error("Connection URL parameter `{}' requires feature `{}'", param, feature)]
80    FeatureRequired { feature: String, param: String },
81
82    #[error("Invalid or incomplete connection URL")]
83    Invalid,
84
85    #[error("Invalid value `{}' for connection URL parameter `{}'", value, param)]
86    InvalidParamValue { param: String, value: String },
87
88    #[error("Invalid pool constraints: pool_min ({}) > pool_max ({}).", min, max)]
89    InvalidPoolConstraints { min: usize, max: usize },
90
91    #[error("URL parse error: {}", _0)]
92    Parse(#[source] ParseError),
93
94    #[error("Unknown connection URL parameter `{}'", param)]
95    UnknownParameter { param: String },
96
97    #[error("Unsupported connection URL scheme `{}'", scheme)]
98    UnsupportedScheme { scheme: String },
99}
100
101/// This type enumerates driver errors.
102#[derive(Debug, Error)]
103pub enum DriverError {
104    #[error("Can't parse server version from string `{}'.", version_string)]
105    CantParseServerVersion { version_string: String },
106
107    #[error("Connection to the server is closed.")]
108    ConnectionClosed,
109
110    #[error("Error converting from mysql value.")]
111    FromValue { value: Value },
112
113    #[error("Error converting from mysql row.")]
114    FromRow { row: Row },
115
116    #[error("Missing named parameter `{}'.", String::from_utf8_lossy(name))]
117    MissingNamedParam { name: Vec<u8> },
118
119    #[error("Named and positional parameters mixed in one statement.")]
120    MixedParams,
121
122    #[error("Named parameters supplied for positional query.")]
123    NamedParamsForPositionalQuery,
124
125    #[error("Transactions couldn't be nested.")]
126    NestedTransaction,
127
128    #[error("Packet out of order.")]
129    PacketOutOfOrder,
130
131    #[error("Pool was disconnected.")]
132    PoolDisconnected,
133
134    #[error("`SET TRANSACTION READ (ONLY|WRITE)' is not supported in your MySQL version.")]
135    ReadOnlyTransNotSupported,
136
137    #[error(
138        "Statement takes {} parameters but {} was supplied.",
139        required,
140        supplied
141    )]
142    StmtParamsMismatch { required: u16, supplied: usize },
143
144    #[error(
145        "MySQL supports up to {} parameters but {} was supplied.",
146        MAX_STATEMENT_PARAMS,
147        supplied
148    )]
149    StmtParamsNumberExceedsLimit { supplied: usize },
150
151    #[error("Unexpected packet.")]
152    UnexpectedPacket { payload: Vec<u8> },
153
154    #[error("Unknown authentication plugin `{}'.", name)]
155    UnknownAuthPlugin { name: String },
156
157    #[error("Packet too large.")]
158    PacketTooLarge,
159
160    #[error("Bad compressed packet header.")]
161    BadCompressedPacketHeader,
162
163    #[error("Named pipe connections temporary disabled (see tokio-rs/tokio#3118)")]
164    NamedPipesDisabled,
165
166    #[error("`mysql_old_password` plugin is insecure and disabled by default")]
167    MysqlOldPasswordDisabled,
168
169    #[error("LOCAL INFILE error: {}", _0)]
170    LocalInfile(#[from] LocalInfileError),
171
172    #[error("No private key found in the file specified")]
173    NoKeyFound,
174
175    #[error("Client asked for SSL but server does not have this capability")]
176    NoClientSslFlagFromServer,
177
178    #[error("mysql_clear_password must be enabled on the client side")]
179    CleartextPluginDisabled,
180}
181
182#[derive(Debug, Error)]
183pub enum LocalInfileError {
184    #[error("The given path is not in the while list: {}", _0)]
185    PathIsNotInTheWhiteList(String),
186    #[error("Error reading `INFILE` data: {}", _0)]
187    ReadError(#[from] io::Error),
188    #[error("Can't handle local infile request. Handler is not specified.")]
189    NoHandler,
190    #[error(transparent)]
191    OtherError(Box<dyn std::error::Error + Send + Sync + 'static>),
192}
193
194impl LocalInfileError {
195    pub fn other<T>(err: T) -> Self
196    where
197        T: std::error::Error + Send + Sync + 'static,
198    {
199        Self::OtherError(Box::new(err))
200    }
201}
202
203impl From<LocalInfileError> for Error {
204    fn from(err: LocalInfileError) -> Self {
205        Self::Driver(err.into())
206    }
207}
208
209impl From<DriverError> for Error {
210    fn from(err: DriverError) -> Self {
211        Error::Driver(err)
212    }
213}
214
215impl From<IoError> for Error {
216    fn from(io: IoError) -> Self {
217        Error::Io(io)
218    }
219}
220
221impl From<io::Error> for IoError {
222    fn from(err: io::Error) -> Self {
223        IoError::Io(err)
224    }
225}
226
227impl From<io::Error> for Error {
228    fn from(err: io::Error) -> Self {
229        Error::Io(err.into())
230    }
231}
232
233impl From<ServerError> for Error {
234    fn from(err: ServerError) -> Self {
235        Error::Server(err)
236    }
237}
238
239impl From<UrlError> for Error {
240    fn from(err: UrlError) -> Self {
241        Error::Url(err)
242    }
243}
244
245#[cfg(feature = "native-tls-tls")]
246impl From<native_tls::Error> for IoError {
247    fn from(err: native_tls::Error) -> Self {
248        IoError::Tls(tls::TlsError::TlsError(err))
249    }
250}
251
252impl From<mysql_common::packets::ServerError<'_>> for ServerError {
253    fn from(packet: mysql_common::packets::ServerError<'_>) -> Self {
254        ServerError {
255            code: packet.error_code(),
256            message: packet.message_str().into(),
257            state: packet
258                .sql_state_ref()
259                .map(|s| s.as_str().into_owned())
260                .unwrap_or_else(|| "HY000".to_owned()),
261        }
262    }
263}
264
265impl From<mysql_common::packets::ServerError<'_>> for Error {
266    fn from(packet: mysql_common::packets::ServerError<'_>) -> Self {
267        Error::Server(packet.into())
268    }
269}
270
271// For convenience
272impl From<(Error, crate::io::Stream)> for Error {
273    fn from((err, _): (Error, crate::io::Stream)) -> Self {
274        err
275    }
276}
277
278impl From<MissingNamedParameterError> for DriverError {
279    fn from(err: MissingNamedParameterError) -> Self {
280        DriverError::MissingNamedParam { name: err.0 }
281    }
282}
283
284impl From<MissingNamedParameterError> for Error {
285    fn from(err: MissingNamedParameterError) -> Self {
286        Error::Driver(err.into())
287    }
288}
289
290impl From<MixedParamsError> for DriverError {
291    fn from(_err: MixedParamsError) -> Self {
292        DriverError::MixedParams
293    }
294}
295
296impl From<MixedParamsError> for Error {
297    fn from(err: MixedParamsError) -> Self {
298        Error::Driver(err.into())
299    }
300}
301
302impl From<ParseError> for UrlError {
303    fn from(err: ParseError) -> Self {
304        UrlError::Parse(err)
305    }
306}
307
308impl From<ParseError> for Error {
309    fn from(err: ParseError) -> Self {
310        Error::Url(err.into())
311    }
312}
313
314impl From<PacketCodecError> for IoError {
315    fn from(err: PacketCodecError) -> Self {
316        match err {
317            PacketCodecError::Io(err) => err.into(),
318            PacketCodecError::PacketTooLarge => {
319                io::Error::new(io::ErrorKind::Other, "packet too large").into()
320            }
321            PacketCodecError::PacketsOutOfSync => {
322                io::Error::new(io::ErrorKind::Other, "packet out of order").into()
323            }
324            PacketCodecError::BadCompressedPacketHeader => {
325                io::Error::new(io::ErrorKind::Other, "bad compressed packet header").into()
326            }
327        }
328    }
329}
330
331impl From<PacketCodecError> for Error {
332    fn from(err: PacketCodecError) -> Self {
333        Error::Io(err.into())
334    }
335}