diff --git a/sqlx-postgres/src/connection/executor.rs b/sqlx-postgres/src/connection/executor.rs index e0f4c3d44a..00a2b434d9 100644 --- a/sqlx-postgres/src/connection/executor.rs +++ b/sqlx-postgres/src/connection/executor.rs @@ -28,7 +28,9 @@ async fn prepare( persistent: bool, resolve_column_origin: bool, ) -> Result<(StatementId, Arc), Error> { - let id = if persistent { + // if cache is disabled, persistent statements get an id but are never evicted + // which causes a memory leak + let id = if persistent && conn.inner.cache_statement.is_enabled() { let id = conn.inner.next_statement_id; conn.inner.next_statement_id = id.next(); id diff --git a/tests/postgres/postgres.rs b/tests/postgres/postgres.rs index 126771565a..26f827b837 100644 --- a/tests/postgres/postgres.rs +++ b/tests/postgres/postgres.rs @@ -845,6 +845,32 @@ async fn it_closes_statements_when_not_persistent_issue_3850() -> anyhow::Result Ok(()) } +#[sqlx_macros::test] +async fn it_closes_statements_when_caching_is_disabled_issue_4328() -> anyhow::Result<()> { + sqlx_test::setup_if_needed(); + + let mut options: PgConnectOptions = env::var("DATABASE_URL")?.parse().unwrap(); + + options = options.statement_cache_capacity(0); + + let mut conn = PgConnection::connect_with(&options).await?; + + let _row = sqlx::query("SELECT $1 AS val") + .bind(Oid(1)) + .fetch_one(&mut conn) + .await?; + + let row = sqlx::query("SELECT count(*) AS num_prepared_statements FROM pg_prepared_statements") + .persistent(false) + .fetch_one(&mut conn) + .await?; + + let n: i64 = row.get("num_prepared_statements"); + assert_eq!(0, n, "no prepared statements should be open"); + + Ok(()) +} + #[sqlx_macros::test] async fn it_sets_application_name() -> anyhow::Result<()> { sqlx_test::setup_if_needed();