From 21e172ea01465fa17bc5adf8762331fc53d3812f Mon Sep 17 00:00:00 2001 From: Taiki Koshino Date: Thu, 3 Sep 2026 00:53:03 +0900 Subject: [PATCH v2] Fix hang on deferred constraint errors in pipeline mode PostgreSQL 19 and later include a psql_pipeline regression test for a deferred constraint violation at commit time. When this test is run through Pgpool-II, it hangs after reporting the constraint violation. The test sends Sync at the end of a pipeline containing an INSERT into a table with a DEFERRABLE INITIALLY DEFERRED primary key. PostgreSQL detects the duplicate key while processing Sync and returns an ErrorResponse. Previously, read_kind_from_backend() removed the pending Sync message when it received the ErrorResponse. ErrorResponse processing then incorrectly concluded that Sync had not yet been received from the frontend and waited for another Sync. Since psql had already sent Sync as part of \endpipeline, both sides waited indefinitely and the psql_pipeline test failed to complete. Keep the pending Sync message in the queue when an ErrorResponse is received. This allows error processing to recognize that Sync has already been received and to continue processing through ReadyForQuery. Test is also added. Author: Taiki Koshino Discussion: https://www.postgresql.org/message-id/OS9P286MB6486B54CF8A848176B90539C94A42%40OS9P286MB6486.JPNP286.PROD.OUTLOOK.COM Backpatch-through: v4.3 --- src/protocol/pool_process_query.c | 9 ++- .../tests/134.pipeline/pgproto.data | 11 ++++ .../regression/tests/134.pipeline/test.sh | 65 +++++++++++++++++++ 3 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 src/test/regression/tests/134.pipeline/pgproto.data create mode 100755 src/test/regression/tests/134.pipeline/test.sh diff --git a/src/protocol/pool_process_query.c b/src/protocol/pool_process_query.c index 631e0dd96..4531dbcb6 100644 --- a/src/protocol/pool_process_query.c +++ b/src/protocol/pool_process_query.c @@ -3858,6 +3858,12 @@ read_kind_from_backend(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend, * Also if it's 't' (parameter description) and the pulled message was * 'describe', the message must not be pulled out so that the row * description message from backend matches the describe message. + * + * If an ErrorResponse is returned while processing Sync, the Sync + * pending message must also be left in the queue. This can happen when + * a deferred constraint check fails while committing an implicit + * transaction. ErrorResponse processing uses the pending Sync message + * to determine that Sync has already been received from the frontend. */ if (SL_MODE && pool_is_doing_extended_query_message() && msg) { @@ -3866,7 +3872,8 @@ read_kind_from_backend(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend, *decided_kind == 'N' || *decided_kind == 'G' || *decided_kind == 'H' || *decided_kind == 'd' || *decided_kind == 'c')) || - (msg->type == POOL_DESCRIBE && *decided_kind == 't')) + (msg->type == POOL_DESCRIBE && *decided_kind == 't') || + (msg->type == POOL_SYNC && *decided_kind == 'E')) { ereport(DEBUG5, (errmsg("read_kind_from_backend: pending message was left"))); diff --git a/src/test/regression/tests/134.pipeline/pgproto.data b/src/test/regression/tests/134.pipeline/pgproto.data new file mode 100644 index 000000000..e291a21b8 --- /dev/null +++ b/src/test/regression/tests/134.pipeline/pgproto.data @@ -0,0 +1,11 @@ +'Q' "DROP TABLE IF EXISTS psql_pipeline_defer" +'Y' +'Q' "CREATE TABLE psql_pipeline_defer (a INTEGER PRIMARY KEY DEFERRABLE INITIALLY DEFERRED)" +'Y' +'P' "" "INSERT INTO psql_pipeline_defer VALUES (1),(1) RETURNING *" 0 +'B' "" "" 0 0 0 +'D' 'P' "" +'E' "" 0 +'S' +'Y' +'X' \ No newline at end of file diff --git a/src/test/regression/tests/134.pipeline/test.sh b/src/test/regression/tests/134.pipeline/test.sh new file mode 100755 index 000000000..399625fb0 --- /dev/null +++ b/src/test/regression/tests/134.pipeline/test.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +#------------------------------------------------------------------- +# Test for a hang when a deferred constraint error is reported while +# processing Sync in extended-query pipeline mode. +# +source $TESTLIBS + +TESTDIR=testdir +PGPROTO=$PGPOOL_INSTALL_DIR/bin/pgproto + +rm -fr $TESTDIR +mkdir $TESTDIR +cd $TESTDIR + +echo -n "creating test environment..." +$PGPOOL_SETUP -m s -n 1 || exit 1 +echo "done." + +source ./bashrc.ports + +./startall +wait_for_pgpool_startup + +timeout 1 $PGPROTO \ + -p $PGPOOL_PORT \ + -d test \ + -f ../pgproto.data \ + > result 2>&1 + +status=$? + +if [ $status != 0 ]; then + if [ $status = 124 ]; then + echo "test failed: pgproto timed out" + else + echo "test failed: pgproto exited with status $status" + fi + + cat result + ./shutdownall + exit 1 +fi + +# +# The deferred primary-key violation must be reported while processing +# Sync, and processing must continue through ReadyForQuery. +# +grep 'ErrorResponse(S ERROR V ERROR C 23505 ' result > /dev/null +if [ $? != 0 ]; then + echo "test failed: expected deferred constraint error was not reported" + cat result + ./shutdownall + exit 1 +fi + +grep 'ReadyForQuery(I)' result > /dev/null +if [ $? != 0 ]; then + echo "test failed: ReadyForQuery was not received after the error" + cat result + ./shutdownall + exit 1 +fi + +./shutdownall +exit 0 \ No newline at end of file -- 2.52.0