LCOV - code coverage report
Current view: top level - libs/proxy/tokio-postgres2/src/error - mod.rs (source / functions) Coverage Total Hit
Test: 07bee600374ccd486c69370d0972d9035964fe68.info Lines: 28.2 % 262 74
Test Date: 2025-02-20 13:11:02 Functions: 13.5 % 52 7

            Line data    Source code
       1              : //! Errors.
       2              : 
       3              : use fallible_iterator::FallibleIterator;
       4              : use postgres_protocol2::message::backend::{ErrorFields, ErrorResponseBody};
       5              : use std::error::{self, Error as _Error};
       6              : use std::fmt;
       7              : use std::io;
       8              : 
       9              : pub use self::sqlstate::*;
      10              : 
      11              : #[allow(clippy::unreadable_literal)]
      12              : mod sqlstate;
      13              : 
      14              : /// The severity of a Postgres error or notice.
      15              : #[derive(Debug, Copy, Clone, PartialEq, Eq)]
      16              : pub enum Severity {
      17              :     /// PANIC
      18              :     Panic,
      19              :     /// FATAL
      20              :     Fatal,
      21              :     /// ERROR
      22              :     Error,
      23              :     /// WARNING
      24              :     Warning,
      25              :     /// NOTICE
      26              :     Notice,
      27              :     /// DEBUG
      28              :     Debug,
      29              :     /// INFO
      30              :     Info,
      31              :     /// LOG
      32              :     Log,
      33              : }
      34              : 
      35              : impl fmt::Display for Severity {
      36            0 :     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
      37            0 :         let s = match *self {
      38            0 :             Severity::Panic => "PANIC",
      39            0 :             Severity::Fatal => "FATAL",
      40            0 :             Severity::Error => "ERROR",
      41            0 :             Severity::Warning => "WARNING",
      42            0 :             Severity::Notice => "NOTICE",
      43            0 :             Severity::Debug => "DEBUG",
      44            0 :             Severity::Info => "INFO",
      45            0 :             Severity::Log => "LOG",
      46              :         };
      47            0 :         fmt.write_str(s)
      48            0 :     }
      49              : }
      50              : 
      51              : impl Severity {
      52            0 :     fn from_str(s: &str) -> Option<Severity> {
      53            0 :         match s {
      54            0 :             "PANIC" => Some(Severity::Panic),
      55            0 :             "FATAL" => Some(Severity::Fatal),
      56            0 :             "ERROR" => Some(Severity::Error),
      57            0 :             "WARNING" => Some(Severity::Warning),
      58            0 :             "NOTICE" => Some(Severity::Notice),
      59            0 :             "DEBUG" => Some(Severity::Debug),
      60            0 :             "INFO" => Some(Severity::Info),
      61            0 :             "LOG" => Some(Severity::Log),
      62            0 :             _ => None,
      63              :         }
      64            0 :     }
      65              : }
      66              : 
      67              : /// A Postgres error or notice.
      68              : #[derive(Debug, Clone, PartialEq, Eq)]
      69              : pub struct DbError {
      70              :     severity: String,
      71              :     parsed_severity: Option<Severity>,
      72              :     code: SqlState,
      73              :     message: String,
      74              :     detail: Option<String>,
      75              :     hint: Option<String>,
      76              :     position: Option<ErrorPosition>,
      77              :     where_: Option<String>,
      78              :     schema: Option<String>,
      79              :     table: Option<String>,
      80              :     column: Option<String>,
      81              :     datatype: Option<String>,
      82              :     constraint: Option<String>,
      83              :     file: Option<String>,
      84              :     line: Option<u32>,
      85              :     routine: Option<String>,
      86              : }
      87              : 
      88              : impl DbError {
      89            1 :     pub(crate) fn parse(fields: &mut ErrorFields<'_>) -> io::Result<DbError> {
      90            1 :         let mut severity = None;
      91            1 :         let mut parsed_severity = None;
      92            1 :         let mut code = None;
      93            1 :         let mut message = None;
      94            1 :         let mut detail = None;
      95            1 :         let mut hint = None;
      96            1 :         let mut normal_position = None;
      97            1 :         let mut internal_position = None;
      98            1 :         let mut internal_query = None;
      99            1 :         let mut where_ = None;
     100            1 :         let mut schema = None;
     101            1 :         let mut table = None;
     102            1 :         let mut column = None;
     103            1 :         let mut datatype = None;
     104            1 :         let mut constraint = None;
     105            1 :         let mut file = None;
     106            1 :         let mut line = None;
     107            1 :         let mut routine = None;
     108              : 
     109            4 :         while let Some(field) = fields.next()? {
     110            3 :             match field.type_() {
     111            1 :                 b'S' => severity = Some(field.value().to_owned()),
     112            1 :                 b'C' => code = Some(SqlState::from_code(field.value())),
     113            1 :                 b'M' => message = Some(field.value().to_owned()),
     114            0 :                 b'D' => detail = Some(field.value().to_owned()),
     115            0 :                 b'H' => hint = Some(field.value().to_owned()),
     116              :                 b'P' => {
     117            0 :                     normal_position = Some(field.value().parse::<u32>().map_err(|_| {
     118            0 :                         io::Error::new(
     119            0 :                             io::ErrorKind::InvalidInput,
     120            0 :                             "`P` field did not contain an integer",
     121            0 :                         )
     122            0 :                     })?);
     123              :                 }
     124              :                 b'p' => {
     125            0 :                     internal_position = Some(field.value().parse::<u32>().map_err(|_| {
     126            0 :                         io::Error::new(
     127            0 :                             io::ErrorKind::InvalidInput,
     128            0 :                             "`p` field did not contain an integer",
     129            0 :                         )
     130            0 :                     })?);
     131              :                 }
     132            0 :                 b'q' => internal_query = Some(field.value().to_owned()),
     133            0 :                 b'W' => where_ = Some(field.value().to_owned()),
     134            0 :                 b's' => schema = Some(field.value().to_owned()),
     135            0 :                 b't' => table = Some(field.value().to_owned()),
     136            0 :                 b'c' => column = Some(field.value().to_owned()),
     137            0 :                 b'd' => datatype = Some(field.value().to_owned()),
     138            0 :                 b'n' => constraint = Some(field.value().to_owned()),
     139            0 :                 b'F' => file = Some(field.value().to_owned()),
     140              :                 b'L' => {
     141            0 :                     line = Some(field.value().parse::<u32>().map_err(|_| {
     142            0 :                         io::Error::new(
     143            0 :                             io::ErrorKind::InvalidInput,
     144            0 :                             "`L` field did not contain an integer",
     145            0 :                         )
     146            0 :                     })?);
     147              :                 }
     148            0 :                 b'R' => routine = Some(field.value().to_owned()),
     149              :                 b'V' => {
     150            0 :                     parsed_severity = Some(Severity::from_str(field.value()).ok_or_else(|| {
     151            0 :                         io::Error::new(
     152            0 :                             io::ErrorKind::InvalidInput,
     153            0 :                             "`V` field contained an invalid value",
     154            0 :                         )
     155            0 :                     })?);
     156              :                 }
     157            0 :                 _ => {}
     158              :             }
     159              :         }
     160              : 
     161              :         Ok(DbError {
     162            1 :             severity: severity
     163            1 :                 .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "`S` field missing"))?,
     164            1 :             parsed_severity,
     165            1 :             code: code
     166            1 :                 .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "`C` field missing"))?,
     167            1 :             message: message
     168            1 :                 .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "`M` field missing"))?,
     169            1 :             detail,
     170            1 :             hint,
     171            1 :             position: match normal_position {
     172            0 :                 Some(position) => Some(ErrorPosition::Original(position)),
     173            1 :                 None => match internal_position {
     174            0 :                     Some(position) => Some(ErrorPosition::Internal {
     175            0 :                         position,
     176            0 :                         query: internal_query.ok_or_else(|| {
     177            0 :                             io::Error::new(
     178            0 :                                 io::ErrorKind::InvalidInput,
     179            0 :                                 "`q` field missing but `p` field present",
     180            0 :                             )
     181            0 :                         })?,
     182              :                     }),
     183            1 :                     None => None,
     184              :                 },
     185              :             },
     186            1 :             where_,
     187            1 :             schema,
     188            1 :             table,
     189            1 :             column,
     190            1 :             datatype,
     191            1 :             constraint,
     192            1 :             file,
     193            1 :             line,
     194            1 :             routine,
     195              :         })
     196            1 :     }
     197              : 
     198              :     /// The field contents are ERROR, FATAL, or PANIC (in an error message),
     199              :     /// or WARNING, NOTICE, DEBUG, INFO, or LOG (in a notice message), or a
     200              :     /// localized translation of one of these.
     201            0 :     pub fn severity(&self) -> &str {
     202            0 :         &self.severity
     203            0 :     }
     204              : 
     205              :     /// A parsed, nonlocalized version of `severity`. (PostgreSQL 9.6+)
     206            0 :     pub fn parsed_severity(&self) -> Option<Severity> {
     207            0 :         self.parsed_severity
     208            0 :     }
     209              : 
     210              :     /// The SQLSTATE code for the error.
     211            0 :     pub fn code(&self) -> &SqlState {
     212            0 :         &self.code
     213            0 :     }
     214              : 
     215              :     /// The primary human-readable error message.
     216              :     ///
     217              :     /// This should be accurate but terse (typically one line).
     218            0 :     pub fn message(&self) -> &str {
     219            0 :         &self.message
     220            0 :     }
     221              : 
     222              :     /// An optional secondary error message carrying more detail about the
     223              :     /// problem.
     224              :     ///
     225              :     /// Might run to multiple lines.
     226            0 :     pub fn detail(&self) -> Option<&str> {
     227            0 :         self.detail.as_deref()
     228            0 :     }
     229              : 
     230              :     /// An optional suggestion what to do about the problem.
     231              :     ///
     232              :     /// This is intended to differ from `detail` in that it offers advice
     233              :     /// (potentially inappropriate) rather than hard facts. Might run to
     234              :     /// multiple lines.
     235            0 :     pub fn hint(&self) -> Option<&str> {
     236            0 :         self.hint.as_deref()
     237            0 :     }
     238              : 
     239              :     /// An optional error cursor position into either the original query string
     240              :     /// or an internally generated query.
     241            0 :     pub fn position(&self) -> Option<&ErrorPosition> {
     242            0 :         self.position.as_ref()
     243            0 :     }
     244              : 
     245              :     /// An indication of the context in which the error occurred.
     246              :     ///
     247              :     /// Presently this includes a call stack traceback of active procedural
     248              :     /// language functions and internally-generated queries. The trace is one
     249              :     /// entry per line, most recent first.
     250            0 :     pub fn where_(&self) -> Option<&str> {
     251            0 :         self.where_.as_deref()
     252            0 :     }
     253              : 
     254              :     /// If the error was associated with a specific database object, the name
     255              :     /// of the schema containing that object, if any. (PostgreSQL 9.3+)
     256            0 :     pub fn schema(&self) -> Option<&str> {
     257            0 :         self.schema.as_deref()
     258            0 :     }
     259              : 
     260              :     /// If the error was associated with a specific table, the name of the
     261              :     /// table. (Refer to the schema name field for the name of the table's
     262              :     /// schema.) (PostgreSQL 9.3+)
     263            0 :     pub fn table(&self) -> Option<&str> {
     264            0 :         self.table.as_deref()
     265            0 :     }
     266              : 
     267              :     /// If the error was associated with a specific table column, the name of
     268              :     /// the column.
     269              :     ///
     270              :     /// (Refer to the schema and table name fields to identify the table.)
     271              :     /// (PostgreSQL 9.3+)
     272            0 :     pub fn column(&self) -> Option<&str> {
     273            0 :         self.column.as_deref()
     274            0 :     }
     275              : 
     276              :     /// If the error was associated with a specific data type, the name of the
     277              :     /// data type. (Refer to the schema name field for the name of the data
     278              :     /// type's schema.) (PostgreSQL 9.3+)
     279            0 :     pub fn datatype(&self) -> Option<&str> {
     280            0 :         self.datatype.as_deref()
     281            0 :     }
     282              : 
     283              :     /// If the error was associated with a specific constraint, the name of the
     284              :     /// constraint.
     285              :     ///
     286              :     /// Refer to fields listed above for the associated table or domain.
     287              :     /// (For this purpose, indexes are treated as constraints, even if they
     288              :     /// weren't created with constraint syntax.) (PostgreSQL 9.3+)
     289            0 :     pub fn constraint(&self) -> Option<&str> {
     290            0 :         self.constraint.as_deref()
     291            0 :     }
     292              : 
     293              :     /// The file name of the source-code location where the error was reported.
     294            0 :     pub fn file(&self) -> Option<&str> {
     295            0 :         self.file.as_deref()
     296            0 :     }
     297              : 
     298              :     /// The line number of the source-code location where the error was
     299              :     /// reported.
     300            0 :     pub fn line(&self) -> Option<u32> {
     301            0 :         self.line
     302            0 :     }
     303              : 
     304              :     /// The name of the source-code routine reporting the error.
     305            0 :     pub fn routine(&self) -> Option<&str> {
     306            0 :         self.routine.as_deref()
     307            0 :     }
     308              : }
     309              : 
     310              : impl fmt::Display for DbError {
     311            2 :     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
     312            2 :         write!(fmt, "{}: {}", self.severity, self.message)?;
     313            2 :         if let Some(detail) = &self.detail {
     314            0 :             write!(fmt, "\nDETAIL: {}", detail)?;
     315            2 :         }
     316            2 :         if let Some(hint) = &self.hint {
     317            0 :             write!(fmt, "\nHINT: {}", hint)?;
     318            2 :         }
     319            2 :         Ok(())
     320            2 :     }
     321              : }
     322              : 
     323              : impl error::Error for DbError {}
     324              : 
     325              : /// Represents the position of an error in a query.
     326              : #[derive(Clone, PartialEq, Eq, Debug)]
     327              : pub enum ErrorPosition {
     328              :     /// A position in the original query.
     329              :     Original(u32),
     330              :     /// A position in an internally generated query.
     331              :     Internal {
     332              :         /// The byte position.
     333              :         position: u32,
     334              :         /// A query generated by the Postgres server.
     335              :         query: String,
     336              :     },
     337              : }
     338              : 
     339              : #[derive(Debug, PartialEq)]
     340              : enum Kind {
     341              :     Io,
     342              :     UnexpectedMessage,
     343              :     Tls,
     344              :     ToSql(usize),
     345              :     FromSql(usize),
     346              :     Column(String),
     347              :     Closed,
     348              :     Db,
     349              :     Parse,
     350              :     Encode,
     351              :     Authentication,
     352              :     Config,
     353              :     Connect,
     354              :     Timeout,
     355              : }
     356              : 
     357              : struct ErrorInner {
     358              :     kind: Kind,
     359              :     cause: Option<Box<dyn error::Error + Sync + Send>>,
     360              : }
     361              : 
     362              : /// An error communicating with the Postgres server.
     363              : pub struct Error(Box<ErrorInner>);
     364              : 
     365              : impl fmt::Debug for Error {
     366            0 :     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
     367            0 :         fmt.debug_struct("Error")
     368            0 :             .field("kind", &self.0.kind)
     369            0 :             .field("cause", &self.0.cause)
     370            0 :             .finish()
     371            0 :     }
     372              : }
     373              : 
     374              : impl fmt::Display for Error {
     375            2 :     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
     376            2 :         match &self.0.kind {
     377            0 :             Kind::Io => fmt.write_str("error communicating with the server")?,
     378            0 :             Kind::UnexpectedMessage => fmt.write_str("unexpected message from server")?,
     379            0 :             Kind::Tls => fmt.write_str("error performing TLS handshake")?,
     380            0 :             Kind::ToSql(idx) => write!(fmt, "error serializing parameter {}", idx)?,
     381            0 :             Kind::FromSql(idx) => write!(fmt, "error deserializing column {}", idx)?,
     382            0 :             Kind::Column(column) => write!(fmt, "invalid column `{}`", column)?,
     383            0 :             Kind::Closed => fmt.write_str("connection closed")?,
     384            2 :             Kind::Db => fmt.write_str("db error")?,
     385            0 :             Kind::Parse => fmt.write_str("error parsing response from server")?,
     386            0 :             Kind::Encode => fmt.write_str("error encoding message to server")?,
     387            0 :             Kind::Authentication => fmt.write_str("authentication error")?,
     388            0 :             Kind::Config => fmt.write_str("invalid configuration")?,
     389            0 :             Kind::Connect => fmt.write_str("error connecting to server")?,
     390            0 :             Kind::Timeout => fmt.write_str("timeout waiting for server")?,
     391              :         };
     392            2 :         if let Some(ref cause) = self.0.cause {
     393            2 :             write!(fmt, ": {}", cause)?;
     394            0 :         }
     395            2 :         Ok(())
     396            2 :     }
     397              : }
     398              : 
     399              : impl error::Error for Error {
     400            0 :     fn source(&self) -> Option<&(dyn error::Error + 'static)> {
     401            0 :         self.0.cause.as_ref().map(|e| &**e as _)
     402            0 :     }
     403              : }
     404              : 
     405              : impl Error {
     406              :     /// Consumes the error, returning its cause.
     407            0 :     pub fn into_source(self) -> Option<Box<dyn error::Error + Sync + Send>> {
     408            0 :         self.0.cause
     409            0 :     }
     410              : 
     411              :     /// Returns the source of this error if it was a `DbError`.
     412              :     ///
     413              :     /// This is a simple convenience method.
     414            0 :     pub fn as_db_error(&self) -> Option<&DbError> {
     415            0 :         self.source().and_then(|e| e.downcast_ref::<DbError>())
     416            0 :     }
     417              : 
     418              :     /// Determines if the error was associated with closed connection.
     419            0 :     pub fn is_closed(&self) -> bool {
     420            0 :         self.0.kind == Kind::Closed
     421            0 :     }
     422              : 
     423              :     /// Returns the SQLSTATE error code associated with the error.
     424              :     ///
     425              :     /// This is a convenience method that downcasts the cause to a `DbError` and returns its code.
     426            0 :     pub fn code(&self) -> Option<&SqlState> {
     427            0 :         self.as_db_error().map(DbError::code)
     428            0 :     }
     429              : 
     430            8 :     fn new(kind: Kind, cause: Option<Box<dyn error::Error + Sync + Send>>) -> Error {
     431            8 :         Error(Box::new(ErrorInner { kind, cause }))
     432            8 :     }
     433              : 
     434            0 :     pub(crate) fn closed() -> Error {
     435            0 :         Error::new(Kind::Closed, None)
     436            0 :     }
     437              : 
     438            0 :     pub(crate) fn unexpected_message() -> Error {
     439            0 :         Error::new(Kind::UnexpectedMessage, None)
     440            0 :     }
     441              : 
     442              :     #[allow(clippy::needless_pass_by_value)]
     443            1 :     pub(crate) fn db(error: ErrorResponseBody) -> Error {
     444            1 :         match DbError::parse(&mut error.fields()) {
     445            1 :             Ok(e) => Error::new(Kind::Db, Some(Box::new(e))),
     446            0 :             Err(e) => Error::new(Kind::Parse, Some(Box::new(e))),
     447              :         }
     448            1 :     }
     449              : 
     450            0 :     pub(crate) fn parse(e: io::Error) -> Error {
     451            0 :         Error::new(Kind::Parse, Some(Box::new(e)))
     452            0 :     }
     453              : 
     454            0 :     pub(crate) fn encode(e: io::Error) -> Error {
     455            0 :         Error::new(Kind::Encode, Some(Box::new(e)))
     456            0 :     }
     457              : 
     458              :     #[allow(clippy::wrong_self_convention)]
     459            0 :     pub(crate) fn to_sql(e: Box<dyn error::Error + Sync + Send>, idx: usize) -> Error {
     460            0 :         Error::new(Kind::ToSql(idx), Some(e))
     461            0 :     }
     462              : 
     463            0 :     pub(crate) fn from_sql(e: Box<dyn error::Error + Sync + Send>, idx: usize) -> Error {
     464            0 :         Error::new(Kind::FromSql(idx), Some(e))
     465            0 :     }
     466              : 
     467            0 :     pub(crate) fn column(column: String) -> Error {
     468            0 :         Error::new(Kind::Column(column), None)
     469            0 :     }
     470              : 
     471            0 :     pub(crate) fn tls(e: Box<dyn error::Error + Sync + Send>) -> Error {
     472            0 :         Error::new(Kind::Tls, Some(e))
     473            0 :     }
     474              : 
     475            6 :     pub(crate) fn io(e: io::Error) -> Error {
     476            6 :         Error::new(Kind::Io, Some(Box::new(e)))
     477            6 :     }
     478              : 
     479            1 :     pub(crate) fn authentication(e: Box<dyn error::Error + Sync + Send>) -> Error {
     480            1 :         Error::new(Kind::Authentication, Some(e))
     481            1 :     }
     482              : 
     483            0 :     pub(crate) fn config(e: Box<dyn error::Error + Sync + Send>) -> Error {
     484            0 :         Error::new(Kind::Config, Some(e))
     485            0 :     }
     486              : 
     487            0 :     pub(crate) fn connect(e: io::Error) -> Error {
     488            0 :         Error::new(Kind::Connect, Some(Box::new(e)))
     489            0 :     }
     490              : 
     491              :     #[doc(hidden)]
     492            0 :     pub fn __private_api_timeout() -> Error {
     493            0 :         Error::new(Kind::Timeout, None)
     494            0 :     }
     495              : }
        

Generated by: LCOV version 2.1-beta