From 06c29fb7df3ec531df82a2b81e077294dec4f94e Mon Sep 17 00:00:00 2001 From: Joey de Waal Date: Mon, 23 Mar 2026 23:02:13 +0100 Subject: [PATCH 1/3] fix(postgres): make advisory lock cancel safe --- sqlx-postgres/src/advisory_lock.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/sqlx-postgres/src/advisory_lock.rs b/sqlx-postgres/src/advisory_lock.rs index 84cad2bfdd..e885e12911 100644 --- a/sqlx-postgres/src/advisory_lock.rs +++ b/sqlx-postgres/src/advisory_lock.rs @@ -199,10 +199,12 @@ impl PgAdvisoryLock { /// See [Postgres' documentation for the Advisory Lock Functions][advisory-funcs] for details. /// /// [advisory-funcs]: https://www.postgresql.org/docs/current/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS - pub async fn acquire>( - &self, - mut conn: C, - ) -> Result> { + pub async fn acquire>(&self, conn: C) -> Result> { + // We're wrapping the connection in a `PgAdvisoryLockGuard` early here on purpose. If this + // future is dropped, the lock will be released in the drop impl. + let mut guard = PgAdvisoryLockGuard::new(self.clone(), conn); + let conn = guard.conn.as_mut().unwrap(); + match &self.key { PgAdvisoryLockKey::BigInt(key) => { crate::query::query("SELECT pg_advisory_lock($1)") @@ -219,7 +221,7 @@ impl PgAdvisoryLock { } } - Ok(PgAdvisoryLockGuard::new(self.clone(), conn)) + Ok(guard) } /// Acquires an exclusive lock using `pg_try_advisory_lock()`, returning immediately From 94894c5d20a36366ea1ae3e3ee587c765bc0b474 Mon Sep 17 00:00:00 2001 From: Joey de Waal Date: Tue, 24 Mar 2026 13:50:18 +0100 Subject: [PATCH 2/3] fix(postgres): prepare advisory lock acquire query first --- sqlx-postgres/src/advisory_lock.rs | 32 +++++++++++++++++------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/sqlx-postgres/src/advisory_lock.rs b/sqlx-postgres/src/advisory_lock.rs index e885e12911..9b5464cc67 100644 --- a/sqlx-postgres/src/advisory_lock.rs +++ b/sqlx-postgres/src/advisory_lock.rs @@ -3,6 +3,8 @@ use crate::Either; use crate::PgConnection; use hkdf::Hkdf; use sha2::Sha256; +use sqlx_core::executor::Executor; +use sqlx_core::sql_str::SqlSafeStr; use std::ops::{Deref, DerefMut}; use std::sync::Arc; use std::sync::OnceLock; @@ -199,27 +201,29 @@ impl PgAdvisoryLock { /// See [Postgres' documentation for the Advisory Lock Functions][advisory-funcs] for details. /// /// [advisory-funcs]: https://www.postgresql.org/docs/current/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS - pub async fn acquire>(&self, conn: C) -> Result> { + pub async fn acquire>( + &self, + mut conn: C, + ) -> Result> { + let query = match &self.key { + PgAdvisoryLockKey::BigInt(_) => "SELECT pg_advisory_lock($1)", + PgAdvisoryLockKey::IntPair(_, _) => "SELECT pg_advisory_lock($1, $2)", + }; + + let stmt = conn.as_mut().prepare(query.into_sql_str()).await?; + let query = crate::query::query_statement(&stmt); + // We're wrapping the connection in a `PgAdvisoryLockGuard` early here on purpose. If this // future is dropped, the lock will be released in the drop impl. let mut guard = PgAdvisoryLockGuard::new(self.clone(), conn); let conn = guard.conn.as_mut().unwrap(); match &self.key { - PgAdvisoryLockKey::BigInt(key) => { - crate::query::query("SELECT pg_advisory_lock($1)") - .bind(key) - .execute(conn.as_mut()) - .await?; - } - PgAdvisoryLockKey::IntPair(key1, key2) => { - crate::query::query("SELECT pg_advisory_lock($1, $2)") - .bind(key1) - .bind(key2) - .execute(conn.as_mut()) - .await?; - } + PgAdvisoryLockKey::BigInt(key) => query.bind(key), + PgAdvisoryLockKey::IntPair(key1, key2) => query.bind(key1).bind(key2), } + .execute(conn.as_mut()) + .await?; Ok(guard) } From afaa3e8a8002e07257a1562c874d4a9c6a0a40f6 Mon Sep 17 00:00:00 2001 From: Joey de Waal Date: Thu, 26 Mar 2026 16:42:52 +0100 Subject: [PATCH 3/3] document cancel safety --- sqlx-postgres/src/advisory_lock.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sqlx-postgres/src/advisory_lock.rs b/sqlx-postgres/src/advisory_lock.rs index 9b5464cc67..979aca06ce 100644 --- a/sqlx-postgres/src/advisory_lock.rs +++ b/sqlx-postgres/src/advisory_lock.rs @@ -201,6 +201,11 @@ impl PgAdvisoryLock { /// See [Postgres' documentation for the Advisory Lock Functions][advisory-funcs] for details. /// /// [advisory-funcs]: https://www.postgresql.org/docs/current/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS + /// + /// # Cancel Safety + /// + /// This method is cancel safe. If the future is dropped before the query completes, a + /// `pg_advisory_unlock()` call is queued and run the next time the connection is used. pub async fn acquire>( &self, mut conn: C, @@ -248,6 +253,12 @@ impl PgAdvisoryLock { /// See [Postgres' documentation for the Advisory Lock Functions][advisory-funcs] for details. /// /// [advisory-funcs]: https://www.postgresql.org/docs/current/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS + /// + /// # Cancel Safety + /// + /// This method is **not** cancel safe. If the future is dropped while the query is in-flight, + /// it is not possible to know whether the lock was acquired, so it cannot be safely released. + /// The lock may remain held until the connection is closed. pub async fn try_acquire>( &self, mut conn: C,