Skip to content

Commit c926468

Browse files
committed
Add DatabaseErrorKind::ReadOnlyTransaction
This is an error that applications are likely to want to handle. A database may be read only during maintenance, or for brief periods after failing over to a hot replica. Applications designed to handle this may wish to transform this error into a user visible HTTP 503 response, or skip certain optional writes. By providing this error variant, we allow them to gracefully do so, without having to resort to parsing error messages as was needed in rust-lang/crates.io#1670
1 parent 50798e5 commit c926468

File tree

5 files changed

+31
-6
lines changed

5 files changed

+31
-6
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ for Rust libraries in [RFC #1105](https://github.com/rust-lang/rfcs/blob/master/
1010

1111
* `NonAggregate` can now be derived for simple cases.
1212

13+
* Added `DatabaseErrorKind::ReadOnlyTransaction` to allow applications to
14+
handle errors caused by writing when only allowed to read.
15+
1316
### Removed
1417

1518
* All previously deprecated items have been removed.

diesel/src/mysql/connection/stmt/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ impl Statement {
147147
match last_error_number {
148148
1062 | 1586 | 1859 => DatabaseErrorKind::UniqueViolation,
149149
1216 | 1217 | 1451 | 1452 | 1830 | 1834 => DatabaseErrorKind::ForeignKeyViolation,
150+
1792 => DatabaseErrorKind::ReadOnlyTransaction,
150151
_ => DatabaseErrorKind::__Unknown,
151152
}
152153
}

diesel/src/pg/connection/result.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,9 @@ impl PgResult {
3434
let error_kind =
3535
match get_result_field(internal_result.as_ptr(), ResultField::SqlState) {
3636
Some(error_codes::UNIQUE_VIOLATION) => DatabaseErrorKind::UniqueViolation,
37-
Some(error_codes::FOREIGN_KEY_VIOLATION) => {
38-
DatabaseErrorKind::ForeignKeyViolation
39-
}
40-
Some(error_codes::SERIALIZATION_FAILURE) => {
41-
DatabaseErrorKind::SerializationFailure
42-
}
37+
Some(error_codes::FOREIGN_KEY_VIOLATION) => DatabaseErrorKind::ForeignKeyViolation,
38+
Some(error_codes::SERIALIZATION_FAILURE) => DatabaseErrorKind::SerializationFailure,
39+
Some(error_codes::READ_ONLY_TRANSACTION) => DatabaseErrorKind::ReadOnlyTransaction,
4340
_ => DatabaseErrorKind::__Unknown,
4441
};
4542
let error_information = Box::new(PgErrorInformation(internal_result));
@@ -167,4 +164,5 @@ mod error_codes {
167164
pub const UNIQUE_VIOLATION: &str = "23505";
168165
pub const FOREIGN_KEY_VIOLATION: &str = "23503";
169166
pub const SERIALIZATION_FAILURE: &str = "40001";
167+
pub const READ_ONLY_TRANSACTION: &str = "25006";
170168
}

diesel/src/result.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,13 @@ pub enum DatabaseErrorKind {
106106
/// transaction isolation levels for other backends.
107107
SerializationFailure,
108108

109+
/// The command could not be completed because the transaction was read
110+
/// only.
111+
///
112+
/// This error will also be returned for `SELECT` statements which attempted
113+
/// to lock the rows.
114+
ReadOnlyTransaction,
115+
109116
#[doc(hidden)]
110117
__Unknown, // Match against _ instead, more variants may be added in the future
111118
}

diesel_tests/tests/errors.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,3 +176,19 @@ fn isolation_errors_are_detected() {
176176
assert_matches!(results[0], Ok(_));
177177
assert_matches!(results[1], Err(DatabaseError(SerializationFailure, _)));
178178
}
179+
180+
#[test]
181+
#[cfg(not(feature = "sqlite"))]
182+
fn read_only_errors_are_detected() {
183+
use diesel::result::DatabaseErrorKind::ReadOnlyTransaction;
184+
185+
let conn = connection_without_transaction();
186+
conn.execute("START TRANSACTION READ ONLY")
187+
.unwrap();
188+
189+
let result = users::table
190+
.for_update()
191+
.load::<User>(&conn);
192+
193+
assert_matches!(result, Err(DatabaseError(ReadOnlyTransaction, _)));
194+
}

0 commit comments

Comments
 (0)