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