sqlint/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 { 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            // Even lipstick will not save this...
51            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                // This is necessary, on top of the other conversions, for the cases where a
254                // native_tls error comes wrapped in a tokio_postgres error.
255                if let Some(tls_error) = try_extracting_tls_error(&e) {
256                    return tls_error;
257                }
258
259                // Same for IO errors.
260                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                    } // sigh...
282                    // https://github.com/sfackler/rust-postgres/blob/0c84ed9f8201f4e5b4803199a24afa2c9f3723b2/tokio-postgres/src/connect_tls.rs#L37
283                    "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                    } // double sigh
293                    _ => {
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}