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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
//! Whether the row about to be inserted would itself fall within a
//! partial unique index's predicate (cratestack#741).
//!
//! This is the half of the conflict-probe fix that isn't just "filter
//! candidate existing rows by the predicate too" (that alone is what
//! `upsert_sql::select_for_update_by_conflict_target` does). Postgres's
//! own partial-index semantics: a row is only ever added to a partial
//! index's B-tree if *that row itself* satisfies the index predicate.
//! An incoming row that does NOT satisfy the predicate therefore can
//! **never** conflict via that index, no matter what else already
//! exists in the table — so a probe that only checks "is there a
//! matching existing row within the predicate" is still wrong on its
//! own: it can find a genuinely unrelated existing row (one the
//! partial index *does* cover) and wrongly report a conflict for an
//! incoming row the index doesn't cover at all. Both halves — this
//! check and the existing-row predicate filter — are required together.
//!
//! The incoming row's own values are re-bound into a one-row derived
//! table (`SELECT $1 AS col1, $2 AS col2, ... `) and the predicate is
//! evaluated against it server-side, rather than attempting to
//! interpret the (opaque, `&'static str`) predicate client-side — the
//! predicate can be arbitrary SQL, so only Postgres itself can
//! evaluate it correctly.
//!
//! Split across three files purely to stay under this codebase's
//! ~200-LoC-per-file convention, not a behavioral boundary:
//! this file builds and runs the probe query; [`upsert_predicate_probe_error`]
//! classifies what it can fail with; [`upsert_predicate_probe_savepoint`]
//! wraps it for the DO NOTHING path's fallback.
//!
//! # Three-valued logic (cratestack#741 finding 1)
//!
//! SQL predicates are three-valued, not boolean: `status = 'active'`
//! evaluates to `NULL`, not `false`, whenever `status` is `NULL`.
//! Decoding `SELECT (<predicate>)` as `(bool,)` therefore fails with a
//! `sqlx::Error::ColumnDecode` (`UnexpectedNullError`) the moment the predicate
//! touches a NULL column — which surfaces to the caller as an opaque
//! 500 (`cratestack_error_from_sqlx` has no dedicated arm for a decode
//! error, so it falls through to `CratestackError::Database`). This
//! module decodes `(Option<bool>,)` instead and treats `NULL` the same
//! as `false`: Postgres's own partial-index semantics only ever admit
//! a row to the index whose predicate evaluates to `true` (`UNKNOWN`,
//! same as `false` here, keeps a row out of the index), so a `NULL`
//! predicate result means the incoming row is — like a `false` result
//! — outside the index's domain and therefore cannot conflict via it.
use CratestackError;
use cratepush_bind_value;
use crate::;
use ;
/// `true` when `predicate`, evaluated against `insert_values`' own
/// column bindings, holds — i.e. whether the row about to be inserted
/// would itself be a member of the partial index's domain. Callers
/// only need this before a *pre-probe* (deciding insert-vs-update
/// branching ahead of the real `ON CONFLICT` statement); once a real
/// conflict has already been confirmed by the database (the post-race
/// fallback read, or the update-policy check on a row already known to
/// conflict), this check is redundant — the database's own decision
/// already implies the incoming row satisfied the predicate.
///
/// Returns a [`ProbeOutcome`] on failure rather than a bare
/// `CratestackError` so `upsert_predicate_probe_savepoint`'s fallback
/// can discriminate `42703` from every other failure by construction
/// — see `upsert_predicate_probe_error`'s module doc comment.
pub async
/// [`incoming_row_satisfies_predicate_inner`], collapsed to a plain
/// `CratestackError` — used directly by the DO UPDATE path
/// (`upsert_exec::run_upsert_in_tx`), which has no savepoint fallback
/// and so propagates either [`ProbeOutcome`] class as an error
/// identically (both still carry the friendly `42703` message when
/// applicable — only the "swallow and fall back" behavior is DO
/// NOTHING-specific, not the error message itself; see that function's
/// doc comment for why DO UPDATE can't safely swallow `42703` the way
/// the DO NOTHING path does).
pub async