pub struct SavepointOp<'t> { /* private fields */ }Expand description
An AtomicOperation scoped to a database SAVEPOINT inside a parent DbOp
or another SavepointOp.
Created by SavepointOperation::with_savepoint /
SavepointOperation::begin_savepoint on any operation — including on another
SavepointOp, which is how savepoints nest.
Statements executed through it run inside the savepoint, so they can be undone
with rollback without poisoning — or ending — the parent
transaction. This is what makes a “loop over N items in one transaction, but
isolate each item’s failure” pattern possible: one COMMIT (one WAL flush)
for the whole batch, while a failing item unwinds only its own writes.
Nesting extends this to per-sub-item isolation within an already-isolated
item, without giving up any of the outer batch’s atomicity.
§Hooks are staged, not executed
Commit hooks registered on a SavepointOp — including the ones repositories
register internally, e.g. via post_persist_hook — are staged in a
private buffer rather than added to the parent operation:
releaseissuesRELEASE SAVEPOINTand then folds the staged hooks into the parent’s buffer through the ordinary registration/merge path, exactly as if they had been registered on the parent directly. For a nested savepoint the “parent” is the enclosingSavepointOp’s own staged buffer — folding there is not yet visible to its parent until it, too, is released.rollbackissuesROLLBACK TO SAVEPOINTand drops the staged hooks. A rolled-back item therefore contributes zero hook state to match its zero database state — no phantom event publishes, no inserts referencing rows that no longer exist.
No hook’s pre_commit / post_commit ever runs at savepoint boundaries,
nested or not. They run once, at the root commit, over
the final merged hook set — so post_commit still only fires after a durable
COMMIT, and on_rollback still only fires when the whole transaction is
gone.
Implementations§
Source§impl<'t> SavepointOp<'t>
impl<'t> SavepointOp<'t>
Sourcepub async fn release(self) -> Result<(), Error>
pub async fn release(self) -> Result<(), Error>
Releases the savepoint, keeping this scope’s work.
Issues RELEASE SAVEPOINT, then folds the staged commit hooks into the
enclosing operation’s buffer via the normal registration/merge path — so
a mergeable hook type accumulates across savepoints exactly as it would
have on the parent, and a non-mergeable one lands at its own position in
release order. For a nested savepoint this is its parent SavepointOp’s
staged buffer, not necessarily the root DbOp — a further release (or
rollback) up the chain is what makes the fold visible further out.
If the RELEASE itself fails the staged hooks are dropped and the error
is returned: the enclosing transaction is in an indeterminate state and
the caller must abandon it rather than commit or release further.
Sourcepub async fn rollback(self) -> Result<(), Error>
pub async fn rollback(self) -> Result<(), Error>
Rolls back to the savepoint, discarding this scope’s work.
Issues ROLLBACK TO SAVEPOINT and drops the staged commit hooks. The
enclosing operation stays alive and usable — including after an error
that would otherwise have poisoned it.
Dropping a SavepointOp without calling either release or rollback
has the same database effect (sqlx queues the rollback on the
connection) and likewise discards the staged hooks.
Trait Implementations§
Source§impl AtomicOperation for SavepointOp<'_>
impl AtomicOperation for SavepointOp<'_>
Source§fn add_commit_hook<H: CommitHook>(&mut self, hook: H) -> Result<(), H>
fn add_commit_hook<H: CommitHook>(&mut self, hook: H) -> Result<(), H>
Refuses, like any other operation, when the enclosing chain ultimately
has nowhere to fold hooks — a savepoint over a bare sqlx::Transaction
or any op that returned HookSlot::unsupported, or one nested under a
HookOperation on the force_execute_pre_commit path.
Source§fn commit_hook<H: CommitHook>(&self) -> Option<&H>
fn commit_hook<H: CommitHook>(&self) -> Option<&H>
Reads the staged buffer first, falling back to the parent’s.
While a savepoint is open the two are not yet merged, so a hook type present in both reports only the staged instance here — they become one hook when the savepoint is released.
Source§fn savepoint_parts(&mut self) -> (&mut Connection, HookSlot<'_>)
fn savepoint_parts(&mut self) -> (&mut Connection, HookSlot<'_>)
Nesting: an inner savepoint folds into this savepoint’s staged buffer,
not straight into the root, so an N-deep chain rolls up one level at a
time. tx, staged and parent_hooks are disjoint fields, so the reads
and borrows below coexist — the split that the trait boundary could not
otherwise express.
Hook capability is propagated, not assumed: this savepoint offers its
staged buffer to an inner savepoint only if its own chain can ultimately
receive hooks. Handing the buffer over unconditionally would let an inner
savepoint accept a hook that had nowhere to go, so add_commit_hook would
report success for work whose pre_commit/post_commit could never run.
Source§fn maybe_now(&self) -> Option<DateTime<Utc>>
fn maybe_now(&self) -> Option<DateTime<Utc>>
Source§fn clock(&self) -> &ClockHandle
fn clock(&self) -> &ClockHandle
Source§fn connection(&mut self) -> &mut Connection
fn connection(&mut self) -> &mut Connection
Source§fn supports_hooks(&self) -> bool
fn supports_hooks(&self) -> bool
Source§fn as_executor(&mut self) -> OneTimeExecutor<'_, &mut Connection>
fn as_executor(&mut self) -> OneTimeExecutor<'_, &mut Connection>
sqlx::Executor implementation that statements should be
executed through. Read moreAuto Trait Implementations§
impl<'t> !RefUnwindSafe for SavepointOp<'t>
impl<'t> !Sync for SavepointOp<'t>
impl<'t> !UnwindSafe for SavepointOp<'t>
impl<'t> Freeze for SavepointOp<'t>
impl<'t> Send for SavepointOp<'t>
impl<'t> Unpin for SavepointOp<'t>
impl<'t> UnsafeUnpin for SavepointOp<'t>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> SavepointOperation for Twhere
T: AtomicOperation + ?Sized,
impl<T> SavepointOperation for Twhere
T: AtomicOperation + ?Sized,
Source§fn with_savepoint<T, E, F>(
&mut self,
f: F,
) -> impl Future<Output = Result<Result<T, E>, Error>>
fn with_savepoint<T, E, F>( &mut self, f: F, ) -> impl Future<Output = Result<Result<T, E>, Error>>
f inside a SAVEPOINT, keeping its work on Ok and undoing it on
Err — see DbOp::with_savepoint for the
full contract, including the two layers of Result. Read moreSource§fn begin_savepoint(
&mut self,
) -> impl Future<Output = Result<SavepointOp<'_>, Error>> + Send
fn begin_savepoint( &mut self, ) -> impl Future<Output = Result<SavepointOp<'_>, Error>> + Send
SAVEPOINT scope explicitly — see
DbOp::begin_savepoint. Must be finished
with release or
rollback; dropping it rolls back. Read more