1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
//! Which database errors mean "run the whole transaction again"
//! (docs/design/procedure-isolation.md §5): the `@isolation` dispatch's
//! attempt loop and the hand-rolled [`crate::run_in_isolated_tx`] both
//! classify through [`retriable_sqlstate`].
//!
//! Three decisions, each closing a request-triggerable re-run of a body:
//!
//! - **Only database errors.** An application or validation error echoing
//! `40001` (`field 'memo' length 40001 exceeds maximum 100`) is not a
//! serialization failure. The hand-rolled helper used to text-match every
//! variant; it was aligned because that is a correctness bug on any path.
//! - **A typed SQLSTATE is authoritative.** A database error that carries a
//! SQLSTATE is retriable iff that SQLSTATE is `40001` or `40P01`; its text
//! is never consulted. Postgres echoes request data into the messages of
//! other SQLSTATEs — `RAISE EXCEPTION 'insufficient funds: requested %'`
//! (`P0001`), `invalid input syntax for type bigint: "40001x"` (`22P02`) —
//! and matching that text re-ran the body, answered `409
//! TRANSACTION_ABORTED` and released the `Idempotency-Key`. Text matching
//! remains only for the untyped `Database(String)` variant, which has no
//! SQLSTATE to read (non-`Database` sqlx errors, hand-built errors). The
//! crate's own read, policy and audit paths build their errors with
//! `cratestack_error_from_sqlx`, so a Postgres error from them is typed.
//! - **`TransactionAborted` is final.** It carries the `40001` its retries
//! ran out on, but it is the outcome of a retry loop, not a statement's
//! failure: an outer loop it propagates into must not run again.
use CratestackError;
pub const PG_SERIALIZATION_FAILURE_SQLSTATE: &str = "40001";
pub const PG_DEADLOCK_DETECTED_SQLSTATE: &str = "40P01";
/// `Some("40001")` for a serialization failure, `Some("40P01")` for a
/// detected deadlock, `None` for anything else.
pub
/// The legacy untyped variant: substring-match the driver's text, the way
/// the original code did. There is no SQLSTATE to prefer.