1use std::error::Error as StdError;
2use std::fmt::{self, Debug, Display, Formatter};
3
4use atoi::atoi;
5use smallvec::alloc::borrow::Cow;
6use sqlx_core::bytes::Bytes;
7pub(crate) use sqlx_core::error::*;
8
9use crate::message::{BackendMessage, BackendMessageFormat, Notice, PgSeverity};
10
11pub struct PgDatabaseError(pub(crate) Notice);
13
14impl PgDatabaseError {
18 #[inline]
19 pub fn severity(&self) -> PgSeverity {
20 self.0.severity()
21 }
22
23 #[inline]
26 pub fn code(&self) -> &str {
27 self.0.code()
28 }
29
30 #[inline]
33 pub fn message(&self) -> &str {
34 self.0.message()
35 }
36
37 #[inline]
40 pub fn detail(&self) -> Option<&str> {
41 self.0.get(b'D')
42 }
43
44 #[inline]
48 pub fn hint(&self) -> Option<&str> {
49 self.0.get(b'H')
50 }
51
52 #[inline]
55 pub fn position(&self) -> Option<PgErrorPosition<'_>> {
56 self.0
57 .get_raw(b'P')
58 .and_then(atoi)
59 .map(PgErrorPosition::Original)
60 .or_else(|| {
61 let position = self.0.get_raw(b'p').and_then(atoi)?;
62 let query = self.0.get(b'q')?;
63
64 Some(PgErrorPosition::Internal { position, query })
65 })
66 }
67
68 pub fn r#where(&self) -> Option<&str> {
72 self.0.get(b'W')
73 }
74
75 pub fn schema(&self) -> Option<&str> {
78 self.0.get(b's')
79 }
80
81 pub fn table(&self) -> Option<&str> {
83 self.0.get(b't')
84 }
85
86 pub fn column(&self) -> Option<&str> {
88 self.0.get(b'c')
89 }
90
91 pub fn data_type(&self) -> Option<&str> {
93 self.0.get(b'd')
94 }
95
96 pub fn constraint(&self) -> Option<&str> {
100 self.0.get(b'n')
101 }
102
103 pub fn file(&self) -> Option<&str> {
105 self.0.get(b'F')
106 }
107
108 pub fn line(&self) -> Option<usize> {
110 self.0.get_raw(b'L').and_then(atoi)
111 }
112
113 pub fn routine(&self) -> Option<&str> {
115 self.0.get(b'R')
116 }
117}
118
119#[derive(Debug, Eq, PartialEq)]
120pub enum PgErrorPosition<'a> {
121 Original(usize),
123
124 Internal {
126 position: usize,
128
129 query: &'a str,
132 },
133}
134
135impl Debug for PgDatabaseError {
136 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
137 f.debug_struct("PgDatabaseError")
138 .field("severity", &self.severity())
139 .field("code", &self.code())
140 .field("message", &self.message())
141 .field("detail", &self.detail())
142 .field("hint", &self.hint())
143 .field("position", &self.position())
144 .field("where", &self.r#where())
145 .field("schema", &self.schema())
146 .field("table", &self.table())
147 .field("column", &self.column())
148 .field("data_type", &self.data_type())
149 .field("constraint", &self.constraint())
150 .field("file", &self.file())
151 .field("line", &self.line())
152 .field("routine", &self.routine())
153 .finish()
154 }
155}
156
157impl Display for PgDatabaseError {
158 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
159 f.write_str(self.message())?;
160 if let Some(line) = self.line() {
161 write!(f, " at line {line}")?;
162 }
163 Ok(())
164 }
165}
166
167impl StdError for PgDatabaseError {}
168
169impl DatabaseError for PgDatabaseError {
170 fn message(&self) -> &str {
171 self.message()
172 }
173
174 fn code(&self) -> Option<Cow<'_, str>> {
175 Some(Cow::Borrowed(self.code()))
176 }
177
178 #[doc(hidden)]
179 fn as_error(&self) -> &(dyn StdError + Send + Sync + 'static) {
180 self
181 }
182
183 #[doc(hidden)]
184 fn as_error_mut(&mut self) -> &mut (dyn StdError + Send + Sync + 'static) {
185 self
186 }
187
188 #[doc(hidden)]
189 fn into_error(self: Box<Self>) -> BoxDynError {
190 self
191 }
192
193 fn is_transient_in_connect_phase(&self) -> bool {
194 [
196 "53300",
200 "57P03",
203 ]
204 .contains(&self.code())
205 }
206
207 fn constraint(&self) -> Option<&str> {
208 self.constraint()
209 }
210
211 fn table(&self) -> Option<&str> {
212 self.table()
213 }
214
215 fn kind(&self) -> ErrorKind {
216 match self.code() {
217 error_codes::UNIQUE_VIOLATION => ErrorKind::UniqueViolation,
218 error_codes::FOREIGN_KEY_VIOLATION => ErrorKind::ForeignKeyViolation,
219 error_codes::NOT_NULL_VIOLATION => ErrorKind::NotNullViolation,
220 error_codes::CHECK_VIOLATION => ErrorKind::CheckViolation,
221 error_codes::EXCLUSION_VIOLATION => ErrorKind::ExclusionViolation,
222 _ => ErrorKind::Other,
223 }
224 }
225}
226
227impl BackendMessage for PgDatabaseError {
229 const FORMAT: BackendMessageFormat = BackendMessageFormat::ErrorResponse;
230
231 #[inline(always)]
232 fn decode_body(buf: Bytes) -> std::result::Result<Self, Error> {
233 Ok(Self(Notice::decode_body(buf)?))
234 }
235}
236
237pub(crate) mod error_codes {
239 pub const UNIQUE_VIOLATION: &str = "23505";
241 pub const FOREIGN_KEY_VIOLATION: &str = "23503";
243 pub const NOT_NULL_VIOLATION: &str = "23502";
245 pub const CHECK_VIOLATION: &str = "23514";
247 pub const EXCLUSION_VIOLATION: &str = "23P01";
249}