sqlint/connector/postgres/
error.rs1use crate::error::{DatabaseConstraint, Error, ErrorKind, Name};
2
3impl From<tokio_postgres::error::Error> for Error {
4 fn from(e: tokio_postgres::error::Error) -> Error {
5 use tokio_postgres::error::DbError;
6
7 if e.is_closed() {
8 return Error::builder(ErrorKind::ConnectionClosed).build();
9 }
10
11 match e.code().map(|c| c.code()) {
12 Some(code) if code == "22001" => {
13 let code = code.to_string();
14
15 let mut builder = Error::builder(ErrorKind::LengthMismatch { column: Name::Unavailable });
16
17 builder.set_original_code(code);
18
19 let db_error = e.into_source().and_then(|e| e.downcast::<DbError>().ok());
20 if let Some(db_error) = db_error {
21 builder.set_original_message(db_error.to_string());
22 }
23
24 builder.build()
25 }
26 Some(code) if code == "23505" => {
27 let code = code.to_string();
28
29 let db_error = e.into_source().and_then(|e| e.downcast::<DbError>().ok());
30 let detail = db_error.as_ref().and_then(|e| e.detail()).map(ToString::to_string);
31
32 let constraint = detail
33 .as_ref()
34 .and_then(|d| d.split(")=(").next())
35 .and_then(|d| d.split(" (").nth(1).map(|s| s.replace('\"', "")))
36 .map(|s| DatabaseConstraint::fields(s.split(", ")))
37 .unwrap_or(DatabaseConstraint::CannotParse);
38
39 let kind = ErrorKind::UniqueConstraintViolation { constraint };
40 let mut builder = Error::builder(kind);
41
42 builder.set_original_code(code);
43
44 if let Some(detail) = detail {
45 builder.set_original_message(detail);
46 }
47
48 builder.build()
49 }
50 Some(code) if code == "23502" => {
52 let code = code.to_string();
53
54 let db_error = e.into_source().and_then(|e| e.downcast::<DbError>().ok());
55 let detail = db_error.as_ref().and_then(|e| e.detail()).map(ToString::to_string);
56
57 let constraint = db_error
58 .as_ref()
59 .map(|e| e.column())
60 .map(DatabaseConstraint::fields)
61 .unwrap_or(DatabaseConstraint::CannotParse);
62
63 let kind = ErrorKind::NullConstraintViolation { constraint };
64 let mut builder = Error::builder(kind);
65
66 builder.set_original_code(code);
67
68 if let Some(detail) = detail {
69 builder.set_original_message(detail);
70 }
71
72 builder.build()
73 }
74 Some(code) if code == "23503" => {
75 let code = code.to_string();
76 let db_error = e.into_source().and_then(|e| e.downcast::<DbError>().ok());
77
78 match db_error.as_ref().and_then(|e| e.column()) {
79 Some(column) => {
80 let mut builder = Error::builder(ErrorKind::ForeignKeyConstraintViolation {
81 constraint: DatabaseConstraint::fields(Some(column)),
82 });
83
84 builder.set_original_code(code);
85
86 if let Some(message) = db_error.as_ref().map(|e| e.message()) {
87 builder.set_original_message(message);
88 }
89
90 builder.build()
91 }
92 None => {
93 let constraint = db_error
94 .as_ref()
95 .map(|e| e.message())
96 .and_then(|e| e.split_whitespace().nth(10))
97 .and_then(|s| s.split('"').nth(1))
98 .map(ToString::to_string)
99 .map(DatabaseConstraint::Index)
100 .unwrap_or(DatabaseConstraint::CannotParse);
101
102 let kind = ErrorKind::ForeignKeyConstraintViolation { constraint };
103 let mut builder = Error::builder(kind);
104
105 builder.set_original_code(code);
106
107 if let Some(message) = db_error.as_ref().map(|e| e.message()) {
108 builder.set_original_message(message);
109 }
110
111 builder.build()
112 }
113 }
114 }
115 Some(code) if code == "3D000" => {
116 let code = code.to_string();
117 let db_error = e.into_source().and_then(|e| e.downcast::<DbError>().ok());
118 let message = db_error.as_ref().map(|e| e.message());
119
120 let db_name =
121 message.as_ref().and_then(|s| s.split_whitespace().nth(1)).and_then(|s| s.split('"').nth(1)).into();
122
123 let kind = ErrorKind::DatabaseDoesNotExist { db_name };
124 let mut builder = Error::builder(kind);
125
126 builder.set_original_code(code);
127
128 if let Some(message) = message {
129 builder.set_original_message(message);
130 }
131
132 builder.build()
133 }
134 Some(code) if code == "28000" => {
135 let code = code.to_string();
136 let db_error = e.into_source().and_then(|e| e.downcast::<DbError>().ok());
137 let message = db_error.as_ref().map(|e| e.message());
138
139 let db_name =
140 message.as_ref().and_then(|m| m.split_whitespace().nth(5)).and_then(|s| s.split('"').nth(1)).into();
141
142 let kind = ErrorKind::DatabaseAccessDenied { db_name };
143 let mut builder = Error::builder(kind);
144
145 builder.set_original_code(code);
146
147 if let Some(message) = message {
148 builder.set_original_message(message);
149 }
150
151 builder.build()
152 }
153 Some(code) if code == "28P01" => {
154 let code = code.to_string();
155 let db_error = e.into_source().and_then(|e| e.downcast::<DbError>().ok());
156 let message = db_error.as_ref().map(|e| e.message());
157
158 let user =
159 message.as_ref().and_then(|m| m.split_whitespace().last()).and_then(|s| s.split('"').nth(1)).into();
160
161 let kind = ErrorKind::AuthenticationFailed { user };
162 let mut builder = Error::builder(kind);
163
164 builder.set_original_code(code);
165
166 if let Some(message) = message {
167 builder.set_original_message(message);
168 }
169
170 builder.build()
171 }
172 Some(code) if code == "40001" => {
173 let code = code.to_string();
174 let db_error = e.into_source().and_then(|e| e.downcast::<DbError>().ok());
175 let message = db_error.as_ref().map(|e| e.message());
176 let mut builder = Error::builder(ErrorKind::TransactionWriteConflict);
177
178 builder.set_original_code(code);
179
180 if let Some(message) = message {
181 builder.set_original_message(message);
182 }
183
184 builder.build()
185 }
186 Some(code) if code == "42P01" => {
187 let code = code.to_string();
188 let db_error = e.into_source().and_then(|e| e.downcast::<DbError>().ok());
189 let message = db_error.as_ref().map(|e| e.message());
190
191 let table =
192 message.as_ref().and_then(|m| m.split_whitespace().nth(1)).and_then(|s| s.split('"').nth(1)).into();
193
194 let kind = ErrorKind::TableDoesNotExist { table };
195 let mut builder = Error::builder(kind);
196
197 builder.set_original_code(code);
198
199 if let Some(message) = message {
200 builder.set_original_message(message);
201 }
202
203 builder.build()
204 }
205 Some(code) if code == "42703" => {
206 let code = code.to_string();
207 let db_error = e.into_source().and_then(|e| e.downcast::<DbError>().ok());
208 let message = db_error.as_ref().map(|e| e.message());
209
210 let column = message
211 .as_ref()
212 .and_then(|m| m.split_whitespace().nth(1))
213 .map(|s| s.split('\"'))
214 .and_then(|mut s| match (s.next(), s.next()) {
215 (Some(column), _) if !column.is_empty() => Some(column),
216 (_, Some(column)) if !column.is_empty() => Some(column),
217 (_, _) => None,
218 })
219 .into();
220
221 let kind = ErrorKind::ColumnNotFound { column };
222 let mut builder = Error::builder(kind);
223
224 builder.set_original_code(code);
225
226 if let Some(message) = message {
227 builder.set_original_message(message);
228 }
229
230 builder.build()
231 }
232
233 Some(code) if code == "42P04" => {
234 let code = code.to_string();
235 let db_error = e.into_source().and_then(|e| e.downcast::<DbError>().ok());
236 let message = db_error.as_ref().map(|e| e.message());
237
238 let db_name =
239 message.as_ref().and_then(|m| m.split_whitespace().nth(1)).and_then(|s| s.split('"').nth(1)).into();
240
241 let kind = ErrorKind::DatabaseAlreadyExists { db_name };
242 let mut builder = Error::builder(kind);
243
244 builder.set_original_code(code);
245
246 if let Some(message) = message {
247 builder.set_original_message(message);
248 }
249
250 builder.build()
251 }
252 code => {
253 if let Some(tls_error) = try_extracting_tls_error(&e) {
256 return tls_error;
257 }
258
259 if let Some(io_error) = try_extracting_io_error(&e) {
261 return io_error;
262 }
263
264 #[cfg(feature = "uuid")]
265 if let Some(uuid_error) = try_extracting_uuid_error(&e) {
266 return uuid_error;
267 }
268
269 let reason = format!("{e}");
270
271 match reason.as_str() {
272 "error connecting to server: timed out" => {
273 let mut builder = Error::builder(ErrorKind::ConnectTimeout);
274
275 if let Some(code) = code {
276 builder.set_original_code(code);
277 };
278
279 builder.set_original_message(reason);
280 builder.build()
281 } "error performing TLS handshake: server does not support TLS" => {
284 let mut builder = Error::builder(ErrorKind::TlsError { message: reason.clone() });
285
286 if let Some(code) = code {
287 builder.set_original_code(code);
288 };
289
290 builder.set_original_message(reason);
291 builder.build()
292 } _ => {
294 let code = code.map(|c| c.to_string());
295 let mut builder = Error::builder(ErrorKind::QueryError(e.into()));
296
297 if let Some(code) = code {
298 builder.set_original_code(code);
299 };
300
301 builder.set_original_message(reason);
302 builder.build()
303 }
304 }
305 }
306 }
307 }
308}
309
310#[cfg(feature = "uuid")]
311fn try_extracting_uuid_error(err: &tokio_postgres::error::Error) -> Option<Error> {
312 use std::error::Error as _;
313
314 err.source()
315 .and_then(|err| err.downcast_ref::<uuid::Error>())
316 .map(|err| ErrorKind::UUIDError(format!("{err}")))
317 .map(|kind| Error::builder(kind).build())
318}
319
320fn try_extracting_tls_error(err: &tokio_postgres::error::Error) -> Option<Error> {
321 use std::error::Error;
322
323 err.source().and_then(|err| err.downcast_ref::<native_tls::Error>()).map(|err| err.into())
324}
325
326fn try_extracting_io_error(err: &tokio_postgres::error::Error) -> Option<Error> {
327 use std::error::Error as _;
328
329 err.source()
330 .and_then(|err| err.downcast_ref::<std::io::Error>())
331 .map(|err| ErrorKind::ConnectionError(Box::new(std::io::Error::new(err.kind(), format!("{err}")))))
332 .map(|kind| Error::builder(kind).build())
333}
334
335impl From<native_tls::Error> for Error {
336 fn from(e: native_tls::Error) -> Error {
337 Error::from(&e)
338 }
339}
340
341impl From<&native_tls::Error> for Error {
342 fn from(e: &native_tls::Error) -> Error {
343 let kind = ErrorKind::TlsError { message: format!("{e}") };
344
345 Error::builder(kind).build()
346 }
347}