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