Skip to main content

AtomicOperation

Trait AtomicOperation 

Source
pub trait AtomicOperation: Send {
    // Required method
    fn connection(&mut self) -> &mut Connection;

    // Provided methods
    fn maybe_now(&self) -> Option<DateTime<Utc>> { ... }
    fn clock(&self) -> &ClockHandle { ... }
    fn as_executor(&mut self) -> OneTimeExecutor<'_, &mut Connection> { ... }
    fn add_commit_hook<H: CommitHook>(&mut self, hook: H) -> Result<(), H> { ... }
    fn commit_hook<H: CommitHook>(&self) -> Option<&H> { ... }
    fn supports_hooks(&self) -> bool { ... }
    fn savepoint_parts(&mut self) -> (&mut Connection, HookSlot<'_>) { ... }
}
Expand description

Trait to signify we can make multiple consistent database roundtrips.

Its a stand in for &mut sqlx::Transaction<'_, DB>. The reason for having a trait is to support custom types that wrap the inner transaction while providing additional functionality.

See DbOp or DbOpWithTime.

Required Methods§

Source

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

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

trait AtomicOperation {
    type Executor<'c>: sqlx::PgExecutor<'c>
        where Self: 'c;

    fn connection<'c>(&'c mut self) -> Self::Executor<'c>;
}

But GATs don’t play well with async_trait::async_trait due to lifetime constraints so we return the concrete &mut db::Connection instead as a work around.

Since this trait is generally applied to types that wrap a sqlx::Transaction there is no variance in the return type - so its fine.

Statements executed directly on the returned connection are not annotated with trace context — use as_executor unless raw connection access is required.

Provided Methods§

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.

Default implementation returns the global clock handle.

Source

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

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

The returned OneTimeExecutor annotates every statement with the current span’s traceparent SQL comment when the tracing-context feature is enabled and a sampled span is active (see crate::sql_commenter). Otherwise statements pass through untouched.

Trade-off: the trace context makes annotated statement text unique, so annotated statements bypass sqlx’s per-connection prepared statement cache (persistent(false)) — costing a server-side parse + plan per execution. Un-annotated traffic keeps full prepared-statement reuse.

Source

fn add_commit_hook<H: CommitHook>(&mut self, hook: H) -> Result<(), H>

Registers a commit hook that will run pre_commit before and post_commit after the transaction commits. Returns Ok(()) if the hook was registered, Err(hook) if hooks are not supported.

Source

fn commit_hook<H: CommitHook>(&self) -> Option<&H>

Typed shared access to the currently-accumulating commit hook of type H, if this operation supports commit hooks and one is registered. Returns the hook a subsequent add_commit_hook::<H> call would merge into.

Source

fn supports_hooks(&self) -> bool

Whether this operation supports commit hooks.

true iff add_commit_hook can register a hook (i.e. the operation is backed by a DbOp-style commit-hook buffer, not a bare sqlx::Transaction). Unlike commit_hook — whose None is ambiguous between “hooks unsupported” and “supported but none registered yet” — this reports support directly, with no registration attempt and no &mut access.

Source

fn savepoint_parts(&mut self) -> (&mut Connection, HookSlot<'_>)

Simultaneous access to the connection and the commit-hook buffer a nested SAVEPOINT folds into when released. Implementing this is the only thing an operation must do to get the whole of SavepointOperationwith_savepoint, begin_savepoint, and arbitrary-depth nesting — for free.

Returning both halves together is not a convenience: it is a requirement. A SavepointOp holds a &mut to the connection and a &mut to the hook buffer for its entire lifetime, and two separate &mut self accessors can never be live at the same time. Returning the pair lets an implementor split the borrow across its own disjoint fields — legal inside the type, impossible across a trait boundary otherwise:

fn savepoint_parts(&mut self) -> (&mut db::Connection, HookSlot<'_>) {
    // `tx` and `commit_hooks` are different fields, so this is fine.
    (self.tx.connection(), HookSlot::root(&mut self.commit_hooks))
}

An operation that wraps another should forward to the inner one, so hook support is preserved:

fn savepoint_parts(&mut self) -> (&mut db::Connection, HookSlot<'_>) {
    self.inner.savepoint_parts()
}

An operation with no hook buffer of its own returns HookSlot::unsupported — savepoints still work at the database level, hook registration inside them refuses, and callers fall back to force_execute_pre_commit exactly as they already do on the operation itself.

The default reports no hook buffer, which is correct for an operation that has none — a bare sqlx::Transaction needs nothing else.

It is not correct for an operation that wraps one which does. Such a type must override this (or implement WrapsOperation, which does it for you), because the default would otherwise refuse hooks inside every savepoint taken through it while the wrapped operation supports them fine. That mismatch is caught rather than left silent: begin_savepoint fails with a protocol error when an operation reports supports_hooks but yields an unsupported slot, which is exactly the shape “delegated supports_hooks, inherited savepoint_parts” produces.

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementations on Foreign Types§

Source§

impl<'c> AtomicOperation for Transaction<'c, Db>

A bare transaction carries no commit-hook buffer, so the defaulted savepoint_parts is already right: savepoints work at the database level and refuse hook registration.

Source§

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

Implementors§