Skip to main content

diesel_async/pg/
error_helper.rs

1use std::error::Error;
2use std::sync::Arc;
3
4use diesel::ConnectionError;
5
6pub(super) struct ErrorHelper(pub(super) tokio_postgres::Error);
7
8impl From<ErrorHelper> for ConnectionError {
9    fn from(postgres_error: ErrorHelper) -> Self {
10        ConnectionError::CouldntSetupConfiguration(postgres_error.into())
11    }
12}
13
14impl From<ErrorHelper> for diesel::result::Error {
15    fn from(ErrorHelper(postgres_error): ErrorHelper) -> Self {
16        from_tokio_postgres_error(Arc::new(postgres_error))
17    }
18}
19
20pub(super) fn from_tokio_postgres_error(
21    postgres_error: Arc<tokio_postgres::Error>,
22) -> diesel::result::Error {
23    use diesel::result::DatabaseErrorKind::*;
24    use tokio_postgres::error::SqlState;
25
26    match postgres_error.code() {
27        Some(code) => {
28            let kind = match *code {
29                SqlState::UNIQUE_VIOLATION => UniqueViolation,
30                SqlState::FOREIGN_KEY_VIOLATION => ForeignKeyViolation,
31                SqlState::RESTRICT_VIOLATION => RestrictViolation,
32                SqlState::EXCLUSION_VIOLATION => ExclusionViolation,
33                SqlState::T_R_SERIALIZATION_FAILURE => SerializationFailure,
34                SqlState::READ_ONLY_SQL_TRANSACTION => ReadOnlyTransaction,
35                SqlState::NOT_NULL_VIOLATION => NotNullViolation,
36                SqlState::CHECK_VIOLATION => CheckViolation,
37                _ => Unknown,
38            };
39
40            diesel::result::Error::DatabaseError(
41                kind,
42                Box::new(PostgresDbErrorWrapper(
43                    postgres_error
44                        .source()
45                        .and_then(|e| e.downcast_ref::<tokio_postgres::error::DbError>().cloned())
46                        .expect("It's a db error, because we've got a SQLState code above"),
47                )) as _,
48            )
49        }
50        None => diesel::result::Error::DatabaseError(
51            UnableToSendCommand,
52            Box::new(postgres_error.to_string()),
53        ),
54    }
55}
56
57struct PostgresDbErrorWrapper(tokio_postgres::error::DbError);
58
59impl diesel::result::DatabaseErrorInformation for PostgresDbErrorWrapper {
60    fn message(&self) -> &str {
61        self.0.message()
62    }
63
64    fn details(&self) -> Option<&str> {
65        self.0.detail()
66    }
67
68    fn hint(&self) -> Option<&str> {
69        self.0.hint()
70    }
71
72    fn table_name(&self) -> Option<&str> {
73        self.0.table()
74    }
75
76    fn column_name(&self) -> Option<&str> {
77        self.0.column()
78    }
79
80    fn constraint_name(&self) -> Option<&str> {
81        self.0.constraint()
82    }
83
84    fn statement_position(&self) -> Option<i32> {
85        use tokio_postgres::error::ErrorPosition;
86        self.0.position().and_then(|e| match *e {
87            ErrorPosition::Original(position) | ErrorPosition::Internal { position, .. } => {
88                position.try_into().ok()
89            }
90        })
91    }
92}