From 4563689ec542ec1e8c976aa74f57224c79a4508f Mon Sep 17 00:00:00 2001 From: Angel Nereira Date: Sun, 24 May 2026 21:56:26 -0500 Subject: [PATCH 1/2] feat(core): expose acquired pool connections --- sqlx-core/src/pool/inner.rs | 5 +++++ sqlx-core/src/pool/mod.rs | 9 +++++++++ tests/any/pool.rs | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/sqlx-core/src/pool/inner.rs b/sqlx-core/src/pool/inner.rs index b698dc9df0..156f6422b6 100644 --- a/sqlx-core/src/pool/inner.rs +++ b/sqlx-core/src/pool/inner.rs @@ -84,6 +84,11 @@ impl PoolInner { self.num_idle.load(Ordering::Acquire) } + pub(super) fn num_acquired(&self) -> u32 { + self.size() + .saturating_sub(u32::try_from(self.num_idle()).unwrap_or(u32::MAX)) + } + pub(super) fn is_closed(&self) -> bool { self.is_closed.load(Ordering::Acquire) } diff --git a/sqlx-core/src/pool/mod.rs b/sqlx-core/src/pool/mod.rs index f11ff1d76a..4b72e9d385 100644 --- a/sqlx-core/src/pool/mod.rs +++ b/sqlx-core/src/pool/mod.rs @@ -541,6 +541,14 @@ impl Pool { self.0.num_idle() } + /// Returns the number of connections currently checked out from the pool. + /// + /// This is an instantaneous snapshot. The value may change immediately as + /// tasks acquire or release connections. + pub fn num_acquired(&self) -> u32 { + self.0.num_acquired() + } + /// Gets a clone of the connection options for this pool pub fn connect_options(&self) -> Arc<::Options> { self.0 @@ -581,6 +589,7 @@ impl fmt::Debug for Pool { fmt.debug_struct("Pool") .field("size", &self.0.size()) .field("num_idle", &self.0.num_idle()) + .field("num_acquired", &self.0.num_acquired()) .field("is_closed", &self.0.is_closed()) .field("options", &self.0.options) .finish() diff --git a/tests/any/pool.rs b/tests/any/pool.rs index a4849940b8..85673e6f0c 100644 --- a/tests/any/pool.rs +++ b/tests/any/pool.rs @@ -71,6 +71,43 @@ async fn pool_should_be_returned_failed_transactions() -> anyhow::Result<()> { Ok(()) } +#[sqlx_macros::test] +async fn pool_should_report_acquired_connections() -> anyhow::Result<()> { + sqlx::any::install_default_drivers(); + + let pool = AnyPoolOptions::new() + .max_connections(2) + .connect(&dotenvy::var("DATABASE_URL")?) + .await?; + + assert_eq!(pool.size(), 1); + assert_eq!(pool.num_idle(), 1); + assert_eq!(pool.num_acquired(), 0); + + let mut conn1 = pool.acquire().await?; + + assert_eq!(pool.size(), 1); + assert_eq!(pool.num_idle(), 0); + assert_eq!(pool.num_acquired(), 1); + + let mut conn2 = pool.acquire().await?; + + assert_eq!(pool.size(), 2); + assert_eq!(pool.num_idle(), 0); + assert_eq!(pool.num_acquired(), 2); + + conn2.return_to_pool().await; + conn1.return_to_pool().await; + + assert_eq!(pool.size(), 2); + assert_eq!(pool.num_idle(), 2); + assert_eq!(pool.num_acquired(), 0); + + pool.close().await; + + Ok(()) +} + #[sqlx_macros::test] async fn test_pool_callbacks() -> anyhow::Result<()> { sqlx::any::install_default_drivers(); From 01781659d8a72ec18765886be979220761d66aa6 Mon Sep 17 00:00:00 2001 From: Angel Nereira Date: Sun, 24 May 2026 22:44:46 -0500 Subject: [PATCH 2/2] test(sqlite): close sqlcipher connections explicitly --- tests/sqlite/sqlcipher.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/sqlite/sqlcipher.rs b/tests/sqlite/sqlcipher.rs index 9be0a179bb..ecfa3158c8 100644 --- a/tests/sqlite/sqlcipher.rs +++ b/tests/sqlite/sqlcipher.rs @@ -56,6 +56,7 @@ async fn it_encrypts() -> anyhow::Result<()> { .await?; fill_db(&mut conn).await?; + conn.close().await?; // Create another connection without key, query should fail let mut conn = SqliteConnectOptions::from_str(&url)?.connect().await?; @@ -67,6 +68,8 @@ async fn it_encrypts() -> anyhow::Result<()> { .await .is_err()); + conn.close().await?; + Ok(()) } @@ -81,6 +84,7 @@ async fn it_can_store_and_read_encrypted_data() -> anyhow::Result<()> { .await?; fill_db(&mut conn).await?; + conn.close().await?; // Create another connection with valid key let mut conn = SqliteConnectOptions::from_str(&url)? @@ -96,6 +100,8 @@ async fn it_can_store_and_read_encrypted_data() -> anyhow::Result<()> { assert!(result.len() > 0); + conn.close().await?; + Ok(()) } @@ -110,6 +116,7 @@ async fn it_fails_if_password_is_incorrect() -> anyhow::Result<()> { .await?; fill_db(&mut conn).await?; + conn.close().await?; // Connection with invalid key should not allow to execute queries let mut conn = SqliteConnectOptions::from_str(&url)? @@ -124,6 +131,8 @@ async fn it_fails_if_password_is_incorrect() -> anyhow::Result<()> { .await .is_err()); + conn.close().await?; + Ok(()) } @@ -148,6 +157,7 @@ async fn it_honors_order_of_encryption_pragmas() -> anyhow::Result<()> { .await?; fill_db(&mut conn).await?; + conn.close().await?; let mut conn = SqliteConnectOptions::from_str(&url)? .pragma("dummy", "pragma") @@ -167,6 +177,8 @@ async fn it_honors_order_of_encryption_pragmas() -> anyhow::Result<()> { assert!(result.len() > 0); + conn.close().await?; + Ok(()) } @@ -186,6 +198,7 @@ async fn it_allows_to_rekey_the_db() -> anyhow::Result<()> { query("PRAGMA rekey = new_password;") .execute(&mut conn) .await?; + conn.close().await?; let mut conn = SqliteConnectOptions::from_str(&url)? .pragma("dummy", "pragma") @@ -201,5 +214,7 @@ async fn it_allows_to_rekey_the_db() -> anyhow::Result<()> { assert!(result.len() > 0); + conn.close().await?; + Ok(()) }