diff --git a/integration/rust/tests/integration/empty_query.rs b/integration/rust/tests/integration/empty_query.rs new file mode 100644 index 000000000..ac0ce1ba9 --- /dev/null +++ b/integration/rust/tests/integration/empty_query.rs @@ -0,0 +1,65 @@ +use anyhow::{Context, Result, ensure}; +use serial_test::serial; +use tokio_postgres::{SimpleQueryMessage, error::SqlState}; + +use crate::setup::{admin_tokio, connections_tokio}; + +#[tokio::test] +#[serial] +async fn empty_query_then_rollback_parser_on() -> Result<()> { + check_empty_query_then_rollback("on").await +} + +#[tokio::test] +#[serial] +async fn empty_query_then_rollback_parser_auto() -> Result<()> { + check_empty_query_then_rollback("auto").await +} + +#[tokio::test] +#[serial] +async fn empty_query_then_rollback_parser_off() -> Result<()> { + check_empty_query_then_rollback("off").await +} + +async fn check_empty_query_then_rollback(level: &str) -> Result<()> { + let admin = admin_tokio().await; + admin + .batch_execute(&format!("SET query_parser TO '{level}'")) + .await?; + + let result = async { + let clients = connections_tokio().await; + let client = &clients[0]; + client.batch_execute("BEGIN").await?; + let error = client + .simple_query("SELECT 1/0") + .await + .err() + .context("division by zero must fail")?; + ensure!(error.code() == Some(&SqlState::DIVISION_BY_ZERO)); + + let messages = client.simple_query(";").await?; + // tokio-postgres exposes EmptyQueryResponse as CommandComplete(0). + ensure!(matches!( + messages.as_slice(), + [SimpleQueryMessage::CommandComplete(0)] + )); + + // An empty query must not clear the failed transaction state. + let error = client + .simple_query("SELECT 1") + .await + .err() + .context("query in failed transaction must fail")?; + ensure!(error.code() == Some(&SqlState::IN_FAILED_SQL_TRANSACTION)); + + client.batch_execute("ROLLBACK").await?; + client.simple_query("SELECT 1").await?; + Ok(()) + } + .await; + + admin.batch_execute("RELOAD").await?; + result +} diff --git a/integration/rust/tests/integration/mod.rs b/integration/rust/tests/integration/mod.rs index a1ae52d22..a3dcbb23d 100644 --- a/integration/rust/tests/integration/mod.rs +++ b/integration/rust/tests/integration/mod.rs @@ -15,6 +15,7 @@ pub mod cross_shard_disabled; mod cross_shard_oid_drift; mod cross_shard_omni_check; pub mod distinct; +mod empty_query; pub mod explain; pub mod fake_transactions; pub mod having; diff --git a/pgdog/src/frontend/client/query_engine/query.rs b/pgdog/src/frontend/client/query_engine/query.rs index 8ba74a6ae..83737d748 100644 --- a/pgdog/src/frontend/client/query_engine/query.rs +++ b/pgdog/src/frontend/client/query_engine/query.rs @@ -573,7 +573,7 @@ impl QueryEngine { && client_request .ast .as_ref() - .is_none_or(|ast| ast.ast.stmts().next().is_some()) + .is_some_and(|ast| ast.ast.stmts().next().is_some()) { let error = ErrorResponse::in_failed_transaction(); diff --git a/pgdog/src/frontend/client/query_engine/test/mod.rs b/pgdog/src/frontend/client/query_engine/test/mod.rs index 9ef0a4463..03202b7bb 100644 --- a/pgdog/src/frontend/client/query_engine/test/mod.rs +++ b/pgdog/src/frontend/client/query_engine/test/mod.rs @@ -35,7 +35,6 @@ mod pub_sub; mod regression_auto_two_pc; mod regression_bind_transaction_end; mod regression_flush_error; -mod regression_parser_off_rollback; mod regression_pinned_portal; mod regression_pinned_sync; mod regression_prepare_transaction_end; diff --git a/pgdog/src/frontend/client/query_engine/test/regression_parser_off_rollback.rs b/pgdog/src/frontend/client/query_engine/test/regression_parser_off_rollback.rs deleted file mode 100644 index a67048fdc..000000000 --- a/pgdog/src/frontend/client/query_engine/test/regression_parser_off_rollback.rs +++ /dev/null @@ -1,41 +0,0 @@ -use pgdog_config::QueryParserLevel; - -use super::prelude::*; -use crate::{ - config::load_test, - expect_message, - net::{CommandComplete, ErrorResponse, ReadyForQuery}, -}; - -#[tokio::test] -async fn empty_query_then_rollback() { - load_test(); - super::change_config(|general| general.query_parser = QueryParserLevel::On); - let mut client = TestClient::new(Parameters::default()).await; - - client.send_simple(Query::new("BEGIN")).await; - client.read_until('Z').await.expect("begin transaction"); - client.send_simple(Query::new("SELECT 1/0")).await; - assert_eq!( - expect_message!(client.read().await, ErrorResponse).code, - "22012" - ); - assert_eq!( - expect_message!(client.read().await, ReadyForQuery).status, - 'E' - ); - - client.send_simple(Query::new(";")).await; - assert_eq!(client.read().await.code(), 'I'); - assert_eq!( - expect_message!(client.read().await, ReadyForQuery).status, - 'E' - ); - - client.send_simple(Query::new("ROLLBACK")).await; - expect_message!(client.read().await, CommandComplete); - assert_eq!( - expect_message!(client.read().await, ReadyForQuery).status, - 'I' - ); -}