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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
//! Classifying [`super::upsert_predicate_probe`]'s own query errors
//! (cratestack#741 finding 2 follow-up). Split out of
//! `upsert_predicate_probe.rs` purely to stay under this codebase's
//! ~200-LoC-per-file convention, not a behavioral boundary.
//!
//! # Actionable error for the `42703` case
//!
//! When the predicate references a column absent from `insert_values`
//! (typically a `@default(...)` column — see
//! `upsert_predicate_probe_savepoint`'s doc comment), the derived-table
//! `SELECT` [`super::upsert_predicate_probe::incoming_row_satisfies_predicate_inner`]
//! builds fails with Postgres `42703 column "..." does not exist`.
//! [`classify_probe_error`] special-cases exactly that SQLSTATE, from
//! exactly this one query, into a [`CratestackError::Validation`]
//! naming the predicate and the likely cause/workaround — not a
//! generic [`cratestack_error_from_sqlx`] mapping (which would leave
//! the caller staring at a raw `DatabaseTyped` 500 with no indication
//! of what to change). This is deliberately narrow: only this probe
//! query, only `42703`; every other SQLSTATE, and every other call
//! site in this crate, keeps using `cratestack_error_from_sqlx`
//! unchanged — broadening that function itself would change unrelated
//! error surfaces that have nothing to do with this probe.
//!
//! # Provably narrow, not incidentally safe
//!
//! [`ProbeOutcome`] exists so the DO NOTHING path's fallback-on-failure
//! behavior (`upsert_predicate_probe_savepoint::try_incoming_row_satisfies_predicate`)
//! is narrow **by construction**, not by the accident that no other
//! error currently happens to reach it. [`classify_probe_error`] is the
//! single point that decides which failures mean "this predicate
//! cannot be evaluated against a synthetic one-row derived table"
//! (`ProbeOutcome::UndefinedColumn`) versus everything else
//! (`ProbeOutcome::Other`) — connection loss, statement timeout
//! (`57014`), a permission error, a poisoned transaction, a genuinely
//! malformed predicate under any other SQLSTATE, etc. Only
//! `UndefinedColumn` is ever treated as "unknown, fall back"; `Other`
//! always propagates. There is currently no SQLSTATE "sibling" to
//! `42703` worth adding to the narrow set: the derived table wraps
//! exactly `insert_values`, so the only way a predicate can fail
//! specifically *because of* that synthetic shape is a reference to a
//! column that isn't in it — an undefined function, a type mismatch, a
//! syntax error, etc. are all genuine predicate-authoring bugs, not
//! artifacts of the derived table, and must surface as errors rather
//! than being silently absorbed.
use CratestackError;
use crate::;
/// Postgres SQLSTATE for "undefined column".
const UNDEFINED_COLUMN_SQLSTATE: &str = "42703";
/// The two classes of failure
/// [`super::upsert_predicate_probe::incoming_row_satisfies_predicate_inner`]'s
/// query can produce, kept as distinct variants (rather than a single
/// `CratestackError`) so callers that need to discriminate — currently
/// only `try_incoming_row_satisfies_predicate`'s savepoint fallback —
/// can `match` on this enum instead of inspecting the mapped error's
/// shape and hoping no future change to [`classify_probe_error`] makes
/// that inspection wrong.
pub
/// Maps an error from the incoming-row probe's own query into a
/// [`ProbeOutcome`]. See the module doc comment for the full reasoning
/// on why `42703` is special-cased and why nothing else is.
pub