mirror of
https://github.com/launchbadge/sqlx.git
synced 2025-12-29 21:00:54 +00:00
feat(core): expand variants and documentation of sqlx::Error
This commit is contained in:
parent
301665360c
commit
2b99b1aeaf
@ -2,6 +2,8 @@ use std::borrow::Cow;
|
||||
use std::error::Error as StdError;
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use either::Either;
|
||||
|
||||
use crate::decode::Error as DecodeError;
|
||||
use crate::encode::Error as EncodeError;
|
||||
|
||||
@ -9,59 +11,83 @@ mod database;
|
||||
|
||||
pub use database::DatabaseError;
|
||||
|
||||
/// `Result` type returned from methods that can have SQLx errors.
|
||||
/// Specialized `Result` type returned from fallible methods within SQLx.
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
/// Error type returned for all methods in SQLX.
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum Error {
|
||||
Configuration {
|
||||
message: Cow<'static, str>,
|
||||
source: Option<Box<dyn StdError + Send + Sync>>,
|
||||
},
|
||||
/// The database URL is malformed or contains invalid or unsupported
|
||||
/// values for one or more options; a value of [`ConnectOptions`] failed
|
||||
/// to be parsed.
|
||||
ConnectOptions { message: Cow<'static, str>, source: Option<Box<dyn StdError + Send + Sync>> },
|
||||
|
||||
Connect(Box<dyn DatabaseError>),
|
||||
/// The database returned an error.
|
||||
Database(Box<dyn DatabaseError>),
|
||||
|
||||
/// An IO error returned while reading or writing a socket attached
|
||||
/// to the database server.
|
||||
///
|
||||
/// Only applicable if the database driver connects to a remote database
|
||||
/// server.
|
||||
///
|
||||
Network(std::io::Error),
|
||||
|
||||
/// Returned by `fetch_one` when no row was returned from the query.
|
||||
/// No rows returned by a query required to return at least one row.
|
||||
///
|
||||
/// Use `fetch_optional` to return `None` instead of signaling an error.
|
||||
/// Returned by `fetch_one` when no rows were returned from
|
||||
/// the query. Use `fetch_optional` to return `None` instead
|
||||
/// of signaling an error.
|
||||
///
|
||||
RowNotFound,
|
||||
|
||||
/// An attempt to act on a closed connection or pool.
|
||||
///
|
||||
/// A connection will close itself on an unrecoverable error in the
|
||||
/// connection (implementation bugs, faulty network, etc.). If the error
|
||||
/// was ignored and the connection is used again, it will
|
||||
/// return `Error::Closed`.
|
||||
///
|
||||
/// A pool will return `Error::Closed` from `Pool::acquire` if `Pool::close`
|
||||
/// was called before `acquire` received a connection.
|
||||
///
|
||||
Closed,
|
||||
|
||||
/// An error occurred decoding a SQL value from the database.
|
||||
Decode(DecodeError),
|
||||
|
||||
/// An error occurred encoding a value to be sent to the database.
|
||||
Encode(EncodeError),
|
||||
|
||||
ColumnIndexOutOfBounds {
|
||||
index: usize,
|
||||
len: usize,
|
||||
},
|
||||
/// An attempt to access a column by index past the end of the row.
|
||||
ColumnIndexOutOfBounds { index: usize, len: usize },
|
||||
|
||||
/// An attempt to access a column by name where no such column is
|
||||
/// present in the row.
|
||||
ColumnNotFound { name: Box<str> },
|
||||
|
||||
/// An error occurred decoding a SQL value of a specific column
|
||||
/// from the database.
|
||||
ColumnDecode { column_index: usize, column_name: Box<str>, source: DecodeError },
|
||||
|
||||
/// An error occurred encoding a value for a specific parameter to
|
||||
/// be sent to the database.
|
||||
ParameterEncode { parameter: Either<usize, Box<str>>, source: EncodeError },
|
||||
}
|
||||
|
||||
impl Error {
|
||||
#[doc(hidden)]
|
||||
pub fn connect<E>(error: E) -> Self
|
||||
where
|
||||
E: DatabaseError,
|
||||
{
|
||||
Self::Connect(Box::new(error))
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn configuration(
|
||||
pub fn opt(
|
||||
message: impl Into<Cow<'static, str>>,
|
||||
source: impl Into<Box<dyn StdError + Send + Sync>>,
|
||||
) -> Self {
|
||||
Self::Configuration { message: message.into(), source: Some(source.into()) }
|
||||
Self::ConnectOptions { message: message.into(), source: Some(source.into()) }
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn configuration_msg(message: impl Into<Cow<'static, str>>) -> Self {
|
||||
Self::Configuration { message: message.into(), source: None }
|
||||
pub fn opt_msg(message: impl Into<Cow<'static, str>>) -> Self {
|
||||
Self::ConnectOptions { message: message.into(), source: None }
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,13 +96,13 @@ impl Display for Error {
|
||||
match self {
|
||||
Self::Network(source) => write!(f, "{}", source),
|
||||
|
||||
Self::Connect(source) => write!(f, "{}", source),
|
||||
Self::Database(source) => write!(f, "{}", source),
|
||||
|
||||
Self::Configuration { message, source: None } => {
|
||||
Self::ConnectOptions { message, source: None } => {
|
||||
write!(f, "{}", message)
|
||||
}
|
||||
|
||||
Self::Configuration { message, source: Some(source) } => {
|
||||
Self::ConnectOptions { message, source: Some(source) } => {
|
||||
write!(f, "{}: {}", message, source)
|
||||
}
|
||||
|
||||
@ -84,14 +110,14 @@ impl Display for Error {
|
||||
f.write_str("no row returned by a query required to return at least one row")
|
||||
}
|
||||
|
||||
Self::Closed => f.write_str("connection or pool was closed"),
|
||||
Self::Closed => f.write_str("connection or pool is closed"),
|
||||
|
||||
Self::Decode(error) => {
|
||||
write!(f, "{}", error)
|
||||
write!(f, "decode: {}", error)
|
||||
}
|
||||
|
||||
Self::Encode(error) => {
|
||||
write!(f, "{}", error)
|
||||
write!(f, "encode: {}", error)
|
||||
}
|
||||
|
||||
Self::ColumnIndexOutOfBounds { index, len } => {
|
||||
@ -101,6 +127,22 @@ impl Display for Error {
|
||||
len, index
|
||||
)
|
||||
}
|
||||
|
||||
Self::ColumnNotFound { name } => {
|
||||
write!(f, "no column found for name `{}`", name)
|
||||
}
|
||||
|
||||
Self::ColumnDecode { column_index, column_name, source } => {
|
||||
write!(f, "decode column {} `{}`: {}", column_index, column_name, source)
|
||||
}
|
||||
|
||||
Self::ParameterEncode { parameter: Either::Left(index), source } => {
|
||||
write!(f, "encode parameter {}: {}", index, source)
|
||||
}
|
||||
|
||||
Self::ParameterEncode { parameter: Either::Right(name), source } => {
|
||||
write!(f, "encode parameter `{}`: {}", name, source)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -108,7 +150,7 @@ impl Display for Error {
|
||||
impl StdError for Error {
|
||||
fn source(&self) -> Option<&(dyn StdError + 'static)> {
|
||||
match self {
|
||||
Self::Configuration { source: Some(source), .. } => Some(&**source),
|
||||
Self::ConnectOptions { source: Some(source), .. } => Some(&**source),
|
||||
Self::Network(source) => Some(source),
|
||||
|
||||
_ => None,
|
||||
@ -116,6 +158,12 @@ impl StdError for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: DatabaseError> From<E> for Error {
|
||||
fn from(error: E) -> Self {
|
||||
Self::Database(Box::new(error))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for Error {
|
||||
fn from(error: std::io::Error) -> Self {
|
||||
Self::Network(error)
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
///
|
||||
/// See <https://en.wikipedia.org/wiki/Isolation_(database_systems)#Isolation_levels>.
|
||||
///
|
||||
#[derive(Debug)]
|
||||
pub enum IsolationLevel {
|
||||
/// The lowest isolation level. Dirty reads are allowed, so one transaction
|
||||
/// may see **not yet committed** changes made by other transactions.
|
||||
|
||||
@ -11,11 +11,10 @@ impl FromStr for MySqlConnectOptions {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let url: Url =
|
||||
s.parse().map_err(|error| Error::configuration("for database URL", error))?;
|
||||
let url: Url = s.parse().map_err(|error| Error::opt("for database URL", error))?;
|
||||
|
||||
if !matches!(url.scheme(), "mysql") {
|
||||
return Err(Error::configuration_msg(format!(
|
||||
return Err(Error::opt_msg(format!(
|
||||
"unsupported URL scheme {:?} for MySQL",
|
||||
url.scheme()
|
||||
)));
|
||||
|
||||
@ -40,10 +40,11 @@ impl dyn AuthPlugin {
|
||||
_ if s == Sha256AuthPlugin.name() => Ok(Box::new(Sha256AuthPlugin)),
|
||||
_ if s == NativeAuthPlugin.name() => Ok(Box::new(NativeAuthPlugin)),
|
||||
|
||||
_ => Err(Error::connect(MySqlDatabaseError::new(
|
||||
_ => Err(MySqlDatabaseError::new(
|
||||
2059,
|
||||
&format!("Authentication plugin '{}' cannot be loaded", s),
|
||||
))),
|
||||
)
|
||||
.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -59,10 +60,10 @@ fn xor_eq(x: &mut [u8], y: &[u8]) {
|
||||
}
|
||||
|
||||
fn err_msg(plugin: &'static str, message: &str) -> Error {
|
||||
Error::connect(MySqlDatabaseError::new(
|
||||
MySqlDatabaseError::new(
|
||||
2061,
|
||||
&format!("Authentication plugin '{}' reported error: {}", plugin, message),
|
||||
))
|
||||
).into()
|
||||
}
|
||||
|
||||
fn err<E>(plugin: &'static str, error: &E) -> Error
|
||||
|
||||
@ -2,7 +2,7 @@ use std::fmt::Debug;
|
||||
|
||||
use bytes::Bytes;
|
||||
use sqlx_core::io::Deserialize;
|
||||
use sqlx_core::{Error, Result};
|
||||
use sqlx_core::Result;
|
||||
|
||||
use crate::protocol::{AuthSwitch, Capabilities, ResultPacket};
|
||||
use crate::MySqlDatabaseError;
|
||||
@ -21,14 +21,14 @@ impl Deserialize<'_, Capabilities> for AuthResponse {
|
||||
Some(0x01) => Ok(Self::MoreData(buf.slice(1..))),
|
||||
Some(0xfe) => AuthSwitch::deserialize(buf).map(Self::Switch),
|
||||
|
||||
Some(tag) => Err(Error::connect(MySqlDatabaseError::malformed_packet(&format!(
|
||||
Some(tag) => Err(MySqlDatabaseError::malformed_packet(&format!(
|
||||
"Received 0x{:x} but expected one of: 0x0 (OK), 0x1 (MORE DATA), or 0xfe (SWITCH) for auth response",
|
||||
tag
|
||||
)))),
|
||||
)).into()),
|
||||
|
||||
None => Err(Error::connect(MySqlDatabaseError::malformed_packet(
|
||||
None => Err(MySqlDatabaseError::malformed_packet(
|
||||
"Received no bytes for auth response",
|
||||
))),
|
||||
).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use bytes::Bytes;
|
||||
use sqlx_core::io::Deserialize;
|
||||
use sqlx_core::{Error, Result};
|
||||
use sqlx_core::Result;
|
||||
|
||||
use super::{Capabilities, ResultPacket};
|
||||
use crate::io::MySqlBufExt;
|
||||
@ -46,9 +46,10 @@ impl Deserialize<'_, Capabilities> for QueryResponse {
|
||||
Ok(Self::ResultSet { columns: columns as u16 })
|
||||
}
|
||||
|
||||
None => Err(Error::connect(MySqlDatabaseError::malformed_packet(
|
||||
None => Err(MySqlDatabaseError::malformed_packet(
|
||||
"Received no bytes for COM_QUERY response",
|
||||
))),
|
||||
)
|
||||
.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use bytes::Bytes;
|
||||
use sqlx_core::io::Deserialize;
|
||||
use sqlx_core::{Error, Result};
|
||||
use sqlx_core::Result;
|
||||
|
||||
use super::{Capabilities, ResultPacket};
|
||||
use crate::protocol::Packet;
|
||||
@ -30,9 +30,10 @@ impl Deserialize<'_, Capabilities> for QueryStep {
|
||||
// If its non-0, then its a Row
|
||||
Some(_) => Ok(Self::Row(Packet { bytes: buf })),
|
||||
|
||||
None => Err(Error::connect(MySqlDatabaseError::malformed_packet(
|
||||
None => Err(MySqlDatabaseError::malformed_packet(
|
||||
"Received no bytes for the next step in a result set",
|
||||
))),
|
||||
)
|
||||
.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use bytes::Bytes;
|
||||
use sqlx_core::io::Deserialize;
|
||||
use sqlx_core::{Error, Result};
|
||||
use sqlx_core::Result;
|
||||
|
||||
use super::{Capabilities, ErrPacket, OkPacket};
|
||||
use crate::MySqlDatabaseError;
|
||||
@ -22,7 +22,7 @@ where
|
||||
pub(crate) fn into_result(self) -> Result<T> {
|
||||
match self {
|
||||
Self::Ok(ok) => Ok(ok),
|
||||
Self::Err(err) => Err(Error::connect(MySqlDatabaseError(err))),
|
||||
Self::Err(err) => Err(MySqlDatabaseError(err).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@ use std::ops::{Deref, DerefMut};
|
||||
use bytes::{Buf, BufMut};
|
||||
use sqlx_core::io::{BufStream, Serialize, Stream};
|
||||
use sqlx_core::net::Stream as NetStream;
|
||||
use sqlx_core::{Error, Result, Runtime};
|
||||
use sqlx_core::{Result, Runtime};
|
||||
|
||||
use crate::protocol::{MaybeCommand, Packet, Quit};
|
||||
use crate::MySqlDatabaseError;
|
||||
@ -101,11 +101,12 @@ impl<Rt: Runtime> MySqlStream<Rt> {
|
||||
if packet.bytes.len() != len {
|
||||
// BUG: something is very wrong somewhere if this branch is executed
|
||||
// either in the SQLx MySQL driver or in the MySQL server
|
||||
return Err(Error::connect(MySqlDatabaseError::malformed_packet(&format!(
|
||||
return Err(MySqlDatabaseError::malformed_packet(&format!(
|
||||
"Received {} bytes for packet but expecting {} bytes",
|
||||
packet.bytes.len(),
|
||||
len
|
||||
))));
|
||||
))
|
||||
.into());
|
||||
}
|
||||
|
||||
Ok(packet)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user