gel_pg_protocol/
errors.rs

1use core::str;
2use paste::paste;
3use std::{collections::HashMap, str::FromStr};
4
5use crate::protocol::{ErrorResponse, NoticeResponse};
6
7#[macro_export]
8macro_rules! pg_error_class {
9    ($(
10        #[doc=$doc:literal]
11        $code:expr => $name:ident
12    ),* $(,)?) => {
13
14        paste!(
15            /// Postgres error classes. See https://www.postgresql.org/docs/current/errcodes-appendix.html.
16            #[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
17            enum PgErrorClass {
18                $(
19                    #[doc=$doc]
20                    [<$name:camel>],
21                )*
22                /// Unknown error class
23                Other([u8; 2])
24            }
25        );
26
27        impl PgErrorClass {
28            paste!(
29                pub const fn from_code(code: [u8; 2]) -> Self {
30                    $(
31                        const [<$name:upper>]: [u8; 2] = [stringify!($code).as_bytes()[0], stringify!($code).as_bytes()[1]];
32                    )*
33
34                    match code {
35                        $(
36                            [<$name:upper>] => Self::[<$name:camel>],
37                        )*
38                        _ => Self::Other(code)
39                    }
40                }
41            );
42
43            pub const fn to_code(self) -> [u8; 2] {
44                match self {
45                    $(
46                        paste!(Self::[<$name:camel>]) => {
47                            let s = stringify!($code).as_bytes();
48                            [s[0], s[1]]
49                        }
50                    )*
51                    Self::Other(code) => code,
52                }
53            }
54
55            pub const fn get_class_string(&self) -> &'static str {
56                match self {
57                    $(
58                        paste!(Self::[<$name:camel>]) => stringify!($name),
59                    )*
60                    Self::Other(..) => "other",
61                }
62            }
63        }
64    };
65}
66
67macro_rules! pg_error {
68    ($(
69        $class:ident {
70            $(
71                $($code_l:literal)? $($code_i:ident)? => $name:ident
72            ),* $(,)?
73        }
74    ),* $(,)?) => {
75        $(
76            paste!(
77                #[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
78                pub enum [<PgError $class:camel>] {
79                    $(
80                        [<$name:camel>],
81                    )*
82                }
83
84                impl [<PgError $class:camel>] {
85                    pub const fn from_code(code: [u8; 3]) -> Option<Self> {
86                        $(
87                            const [<$name:upper>]: [u8; 3] = [stringify!($($code_i)? $($code_l)?).as_bytes()[0], stringify!($($code_i)? $($code_l)?).as_bytes()[1], stringify!($($code_i)? $($code_l)?).as_bytes()[2]];
88                        )*
89
90                        match code {
91                            $(
92                                [<$name:upper>] => Some(Self::[<$name:camel>]),
93                            )*
94                            _ => None
95                        }
96                    }
97
98                    pub const fn to_code(self) -> [u8; 5] {
99                        match self {
100                            $(
101                                Self::[<$name:camel>] => {
102                                    let s = stringify!($($code_i)? $($code_l)?).as_bytes();
103                                    let c = paste!(PgErrorClass::[<$class:camel>].to_code());
104                                    [c[0], c[1], s[0], s[1], s[2]]
105                                }
106                            )*
107                        }
108                    }
109
110                    pub const fn get_code_string(&self) -> &'static str {
111                        match self {
112                            $(
113                                Self::[<$name:camel>] => {
114                                    const CODE: [u8; 5] = [<PgError $class:camel>]::[<$name:camel>].to_code();
115                                    match str::from_utf8(&CODE) {
116                                        Ok(s) => s,
117                                        _ => panic!()
118                                    }
119                                }
120                            )*
121                        }
122                    }
123
124                    pub const fn get_error_string(&self) -> &'static str {
125                        match self {
126                            $(
127                                paste!(Self::[<$name:camel>]) => stringify!($name),
128                            )*
129                        }
130                    }
131                }
132            );
133        )*
134
135        paste!(
136            /// Postgres error codes. See <https://www.postgresql.org/docs/current/errcodes-appendix.html>.
137            #[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
138            pub enum PgError {
139                $(
140                    [<$class:camel>]([<PgError $class:camel>]),
141                )*
142                Other([u8; 5])
143            }
144
145            impl PgError {
146                pub const fn from_code(code: [u8; 5]) -> Self {
147                    match PgErrorClass::from_code([code[0], code[1]]) {
148                        $(
149                            PgErrorClass::[<$class:camel>] => {
150                                if let Some(code) = [<PgError $class:camel>]::from_code([code[2], code[3], code[4]]) {
151                                    Self::[<$class:camel>](code)
152                                } else {
153                                    Self::Other(code)
154                                }
155                            }
156                        )*,
157                        PgErrorClass::Other(..) => Self::Other(code)
158                    }
159                }
160
161                pub const fn to_code(self) -> [u8; 5] {
162                    match self {
163                        $(
164                            Self::[<$class:camel>](error) => {
165                                error.to_code()
166                            }
167                        )*
168                        Self::Other(code) => code,
169                    }
170                }
171
172                pub const fn get_code_string(&self) -> &str {
173                    match self {
174                        $(
175                            Self::[<$class:camel>](error) => error.get_code_string(),
176                        )*
177                        Self::Other(code) => match str::from_utf8(code) {
178                            Ok(s) => s,
179                            _ => ""
180                        }
181                    }
182                }
183
184                pub const fn get_error_string(self) -> &'static str {
185                    match self {
186                        $(
187                            Self::[<$class:camel>](error) => error.get_error_string(),
188                        )*
189                        Self::Other(..) => "other",
190                    }
191                }
192            }
193        );
194    };
195}
196
197impl std::fmt::Display for PgErrorClass {
198    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
199        let code = self.to_code();
200        for &byte in &code {
201            if byte.is_ascii() {
202                write!(f, "{}", byte as char)?;
203            } else {
204                write!(f, "{{{byte:02X}}}")?;
205            }
206        }
207        Ok(())
208    }
209}
210
211impl std::fmt::Debug for PgErrorClass {
212    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
213        let clazz = self.get_class_string();
214        write!(f, "{clazz}({self})")?;
215        Ok(())
216    }
217}
218
219impl std::fmt::Display for PgError {
220    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
221        let code = self.to_code();
222        for &byte in &code {
223            if byte.is_ascii() {
224                write!(f, "{}", byte as char)?;
225            } else {
226                write!(f, "{{{byte:02X}}}")?;
227            }
228        }
229        Ok(())
230    }
231}
232
233impl std::fmt::Debug for PgError {
234    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
235        let clazz = self.get_error_string();
236        write!(f, "{self}: {clazz}")?;
237        Ok(())
238    }
239}
240
241impl From<[u8; 5]> for PgError {
242    fn from(code: [u8; 5]) -> Self {
243        Self::from_code(code)
244    }
245}
246
247impl From<PgError> for [u8; 5] {
248    fn from(error: PgError) -> Self {
249        error.to_code()
250    }
251}
252
253#[derive(Debug)]
254pub struct PgErrorParseError {
255    kind: PgErrorParseErrorKind,
256}
257
258#[derive(Debug)]
259pub enum PgErrorParseErrorKind {
260    InvalidLength,
261    InvalidCharacter,
262}
263
264impl FromStr for PgError {
265    type Err = PgErrorParseError;
266
267    fn from_str(s: &str) -> Result<Self, Self::Err> {
268        if s.len() != 5 {
269            return Err(PgErrorParseError {
270                kind: PgErrorParseErrorKind::InvalidLength,
271            });
272        }
273
274        let code = s.as_bytes();
275        if !code.is_ascii() {
276            return Err(PgErrorParseError {
277                kind: PgErrorParseErrorKind::InvalidCharacter,
278            });
279        }
280
281        Ok(PgError::from_code([
282            code[0], code[1], code[2], code[3], code[4],
283        ]))
284    }
285}
286
287impl std::fmt::Display for PgErrorParseError {
288    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
289        match self.kind {
290            PgErrorParseErrorKind::InvalidLength => write!(f, "Invalid PgError code length"),
291            PgErrorParseErrorKind::InvalidCharacter => {
292                write!(f, "Invalid character in PgError code")
293            }
294        }
295    }
296}
297
298impl std::error::Error for PgErrorParseError {}
299
300impl std::error::Error for PgError {}
301
302/// A fully-qualified Postgres wire error message.
303#[derive(Debug)]
304pub struct PgServerError {
305    pub code: PgError,
306    pub severity: PgErrorSeverity,
307    pub message: String,
308    pub extra: HashMap<PgServerErrorField, String>,
309}
310
311impl PgServerError {
312    pub fn new(
313        code: PgError,
314        arg: impl AsRef<str>,
315        extra: HashMap<PgServerErrorField, String>,
316    ) -> Self {
317        Self {
318            code,
319            severity: PgErrorSeverity::Error,
320            message: arg.as_ref().to_owned(),
321            extra,
322        }
323    }
324
325    /// Iterate all the fields of this error.
326    pub fn fields(&self) -> impl Iterator<Item = (PgServerErrorField, &str)> {
327        PgServerErrorFieldIterator::new(self)
328    }
329}
330
331struct PgServerErrorBasicFieldIterator<'a> {
332    error: &'a PgServerError,
333    index: usize,
334}
335
336impl<'a> PgServerErrorBasicFieldIterator<'a> {
337    fn new(error: &'a PgServerError) -> Self {
338        Self { error, index: 0 }
339    }
340}
341
342impl<'a> Iterator for PgServerErrorBasicFieldIterator<'a> {
343    type Item = (PgServerErrorField, &'a str);
344
345    fn next(&mut self) -> Option<Self::Item> {
346        let result = match self.index {
347            0 => Some((PgServerErrorField::Code, self.error.code.get_code_string())),
348            1 => Some((PgServerErrorField::Message, self.error.message.as_str())),
349            2 => Some((
350                PgServerErrorField::SeverityNonLocalized,
351                self.error.severity.as_ref(),
352            )),
353            _ => None,
354        };
355        self.index += 1;
356        result
357    }
358}
359
360#[allow(clippy::type_complexity)]
361struct PgServerErrorFieldIterator<'a> {
362    iter: std::iter::Chain<
363        PgServerErrorBasicFieldIterator<'a>,
364        std::iter::Map<
365            std::collections::hash_map::Iter<'a, PgServerErrorField, String>,
366            fn((&'a PgServerErrorField, &'a String)) -> (PgServerErrorField, &'a str),
367        >,
368    >,
369}
370
371impl<'a> PgServerErrorFieldIterator<'a> {
372    pub fn new(error: &'a PgServerError) -> Self {
373        let f: fn((&'a PgServerErrorField, &'a String)) -> (PgServerErrorField, &'a str) =
374            |(f, e)| (*f, e.as_str());
375        let a = PgServerErrorBasicFieldIterator::new(error);
376        let b = error.extra.iter().map(f);
377        let iter = Iterator::chain(a, b);
378        Self { iter }
379    }
380}
381
382impl<'a> Iterator for PgServerErrorFieldIterator<'a> {
383    type Item = (PgServerErrorField, &'a str);
384
385    fn next(&mut self) -> Option<Self::Item> {
386        self.iter.next()
387    }
388}
389
390impl std::fmt::Display for PgServerError {
391    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
392        write!(f, "Server error: {}: {}", self.code, self.message)
393    }
394}
395
396impl std::error::Error for PgServerError {
397    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
398        Some(&self.code)
399    }
400}
401
402impl From<ErrorResponse<'_>> for PgServerError {
403    fn from(error: ErrorResponse) -> Self {
404        let mut code = String::new();
405        let mut message = String::new();
406        let mut extra = HashMap::new();
407        let mut severity = PgErrorSeverity::Error;
408
409        for field in error.fields() {
410            let value = field.value().to_string_lossy().into_owned();
411            match PgServerErrorField::try_from(field.etype()) {
412                Ok(PgServerErrorField::Code) => code = value,
413                Ok(PgServerErrorField::Message) => message = value,
414                Ok(PgServerErrorField::SeverityNonLocalized) => {
415                    severity = PgErrorSeverity::from_str(&value).unwrap_or_default()
416                }
417                Ok(field_type) => {
418                    extra.insert(field_type, value);
419                }
420                Err(_) => {}
421            }
422        }
423
424        // It's very unlikely the server will give us a non-five-character code
425        let code = match PgError::from_str(&code) {
426            Ok(code) => code,
427            Err(_) => PgError::Other(*b"?????"),
428        };
429
430        PgServerError {
431            code,
432            severity,
433            message,
434            extra,
435        }
436    }
437}
438
439impl From<NoticeResponse<'_>> for PgServerError {
440    fn from(error: NoticeResponse) -> Self {
441        let mut code = String::new();
442        let mut message = String::new();
443        let mut extra = HashMap::new();
444        let mut severity = PgErrorSeverity::Error;
445
446        for field in error.fields() {
447            let value = field.value().to_string_lossy().into_owned();
448            match PgServerErrorField::try_from(field.ntype()) {
449                Ok(PgServerErrorField::Code) => code = value,
450                Ok(PgServerErrorField::Message) => message = value,
451                Ok(PgServerErrorField::SeverityNonLocalized) => {
452                    severity = PgErrorSeverity::from_str(&value).unwrap_or_default()
453                }
454                Ok(field_type) => {
455                    extra.insert(field_type, value);
456                }
457                Err(_) => {}
458            }
459        }
460
461        // It's very unlikely the server will give us a non-five-character code
462        let code = match PgError::from_str(&code) {
463            Ok(code) => code,
464            Err(_) => PgError::Other(*b"?????"),
465        };
466
467        PgServerError {
468            code,
469            severity,
470            message,
471            extra,
472        }
473    }
474}
475
476/// Enum representing the field types in ErrorResponse and NoticeResponse messages.
477///
478/// See <https://www.postgresql.org/docs/current/protocol-error-fields.html>
479#[repr(u8)]
480#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, derive_more::TryFrom)]
481#[try_from(repr)]
482pub enum PgServerErrorField {
483    /// Severity: ERROR, FATAL, PANIC, WARNING, NOTICE, DEBUG, INFO, or LOG
484    Severity = b'S',
485    /// Severity (non-localized): ERROR, FATAL, PANIC, WARNING, NOTICE, DEBUG, INFO, or LOG
486    SeverityNonLocalized = b'V',
487    /// SQLSTATE code for the error
488    Code = b'C',
489    /// Primary human-readable error message
490    Message = b'M',
491    /// Optional secondary error message with more detail
492    Detail = b'D',
493    /// Optional suggestion on how to resolve the problem
494    Hint = b'H',
495    /// Error cursor position as an index into the original query string
496    Position = b'P',
497    /// Internal position for internally generated commands
498    InternalPosition = b'p',
499    /// Text of a failed internally-generated command
500    InternalQuery = b'q',
501    /// Context in which the error occurred (e.g., call stack traceback)
502    Where = b'W',
503    /// Schema name associated with the error
504    SchemaName = b's',
505    /// Table name associated with the error
506    TableName = b't',
507    /// Column name associated with the error
508    ColumnName = b'c',
509    /// Data type name associated with the error
510    DataTypeName = b'd',
511    /// Constraint name associated with the error
512    ConstraintName = b'n',
513    /// Source-code file name where the error was reported
514    File = b'F',
515    /// Source-code line number where the error was reported
516    Line = b'L',
517    /// Source-code routine name reporting the error
518    Routine = b'R',
519}
520
521/// Enum representing the severity levels of PostgreSQL errors and notices.
522#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
523pub enum PgErrorSeverity {
524    #[default]
525    Error,
526    Fatal,
527    Panic,
528    Warning,
529    Notice,
530    Debug,
531    Info,
532    Log,
533}
534
535impl std::fmt::Display for PgErrorSeverity {
536    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
537        match self {
538            PgErrorSeverity::Error => write!(f, "ERROR"),
539            PgErrorSeverity::Fatal => write!(f, "FATAL"),
540            PgErrorSeverity::Panic => write!(f, "PANIC"),
541            PgErrorSeverity::Warning => write!(f, "WARNING"),
542            PgErrorSeverity::Notice => write!(f, "NOTICE"),
543            PgErrorSeverity::Debug => write!(f, "DEBUG"),
544            PgErrorSeverity::Info => write!(f, "INFO"),
545            PgErrorSeverity::Log => write!(f, "LOG"),
546        }
547    }
548}
549impl std::str::FromStr for PgErrorSeverity {
550    type Err = ();
551
552    fn from_str(s: &str) -> Result<Self, Self::Err> {
553        match s.to_uppercase().as_str() {
554            "ERROR" => Ok(PgErrorSeverity::Error),
555            "FATAL" => Ok(PgErrorSeverity::Fatal),
556            "PANIC" => Ok(PgErrorSeverity::Panic),
557            "WARNING" => Ok(PgErrorSeverity::Warning),
558            "NOTICE" => Ok(PgErrorSeverity::Notice),
559            "DEBUG" => Ok(PgErrorSeverity::Debug),
560            "INFO" => Ok(PgErrorSeverity::Info),
561            "LOG" => Ok(PgErrorSeverity::Log),
562            _ => Err(()),
563        }
564    }
565}
566
567impl std::ops::Deref for PgErrorSeverity {
568    type Target = str;
569
570    fn deref(&self) -> &Self::Target {
571        match self {
572            PgErrorSeverity::Error => "ERROR",
573            PgErrorSeverity::Fatal => "FATAL",
574            PgErrorSeverity::Panic => "PANIC",
575            PgErrorSeverity::Warning => "WARNING",
576            PgErrorSeverity::Notice => "NOTICE",
577            PgErrorSeverity::Debug => "DEBUG",
578            PgErrorSeverity::Info => "INFO",
579            PgErrorSeverity::Log => "LOG",
580        }
581    }
582}
583
584pg_error_class!(
585    /// Successful Completion
586    00 => successful_completion,
587    /// Warning
588    01 => warning,
589    /// No Data (this is also a warning class per the SQL standard)
590    02 => no_data,
591    /// SQL Statement Not Yet Complete
592    03 => sql_statement_not_yet_complete,
593    /// Connection Exception
594    08 => connection_exception,
595    /// Triggered Action Exception
596    09 => triggered_action_exception,
597    /// Feature Not Supported
598    0A => feature_not_supported,
599    /// Invalid Transaction Initiation
600    0B => invalid_transaction_initiation,
601    /// Locator Exception
602    0F => locator_exception,
603    /// Invalid Grantor
604    0L => invalid_grantor,
605    /// Invalid Role Specification
606    0P => invalid_role_specification,
607    /// Diagnostics Exception
608    0Z => diagnostics_exception,
609    /// Case Not Found
610    20 => case_not_found,
611    /// Cardinality Violation
612    21 => cardinality_violation,
613    /// Data Exception
614    22 => data_exception,
615    /// Integrity Constraint Violation
616    23 => integrity_constraint_violation,
617    /// Invalid Cursor State
618    24 => invalid_cursor_state,
619    /// Invalid Transaction State
620    25 => invalid_transaction_state,
621    /// Invalid SQL Statement Name
622    26 => invalid_sql_statement_name,
623    /// Triggered Data Change Violation
624    27 => triggered_data_change_violation,
625    /// Invalid Authorization Specification
626    28 => invalid_authorization_specification,
627    /// Dependent Privilege Descriptors Still Exist
628    2B => dependent_privilege_descriptors_still_exist,
629    /// Invalid Transaction Termination
630    2D => invalid_transaction_termination,
631    /// SQL Routine Exception
632    2F => sql_routine_exception,
633    /// Invalid Cursor Name
634    34 => invalid_cursor_name,
635    /// External Routine Exception
636    38 => external_routine_exception,
637    /// External Routine Invocation Exception
638    39 => external_routine_invocation_exception,
639    /// Savepoint Exception
640    3B => savepoint_exception,
641    /// Invalid Catalog Name
642    3D => invalid_catalog_name,
643    /// Invalid Schema Name
644    3F => invalid_schema_name,
645    /// Transaction Rollback
646    40 => transaction_rollback,
647    /// Syntax Error or Access Rule Violation
648    42 => syntax_error_or_access_rule_violation,
649    /// WITH CHECK OPTION Violation
650    44 => with_check_option_violation,
651    /// Insufficient Resources
652    53 => insufficient_resources,
653    /// Program Limit Exceeded
654    54 => program_limit_exceeded,
655    /// Object Not In Prerequisite State
656    55 => object_not_in_prerequisite_state,
657    /// Operator Intervention
658    57 => operator_intervention,
659    /// System Error (errors external to PostgreSQL itself)
660    58 => system_error,
661    /// Configuration File Error
662    F0 => config_file_error,
663    /// Foreign Data Wrapper Error (SQL/MED)
664    HV => fdw_error,
665    /// PL/pgSQL Error
666    P0 => plpgsql_error,
667    /// Internal Error
668    XX => internal_error
669);
670
671pg_error!(
672    successful_completion {
673        000 => successful_completion,
674    },
675    warning {
676        000 => warning,
677        00C => dynamic_result_sets_returned,
678        008 => implicit_zero_bit_padding,
679        003 => null_value_eliminated_in_set_function,
680        007 => privilege_not_granted,
681        006 => privilege_not_revoked,
682        004 => string_data_right_truncation,
683        P01 => deprecated_feature,
684    },
685    no_data {
686        000 => no_data,
687        001 => no_additional_dynamic_result_sets_returned,
688    },
689    sql_statement_not_yet_complete {
690        000 => sql_statement_not_yet_complete,
691    },
692    connection_exception {
693        000 => connection_exception,
694        003 => connection_does_not_exist,
695        006 => connection_failure,
696        001 => sqlclient_unable_to_establish_sqlconnection,
697        004 => sqlserver_rejected_establishment_of_sqlconnection,
698        007 => transaction_resolution_unknown,
699        P01 => protocol_violation,
700    },
701    triggered_action_exception {
702        000 => triggered_action_exception,
703    },
704    feature_not_supported {
705        000 => feature_not_supported,
706    },
707    invalid_transaction_initiation {
708        000 => invalid_transaction_initiation,
709    },
710    locator_exception {
711        000 => locator_exception,
712        001 => invalid_locator_specification,
713    },
714    invalid_grantor {
715        000 => invalid_grantor,
716        P01 => invalid_grant_operation,
717    },
718    invalid_role_specification {
719        000 => invalid_role_specification,
720    },
721    diagnostics_exception {
722        000 => diagnostics_exception,
723        002 => stacked_diagnostics_accessed_without_active_handler,
724    },
725    case_not_found {
726        000 => case_not_found,
727    },
728    cardinality_violation {
729        000 => cardinality_violation,
730    },
731    data_exception {
732        000 => data_exception,
733        "02E" => array_subscript_error,
734        021 => character_not_in_repertoire,
735        008 => datetime_field_overflow,
736        012 => division_by_zero,
737        005 => error_in_assignment,
738        00B => escape_character_conflict,
739        022 => indicator_overflow,
740        015 => interval_field_overflow,
741        "01E" => invalid_argument_for_logarithm,
742        014 => invalid_argument_for_ntile_function,
743        016 => invalid_argument_for_nth_value_function,
744        01F => invalid_argument_for_power_function,
745        01G => invalid_argument_for_width_bucket_function,
746        018 => invalid_character_value_for_cast,
747        007 => invalid_datetime_format,
748        019 => invalid_escape_character,
749        00D => invalid_escape_octet,
750        025 => invalid_escape_sequence,
751        P06 => nonstandard_use_of_escape_character,
752        010 => invalid_indicator_parameter_value,
753        023 => invalid_parameter_value,
754        013 => invalid_preceding_or_following_size,
755        01B => invalid_regular_expression,
756        01W => invalid_row_count_in_limit_clause,
757        01X => invalid_row_count_in_result_offset_clause,
758        02H => invalid_tablesample_argument,
759        02G => invalid_tablesample_repeat,
760        009 => invalid_time_zone_displacement_value,
761        00C => invalid_use_of_escape_character,
762        00G => most_specific_type_mismatch,
763        004 => null_value_not_allowed,
764        002 => null_value_no_indicator_parameter,
765        003 => numeric_value_out_of_range,
766        00H => sequence_generator_limit_exceeded,
767        026 => string_data_length_mismatch,
768        001 => string_data_right_truncation,
769        011 => substring_error,
770        027 => trim_error,
771        024 => unterminated_c_string,
772        00F => zero_length_character_string,
773        P01 => floating_point_exception,
774        P02 => invalid_text_representation,
775        P03 => invalid_binary_representation,
776        P04 => bad_copy_file_format,
777        P05 => untranslatable_character,
778        00L => not_an_xml_document,
779        00M => invalid_xml_document,
780        00N => invalid_xml_content,
781        00S => invalid_xml_comment,
782        00T => invalid_xml_processing_instruction,
783        030 => duplicate_json_object_key_value,
784        031 => invalid_argument_for_sql_json_datetime_function,
785        032 => invalid_json_text,
786        033 => invalid_sql_json_subscript,
787        034 => more_than_one_sql_json_item,
788        035 => no_sql_json_item,
789        036 => non_numeric_sql_json_item,
790        037 => non_unique_keys_in_a_json_object,
791        038 => singleton_sql_json_item_required,
792        039 => sql_json_array_not_found,
793        03A => sql_json_member_not_found,
794        03B => sql_json_number_not_found,
795        03C => sql_json_object_not_found,
796        03D => too_many_json_array_elements,
797        "03E" => too_many_json_object_members,
798        03F => sql_json_scalar_required,
799        03G => sql_json_item_cannot_be_cast_to_target_type,
800    },
801    integrity_constraint_violation {
802        000 => integrity_constraint_violation,
803        001 => restrict_violation,
804        502 => not_null_violation,
805        503 => foreign_key_violation,
806        505 => unique_violation,
807        514 => check_violation,
808        P01 => exclusion_violation,
809    },
810    invalid_cursor_state {
811        000 => invalid_cursor_state,
812    },
813    invalid_transaction_state {
814        000 => invalid_transaction_state,
815        001 => active_sql_transaction,
816        002 => branch_transaction_already_active,
817        008 => held_cursor_requires_same_isolation_level,
818        003 => inappropriate_access_mode_for_branch_transaction,
819        004 => inappropriate_isolation_level_for_branch_transaction,
820        005 => no_active_sql_transaction_for_branch_transaction,
821        006 => read_only_sql_transaction,
822        007 => schema_and_data_statement_mixing_not_supported,
823        P01 => no_active_sql_transaction,
824        P02 => in_failed_sql_transaction,
825        P03 => idle_in_transaction_session_timeout,
826        P04 => transaction_timeout,
827    },
828    invalid_sql_statement_name {
829        000 => invalid_sql_statement_name,
830    },
831    triggered_data_change_violation {
832        000 => triggered_data_change_violation,
833    },
834    invalid_authorization_specification {
835        000 => invalid_authorization_specification,
836        P01 => invalid_password,
837    },
838    dependent_privilege_descriptors_still_exist {
839        000 => dependent_privilege_descriptors_still_exist,
840        P01 => dependent_objects_still_exist,
841    },
842    invalid_transaction_termination {
843        000 => invalid_transaction_termination,
844    },
845    sql_routine_exception {
846        000 => sql_routine_exception,
847        005 => function_executed_no_return_statement,
848        002 => modifying_sql_data_not_permitted,
849        003 => prohibited_sql_statement_attempted,
850        004 => reading_sql_data_not_permitted,
851    },
852    invalid_cursor_name {
853        000 => invalid_cursor_name,
854    },
855    external_routine_exception {
856        000 => external_routine_exception,
857        001 => containing_sql_not_permitted,
858        002 => modifying_sql_data_not_permitted,
859        003 => prohibited_sql_statement_attempted,
860        004 => reading_sql_data_not_permitted,
861    },
862    external_routine_invocation_exception {
863        000 => external_routine_invocation_exception,
864        001 => invalid_sqlstate_returned,
865        004 => null_value_not_allowed,
866        P01 => trigger_protocol_violated,
867        P02 => srf_protocol_violated,
868        P03 => event_trigger_protocol_violated,
869    },
870    savepoint_exception {
871        000 => savepoint_exception,
872        001 => invalid_savepoint_specification,
873    },
874    invalid_catalog_name {
875        000 => invalid_catalog_name,
876    },
877    invalid_schema_name {
878        000 => invalid_schema_name,
879    },
880    transaction_rollback {
881        000 => transaction_rollback,
882        002 => transaction_integrity_constraint_violation,
883        001 => serialization_failure,
884        003 => statement_completion_unknown,
885        P01 => deadlock_detected,
886    },
887    syntax_error_or_access_rule_violation {
888        000 => syntax_error_or_access_rule_violation,
889        601 => syntax_error,
890        501 => insufficient_privilege,
891        846 => cannot_coerce,
892        803 => grouping_error,
893        P20 => windowing_error,
894        P19 => invalid_recursion,
895        830 => invalid_foreign_key,
896        602 => invalid_name,
897        622 => name_too_long,
898        939 => reserved_name,
899        804 => datatype_mismatch,
900        P18 => indeterminate_datatype,
901        P21 => collation_mismatch,
902        P22 => indeterminate_collation,
903        809 => wrong_object_type,
904        8C9 => generated_always,
905        703 => undefined_column,
906        883 => undefined_function,
907        P01 => undefined_table,
908        P02 => undefined_parameter,
909        704 => undefined_object,
910        701 => duplicate_column,
911        P03 => duplicate_cursor,
912        P04 => duplicate_database,
913        723 => duplicate_function,
914        P05 => duplicate_prepared_statement,
915        P06 => duplicate_schema,
916        P07 => duplicate_table,
917        712 => duplicate_alias,
918        710 => duplicate_object,
919        702 => ambiguous_column,
920        725 => ambiguous_function,
921        P08 => ambiguous_parameter,
922        P09 => ambiguous_alias,
923        P10 => invalid_column_reference,
924        611 => invalid_column_definition,
925        P11 => invalid_cursor_definition,
926        P12 => invalid_database_definition,
927        P13 => invalid_function_definition,
928        P14 => invalid_prepared_statement_definition,
929        P15 => invalid_schema_definition,
930        P16 => invalid_table_definition,
931        P17 => invalid_object_definition,
932    },
933    with_check_option_violation {
934        000 => with_check_option_violation,
935    },
936    insufficient_resources {
937        000 => insufficient_resources,
938        100 => disk_full,
939        200 => out_of_memory,
940        300 => too_many_connections,
941        400 => configuration_limit_exceeded,
942    },
943    program_limit_exceeded {
944        000 => program_limit_exceeded,
945        001 => statement_too_complex,
946        011 => too_many_columns,
947        023 => too_many_arguments,
948    },
949    object_not_in_prerequisite_state {
950        000 => object_not_in_prerequisite_state,
951        006 => object_in_use,
952        P02 => cant_change_runtime_param,
953        P03 => lock_not_available,
954        P04 => unsafe_new_enum_value_usage,
955    },
956    operator_intervention {
957        000 => operator_intervention,
958        014 => query_canceled,
959        P01 => admin_shutdown,
960        P02 => crash_shutdown,
961        P03 => cannot_connect_now,
962        P04 => database_dropped,
963        P05 => idle_session_timeout,
964    },
965    system_error {
966        000 => system_error,
967        030 => io_error,
968        P01 => undefined_file,
969        P02 => duplicate_file,
970    },
971    config_file_error {
972        000 => config_file_error,
973        001 => lock_file_exists,
974    },
975    fdw_error {
976        000 => fdw_error,
977        005 => fdw_column_name_not_found,
978        002 => fdw_dynamic_parameter_value_needed,
979        010 => fdw_function_sequence_error,
980        021 => fdw_inconsistent_descriptor_information,
981        024 => fdw_invalid_attribute_value,
982        007 => fdw_invalid_column_name,
983        008 => fdw_invalid_column_number,
984        004 => fdw_invalid_data_type,
985        006 => fdw_invalid_data_type_descriptors,
986        091 => fdw_invalid_descriptor_field_identifier,
987        00B => fdw_invalid_handle,
988        00C => fdw_invalid_option_index,
989        00D => fdw_invalid_option_name,
990        090 => fdw_invalid_string_length_or_buffer_length,
991        00A => fdw_invalid_string_format,
992        009 => fdw_invalid_use_of_null_pointer,
993        014 => fdw_too_many_handles,
994        001 => fdw_out_of_memory,
995        00P => fdw_no_schemas,
996        00J => fdw_option_name_not_found,
997        00K => fdw_reply_handle,
998        00Q => fdw_schema_not_found,
999        00R => fdw_table_not_found,
1000        00L => fdw_unable_to_create_execution,
1001        00M => fdw_unable_to_create_reply,
1002        00N => fdw_unable_to_establish_connection,
1003    },
1004    plpgsql_error {
1005        000 => plpgsql_error,
1006        001 => raise_exception,
1007        002 => no_data_found,
1008        003 => too_many_rows,
1009        004 => assert_failure,
1010    },
1011    internal_error {
1012        000 => internal_error,
1013        001 => data_corrupted,
1014        002 => index_corrupted,
1015    }
1016);
1017
1018#[cfg(test)]
1019mod tests {
1020    use super::*;
1021
1022    #[test]
1023    pub fn test_codes() {
1024        assert_eq!(PgError::from_code(*b"badco"), PgError::Other(*b"badco"));
1025        assert_eq!(
1026            PgError::from_code(*b"00000"),
1027            PgError::SuccessfulCompletion(PgErrorSuccessfulCompletion::SuccessfulCompletion)
1028        );
1029        assert_eq!(
1030            PgError::from_code(*b"22P04"),
1031            PgError::DataException(PgErrorDataException::BadCopyFileFormat)
1032        );
1033        assert_eq!(
1034            PgError::from_code(*b"2F003"),
1035            PgError::SqlRoutineException(
1036                PgErrorSqlRoutineException::ProhibitedSqlStatementAttempted
1037            )
1038        );
1039        assert_eq!(
1040            PgError::from_code(*b"XX002"),
1041            PgError::InternalError(PgErrorInternalError::IndexCorrupted)
1042        );
1043        assert_eq!(PgError::from_code(*b"XXXXX"), PgError::Other(*b"XXXXX"));
1044
1045        assert_eq!(format!("{}", PgError::from_code(*b"badco")), "badco");
1046        assert_eq!(format!("{}", PgError::from_code(*b"00000")), "00000");
1047        assert_eq!(format!("{}", PgError::from_code(*b"22P04")), "22P04");
1048        assert_eq!(format!("{}", PgError::from_code(*b"2F003")), "2F003");
1049        assert_eq!(format!("{}", PgError::from_code(*b"XX002")), "XX002");
1050        assert_eq!(format!("{}", PgError::from_code(*b"XXXXX")), "XXXXX");
1051
1052        assert_eq!(
1053            format!("{:?}", PgError::from_code(*b"badco")),
1054            "badco: other"
1055        );
1056        assert_eq!(
1057            format!("{:?}", PgError::from_code(*b"00000")),
1058            "00000: successful_completion"
1059        );
1060        assert_eq!(
1061            format!("{:?}", PgError::from_code(*b"22P04")),
1062            "22P04: bad_copy_file_format"
1063        );
1064        assert_eq!(
1065            format!("{:?}", PgError::from_code(*b"2F003")),
1066            "2F003: prohibited_sql_statement_attempted"
1067        );
1068        assert_eq!(
1069            format!("{:?}", PgError::from_code(*b"XX002")),
1070            "XX002: index_corrupted"
1071        );
1072        assert_eq!(
1073            format!("{:?}", PgError::from_code(*b"XXXXX")),
1074            "XXXXX: other"
1075        );
1076    }
1077
1078    #[test]
1079    fn test_pg_server_error() {
1080        let error = PgServerError::new(
1081            PgError::from_code("28000".as_bytes().try_into().unwrap()),
1082            "message!",
1083            Default::default(),
1084        );
1085        let map: HashMap<_, _> = error.fields().collect();
1086        assert_eq!(
1087            map,
1088            HashMap::from([
1089                (PgServerErrorField::SeverityNonLocalized, "ERROR"),
1090                (PgServerErrorField::Message, "message!"),
1091                (PgServerErrorField::Code, "28000"),
1092            ])
1093        );
1094    }
1095}