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§
Sourcefn connection(&mut self) -> &mut Connection
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§
Sourcefn maybe_now(&self) -> Option<DateTime<Utc>>
fn maybe_now(&self) -> Option<DateTime<Utc>>
Function for querying when the operation is taking place - if it is cached.
Sourcefn clock(&self) -> &ClockHandle
fn clock(&self) -> &ClockHandle
Returns the clock handle for time operations.
Default implementation returns the global clock handle.
Sourcefn as_executor(&mut self) -> OneTimeExecutor<'_, &mut Connection>
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.
Sourcefn add_commit_hook<H: CommitHook>(&mut self, hook: H) -> Result<(), H>
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.
Sourcefn commit_hook<H: CommitHook>(&self) -> Option<&H>
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.
Sourcefn supports_hooks(&self) -> bool
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.
Sourcefn savepoint_parts(&mut self) -> (&mut Connection, HookSlot<'_>)
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
SavepointOperation — with_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 — the
delegate_atomic_operation! macro
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.
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.