From 59399b9853c581f4eabab64080577c737a397a73 Mon Sep 17 00:00:00 2001 From: Joey de Waal Date: Sat, 5 Oct 2024 20:41:04 +0200 Subject: [PATCH 1/6] Postgres: force generic plan when describing statement with args --- Cargo.lock | 1 + sqlx-postgres/src/connection/describe.rs | 3 +++ 2 files changed, 4 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 2479456482..9420f19770 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1995,6 +1995,7 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" dependencies = [ + "bindgen", "cc", "pkg-config", "vcpkg", diff --git a/sqlx-postgres/src/connection/describe.rs b/sqlx-postgres/src/connection/describe.rs index 18aed411b3..7a77ebd295 100644 --- a/sqlx-postgres/src/connection/describe.rs +++ b/sqlx-postgres/src/connection/describe.rs @@ -12,6 +12,7 @@ use crate::HashMap; use crate::{PgColumn, PgConnection, PgTypeInfo}; use futures_core::future::BoxFuture; use smallvec::SmallVec; +use sqlx_core::executor::Executor; use sqlx_core::query_builder::QueryBuilder; use std::sync::Arc; @@ -523,6 +524,8 @@ WHERE rngtypid = $1 let mut comma = false; if params_len > 0 { + self.execute("set plan_cache_mode = force_generic_plan;").await?; + explain += "("; // fill the arguments list with NULL, which should theoretically be valid From 4ceb87900dc14f43d22f89ee75e6b4545470eb44 Mon Sep 17 00:00:00 2001 From: Joey de Waal Date: Sat, 5 Oct 2024 20:57:16 +0200 Subject: [PATCH 2/6] cargo fmt --- sqlx-postgres/src/connection/describe.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sqlx-postgres/src/connection/describe.rs b/sqlx-postgres/src/connection/describe.rs index 7a77ebd295..791f57e6cd 100644 --- a/sqlx-postgres/src/connection/describe.rs +++ b/sqlx-postgres/src/connection/describe.rs @@ -524,7 +524,8 @@ WHERE rngtypid = $1 let mut comma = false; if params_len > 0 { - self.execute("set plan_cache_mode = force_generic_plan;").await?; + self.execute("set plan_cache_mode = force_generic_plan;") + .await?; explain += "("; From 5bba30d6b5ccf3bd19c0a2cc8e3c81fb77ef17cd Mon Sep 17 00:00:00 2001 From: Joey de Waal Date: Sat, 5 Oct 2024 21:51:31 +0200 Subject: [PATCH 3/6] Postgres: set plan_cache_mode in transaction --- sqlx-postgres/src/connection/describe.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sqlx-postgres/src/connection/describe.rs b/sqlx-postgres/src/connection/describe.rs index 791f57e6cd..7614e6c04b 100644 --- a/sqlx-postgres/src/connection/describe.rs +++ b/sqlx-postgres/src/connection/describe.rs @@ -12,6 +12,7 @@ use crate::HashMap; use crate::{PgColumn, PgConnection, PgTypeInfo}; use futures_core::future::BoxFuture; use smallvec::SmallVec; +use sqlx_core::acquire::Acquire; use sqlx_core::executor::Executor; use sqlx_core::query_builder::QueryBuilder; use std::sync::Arc; @@ -522,9 +523,10 @@ WHERE rngtypid = $1 let mut explain = format!("EXPLAIN (VERBOSE, FORMAT JSON) EXECUTE {stmt_id_display}"); let mut comma = false; + let mut tx = self.begin().await?; if params_len > 0 { - self.execute("set plan_cache_mode = force_generic_plan;") + tx.execute("set local plan_cache_mode = force_generic_plan;") .await?; explain += "("; @@ -543,7 +545,9 @@ WHERE rngtypid = $1 } let (Json(explains),): (Json>,) = - query_as(&explain).fetch_one(self).await?; + query_as(&explain).fetch_one(&mut *tx).await?; + + tx.rollback().await?; let mut nullables = Vec::new(); From 819f189cc11a9d7d99a3e479bfb41d389da183e3 Mon Sep 17 00:00:00 2001 From: Joey de Waal Date: Sat, 5 Oct 2024 22:01:30 +0200 Subject: [PATCH 4/6] Postgres: only set force_generic_plan for versions that support it --- sqlx-postgres/src/connection/describe.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/sqlx-postgres/src/connection/describe.rs b/sqlx-postgres/src/connection/describe.rs index 7614e6c04b..1063af1224 100644 --- a/sqlx-postgres/src/connection/describe.rs +++ b/sqlx-postgres/src/connection/describe.rs @@ -526,8 +526,19 @@ WHERE rngtypid = $1 let mut tx = self.begin().await?; if params_len > 0 { - tx.execute("set local plan_cache_mode = force_generic_plan;") - .await?; + tx.execute( + " DO $$ + BEGIN + IF EXISTS ( + SELECT 1 + FROM pg_settings + WHERE name = 'plan_cache_mode' + ) THEN + SET LOCAL plan_cache_mode = 'force_generic_plan'; + END IF; + END $$;", + ) + .await?; explain += "("; From 12c2986a704bbd15b4227c04ba82c0bc11a57fe4 Mon Sep 17 00:00:00 2001 From: Joey de Waal Date: Tue, 8 Oct 2024 21:10:10 +0200 Subject: [PATCH 5/6] Postgres: reduce database calls in query macro's --- sqlx-postgres/src/connection/describe.rs | 48 +++++++++++++----------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/sqlx-postgres/src/connection/describe.rs b/sqlx-postgres/src/connection/describe.rs index 1063af1224..7bd0431d2d 100644 --- a/sqlx-postgres/src/connection/describe.rs +++ b/sqlx-postgres/src/connection/describe.rs @@ -11,10 +11,12 @@ use crate::types::Oid; use crate::HashMap; use crate::{PgColumn, PgConnection, PgTypeInfo}; use futures_core::future::BoxFuture; +use futures_util::TryFutureExt; use smallvec::SmallVec; -use sqlx_core::acquire::Acquire; use sqlx_core::executor::Executor; +use sqlx_core::from_row::FromRow; use sqlx_core::query_builder::QueryBuilder; +use sqlx_core::raw_sql::raw_sql; use std::sync::Arc; /// Describes the type of the `pg_type.typtype` column @@ -521,25 +523,25 @@ WHERE rngtypid = $1 .display() .ok_or_else(|| err_protocol!("cannot EXPLAIN unnamed statement: {stmt_id:?}"))?; - let mut explain = format!("EXPLAIN (VERBOSE, FORMAT JSON) EXECUTE {stmt_id_display}"); + let mut explain = format!( + " + BEGIN; + DO $$ + BEGIN + IF EXISTS ( + SELECT 1 + FROM pg_settings + WHERE name = 'plan_cache_mode' + ) THEN + SET LOCAL plan_cache_mode = 'force_generic_plan'; + END IF; + END $$; + EXPLAIN (VERBOSE, FORMAT JSON) EXECUTE {stmt_id_display} + " + ); let mut comma = false; - let mut tx = self.begin().await?; if params_len > 0 { - tx.execute( - " DO $$ - BEGIN - IF EXISTS ( - SELECT 1 - FROM pg_settings - WHERE name = 'plan_cache_mode' - ) THEN - SET LOCAL plan_cache_mode = 'force_generic_plan'; - END IF; - END $$;", - ) - .await?; - explain += "("; // fill the arguments list with NULL, which should theoretically be valid @@ -552,13 +554,15 @@ WHERE rngtypid = $1 comma = true; } - explain += ")"; + explain += "); + ROLLBACK; + "; } - let (Json(explains),): (Json>,) = - query_as(&explain).fetch_one(&mut *tx).await?; - - tx.rollback().await?; + let (Json(explains),): (Json>,) = self + .fetch_one(raw_sql(&explain)) + .map_ok(|row| FromRow::from_row(&row)) + .await??; let mut nullables = Vec::new(); From 0e2ec965210d02d8239aeed761d556a93ed7b2b5 Mon Sep 17 00:00:00 2001 From: Joey de Waal Date: Thu, 10 Oct 2024 21:56:58 +0200 Subject: [PATCH 6/6] Postgres: set generic execution plan only in query macro --- sqlx-macros-core/src/database/mod.rs | 22 ++++++++++++++++- sqlx-postgres/src/connection/describe.rs | 31 +++--------------------- 2 files changed, 25 insertions(+), 28 deletions(-) diff --git a/sqlx-macros-core/src/database/mod.rs b/sqlx-macros-core/src/database/mod.rs index a2d0a1fa0d..50ef911495 100644 --- a/sqlx-macros-core/src/database/mod.rs +++ b/sqlx-macros-core/src/database/mod.rs @@ -54,7 +54,27 @@ impl CachingDescribeBlocking { let conn = match cache.entry(database_url.to_string()) { hash_map::Entry::Occupied(hit) => hit.into_mut(), hash_map::Entry::Vacant(miss) => { - miss.insert(DB::Connection::connect(database_url).await?) + let conn = miss.insert(DB::Connection::connect(database_url).await?); + + #[cfg(feature = "postgres")] + if DB::NAME == sqlx_postgres::Postgres::NAME { + conn.execute( + " + DO $$ + BEGIN + IF EXISTS ( + SELECT 1 + FROM pg_settings + WHERE name = 'plan_cache_mode' + ) THEN + SET SESSION plan_cache_mode = 'force_generic_plan'; + END IF; + END $$; + ", + ) + .await?; + } + conn } }; diff --git a/sqlx-postgres/src/connection/describe.rs b/sqlx-postgres/src/connection/describe.rs index 7bd0431d2d..18aed411b3 100644 --- a/sqlx-postgres/src/connection/describe.rs +++ b/sqlx-postgres/src/connection/describe.rs @@ -11,12 +11,8 @@ use crate::types::Oid; use crate::HashMap; use crate::{PgColumn, PgConnection, PgTypeInfo}; use futures_core::future::BoxFuture; -use futures_util::TryFutureExt; use smallvec::SmallVec; -use sqlx_core::executor::Executor; -use sqlx_core::from_row::FromRow; use sqlx_core::query_builder::QueryBuilder; -use sqlx_core::raw_sql::raw_sql; use std::sync::Arc; /// Describes the type of the `pg_type.typtype` column @@ -523,22 +519,7 @@ WHERE rngtypid = $1 .display() .ok_or_else(|| err_protocol!("cannot EXPLAIN unnamed statement: {stmt_id:?}"))?; - let mut explain = format!( - " - BEGIN; - DO $$ - BEGIN - IF EXISTS ( - SELECT 1 - FROM pg_settings - WHERE name = 'plan_cache_mode' - ) THEN - SET LOCAL plan_cache_mode = 'force_generic_plan'; - END IF; - END $$; - EXPLAIN (VERBOSE, FORMAT JSON) EXECUTE {stmt_id_display} - " - ); + let mut explain = format!("EXPLAIN (VERBOSE, FORMAT JSON) EXECUTE {stmt_id_display}"); let mut comma = false; if params_len > 0 { @@ -554,15 +535,11 @@ WHERE rngtypid = $1 comma = true; } - explain += "); - ROLLBACK; - "; + explain += ")"; } - let (Json(explains),): (Json>,) = self - .fetch_one(raw_sql(&explain)) - .map_ok(|row| FromRow::from_row(&row)) - .await??; + let (Json(explains),): (Json>,) = + query_as(&explain).fetch_one(self).await?; let mut nullables = Vec::new();