mod sql_state;
pub use postgres_types::{WasNull, WrongType};
use core::{
convert::Infallible,
fmt,
ops::{Deref, DerefMut},
};
use std::{backtrace::Backtrace, error, io};
use fallible_iterator::FallibleIterator;
use postgres_protocol::message::backend::ErrorFields;
use super::from_sql::FromSqlError;
pub use self::sql_state::SqlState;
pub struct Error(Box<dyn error::Error + Send + Sync>);
impl Error {
pub fn is_driver_down(&self) -> bool {
self.0.is::<DriverDown>() || self.0.is::<DriverDownReceiving>()
}
pub(crate) fn todo() -> Self {
Self(Box::new(ToDo {
back_trace: Backtrace::capture(),
}))
}
pub(crate) fn driver_io(read: Option<io::Error>, write: Option<io::Error>) -> Self {
match (read, write) {
(Some(read), Some(write)) => Self::from(DriverIoErrorMulti { read, write }),
(Some(read), None) => Self::from(read),
(None, Some(write)) => Self::from(write),
_ => unreachable!("Driver must not report error when it doesn't produce any"),
}
}
#[cold]
#[inline(never)]
pub(crate) fn db(mut fields: ErrorFields<'_>) -> Error {
match DbError::parse(&mut fields) {
Ok(e) => Error::from(e),
Err(e) => Error::from(e),
}
}
pub(crate) fn unexpected() -> Self {
Self(Box::new(UnexpectedMessage {
back_trace: Backtrace::capture(),
}))
}
}
impl Deref for Error {
type Target = dyn error::Error + Send + Sync;
fn deref(&self) -> &Self::Target {
&*self.0
}
}
impl DerefMut for Error {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut *self.0
}
}
impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
self.0.source()
}
}
macro_rules! from_impl {
($i: ty) => {
impl From<$i> for Error {
fn from(e: $i) -> Self {
Self(Box::new(e))
}
}
};
}
#[derive(Debug)]
pub struct ToDo {
back_trace: Backtrace,
}
impl fmt::Display for ToDo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "WIP error type with thread backtrace: {}", self.back_trace)
}
}
impl error::Error for ToDo {}
#[derive(Debug)]
pub struct Completed;
impl fmt::Display for Completed {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Response has already finished. No more database response available")
}
}
impl error::Error for Completed {}
from_impl!(Completed);
#[derive(Default)]
pub struct DriverDown;
impl fmt::Debug for DriverDown {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("DriverDown").finish()
}
}
impl fmt::Display for DriverDown {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Client's Driver is dropped and unaccessible. Associated query has not been sent to database.")
}
}
impl error::Error for DriverDown {}
from_impl!(DriverDown);
#[derive(Debug)]
pub struct DriverDownReceiving;
impl fmt::Display for DriverDownReceiving {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Client's Driver is dropped and unaccessible. Associated query MAY have been sent to database.")
}
}
impl error::Error for DriverDownReceiving {}
from_impl!(DriverDownReceiving);
#[derive(Debug)]
pub struct DriverIoErrorMulti {
read: io::Error,
write: io::Error,
}
impl fmt::Display for DriverIoErrorMulti {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Multiple IO error from driver {{ read error: {}, write error: {} }}",
self.read, self.write
)
}
}
impl error::Error for DriverIoErrorMulti {}
from_impl!(DriverIoErrorMulti);
pub struct InvalidColumnIndex(pub String);
impl fmt::Debug for InvalidColumnIndex {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("InvalidColumnIndex").finish()
}
}
impl fmt::Display for InvalidColumnIndex {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "invalid column index: {}", self.0)
}
}
impl error::Error for InvalidColumnIndex {}
from_impl!(InvalidColumnIndex);
#[derive(Debug)]
pub struct InvalidParamCount {
pub expected: usize,
pub params: usize,
}
impl fmt::Display for InvalidParamCount {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"expected Statement bind to {} parameters but got {}.\r\n",
self.expected, self.params
)?;
f.write_str("note: consider use `Statement::bind` or check the parameter values count if already used")
}
}
impl error::Error for InvalidParamCount {}
from_impl!(InvalidParamCount);
impl From<Infallible> for Error {
fn from(e: Infallible) -> Self {
match e {}
}
}
from_impl!(io::Error);
impl From<FromSqlError> for Error {
fn from(e: FromSqlError) -> Self {
Self(e)
}
}
#[derive(Debug)]
pub enum ConfigError {
EmptyHost,
EmptyPort,
}
impl fmt::Display for ConfigError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Config error: ")?;
match self {
Self::EmptyHost => f.write_str("no available host name found"),
Self::EmptyPort => f.write_str("no available host port found"),
}
}
}
impl error::Error for ConfigError {}
from_impl!(ConfigError);
#[derive(Debug)]
pub enum AuthenticationError {
MissingUserName,
MissingPassWord,
WrongPassWord,
}
impl fmt::Display for AuthenticationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Self::MissingUserName => f.write_str("username is missing")?,
Self::MissingPassWord => f.write_str("password is missing")?,
Self::WrongPassWord => f.write_str("password is wrong")?,
}
f.write_str(" for authentication")
}
}
impl error::Error for AuthenticationError {}
from_impl!(AuthenticationError);
#[non_exhaustive]
#[derive(Debug)]
pub enum SystemError {
Unix,
}
impl fmt::Display for SystemError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Self::Unix => f.write_str("unix")?,
}
f.write_str(" system is not available")
}
}
impl error::Error for SystemError {}
from_impl!(SystemError);
#[non_exhaustive]
#[derive(Debug)]
pub enum FeatureError {
Tls,
Quic,
}
impl fmt::Display for FeatureError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Self::Tls => f.write_str("tls")?,
Self::Quic => f.write_str("quic")?,
}
f.write_str(" feature is not enabled")
}
}
impl error::Error for FeatureError {}
from_impl!(FeatureError);
#[derive(Debug, PartialEq, Eq)]
pub enum RuntimeError {
RequireNoTokio,
}
impl fmt::Display for RuntimeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Self::RequireNoTokio => f.write_str("Tokio runtime detected. Must be called from outside of tokio"),
}
}
}
impl error::Error for RuntimeError {}
from_impl!(RuntimeError);
#[derive(Debug)]
pub struct UnexpectedMessage {
back_trace: Backtrace,
}
impl fmt::Display for UnexpectedMessage {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Unexpected message from database with stack trace:\r\n")?;
write!(f, "{}", self.back_trace)
}
}
impl error::Error for UnexpectedMessage {}
#[cold]
#[inline(never)]
pub(crate) fn unexpected_eof_err() -> io::Error {
io::Error::new(
io::ErrorKind::UnexpectedEof,
"zero byte read. remote close connection unexpectedly",
)
}
from_impl!(WrongType);
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DbError {
severity: String,
parsed_severity: Option<Severity>,
code: SqlState,
message: String,
detail: Option<String>,
hint: Option<String>,
position: Option<ErrorPosition>,
where_: Option<String>,
schema: Option<String>,
table: Option<String>,
column: Option<String>,
datatype: Option<String>,
constraint: Option<String>,
file: Option<String>,
line: Option<u32>,
routine: Option<String>,
}
impl DbError {
fn parse(fields: &mut ErrorFields<'_>) -> io::Result<DbError> {
let mut severity = None;
let mut parsed_severity = None;
let mut code = None;
let mut message = None;
let mut detail = None;
let mut hint = None;
let mut normal_position = None;
let mut internal_position = None;
let mut internal_query = None;
let mut where_ = None;
let mut schema = None;
let mut table = None;
let mut column = None;
let mut datatype = None;
let mut constraint = None;
let mut file = None;
let mut line = None;
let mut routine = None;
while let Some(field) = fields.next()? {
let value = String::from_utf8_lossy(field.value_bytes());
match field.type_() {
b'S' => severity = Some(value.into_owned()),
b'C' => code = Some(SqlState::from_code(&value)),
b'M' => message = Some(value.into_owned()),
b'D' => detail = Some(value.into_owned()),
b'H' => hint = Some(value.into_owned()),
b'P' => {
normal_position = Some(value.parse::<u32>().map_err(|_| {
io::Error::new(io::ErrorKind::InvalidInput, "`P` field did not contain an integer")
})?);
}
b'p' => {
internal_position = Some(value.parse::<u32>().map_err(|_| {
io::Error::new(io::ErrorKind::InvalidInput, "`p` field did not contain an integer")
})?);
}
b'q' => internal_query = Some(value.into_owned()),
b'W' => where_ = Some(value.into_owned()),
b's' => schema = Some(value.into_owned()),
b't' => table = Some(value.into_owned()),
b'c' => column = Some(value.into_owned()),
b'd' => datatype = Some(value.into_owned()),
b'n' => constraint = Some(value.into_owned()),
b'F' => file = Some(value.into_owned()),
b'L' => {
line = Some(value.parse::<u32>().map_err(|_| {
io::Error::new(io::ErrorKind::InvalidInput, "`L` field did not contain an integer")
})?);
}
b'R' => routine = Some(value.into_owned()),
b'V' => {
parsed_severity = Some(Severity::from_str(&value).ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidInput, "`V` field contained an invalid value")
})?);
}
_ => {}
}
}
Ok(DbError {
severity: severity.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "`S` field missing"))?,
parsed_severity,
code: code.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "`C` field missing"))?,
message: message.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "`M` field missing"))?,
detail,
hint,
position: match normal_position {
Some(position) => Some(ErrorPosition::Original(position)),
None => match internal_position {
Some(position) => Some(ErrorPosition::Internal {
position,
query: internal_query.ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidInput, "`q` field missing but `p` field present")
})?,
}),
None => None,
},
},
where_,
schema,
table,
column,
datatype,
constraint,
file,
line,
routine,
})
}
pub fn severity(&self) -> &str {
&self.severity
}
pub fn parsed_severity(&self) -> Option<Severity> {
self.parsed_severity
}
pub fn code(&self) -> &SqlState {
&self.code
}
pub fn message(&self) -> &str {
&self.message
}
pub fn detail(&self) -> Option<&str> {
self.detail.as_deref()
}
pub fn hint(&self) -> Option<&str> {
self.hint.as_deref()
}
pub fn position(&self) -> Option<&ErrorPosition> {
self.position.as_ref()
}
pub fn where_(&self) -> Option<&str> {
self.where_.as_deref()
}
pub fn schema(&self) -> Option<&str> {
self.schema.as_deref()
}
pub fn table(&self) -> Option<&str> {
self.table.as_deref()
}
pub fn column(&self) -> Option<&str> {
self.column.as_deref()
}
pub fn datatype(&self) -> Option<&str> {
self.datatype.as_deref()
}
pub fn constraint(&self) -> Option<&str> {
self.constraint.as_deref()
}
pub fn file(&self) -> Option<&str> {
self.file.as_deref()
}
pub fn line(&self) -> Option<u32> {
self.line
}
pub fn routine(&self) -> Option<&str> {
self.routine.as_deref()
}
}
impl fmt::Display for DbError {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "{}: {}", self.severity, self.message)?;
if let Some(detail) = &self.detail {
write!(fmt, "\nDETAIL: {}", detail)?;
}
if let Some(hint) = &self.hint {
write!(fmt, "\nHINT: {}", hint)?;
}
Ok(())
}
}
impl error::Error for DbError {}
from_impl!(DbError);
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Severity {
Panic,
Fatal,
Error,
Warning,
Notice,
Debug,
Info,
Log,
}
impl fmt::Display for Severity {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match *self {
Severity::Panic => "PANIC",
Severity::Fatal => "FATAL",
Severity::Error => "ERROR",
Severity::Warning => "WARNING",
Severity::Notice => "NOTICE",
Severity::Debug => "DEBUG",
Severity::Info => "INFO",
Severity::Log => "LOG",
};
fmt.write_str(s)
}
}
impl Severity {
fn from_str(s: &str) -> Option<Severity> {
match s {
"PANIC" => Some(Severity::Panic),
"FATAL" => Some(Severity::Fatal),
"ERROR" => Some(Severity::Error),
"WARNING" => Some(Severity::Warning),
"NOTICE" => Some(Severity::Notice),
"DEBUG" => Some(Severity::Debug),
"INFO" => Some(Severity::Info),
"LOG" => Some(Severity::Log),
_ => None,
}
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum ErrorPosition {
Original(u32),
Internal {
position: u32,
query: String,
},
}