quaint_forked/connector/postgres/
error.rs

1use 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            // Even lipstick will not save this...
53            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                // This is necessary, on top of the other conversions, for the cases where a
271                // native_tls error comes wrapped in a tokio_postgres error.
272                if let Some(tls_error) = try_extracting_tls_error(&e) {
273                    return tls_error;
274                }
275
276                // Same for IO errors.
277                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                    } // sigh...
299                    // https://github.com/sfackler/rust-postgres/blob/0c84ed9f8201f4e5b4803199a24afa2c9f3723b2/tokio-postgres/src/connect_tls.rs#L37
300                    "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                    } // double sigh
312                    _ => {
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}