xitca_postgres/
error.rs

1//! library error types with re-export error from `rust-postgres`
2//!
3//! this crate only exposes a single [Error] type from API where type erase is used to hide complexity
4
5mod sql_state;
6
7pub use postgres_types::{WasNull, WrongType};
8
9use core::{
10    convert::Infallible,
11    fmt,
12    ops::{Deref, DerefMut},
13};
14
15use std::{backtrace::Backtrace, error, io};
16
17use fallible_iterator::FallibleIterator;
18use postgres_protocol::message::backend::ErrorFields;
19
20use super::from_sql::FromSqlError;
21
22pub use self::sql_state::SqlState;
23
24/// public facing error type. providing basic format and display based error handling.
25///
26/// for typed based error handling runtime type cast is needed with the help of other
27/// public error types offered by this module.
28///
29/// # Example
30/// ```rust
31/// use xitca_postgres::error::{DriverDown, Error};
32///
33/// fn is_driver_down(e: Error) -> bool {
34///     // downcast error to DriverDown error type to check if client driver is gone.
35///     e.downcast_ref::<DriverDown>().is_some()
36/// }
37/// ```
38pub struct Error(Box<dyn error::Error + Send + Sync>);
39
40impl Error {
41    pub fn is_driver_down(&self) -> bool {
42        self.0.is::<DriverDown>() || self.0.is::<DriverDownReceiving>()
43    }
44
45    pub(crate) fn todo() -> Self {
46        Self(Box::new(ToDo {
47            back_trace: Backtrace::capture(),
48        }))
49    }
50
51    pub(crate) fn driver_io(read: Option<io::Error>, write: Option<io::Error>) -> Self {
52        match (read, write) {
53            (Some(read), Some(write)) => Self::from(DriverIoErrorMulti { read, write }),
54            (Some(read), None) => Self::from(read),
55            (None, Some(write)) => Self::from(write),
56            _ => unreachable!("Driver must not report error when it doesn't produce any"),
57        }
58    }
59
60    #[cold]
61    #[inline(never)]
62    pub(crate) fn db(mut fields: ErrorFields<'_>) -> Error {
63        match DbError::parse(&mut fields) {
64            Ok(e) => Error::from(e),
65            Err(e) => Error::from(e),
66        }
67    }
68
69    pub(crate) fn unexpected() -> Self {
70        Self(Box::new(UnexpectedMessage {
71            back_trace: Backtrace::capture(),
72        }))
73    }
74}
75
76impl Deref for Error {
77    type Target = dyn error::Error + Send + Sync;
78
79    fn deref(&self) -> &Self::Target {
80        &*self.0
81    }
82}
83
84impl DerefMut for Error {
85    fn deref_mut(&mut self) -> &mut Self::Target {
86        &mut *self.0
87    }
88}
89
90impl fmt::Debug for Error {
91    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92        fmt::Debug::fmt(&self.0, f)
93    }
94}
95
96impl fmt::Display for Error {
97    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98        fmt::Display::fmt(&self.0, f)
99    }
100}
101
102impl error::Error for Error {
103    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
104        self.0.source()
105    }
106}
107
108macro_rules! from_impl {
109    ($i: ty) => {
110        impl From<$i> for Error {
111            fn from(e: $i) -> Self {
112                Self(Box::new(e))
113            }
114        }
115    };
116}
117
118/// work in progress error type with thread backtrace.
119/// use `RUST_BACKTRACE=1` env when starting your program to enable capture and format
120#[derive(Debug)]
121pub struct ToDo {
122    back_trace: Backtrace,
123}
124
125impl fmt::Display for ToDo {
126    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127        write!(f, "WIP error type with thread backtrace: {}", self.back_trace)
128    }
129}
130
131impl error::Error for ToDo {}
132
133/// [`Response`] has already finished. Polling it afterwards will cause this error.
134///
135/// [`Response`]: crate::driver::codec::Response
136#[derive(Debug)]
137pub struct Completed;
138
139impl fmt::Display for Completed {
140    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141        f.write_str("Response has already finished. No more database response available")
142    }
143}
144
145impl error::Error for Completed {}
146
147from_impl!(Completed);
148
149/// error indicate [`Client`]'s [`Driver`] is dropped and can't be accessed anymore when sending request to driver.
150///
151/// database query related to this error has not been sent to database and it's safe to retry operation if
152/// desired.
153///
154/// # Error source
155/// detailed reason of driver shutdown can be obtained from output of [`Driver`]'s [`AsyncLendingIterator`] or
156/// [`IntoFuture`] trait impl method
157/// ## Examples
158/// ```
159/// # use std::future::IntoFuture;
160/// # use xitca_postgres::{Error, Execute, Postgres};
161/// # async fn driver_error() -> Result<(), Error> {
162/// // start a connection and spawn driver task
163/// let (cli, drv) = Postgres::new("<db_confg>").connect().await?;
164/// // keep the driver task's join handle for later use
165/// let handle = tokio::spawn(drv.into_future());
166///
167/// // when query returns error immediately we check if the driver is gone.
168/// if let Err(e) = "".query(&cli).await {
169///     if e.is_driver_down() {
170///         // driver is gone and we want to know detail reason in this case.
171///         // await on the join handle will return the output of Driver task.
172///         let opt = handle.await.unwrap();
173///         println!("{opt:?}");
174///     }
175/// }
176/// # Ok(())
177/// # }
178/// ```
179///
180/// [`Client`]: crate::client::Client
181/// [`Driver`]: crate::driver::Driver
182/// [`AsyncLendingIterator`]: crate::iter::AsyncLendingIterator
183/// [`IntoFuture`]: core::future::IntoFuture
184#[derive(Default)]
185pub struct DriverDown;
186
187impl fmt::Debug for DriverDown {
188    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
189        f.debug_struct("DriverDown").finish()
190    }
191}
192
193impl fmt::Display for DriverDown {
194    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
195        f.write_str("Client's Driver is dropped and unaccessible. Associated query has not been sent to database.")
196    }
197}
198
199impl error::Error for DriverDown {}
200
201from_impl!(DriverDown);
202
203/// error indicate [Client]'s [Driver] is dropped and can't be accessed anymore when receiving response
204/// from server.
205///
206/// all mid flight response and unfinished response data are lost and can't be recovered. database query
207/// related to this error may or may not executed successfully and it should not be retried blindly.
208///
209/// [Client]: crate::client::Client
210/// [Driver]: crate::driver::Driver
211#[derive(Debug)]
212pub struct DriverDownReceiving;
213
214impl fmt::Display for DriverDownReceiving {
215    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216        f.write_str("Client's Driver is dropped and unaccessible. Associated query MAY have been sent to database.")
217    }
218}
219
220impl error::Error for DriverDownReceiving {}
221
222from_impl!(DriverDownReceiving);
223
224/// driver shutdown outcome can contain multiple io error for detailed read/write errors.
225#[derive(Debug)]
226pub struct DriverIoErrorMulti {
227    read: io::Error,
228    write: io::Error,
229}
230
231impl fmt::Display for DriverIoErrorMulti {
232    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
233        write!(
234            f,
235            "Multiple IO error from driver {{ read error: {}, write error: {} }}",
236            self.read, self.write
237        )
238    }
239}
240
241impl error::Error for DriverIoErrorMulti {}
242
243from_impl!(DriverIoErrorMulti);
244
245pub struct InvalidColumnIndex(pub String);
246
247impl fmt::Debug for InvalidColumnIndex {
248    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
249        f.debug_struct("InvalidColumnIndex").finish()
250    }
251}
252
253impl fmt::Display for InvalidColumnIndex {
254    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
255        write!(f, "invalid column index: {}", self.0)
256    }
257}
258
259impl error::Error for InvalidColumnIndex {}
260
261from_impl!(InvalidColumnIndex);
262
263#[derive(Debug)]
264pub struct InvalidParamCount {
265    pub expected: usize,
266    pub params: usize,
267}
268
269impl fmt::Display for InvalidParamCount {
270    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
271        write!(
272            f,
273            "expected Statement bind to {} parameters but got {}.\r\n",
274            self.expected, self.params
275        )?;
276        f.write_str("note: consider use `Statement::bind` or check the parameter values count if already used")
277    }
278}
279
280impl error::Error for InvalidParamCount {}
281
282from_impl!(InvalidParamCount);
283
284impl From<Infallible> for Error {
285    fn from(e: Infallible) -> Self {
286        match e {}
287    }
288}
289
290from_impl!(io::Error);
291
292impl From<FromSqlError> for Error {
293    fn from(e: FromSqlError) -> Self {
294        Self(e)
295    }
296}
297
298/// error happens when [`Config`] fail to provide necessary information.
299///
300/// [`Config`]: crate::config::Config
301#[derive(Debug)]
302pub enum ConfigError {
303    EmptyHost,
304    EmptyPort,
305}
306
307impl fmt::Display for ConfigError {
308    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
309        f.write_str("Config error: ")?;
310        match self {
311            Self::EmptyHost => f.write_str("no available host name found"),
312            Self::EmptyPort => f.write_str("no available host port found"),
313        }
314    }
315}
316
317impl error::Error for ConfigError {}
318
319from_impl!(ConfigError);
320
321/// error happens when library user failed to provide valid authentication info to database server.
322#[derive(Debug)]
323pub enum AuthenticationError {
324    MissingUserName,
325    MissingPassWord,
326    WrongPassWord,
327}
328
329impl fmt::Display for AuthenticationError {
330    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
331        match *self {
332            Self::MissingUserName => f.write_str("username is missing")?,
333            Self::MissingPassWord => f.write_str("password is missing")?,
334            Self::WrongPassWord => f.write_str("password is wrong")?,
335        }
336        f.write_str(" for authentication")
337    }
338}
339
340impl error::Error for AuthenticationError {}
341
342from_impl!(AuthenticationError);
343
344#[non_exhaustive]
345#[derive(Debug)]
346pub enum SystemError {
347    Unix,
348}
349
350impl fmt::Display for SystemError {
351    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
352        match *self {
353            Self::Unix => f.write_str("unix")?,
354        }
355        f.write_str(" system is not available")
356    }
357}
358
359impl error::Error for SystemError {}
360
361from_impl!(SystemError);
362
363#[non_exhaustive]
364#[derive(Debug)]
365pub enum FeatureError {
366    Tls,
367    Quic,
368}
369
370impl fmt::Display for FeatureError {
371    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
372        match *self {
373            Self::Tls => f.write_str("tls")?,
374            Self::Quic => f.write_str("quic")?,
375        }
376        f.write_str(" feature is not enabled")
377    }
378}
379
380impl error::Error for FeatureError {}
381
382from_impl!(FeatureError);
383
384#[derive(Debug, PartialEq, Eq)]
385pub enum RuntimeError {
386    RequireNoTokio,
387}
388
389impl fmt::Display for RuntimeError {
390    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
391        match *self {
392            Self::RequireNoTokio => f.write_str("Tokio runtime detected. Must be called from outside of tokio"),
393        }
394    }
395}
396
397impl error::Error for RuntimeError {}
398
399from_impl!(RuntimeError);
400
401/// error for database returning backend message type that is not expected.
402/// it indicates there might be protocol error on either side of the connection.
403#[derive(Debug)]
404pub struct UnexpectedMessage {
405    back_trace: Backtrace,
406}
407
408impl fmt::Display for UnexpectedMessage {
409    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
410        f.write_str("Unexpected message from database with stack trace:\r\n")?;
411        write!(f, "{}", self.back_trace)
412    }
413}
414
415impl error::Error for UnexpectedMessage {}
416
417#[cold]
418#[inline(never)]
419pub(crate) fn unexpected_eof_err() -> io::Error {
420    io::Error::new(
421        io::ErrorKind::UnexpectedEof,
422        "zero byte read. remote close connection unexpectedly",
423    )
424}
425
426from_impl!(WrongType);
427
428/// A Postgres error or notice.
429#[derive(Debug, Clone, PartialEq, Eq)]
430pub struct DbError {
431    severity: String,
432    parsed_severity: Option<Severity>,
433    code: SqlState,
434    message: String,
435    detail: Option<String>,
436    hint: Option<String>,
437    position: Option<ErrorPosition>,
438    where_: Option<String>,
439    schema: Option<String>,
440    table: Option<String>,
441    column: Option<String>,
442    datatype: Option<String>,
443    constraint: Option<String>,
444    file: Option<String>,
445    line: Option<u32>,
446    routine: Option<String>,
447}
448
449impl DbError {
450    fn parse(fields: &mut ErrorFields<'_>) -> io::Result<DbError> {
451        let mut severity = None;
452        let mut parsed_severity = None;
453        let mut code = None;
454        let mut message = None;
455        let mut detail = None;
456        let mut hint = None;
457        let mut normal_position = None;
458        let mut internal_position = None;
459        let mut internal_query = None;
460        let mut where_ = None;
461        let mut schema = None;
462        let mut table = None;
463        let mut column = None;
464        let mut datatype = None;
465        let mut constraint = None;
466        let mut file = None;
467        let mut line = None;
468        let mut routine = None;
469
470        while let Some(field) = fields.next()? {
471            let value = String::from_utf8_lossy(field.value_bytes());
472            match field.type_() {
473                b'S' => severity = Some(value.into_owned()),
474                b'C' => code = Some(SqlState::from_code(&value)),
475                b'M' => message = Some(value.into_owned()),
476                b'D' => detail = Some(value.into_owned()),
477                b'H' => hint = Some(value.into_owned()),
478                b'P' => {
479                    normal_position = Some(value.parse::<u32>().map_err(|_| {
480                        io::Error::new(io::ErrorKind::InvalidInput, "`P` field did not contain an integer")
481                    })?);
482                }
483                b'p' => {
484                    internal_position = Some(value.parse::<u32>().map_err(|_| {
485                        io::Error::new(io::ErrorKind::InvalidInput, "`p` field did not contain an integer")
486                    })?);
487                }
488                b'q' => internal_query = Some(value.into_owned()),
489                b'W' => where_ = Some(value.into_owned()),
490                b's' => schema = Some(value.into_owned()),
491                b't' => table = Some(value.into_owned()),
492                b'c' => column = Some(value.into_owned()),
493                b'd' => datatype = Some(value.into_owned()),
494                b'n' => constraint = Some(value.into_owned()),
495                b'F' => file = Some(value.into_owned()),
496                b'L' => {
497                    line = Some(value.parse::<u32>().map_err(|_| {
498                        io::Error::new(io::ErrorKind::InvalidInput, "`L` field did not contain an integer")
499                    })?);
500                }
501                b'R' => routine = Some(value.into_owned()),
502                b'V' => {
503                    parsed_severity = Some(Severity::from_str(&value).ok_or_else(|| {
504                        io::Error::new(io::ErrorKind::InvalidInput, "`V` field contained an invalid value")
505                    })?);
506                }
507                _ => {}
508            }
509        }
510
511        Ok(DbError {
512            severity: severity.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "`S` field missing"))?,
513            parsed_severity,
514            code: code.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "`C` field missing"))?,
515            message: message.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "`M` field missing"))?,
516            detail,
517            hint,
518            position: match normal_position {
519                Some(position) => Some(ErrorPosition::Original(position)),
520                None => match internal_position {
521                    Some(position) => Some(ErrorPosition::Internal {
522                        position,
523                        query: internal_query.ok_or_else(|| {
524                            io::Error::new(io::ErrorKind::InvalidInput, "`q` field missing but `p` field present")
525                        })?,
526                    }),
527                    None => None,
528                },
529            },
530            where_,
531            schema,
532            table,
533            column,
534            datatype,
535            constraint,
536            file,
537            line,
538            routine,
539        })
540    }
541
542    /// The field contents are ERROR, FATAL, or PANIC (in an error message),
543    /// or WARNING, NOTICE, DEBUG, INFO, or LOG (in a notice message), or a
544    /// localized translation of one of these.
545    pub fn severity(&self) -> &str {
546        &self.severity
547    }
548
549    /// A parsed, nonlocalized version of `severity`. (PostgreSQL 9.6+)
550    pub fn parsed_severity(&self) -> Option<Severity> {
551        self.parsed_severity
552    }
553
554    /// The SQLSTATE code for the error.
555    pub fn code(&self) -> &SqlState {
556        &self.code
557    }
558
559    /// The primary human-readable error message.
560    ///
561    /// This should be accurate but terse (typically one line).
562    pub fn message(&self) -> &str {
563        &self.message
564    }
565
566    /// An optional secondary error message carrying more detail about the
567    /// problem.
568    ///
569    /// Might run to multiple lines.
570    pub fn detail(&self) -> Option<&str> {
571        self.detail.as_deref()
572    }
573
574    /// An optional suggestion what to do about the problem.
575    ///
576    /// This is intended to differ from `detail` in that it offers advice
577    /// (potentially inappropriate) rather than hard facts. Might run to
578    /// multiple lines.
579    pub fn hint(&self) -> Option<&str> {
580        self.hint.as_deref()
581    }
582
583    /// An optional error cursor position into either the original query string
584    /// or an internally generated query.
585    pub fn position(&self) -> Option<&ErrorPosition> {
586        self.position.as_ref()
587    }
588
589    /// An indication of the context in which the error occurred.
590    ///
591    /// Presently this includes a call stack traceback of active procedural
592    /// language functions and internally-generated queries. The trace is one
593    /// entry per line, most recent first.
594    pub fn where_(&self) -> Option<&str> {
595        self.where_.as_deref()
596    }
597
598    /// If the error was associated with a specific database object, the name
599    /// of the schema containing that object, if any. (PostgreSQL 9.3+)
600    pub fn schema(&self) -> Option<&str> {
601        self.schema.as_deref()
602    }
603
604    /// If the error was associated with a specific table, the name of the
605    /// table. (Refer to the schema name field for the name of the table's
606    /// schema.) (PostgreSQL 9.3+)
607    pub fn table(&self) -> Option<&str> {
608        self.table.as_deref()
609    }
610
611    /// If the error was associated with a specific table column, the name of
612    /// the column.
613    ///
614    /// (Refer to the schema and table name fields to identify the table.)
615    /// (PostgreSQL 9.3+)
616    pub fn column(&self) -> Option<&str> {
617        self.column.as_deref()
618    }
619
620    /// If the error was associated with a specific data type, the name of the
621    /// data type. (Refer to the schema name field for the name of the data
622    /// type's schema.) (PostgreSQL 9.3+)
623    pub fn datatype(&self) -> Option<&str> {
624        self.datatype.as_deref()
625    }
626
627    /// If the error was associated with a specific constraint, the name of the
628    /// constraint.
629    ///
630    /// Refer to fields listed above for the associated table or domain.
631    /// (For this purpose, indexes are treated as constraints, even if they
632    /// weren't created with constraint syntax.) (PostgreSQL 9.3+)
633    pub fn constraint(&self) -> Option<&str> {
634        self.constraint.as_deref()
635    }
636
637    /// The file name of the source-code location where the error was reported.
638    pub fn file(&self) -> Option<&str> {
639        self.file.as_deref()
640    }
641
642    /// The line number of the source-code location where the error was
643    /// reported.
644    pub fn line(&self) -> Option<u32> {
645        self.line
646    }
647
648    /// The name of the source-code routine reporting the error.
649    pub fn routine(&self) -> Option<&str> {
650        self.routine.as_deref()
651    }
652}
653
654impl fmt::Display for DbError {
655    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
656        write!(fmt, "{}: {}", self.severity, self.message)?;
657        if let Some(detail) = &self.detail {
658            write!(fmt, "\nDETAIL: {}", detail)?;
659        }
660        if let Some(hint) = &self.hint {
661            write!(fmt, "\nHINT: {}", hint)?;
662        }
663        Ok(())
664    }
665}
666
667impl error::Error for DbError {}
668
669from_impl!(DbError);
670
671/// The severity of a Postgres error or notice.
672#[derive(Debug, Copy, Clone, PartialEq, Eq)]
673pub enum Severity {
674    /// PANIC
675    Panic,
676    /// FATAL
677    Fatal,
678    /// ERROR
679    Error,
680    /// WARNING
681    Warning,
682    /// NOTICE
683    Notice,
684    /// DEBUG
685    Debug,
686    /// INFO
687    Info,
688    /// LOG
689    Log,
690}
691
692impl fmt::Display for Severity {
693    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
694        let s = match *self {
695            Severity::Panic => "PANIC",
696            Severity::Fatal => "FATAL",
697            Severity::Error => "ERROR",
698            Severity::Warning => "WARNING",
699            Severity::Notice => "NOTICE",
700            Severity::Debug => "DEBUG",
701            Severity::Info => "INFO",
702            Severity::Log => "LOG",
703        };
704        fmt.write_str(s)
705    }
706}
707
708impl Severity {
709    fn from_str(s: &str) -> Option<Severity> {
710        match s {
711            "PANIC" => Some(Severity::Panic),
712            "FATAL" => Some(Severity::Fatal),
713            "ERROR" => Some(Severity::Error),
714            "WARNING" => Some(Severity::Warning),
715            "NOTICE" => Some(Severity::Notice),
716            "DEBUG" => Some(Severity::Debug),
717            "INFO" => Some(Severity::Info),
718            "LOG" => Some(Severity::Log),
719            _ => None,
720        }
721    }
722}
723
724/// Represents the position of an error in a query.
725#[derive(Clone, PartialEq, Eq, Debug)]
726pub enum ErrorPosition {
727    /// A position in the original query.
728    Original(u32),
729    /// A position in an internally generated query.
730    Internal {
731        /// The byte position.
732        position: u32,
733        /// A query generated by the Postgres server.
734        query: String,
735    },
736}