Re: BackgroundPsql swallowing errors on windows
Arseniy Mukhin <arseniy.mukhin.dev@gmail.com>
From: Arseniy Mukhin <arseniy.mukhin.dev@gmail.com>
To: Tom Lane <tgl@sss.pgh.pa.us>
Cc: Noah Misch <noah@leadboat.com>, Andres Freund <andres@anarazel.de>,
pgsql-hackers@postgresql.org
Date: 2025-09-27T18:39:07Z
Lists: pgsql-hackers
Commits
Same data as JSON:
GET /api/v1/messages/:b64id/commits
the thread's linked commits as JSON, with link sources.
API reference →
-
tests: BackgroundPsql: Fix potential for lost errors on windows
- bb2bf22761e1 13.21 landed
- c21ebcca7965 14.18 landed
- 70b650d18553 15.13 landed
- 4c9397c374d9 16.9 landed
- f61769adaa62 17.5 landed
- 8b886a4e3488 18.0 landed
-
Improve handling of empty query results in BackgroundPsql::query()
- 70291a3c66ec 18.0 cited
-
Extend Cluster.pm's background_psql() to be able to start asynchronously
- ba08edb06545 18.0 cited
Hi,
I think I encountered the bug that relates to the patch from this
thread so I decided to write a report here.
version(): PostgreSQL 19devel on x86_64-linux, compiled by gcc-13.3.0,
64-bit. (master)
While writing a TAP test I noticed that background_psql hangs on a
simple query without any reason. The most simple reproducer I came up
with:
my $psql = $node->background_psql('postgres', timeout => 3);
$psql->query(q(\warn AAAAA));
$psql->query("select 1");
Here $psql->query("select 1;") hangs until timeout.
Here what I managed to understand after some investigation:
Just to remind how banner and banner_match look like:
my $banner = "background_psql: QUERY_SEPARATOR $query_cnt:";
my $banner_match = qr/(^|\n)$banner\r?\n/;
psql->query() hangs in an endless loop in pump_until() as the
termination condition (last if $$stream =~ /$until/) is never met.
Unfortunately, logs don't show the reason why we are stuck here (maybe
I do something wrong but it seems that [0] explains why pump_until()
timeout diag code doesn't work), so to get more information we need to
add some additional logging in pump_until(). If we add logging of
$$stream and $until then we can see next lines for pump_until() stderr
call:
STREAM: AAAAAbackground_psql: QUERY_SEPARATOR 2:
UNTIL: (?^:(^|\n)background_psql: QUERY_SEPARATOR 2:\r?\n)
STREAM here is what we have in stderr and UNTIL is just a banner_match.
You can see that we have stderr from the previous query ('AAAAA')
concatenated with the banner on the same line. So it doesn't match
what we have in $until pattern as it requires (^|\n) to be before the
banner. This way we have an endless loop. It seems that the reason we
don't have line separator after 'AAAAA' is a difference between how we
inject and remove the banner:
Here is a how we inject banner:
$self->{stdin} .= "$query\n;\n\\echo $banner\n\\warn $banner\n";
How we remove banner from stderr
$self->{stderr} =~ s/$banner_match//;
We remove from stderr not only the banner we previously injected with
warn, but also the line separator before the banner.
[0] https://www.postgresql.org/message-id/flat/1100715.1712265845@sss.pgh.pa.us
Best regards,
Arseniy Mukhin