sqlx icon indicating copy to clipboard operation
sqlx copied to clipboard

Feature Request: Expose `transaction_depth` with a Read-Only Getter Method

Open mpyw opened this issue 1 year ago • 2 comments

Is your feature request related to a problem? Please describe.

transaction_depth is currently marked as pub(crate) in the connection struct, which makes it inaccessible from outside the crate. This limitation poses a challenge when working with advisory locks, as described in the following library's README:

mpyw/laravel-database-advisory-lock: Advisory Locking Features for Postgres/MySQL/MariaDB on Laravel

Session-level advisory locks must be acquired before entering a transaction and released only after the transaction has been committed. To ensure consistency, it's crucial to verify whether a transaction is already active before acquiring the lock.

Describe the solution you'd like

I would like to request a read-only Getter method for the transaction_depth field to be added, so that it can be accessed from outside the crate.

Describe alternatives you've considered

An alternative could be to use a custom wrapper around the connection, but this would add unnecessary complexity and overhead. Another option might be to manage transaction depth manually in user code, but this is error-prone and defeats the purpose of the existing transaction_depth field.

Additional context

Providing access to transaction_depth would allow for safer and more consistent handling of nested transactions, particularly in scenarios involving advisory locks.

mpyw avatar Aug 10 '24 05:08 mpyw

The trait method signature would be:

/// Returns the current transaction depth.
///
/// Transaction depth indicates the level of nested transactions:
/// - Level 0: No active transaction.
/// - Level 1: A transaction is active.
/// - Level 2 or higher: A transaction is active and one or more SAVEPOINTs have been created within it.
fn get_transaction_depth(&self) -> BoxFuture<'_, Result<usize, Error>>;

PgConnection MySqlConnection are easy to be inplemented:

// MySqlConnection
fn get_transaction_depth(&self) -> usize {
    self.inner.transaction_depth
}
// PgConnection
fn get_transaction_depth(&self) -> usize {
    self.transaction_depth
}

But SqliteConnection requires asynchronously locking:

// SqliteConnection
async fn get_transaction_depth(&mut self) -> Result<usize, Error> {
    Ok(self.lock_handle().await?.guard.transaction_depth)
}

How should we achive this?

mpyw avatar Aug 11 '24 06:08 mpyw

Trait splitting?

pub trait TransactionDepth {
    /// Returns the current transaction depth synchronously.
    ///
    /// Transaction depth indicates the level of nested transactions:
    /// - Level 0: No active transaction.
    /// - Level 1: A transaction is active.
    /// - Level 2 or higher: A transaction is active and one or more SAVEPOINTs have been created within it.
    fn get_transaction_depth(&self) -> usize;
}

pub trait AsyncTransactionDepth {
    /// Returns the current transaction depth asynchronously.
    ///
    /// Transaction depth indicates the level of nested transactions:
    /// - Level 0: No active transaction.
    /// - Level 1: A transaction is active.
    /// - Level 2 or higher: A transaction is active and one or more SAVEPOINTs have been created within it.
    fn get_transaction_depth(&mut self) -> BoxFuture<'_, Result<usize, Error>>;
}
impl TransactionDepth for PgConnection {
    fn get_transaction_depth(&self) -> usize {
        self.transaction_depth
    }
}

impl TransactionDepth for MySqlConnection {
    fn get_transaction_depth(&self) -> usize {
        self.inner.transaction_depth
    }
}

impl AsyncTransactionDepth for SqliteConnection {
    fn get_transaction_depth(&mut self) -> BoxFuture<'_, Result<usize, Error>> {
        Box::pin(async move {
            Ok(self.lock_handle().await?.guard.transaction_depth)
        })
    }
}

mpyw avatar Aug 11 '24 06:08 mpyw

Closed by #3765

abonander avatar Jul 15 '25 01:07 abonander