Hi,

While running pgbench with multiple threads and --verbose-errors,
I found that some verbose error messages were corrupted.
This issue happens because printVerboseErrorMessages() uses
a function-local static PQExpBuffer for building those messages.
Since that buffer is shared across threads, it is not thread-safe.

Attached patch fixes this issue by changing printVerboseErrorMessages()
to use a local PQExpBufferData instead of a static one. Thoughts?

Since this issue was introduced in v15, the patch should be
backpatched to v15 if accepted.

Regards,

-- 
Fujii Masao
From f1056e0793812118c462a2338ff2ca5ab5ce55ce Mon Sep 17 00:00:00 2001
From: Fujii Masao <[email protected]>
Date: Fri, 24 Apr 2026 14:50:48 +0900
Subject: [PATCH v1] pgbench: fix verbose error message corruption with
 multiple threads

When pgbench runs with multiple threads and verbose error reporting is
enabled (--verbose-errors), multiple clients can build verbose error
messages concurrently. Previously, a function-local static
PQExpBuffer was used for these messages, causing the buffer to be
shared across threads. This was not thread-safe and could result in
corrupted or incorrect log output.

Fix this by using a local PQExpBufferData instead of a static buffer.
This keeps verbose error messages correct during concurrent execution.

Backpatch to v15, where this issue was introduced.
---
 src/bin/pgbench/pgbench.c | 23 +++++++++++------------
 1 file changed, 11 insertions(+), 12 deletions(-)

diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 80d14108c28..dc0c48f86f0 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -3586,23 +3586,20 @@ getTransactionStatus(PGconn *con)
 static void
 printVerboseErrorMessages(CState *st, pg_time_usec_t *now, bool is_retry)
 {
-       static PQExpBuffer buf = NULL;
+       PQExpBufferData buf;
 
-       if (buf == NULL)
-               buf = createPQExpBuffer();
-       else
-               resetPQExpBuffer(buf);
+       initPQExpBuffer(&buf);
 
-       printfPQExpBuffer(buf, "client %d ", st->id);
-       appendPQExpBuffer(buf, "%s",
+       printfPQExpBuffer(&buf, "client %d ", st->id);
+       appendPQExpBuffer(&buf, "%s",
                                          (is_retry ?
                                           "repeats the transaction after the 
error" :
                                           "ends the failed transaction"));
-       appendPQExpBuffer(buf, " (try %u", st->tries);
+       appendPQExpBuffer(&buf, " (try %u", st->tries);
 
        /* Print max_tries if it is not unlimitted. */
        if (max_tries)
-               appendPQExpBuffer(buf, "/%u", max_tries);
+               appendPQExpBuffer(&buf, "/%u", max_tries);
 
        /*
         * If the latency limit is used, print a percentage of the current
@@ -3611,12 +3608,14 @@ printVerboseErrorMessages(CState *st, pg_time_usec_t 
*now, bool is_retry)
        if (latency_limit)
        {
                pg_time_now_lazy(now);
-               appendPQExpBuffer(buf, ", %.3f%% of the maximum time of tries 
was used",
+               appendPQExpBuffer(&buf, ", %.3f%% of the maximum time of tries 
was used",
                                                  (100.0 * (*now - 
st->txn_scheduled) / latency_limit));
        }
-       appendPQExpBuffer(buf, ")\n");
+       appendPQExpBuffer(&buf, ")\n");
 
-       pg_log_info("%s", buf->data);
+       pg_log_info("%s", buf.data);
+
+       termPQExpBuffer(&buf);
 }
 
 /*
-- 
2.53.0

Attachment: v1-0001-pgbench-fix-verbose-error-message-corruption-with.patch
Description: Binary data

Reply via email to