From 486860d60cb2d2255157885519c84e99dea19fd6 Mon Sep 17 00:00:00 2001
From: Fujii Masao <fujii@postgresql.org>
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 c969afab3a5..45e6cfed70a 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -3630,22 +3630,19 @@ 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);
-	appendPQExpBufferStr(buf, (is_retry ?
+	printfPQExpBuffer(&buf, "client %d ", st->id);
+	appendPQExpBufferStr(&buf, (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 unlimited. */
 	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
@@ -3654,12 +3651,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));
 	}
-	appendPQExpBufferStr(buf, ")\n");
+	appendPQExpBufferStr(&buf, ")\n");
 
-	pg_log_info("%s", buf->data);
+	pg_log_info("%s", buf.data);
+
+	termPQExpBuffer(&buf);
 }
 
 /*
-- 
2.53.0

