1mod 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
24pub 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#[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#[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#[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#[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#[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#[derive(Debug)]
302pub enum ConfigError {
303 EmptyHost,
304 EmptyPort,
305 MissingUserName,
306 MissingPassWord,
307 WrongPassWord,
308}
309
310impl fmt::Display for ConfigError {
311 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
312 f.write_str("Config error: ")?;
313 match self {
314 Self::EmptyHost => f.write_str("no available host name found"),
315 Self::EmptyPort => f.write_str("no available host port found"),
316 Self::MissingUserName => f.write_str("username is missing"),
317 Self::MissingPassWord => f.write_str("password is missing"),
318 Self::WrongPassWord => f.write_str("password is wrong"),
319 }
320 }
321}
322
323impl error::Error for ConfigError {}
324
325from_impl!(ConfigError);
326
327#[non_exhaustive]
328#[derive(Debug)]
329pub enum SystemError {
330 Unix,
331}
332
333impl fmt::Display for SystemError {
334 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
335 match *self {
336 Self::Unix => f.write_str("unix")?,
337 }
338 f.write_str(" system is not available")
339 }
340}
341
342impl error::Error for SystemError {}
343
344from_impl!(SystemError);
345
346#[non_exhaustive]
347#[derive(Debug)]
348pub enum FeatureError {
349 Tls,
350 Quic,
351}
352
353impl fmt::Display for FeatureError {
354 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
355 match *self {
356 Self::Tls => f.write_str("tls")?,
357 Self::Quic => f.write_str("quic")?,
358 }
359 f.write_str(" feature is not enabled")
360 }
361}
362
363impl error::Error for FeatureError {}
364
365from_impl!(FeatureError);
366
367#[derive(Debug, PartialEq, Eq)]
368pub enum RuntimeError {
369 RequireNoTokio,
370}
371
372impl fmt::Display for RuntimeError {
373 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
374 match *self {
375 Self::RequireNoTokio => f.write_str("Tokio runtime detected. Must be called from outside of tokio"),
376 }
377 }
378}
379
380impl error::Error for RuntimeError {}
381
382from_impl!(RuntimeError);
383
384#[derive(Debug)]
387pub struct UnexpectedMessage {
388 back_trace: Backtrace,
389}
390
391impl fmt::Display for UnexpectedMessage {
392 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
393 f.write_str("Unexpected message from database with stack trace:\r\n")?;
394 write!(f, "{}", self.back_trace)
395 }
396}
397
398impl error::Error for UnexpectedMessage {}
399
400#[cold]
401#[inline(never)]
402pub(crate) fn unexpected_eof_err() -> io::Error {
403 io::Error::new(
404 io::ErrorKind::UnexpectedEof,
405 "zero byte read. remote close connection unexpectedly",
406 )
407}
408
409from_impl!(WrongType);
410
411#[derive(Debug, Clone, PartialEq, Eq)]
413pub struct DbError {
414 severity: String,
415 parsed_severity: Option<Severity>,
416 code: SqlState,
417 message: String,
418 detail: Option<String>,
419 hint: Option<String>,
420 position: Option<ErrorPosition>,
421 where_: Option<String>,
422 schema: Option<String>,
423 table: Option<String>,
424 column: Option<String>,
425 datatype: Option<String>,
426 constraint: Option<String>,
427 file: Option<String>,
428 line: Option<u32>,
429 routine: Option<String>,
430}
431
432impl DbError {
433 fn parse(fields: &mut ErrorFields<'_>) -> io::Result<DbError> {
434 let mut severity = None;
435 let mut parsed_severity = None;
436 let mut code = None;
437 let mut message = None;
438 let mut detail = None;
439 let mut hint = None;
440 let mut normal_position = None;
441 let mut internal_position = None;
442 let mut internal_query = None;
443 let mut where_ = None;
444 let mut schema = None;
445 let mut table = None;
446 let mut column = None;
447 let mut datatype = None;
448 let mut constraint = None;
449 let mut file = None;
450 let mut line = None;
451 let mut routine = None;
452
453 while let Some(field) = fields.next()? {
454 let value = String::from_utf8_lossy(field.value_bytes());
455 match field.type_() {
456 b'S' => severity = Some(value.into_owned()),
457 b'C' => code = Some(SqlState::from_code(&value)),
458 b'M' => message = Some(value.into_owned()),
459 b'D' => detail = Some(value.into_owned()),
460 b'H' => hint = Some(value.into_owned()),
461 b'P' => {
462 normal_position = Some(value.parse::<u32>().map_err(|_| {
463 io::Error::new(io::ErrorKind::InvalidInput, "`P` field did not contain an integer")
464 })?);
465 }
466 b'p' => {
467 internal_position = Some(value.parse::<u32>().map_err(|_| {
468 io::Error::new(io::ErrorKind::InvalidInput, "`p` field did not contain an integer")
469 })?);
470 }
471 b'q' => internal_query = Some(value.into_owned()),
472 b'W' => where_ = Some(value.into_owned()),
473 b's' => schema = Some(value.into_owned()),
474 b't' => table = Some(value.into_owned()),
475 b'c' => column = Some(value.into_owned()),
476 b'd' => datatype = Some(value.into_owned()),
477 b'n' => constraint = Some(value.into_owned()),
478 b'F' => file = Some(value.into_owned()),
479 b'L' => {
480 line = Some(value.parse::<u32>().map_err(|_| {
481 io::Error::new(io::ErrorKind::InvalidInput, "`L` field did not contain an integer")
482 })?);
483 }
484 b'R' => routine = Some(value.into_owned()),
485 b'V' => {
486 parsed_severity = Some(Severity::from_str(&value).ok_or_else(|| {
487 io::Error::new(io::ErrorKind::InvalidInput, "`V` field contained an invalid value")
488 })?);
489 }
490 _ => {}
491 }
492 }
493
494 Ok(DbError {
495 severity: severity.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "`S` field missing"))?,
496 parsed_severity,
497 code: code.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "`C` field missing"))?,
498 message: message.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "`M` field missing"))?,
499 detail,
500 hint,
501 position: match normal_position {
502 Some(position) => Some(ErrorPosition::Original(position)),
503 None => match internal_position {
504 Some(position) => Some(ErrorPosition::Internal {
505 position,
506 query: internal_query.ok_or_else(|| {
507 io::Error::new(io::ErrorKind::InvalidInput, "`q` field missing but `p` field present")
508 })?,
509 }),
510 None => None,
511 },
512 },
513 where_,
514 schema,
515 table,
516 column,
517 datatype,
518 constraint,
519 file,
520 line,
521 routine,
522 })
523 }
524
525 pub fn severity(&self) -> &str {
529 &self.severity
530 }
531
532 pub fn parsed_severity(&self) -> Option<Severity> {
534 self.parsed_severity
535 }
536
537 pub fn code(&self) -> &SqlState {
539 &self.code
540 }
541
542 pub fn message(&self) -> &str {
546 &self.message
547 }
548
549 pub fn detail(&self) -> Option<&str> {
554 self.detail.as_deref()
555 }
556
557 pub fn hint(&self) -> Option<&str> {
563 self.hint.as_deref()
564 }
565
566 pub fn position(&self) -> Option<&ErrorPosition> {
569 self.position.as_ref()
570 }
571
572 pub fn where_(&self) -> Option<&str> {
578 self.where_.as_deref()
579 }
580
581 pub fn schema(&self) -> Option<&str> {
584 self.schema.as_deref()
585 }
586
587 pub fn table(&self) -> Option<&str> {
591 self.table.as_deref()
592 }
593
594 pub fn column(&self) -> Option<&str> {
600 self.column.as_deref()
601 }
602
603 pub fn datatype(&self) -> Option<&str> {
607 self.datatype.as_deref()
608 }
609
610 pub fn constraint(&self) -> Option<&str> {
617 self.constraint.as_deref()
618 }
619
620 pub fn file(&self) -> Option<&str> {
622 self.file.as_deref()
623 }
624
625 pub fn line(&self) -> Option<u32> {
628 self.line
629 }
630
631 pub fn routine(&self) -> Option<&str> {
633 self.routine.as_deref()
634 }
635}
636
637impl fmt::Display for DbError {
638 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
639 write!(fmt, "{}: {}", self.severity, self.message)?;
640 if let Some(detail) = &self.detail {
641 write!(fmt, "\nDETAIL: {detail}")?;
642 }
643 if let Some(hint) = &self.hint {
644 write!(fmt, "\nHINT: {hint}")?;
645 }
646 Ok(())
647 }
648}
649
650impl error::Error for DbError {}
651
652from_impl!(DbError);
653
654#[derive(Debug, Copy, Clone, PartialEq, Eq)]
656pub enum Severity {
657 Panic,
659 Fatal,
661 Error,
663 Warning,
665 Notice,
667 Debug,
669 Info,
671 Log,
673}
674
675impl fmt::Display for Severity {
676 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
677 let s = match *self {
678 Severity::Panic => "PANIC",
679 Severity::Fatal => "FATAL",
680 Severity::Error => "ERROR",
681 Severity::Warning => "WARNING",
682 Severity::Notice => "NOTICE",
683 Severity::Debug => "DEBUG",
684 Severity::Info => "INFO",
685 Severity::Log => "LOG",
686 };
687 fmt.write_str(s)
688 }
689}
690
691impl Severity {
692 fn from_str(s: &str) -> Option<Severity> {
693 match s {
694 "PANIC" => Some(Severity::Panic),
695 "FATAL" => Some(Severity::Fatal),
696 "ERROR" => Some(Severity::Error),
697 "WARNING" => Some(Severity::Warning),
698 "NOTICE" => Some(Severity::Notice),
699 "DEBUG" => Some(Severity::Debug),
700 "INFO" => Some(Severity::Info),
701 "LOG" => Some(Severity::Log),
702 _ => None,
703 }
704 }
705}
706
707#[derive(Clone, PartialEq, Eq, Debug)]
709pub enum ErrorPosition {
710 Original(u32),
712 Internal {
714 position: u32,
716 query: String,
718 },
719}