From f968080ba2d92c942f4fd44aa266d29cbf008348 Mon Sep 17 00:00:00 2001 From: Anthonin Bonnefoy Date: Tue, 3 Jun 2025 10:31:26 +0200 Subject: Abort connection when sync is sent after a COPY When the backend reads COPY data, it ignores all Sync messages. With psql pipelining, it's possible to manually send Sync with \sendpipeline which leaves the frontend in an unrecoverable state as the backend won't send the necessary ReadyForQuery. It could be possible to artificially reduce the piped_syncs and requested_results, but libpq's state will still have queued Sync in its command queue. And the only way to consume those without directly calling pqCommandQueueAdvance is to process ReadyForQuery messages that won't be sent since the backend ignored the Syncs. Thus, this patch abort the connection if we detect excessive sync after a COPY in a pipeline to avoid staying in an inconsistent protocol state. --- src/bin/psql/common.c | 15 +++++++++++++++ src/bin/psql/t/001_basic.pl | 14 ++++++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c index 3e4e444f3fd..ed745270b6c 100644 --- a/src/bin/psql/common.c +++ b/src/bin/psql/common.c @@ -1867,6 +1867,21 @@ ExecQueryAndProcessResults(const char *query, { FILE *copy_stream = NULL; + if (pset.piped_syncs > 1) + { + /* + * When reading COPY data, the backend ignores SYNC messages + * and won't send a matching ReadyForQuery response. Even if + * we adjust piped_syncs and requested_results, it is not + * possible to salvage this as the SYNC will still be in + * libpq's command queue and we would be stuck in a busy + * pipeline state. Thus, we abort the connection to avoid this + * state. + */ + pg_log_info("\\syncpipeline after COPY is not supported, aborting connection"); + exit(EXIT_BADCONN); + } + /* * For COPY OUT, direct the output to the default place (probably * a pager pipe) for \watch, or to pset.copyStream for \copy, diff --git a/src/bin/psql/t/001_basic.pl b/src/bin/psql/t/001_basic.pl index 4050f9a5e3e..d8fcf0575e0 100644 --- a/src/bin/psql/t/001_basic.pl +++ b/src/bin/psql/t/001_basic.pl @@ -513,15 +513,25 @@ SELECT 'val1' \\bind \\sendpipeline qr/server closed the connection unexpectedly/, 'protocol sync loss in pipeline: bind COPY, SELECT, sync and getresult'); -# This time, test without the \getresults. +# This time, test without the \getresults and \syncpipeline. psql_fails_like( $node, qq{\\startpipeline COPY psql_pipeline FROM STDIN; SELECT 'val1'; -\\syncpipeline \\endpipeline}, qr/server closed the connection unexpectedly/, 'protocol sync loss in pipeline: COPY, SELECT and sync'); +# Test sending a sync after a COPY, this will abort the connection from the +# frontend +psql_fails_like( + $node, + qq{\\startpipeline +COPY psql_pipeline FROM STDIN; +\\syncpipeline +\\endpipeline}, + qr/\\syncpipeline after COPY is not supported, aborting connection/, + 'sending sync after COPY'); + done_testing(); -- 2.49.0