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
//! [`ApplyError`] — errors raised by every stage of `apply()`.
use thiserror::Error;
/// Errors raised by [`apply`](super::apply) and its sub-stages.
#[derive(Debug, Error)]
pub enum ApplyError {
/// `tokio_postgres` reported an error.
#[error("postgres error: {0}")]
Postgres(#[from] tokio_postgres::Error),
/// Reading the plan directory failed.
#[error("plan i/o: {0}")]
PlanIo(#[from] pgevolve_core::plan::PlanIoError),
/// Catalog read or assemble failed during preflight.
#[error("catalog: {0}")]
Catalog(#[from] pgevolve_core::catalog::CatalogError),
/// The advisory lock is already held by another session.
#[error("pgevolve advisory lock is held by another session")]
LockHeld,
/// The live target's identity hash does not match the plan's.
#[error("target identity mismatch: plan={plan} live={live}")]
TargetIdentityMismatch {
/// Plan-time target identity.
plan: String,
/// Live identity computed at apply time.
live: String,
},
/// The live catalog has drifted from the plan's `target_snapshot` since
/// planning. The list of changes is rendered for the user.
#[error("drift detected since planning: {0} change(s)")]
DriftDetected(usize),
/// One or more destructive intents were not approved.
#[error("unapproved destructive intents: {count}")]
UnapprovedIntents {
/// Number of unapproved intents.
count: usize,
/// Brief listing for diagnostics: `(id, target, reason)` per intent.
details: Vec<(u32, String, String)>,
},
/// A single step failed; the executor rolled back the enclosing group
/// (if transactional) or stopped (if not).
#[error("step {step_no} (group {group_no}) failed: {error}")]
StepFailed {
/// Failing step number.
step_no: u32,
/// Enclosing group id.
group_no: u32,
/// Postgres error message.
error: String,
},
/// The executor aborted cleanly after a step because the caller passed
/// `ApplyOverrides::abort_after_step`. Used by the testkit's chaos
/// harness to validate the re-plan-and-resume recovery path.
#[error("aborted cleanly after step {step_no}")]
AbortedAfterStep {
/// Step at which the abort fired.
step_no: u32,
},
/// One or more `LintAtPlan` findings lack a matching `[[lint_waiver]]` in
/// `intent.toml`. The plan cannot be applied until waivers are added or the
/// source is corrected.
#[error("unwaived LintAtPlan findings at apply time: {count} finding(s)")]
LintWaiverMissing {
/// Number of unwaived findings.
count: usize,
/// Brief listing for diagnostics: `(rule, target)` per finding.
details: Vec<(String, String)>,
},
/// A plan step's SQL referenced an env var that wasn't set.
#[error(
"missing env var ${{{0}}} referenced by step {1}; required for subscription CONNECTION resolution"
)]
MissingEnvVar(String, u32),
/// An internal invariant was violated or an unexpected value was encountered.
///
/// Used for failures that are not Postgres errors (e.g., parsing a value
/// returned from Postgres that should always be valid, but is not).
#[error("internal error: {0}")]
Internal(String),
}