diff --git a/Cargo.lock b/Cargo.lock index bc7e6cf7..579381b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -613,17 +613,17 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.66.1" +version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b84e06fc203107bfbad243f4aba2af864eb7db3b1cf46ea0a023b0b433d2a7" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ "bitflags 2.9.1", "cexpr", "clang-sys", + "itertools 0.12.1", "lazy_static", "lazycell", "log", - "peeking_take_while", "prettyplease", "proc-macro2", "quote", @@ -636,37 +636,34 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.69.5" +version = "0.71.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" dependencies = [ "bitflags 2.9.1", "cexpr", "clang-sys", - "itertools 0.12.1", - "lazy_static", - "lazycell", + "itertools 0.13.0", "log", "prettyplease", "proc-macro2", "quote", "regex", - "rustc-hash 1.1.0", + "rustc-hash 2.1.1", "shlex", "syn 2.0.101", - "which 4.4.2", ] [[package]] name = "bindgen" -version = "0.71.1" +version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ "bitflags 2.9.1", "cexpr", "clang-sys", - "itertools 0.13.0", + "itertools 0.10.5", "log", "prettyplease", "proc-macro2", @@ -2770,12 +2767,6 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" -[[package]] -name = "peeking_take_while" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" - [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -2849,9 +2840,9 @@ dependencies = [ [[package]] name = "pg_query" version = "6.1.1" -source = "git+https://github.com/pgdogdev/pg_query.rs.git?rev=f8c216209f90525f065b47ffde9eb5da803d2dc6#f8c216209f90525f065b47ffde9eb5da803d2dc6" +source = "git+https://github.com/pgdogdev/pg_query.rs.git?rev=d30dcb47fdf3fa77d102b813a34392146642903a#d30dcb47fdf3fa77d102b813a34392146642903a" dependencies = [ - "bindgen 0.66.1", + "bindgen 0.72.1", "cc", "fs_extra", "glob", diff --git a/pgdog-plugin/Cargo.toml b/pgdog-plugin/Cargo.toml index 85801abe..f09d86ea 100644 --- a/pgdog-plugin/Cargo.toml +++ b/pgdog-plugin/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["rlib", "cdylib"] [dependencies] libloading = "0.8" -pg_query = { git = "https://github.com/pgdogdev/pg_query.rs.git", rev = "f8c216209f90525f065b47ffde9eb5da803d2dc6" } +pg_query = { git = "https://github.com/pgdogdev/pg_query.rs.git", rev = "d30dcb47fdf3fa77d102b813a34392146642903a" } pgdog-macros = { path = "../pgdog-macros", version = "0.1.1" } tracing = "0.1" diff --git a/pgdog/Cargo.toml b/pgdog/Cargo.toml index e728f524..d88a8d81 100644 --- a/pgdog/Cargo.toml +++ b/pgdog/Cargo.toml @@ -43,7 +43,7 @@ base64 = "0.22" md5 = "0.7" futures = "0.3" csv-core = "0.1" -pg_query = { git = "https://github.com/pgdogdev/pg_query.rs.git", rev = "f8c216209f90525f065b47ffde9eb5da803d2dc6" } +pg_query = { git = "https://github.com/pgdogdev/pg_query.rs.git", rev = "d30dcb47fdf3fa77d102b813a34392146642903a" } regex = "1" semver = "1" uuid = { version = "1", features = ["v4", "serde"] } diff --git a/pgdog/src/frontend/client/query_engine/test/idle_in_transaction_recovery.rs b/pgdog/src/frontend/client/query_engine/test/idle_in_transaction_recovery.rs new file mode 100644 index 00000000..7ec77868 --- /dev/null +++ b/pgdog/src/frontend/client/query_engine/test/idle_in_transaction_recovery.rs @@ -0,0 +1,78 @@ +use std::time::Duration; + +use pgdog_postgres_types::Format; +use tokio::time::sleep; + +use crate::{ + backend::{server::test::test_server, Server}, + expect_message, + net::{DataRow, RowDescription}, +}; + +use super::prelude::*; + +#[tokio::test] +async fn test_idle_in_transaction_partial_recovery() { + crate::logger(); + + // Direct connection for testing state. + let mut test_server = test_server().await; + + let mut client = TestClient::new_replicas(Parameters::default()) + .await + .leak_pool(); + + client.send_simple(Query::new("BEGIN")).await; + client.read_until('Z').await.unwrap(); + + client + .send_simple(Query::new("SELECT pg_backend_pid()::text")) + .await; + expect_message!(client.read().await, RowDescription); + let rd = expect_message!(client.read().await, DataRow); + let pid: String = rd.get(0, Format::Text).unwrap(); + + client.read_until('Z').await.unwrap(); + + // This won't fire because we'll be stuck inside the extended exchange. + client + .send_simple(Query::new("SET idle_in_transaction_session_timeout TO 100")) + .await; + client.read_until('Z').await.unwrap(); + + client.send(Parse::named("__test_1", "SELECT $1")).await; + client.send(Flush).await; + client.try_process().await.unwrap(); + client.read_until('1').await.unwrap(); + + // Stuck inside extended exchange, idle in transaction timeout will not fire. + sleep(Duration::from_millis(100)).await; + + client.send(Parse::named("__test_2", "SELECT $1")).await; + client.send(Flush).await; + + client.try_process().await.unwrap(); + client.read_until('1').await.unwrap(); + + // Server in active state. + assert_server_state(&mut test_server, &pid, "active").await; + + client.send(Terminate).await; + drop(client); + + sleep(Duration::from_millis(50)).await; + + // Cleanup works. + assert_server_state(&mut test_server, &pid, "idle").await; +} + +async fn assert_server_state(conn: &mut Server, pid: &str, expected: &str) { + let response: Vec = conn + .fetch_all(format!( + "SELECT state::text FROM pg_stat_activity WHERE pid = {}", + pid + )) + .await + .unwrap(); + assert_eq!(response[0], expected); +} diff --git a/pgdog/src/frontend/client/query_engine/test/mod.rs b/pgdog/src/frontend/client/query_engine/test/mod.rs index 0f7b1ab6..d4149f43 100644 --- a/pgdog/src/frontend/client/query_engine/test/mod.rs +++ b/pgdog/src/frontend/client/query_engine/test/mod.rs @@ -13,6 +13,7 @@ mod extended; mod fatal_error; mod graceful_disconnect; mod graceful_shutdown; +mod idle_in_transaction_recovery; mod lock_session; mod omni; pub mod prelude; diff --git a/pgdog/src/frontend/client/test/test_client.rs b/pgdog/src/frontend/client/test/test_client.rs index ec54b787..f4d5f60d 100644 --- a/pgdog/src/frontend/client/test/test_client.rs +++ b/pgdog/src/frontend/client/test/test_client.rs @@ -111,6 +111,7 @@ pub struct TestClient { pub(crate) client: Client, pub(crate) engine: QueryEngine, pub(crate) conn: TcpStream, + pub(crate) leak_pool: bool, } impl TestClient { @@ -119,13 +120,14 @@ impl TestClient { /// /// Config needs to be loaded. /// - async fn new(params: Parameters) -> Self { + pub(crate) async fn new(params: Parameters) -> Self { let (conn, client) = new_client_pair(params).await; Self { conn, engine: QueryEngine::from_client(&client).expect("create query engine from client"), client, + leak_pool: false, } } @@ -135,6 +137,11 @@ impl TestClient { Self::new(params).await } + pub(crate) fn leak_pool(mut self) -> Self { + self.leak_pool = true; + self + } + /// New client with replicas but not sharded. pub(crate) async fn new_replicas(params: Parameters) -> Self { load_test_replicas(); @@ -243,7 +250,9 @@ impl TestClient { impl Drop for TestClient { fn drop(&mut self) { - shutdown(); + if !self.leak_pool { + shutdown(); + } } }