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
//! [`resolve_pre_probe`] — the DO NOTHING path's pre-probe branch
//! decision (cratestack#487 / cratestack#741). Split out of
//! `upsert_do_nothing_exec.rs` purely to stay under this codebase's
//! ~200-LoC-per-file convention, not a behavioral boundary.
use CratestackError;
use crate::;
use try_incoming_row_satisfies_predicate;
use select_for_update_by_conflict_target;
/// Probe under a row lock, exactly like the DO UPDATE path
/// (`upsert_exec::run_upsert_in_tx`). If a row is already there,
/// holding that lock for the rest of this transaction guarantees it is
/// still there when the caller commits — DO NOTHING semantics are then
/// just "return what the probe found", no second statement required.
/// `None` means the insert branch:
/// `run_upsert_do_nothing_in_tx` issues the real
/// `ON CONFLICT DO NOTHING RETURNING` next.
///
/// The predicate travels with this pre-probe in two distinct ways
/// (cratestack#741), both required — see `upsert_exec::run_upsert_in_tx`'s
/// matching comment for the full reasoning: filtering candidate
/// existing rows by the predicate alone is not sufficient, because an
/// incoming row that doesn't itself satisfy the predicate can never
/// conflict via a partial index no matter what else exists.
///
/// `try_incoming_row_satisfies_predicate` can itself fail to evaluate
/// the predicate (cratestack#741 finding 2) — most commonly because the
/// predicate references a column a `@default(...)` schema attribute
/// excludes from `insert_values` (the database's own column DEFAULT
/// fills it, so this crate never learns its value client-side, and the
/// one-row derived table the check builds has no such column: Postgres
/// raises `42703 column "..." does not exist`). That specific failure
/// (and ONLY that one — see `upsert_predicate_probe_error`'s module doc
/// comment for why nothing else is treated this way) is NOT propagated
/// as a 500 here: unlike the DO UPDATE path (see `run_upsert_in_tx`'s
/// matching comment for why that one is different), DO NOTHING's real
/// `ON CONFLICT ... DO NOTHING RETURNING` statement
/// (`upsert_do_nothing_insert::run_insert_branch`) is unconditionally
/// the authoritative race guard and decides Inserted-vs-Existing
/// correctly on its own — the pre-probe exists purely to avoid an extra
/// round trip in the common case (see `upsert_do_nothing_exec`'s module
/// doc comment). So when the pre-probe can't be evaluated for that one
/// reason, the safe move is simply to skip it entirely and fall
/// straight through to that authoritative statement, rather than guess.
/// Every OTHER probe failure still propagates normally through the `?`
/// below. (`try_incoming_row_satisfies_predicate` runs the check in its
/// own SAVEPOINT precisely so a failed check doesn't poison the rest of
/// `tx` before we get to that authoritative statement.)
pub async