sqlx/sqlx-core/src/error.rs

193 lines
5.9 KiB
Rust

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;
mod database;
pub use database::DatabaseError;
use crate::Column;
/// 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 {
/// 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>> },
/// 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),
/// No rows returned by a query required to return at least one row.
///
/// 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),
/// 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 opt(
message: impl Into<Cow<'static, str>>,
source: impl Into<Box<dyn StdError + Send + Sync>>,
) -> Self {
Self::ConnectOptions { message: message.into(), source: Some(source.into()) }
}
#[doc(hidden)]
pub fn opt_msg(message: impl Into<Cow<'static, str>>) -> Self {
Self::ConnectOptions { message: message.into(), source: None }
}
#[doc(hidden)]
pub fn column_decode(column: &impl Column, source: DecodeError) -> Self {
crate::Error::ColumnDecode {
source,
column_index: column.index(),
column_name: column.name().to_owned().into_boxed_str(),
}
}
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::Network(source) => write!(f, "{}", source),
Self::Database(source) => write!(f, "{}", source),
Self::ConnectOptions { message, source: None } => {
write!(f, "{}", message)
}
Self::ConnectOptions { message, source: Some(source) } => {
write!(f, "{}: {}", message, source)
}
Self::RowNotFound => {
f.write_str("No row returned by a query required to return at least one row")
}
Self::Closed => f.write_str("Connection or pool is closed"),
Self::Decode(error) => {
write!(f, "Decode: {}", error)
}
Self::Encode(error) => {
write!(f, "Encode: {}", error)
}
Self::ColumnIndexOutOfBounds { index, len } => {
write!(
f,
"Column index out of bounds: the len is {}, but the index is {}",
len, index
)
}
Self::ColumnNotFound { name } => {
write!(f, "No column found for name `{}`", name)
}
Self::ColumnDecode { column_index, column_name, source } => {
if column_name.is_empty() {
write!(f, "Decode column {}: {}", column_index, source)
} else {
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)
}
}
}
}
impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
match self {
Self::ConnectOptions { source: Some(source), .. } => Some(&**source),
Self::Network(source) => Some(source),
_ => None,
}
}
}
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)
}
}
impl From<std::io::ErrorKind> for Error {
fn from(error: std::io::ErrorKind) -> Self {
Self::Network(error.into())
}
}