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 #[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
17 enum PgErrorClass {
18 $(
19 #[doc=$doc]
20 [<$name:camel>],
21 )*
22 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 #[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#[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 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 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 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#[repr(u8)]
480#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, derive_more::TryFrom)]
481#[try_from(repr)]
482pub enum PgServerErrorField {
483 Severity = b'S',
485 SeverityNonLocalized = b'V',
487 Code = b'C',
489 Message = b'M',
491 Detail = b'D',
493 Hint = b'H',
495 Position = b'P',
497 InternalPosition = b'p',
499 InternalQuery = b'q',
501 Where = b'W',
503 SchemaName = b's',
505 TableName = b't',
507 ColumnName = b'c',
509 DataTypeName = b'd',
511 ConstraintName = b'n',
513 File = b'F',
515 Line = b'L',
517 Routine = b'R',
519}
520
521#[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 00 => successful_completion,
587 01 => warning,
589 02 => no_data,
591 03 => sql_statement_not_yet_complete,
593 08 => connection_exception,
595 09 => triggered_action_exception,
597 0A => feature_not_supported,
599 0B => invalid_transaction_initiation,
601 0F => locator_exception,
603 0L => invalid_grantor,
605 0P => invalid_role_specification,
607 0Z => diagnostics_exception,
609 20 => case_not_found,
611 21 => cardinality_violation,
613 22 => data_exception,
615 23 => integrity_constraint_violation,
617 24 => invalid_cursor_state,
619 25 => invalid_transaction_state,
621 26 => invalid_sql_statement_name,
623 27 => triggered_data_change_violation,
625 28 => invalid_authorization_specification,
627 2B => dependent_privilege_descriptors_still_exist,
629 2D => invalid_transaction_termination,
631 2F => sql_routine_exception,
633 34 => invalid_cursor_name,
635 38 => external_routine_exception,
637 39 => external_routine_invocation_exception,
639 3B => savepoint_exception,
641 3D => invalid_catalog_name,
643 3F => invalid_schema_name,
645 40 => transaction_rollback,
647 42 => syntax_error_or_access_rule_violation,
649 44 => with_check_option_violation,
651 53 => insufficient_resources,
653 54 => program_limit_exceeded,
655 55 => object_not_in_prerequisite_state,
657 57 => operator_intervention,
659 58 => system_error,
661 F0 => config_file_error,
663 HV => fdw_error,
665 P0 => plpgsql_error,
667 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}