Skip to main content

SavepointOp

Struct SavepointOp 

Source
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:

  • release issues RELEASE SAVEPOINT and 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 enclosing SavepointOp’s own staged buffer — folding there is not yet visible to its parent until it, too, is released.
  • rollback issues ROLLBACK TO SAVEPOINT and 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>

Source

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.

Source

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<'_>

Source§

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>

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<'_>)

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>>

Function for querying when the operation is taking place - if it is cached.
Source§

fn clock(&self) -> &ClockHandle

Returns the clock handle for time operations. Read more
Source§

fn connection(&mut self) -> &mut Connection

Returns the raw underlying connection. The desired way to represent this would actually be as a GAT: Read more
Source§

fn supports_hooks(&self) -> bool

Whether this operation supports commit hooks. Read more
Source§

fn as_executor(&mut self) -> OneTimeExecutor<'_, &mut Connection>

Returns the sqlx::Executor implementation that statements should be executed through. Read more

Auto 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> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> BatchIsolation for T

Source§

fn run_isolated<'a, T, V, E, F>( &'a mut self, items: &'a [T], f: F, ) -> impl Future<Output = Result<Vec<Result<V, E>>, Error>> + 'a
where T: 'a, V: 'a, E: 'a, F: AsyncFnOnce(&mut SavepointOp<'_>, &T) -> Result<V, E> + Clone + Sync + 'a,

Runs f once per item, each inside its own SAVEPOINT, in item order. Read more
Source§

fn run_bisected<'a, T, E, F>( &'a mut self, items: &'a [T], budget: BisectBudget, f: F, ) -> impl Future<Output = Result<BisectOutcomes<E>, Error>> + 'a
where T: 'a, E: Error + 'static, F: AsyncFnOnce(&mut SavepointOp<'_>, &[T]) -> Result<(), E> + Clone + Sync + 'a,

Probes the whole slice at once, bisecting only on failure. Read more
Source§

fn run_bisected_with<'a, T, E, F, P>( &'a mut self, items: &'a [T], budget: BisectBudget, policy: TransientPolicy<P>, f: F, ) -> impl Future<Output = Result<BisectOutcomes<E>, Error>> + 'a
where T: 'a, E: Display + 'a, P: Fn(&E) -> bool + 'a, F: AsyncFnOnce(&mut SavepointOp<'_>, &[T]) -> Result<(), E> + Clone + Sync + 'a,

run_bisected with a caller-supplied notion of which failures are transient. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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 more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> SavepointOperation for T
where T: AtomicOperation + ?Sized,

Source§

fn with_savepoint<T, E, F>( &mut self, f: F, ) -> impl Future<Output = Result<Result<T, E>, Error>>
where F: AsyncFnOnce(&mut SavepointOp<'_>) -> Result<T, E>,

Runs 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 more
Source§

fn begin_savepoint( &mut self, ) -> impl Future<Output = Result<SavepointOp<'_>, Error>> + Send

Begins a SAVEPOINT scope explicitly — see DbOp::begin_savepoint. Must be finished with release or rollback; dropping it rolls back. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more