From 83253fc28dbf76df9f1718f6f6d8a3e644ddb494 Mon Sep 17 00:00:00 2001
From: Anthonin Bonnefoy <anthonin.bonnefoy@datadoghq.com>
Date: Wed, 8 Jul 2026 08:39:07 +0200
Subject: Make pqtrace write lines atomically

Currently, pqtrace writes are done by doing multiple fprintf per line to
the Pfdebug fd. This commit changes fe-trace.c to buffer each line in a
PQExpBuffer, and write lines in Pfdebug in a single fwrite.

This allows multiple connections to write on stdout without interfering
with each other.
---
 src/interfaces/libpq/fe-trace.c | 513 +++++++++++++++++---------------
 1 file changed, 265 insertions(+), 248 deletions(-)

diff --git a/src/interfaces/libpq/fe-trace.c b/src/interfaces/libpq/fe-trace.c
index e136c77e6b0..383a924d5c5 100644
--- a/src/interfaces/libpq/fe-trace.c
+++ b/src/interfaces/libpq/fe-trace.c
@@ -104,7 +104,7 @@ pqTraceFormatTimestamp(char *timestr, size_t ts_len)
  *   pqTraceOutputByte1: output a 1-char message to the log
  */
 static void
-pqTraceOutputByte1(FILE *pfdebug, const char *data, int *cursor)
+pqTraceOutputByte1(PQExpBuffer buf, const char *data, int *cursor)
 {
 	const char *v = data + *cursor;
 
@@ -113,9 +113,9 @@ pqTraceOutputByte1(FILE *pfdebug, const char *data, int *cursor)
 	 * that completes ErrorResponse and NoticeResponse messages.
 	 */
 	if (!isprint((unsigned char) *v))
-		fprintf(pfdebug, " \\x%02x", (unsigned char) *v);
+		appendPQExpBuffer(buf, " \\x%02x", (unsigned char) *v);
 	else
-		fprintf(pfdebug, " %c", *v);
+		appendPQExpBuffer(buf, " %c", *v);
 	*cursor += 1;
 }
 
@@ -123,7 +123,7 @@ pqTraceOutputByte1(FILE *pfdebug, const char *data, int *cursor)
  *   pqTraceOutputInt16: output a 2-byte integer message to the log
  */
 static int
-pqTraceOutputInt16(FILE *pfdebug, const char *data, int *cursor)
+pqTraceOutputInt16(PQExpBuffer buf, const char *data, int *cursor)
 {
 	uint16		tmp;
 	int			result;
@@ -131,7 +131,7 @@ pqTraceOutputInt16(FILE *pfdebug, const char *data, int *cursor)
 	memcpy(&tmp, data + *cursor, 2);
 	*cursor += 2;
 	result = (int) pg_ntoh16(tmp);
-	fprintf(pfdebug, " %d", result);
+	appendPQExpBuffer(buf, " %d", result);
 
 	return result;
 }
@@ -142,7 +142,7 @@ pqTraceOutputInt16(FILE *pfdebug, const char *data, int *cursor)
  * If 'suppress' is true, print a literal NNNN instead of the actual number.
  */
 static int
-pqTraceOutputInt32(FILE *pfdebug, const char *data, int *cursor, bool suppress)
+pqTraceOutputInt32(PQExpBuffer buf, const char *data, int *cursor, bool suppress)
 {
 	int			result;
 
@@ -150,9 +150,9 @@ pqTraceOutputInt32(FILE *pfdebug, const char *data, int *cursor, bool suppress)
 	*cursor += 4;
 	result = (int) pg_ntoh32(result);
 	if (suppress)
-		fprintf(pfdebug, " NNNN");
+		appendPQExpBufferStr(buf, " NNNN");
 	else
-		fprintf(pfdebug, " %d", result);
+		appendPQExpBuffer(buf, " %d", result);
 
 	return result;
 }
@@ -163,24 +163,21 @@ pqTraceOutputInt32(FILE *pfdebug, const char *data, int *cursor, bool suppress)
  * If 'suppress' is true, print a literal "SSSS" instead of the actual string.
  */
 static void
-pqTraceOutputString(FILE *pfdebug, const char *data, int *cursor, bool suppress)
+pqTraceOutputString(PQExpBuffer buf, const char *data, int *cursor, bool suppress)
 {
-	int			len;
-
 	if (suppress)
 	{
-		fprintf(pfdebug, " \"SSSS\"");
+		appendPQExpBufferStr(buf, " \"SSSS\"");
 		*cursor += strlen(data + *cursor) + 1;
 	}
 	else
 	{
-		len = fprintf(pfdebug, " \"%s\"", data + *cursor);
+		appendPQExpBuffer(buf, " \"%s\"", data + *cursor);
 
 		/*
-		 * This is a null-terminated string. So add 1 after subtracting 3
-		 * which is the double quotes and space length from len.
+		 * This is a null-terminated string. So add 1.
 		 */
-		*cursor += (len - 3 + 1);
+		*cursor += strlen(data + *cursor) + 1;
 	}
 }
 
@@ -190,7 +187,7 @@ pqTraceOutputString(FILE *pfdebug, const char *data, int *cursor, bool suppress)
  * If 'suppress' is true, print a literal 'BBBB' instead of the actual bytes.
  */
 static void
-pqTraceOutputNchar(FILE *pfdebug, int len, const char *data, int *cursor, bool suppress)
+pqTraceOutputNchar(PQExpBuffer buf, int len, const char *data, int *cursor, bool suppress)
 {
 	int			i,
 				next;			/* first char not yet printed */
@@ -198,12 +195,12 @@ pqTraceOutputNchar(FILE *pfdebug, int len, const char *data, int *cursor, bool s
 
 	if (suppress)
 	{
-		fprintf(pfdebug, " 'BBBB'");
+		appendPQExpBufferStr(buf, " 'BBBB'");
 		*cursor += len;
 		return;
 	}
 
-	fprintf(pfdebug, " \'");
+	appendPQExpBufferStr(buf, " \'");
 
 	for (next = i = 0; i < len; ++i)
 	{
@@ -211,15 +208,15 @@ pqTraceOutputNchar(FILE *pfdebug, int len, const char *data, int *cursor, bool s
 			continue;
 		else
 		{
-			fwrite(v + next, 1, i - next, pfdebug);
-			fprintf(pfdebug, "\\x%02x", (unsigned char) v[i]);
+			appendBinaryPQExpBuffer(buf, v + next, i - next);
+			appendPQExpBuffer(buf, "\\x%02x", (unsigned char) v[i]);
 			next = i + 1;
 		}
 	}
 	if (next < len)
-		fwrite(v + next, 1, len - next, pfdebug);
+		appendBinaryPQExpBuffer(buf, v + next, len - next);
 
-	fprintf(pfdebug, "\'");
+	appendPQExpBufferStr(buf, "\'");
 	*cursor += len;
 }
 
@@ -229,24 +226,24 @@ pqTraceOutputNchar(FILE *pfdebug, int len, const char *data, int *cursor, bool s
  * If 'suppress' is true, print a literal 'BBBB' instead of the actual bytes.
  */
 static void
-pqTraceOutputNbyte(FILE *pfdebug, int len, const char *data, int *cursor, bool suppress)
+pqTraceOutputNbyte(PQExpBuffer buf, int len, const char *data, int *cursor, bool suppress)
 {
 	int			i;
 	const char *v = data + *cursor;
 
 	if (suppress)
 	{
-		fprintf(pfdebug, " 'BBBB'");
+		appendPQExpBufferStr(buf, " 'BBBB'");
 		*cursor += len;
 		return;
 	}
 
-	fprintf(pfdebug, " \'");
+	appendPQExpBufferStr(buf, " \'");
 
 	for (i = 0; i < len; ++i)
-		fprintf(pfdebug, "\\x%02x", (unsigned char) v[i]);
+		appendPQExpBuffer(buf, "\\x%02x", (unsigned char) v[i]);
 
-	fprintf(pfdebug, "\'");
+	appendPQExpBufferStr(buf, "\'");
 	*cursor += len;
 }
 
@@ -255,261 +252,261 @@ pqTraceOutputNbyte(FILE *pfdebug, int len, const char *data, int *cursor, bool s
  */
 
 static void
-pqTraceOutput_NotificationResponse(FILE *f, const char *message, int *cursor, bool regress)
+pqTraceOutput_NotificationResponse(PQExpBuffer buf, const char *message, int *cursor, bool regress)
 {
-	fprintf(f, "NotificationResponse\t");
-	pqTraceOutputInt32(f, message, cursor, regress);
-	pqTraceOutputString(f, message, cursor, false);
-	pqTraceOutputString(f, message, cursor, false);
+	appendPQExpBufferStr(buf, "NotificationResponse\t");
+	pqTraceOutputInt32(buf, message, cursor, regress);
+	pqTraceOutputString(buf, message, cursor, false);
+	pqTraceOutputString(buf, message, cursor, false);
 }
 
 static void
-pqTraceOutput_Bind(FILE *f, const char *message, int *cursor)
+pqTraceOutput_Bind(PQExpBuffer buf, const char *message, int *cursor)
 {
 	int			nparams;
 
-	fprintf(f, "Bind\t");
-	pqTraceOutputString(f, message, cursor, false);
-	pqTraceOutputString(f, message, cursor, false);
-	nparams = pqTraceOutputInt16(f, message, cursor);
+	appendPQExpBufferStr(buf, "Bind\t");
+	pqTraceOutputString(buf, message, cursor, false);
+	pqTraceOutputString(buf, message, cursor, false);
+	nparams = pqTraceOutputInt16(buf, message, cursor);
 
 	for (int i = 0; i < nparams; i++)
-		pqTraceOutputInt16(f, message, cursor);
+		pqTraceOutputInt16(buf, message, cursor);
 
-	nparams = pqTraceOutputInt16(f, message, cursor);
+	nparams = pqTraceOutputInt16(buf, message, cursor);
 
 	for (int i = 0; i < nparams; i++)
 	{
 		int			nbytes;
 
-		nbytes = pqTraceOutputInt32(f, message, cursor, false);
+		nbytes = pqTraceOutputInt32(buf, message, cursor, false);
 		if (nbytes == -1)
 			continue;
-		pqTraceOutputNchar(f, nbytes, message, cursor, false);
+		pqTraceOutputNchar(buf, nbytes, message, cursor, false);
 	}
 
-	nparams = pqTraceOutputInt16(f, message, cursor);
+	nparams = pqTraceOutputInt16(buf, message, cursor);
 	for (int i = 0; i < nparams; i++)
-		pqTraceOutputInt16(f, message, cursor);
+		pqTraceOutputInt16(buf, message, cursor);
 }
 
 static void
-pqTraceOutput_Close(FILE *f, const char *message, int *cursor)
+pqTraceOutput_Close(PQExpBuffer buf, const char *message, int *cursor)
 {
-	fprintf(f, "Close\t");
-	pqTraceOutputByte1(f, message, cursor);
-	pqTraceOutputString(f, message, cursor, false);
+	appendPQExpBufferStr(buf, "Close\t");
+	pqTraceOutputByte1(buf, message, cursor);
+	pqTraceOutputString(buf, message, cursor, false);
 }
 
 static void
-pqTraceOutput_CommandComplete(FILE *f, const char *message, int *cursor)
+pqTraceOutput_CommandComplete(PQExpBuffer buf, const char *message, int *cursor)
 {
-	fprintf(f, "CommandComplete\t");
-	pqTraceOutputString(f, message, cursor, false);
+	appendPQExpBufferStr(buf, "CommandComplete\t");
+	pqTraceOutputString(buf, message, cursor, false);
 }
 
 static void
-pqTraceOutput_CopyData(FILE *f, const char *message, int *cursor, int length,
+pqTraceOutput_CopyData(PQExpBuffer buf, const char *message, int *cursor, int length,
 					   bool suppress)
 {
-	fprintf(f, "CopyData\t");
-	pqTraceOutputNchar(f, length - *cursor + 1, message, cursor, suppress);
+	appendPQExpBufferStr(buf, "CopyData\t");
+	pqTraceOutputNchar(buf, length - *cursor + 1, message, cursor, suppress);
 }
 
 static void
-pqTraceOutput_DataRow(FILE *f, const char *message, int *cursor)
+pqTraceOutput_DataRow(PQExpBuffer buf, const char *message, int *cursor)
 {
 	int			nfields;
 	int			len;
 	int			i;
 
-	fprintf(f, "DataRow\t");
-	nfields = pqTraceOutputInt16(f, message, cursor);
+	appendPQExpBufferStr(buf, "DataRow\t");
+	nfields = pqTraceOutputInt16(buf, message, cursor);
 	for (i = 0; i < nfields; i++)
 	{
-		len = pqTraceOutputInt32(f, message, cursor, false);
+		len = pqTraceOutputInt32(buf, message, cursor, false);
 		if (len == -1)
 			continue;
-		pqTraceOutputNchar(f, len, message, cursor, false);
+		pqTraceOutputNchar(buf, len, message, cursor, false);
 	}
 }
 
 static void
-pqTraceOutput_Describe(FILE *f, const char *message, int *cursor)
+pqTraceOutput_Describe(PQExpBuffer buf, const char *message, int *cursor)
 {
-	fprintf(f, "Describe\t");
-	pqTraceOutputByte1(f, message, cursor);
-	pqTraceOutputString(f, message, cursor, false);
+	appendPQExpBufferStr(buf, "Describe\t");
+	pqTraceOutputByte1(buf, message, cursor);
+	pqTraceOutputString(buf, message, cursor, false);
 }
 
 /* shared code NoticeResponse / ErrorResponse */
 static void
-pqTraceOutputNR(FILE *f, const char *type, const char *message, int *cursor,
+pqTraceOutputNR(PQExpBuffer buf, const char *type, const char *message, int *cursor,
 				bool regress)
 {
-	fprintf(f, "%s\t", type);
+	appendPQExpBuffer(buf, "%s\t", type);
 	for (;;)
 	{
 		char		field;
 		bool		suppress;
 
-		pqTraceOutputByte1(f, message, cursor);
+		pqTraceOutputByte1(buf, message, cursor);
 		field = message[*cursor - 1];
 		if (field == '\0')
 			break;
 
 		suppress = regress && (field == 'L' || field == 'F' || field == 'R');
-		pqTraceOutputString(f, message, cursor, suppress);
+		pqTraceOutputString(buf, message, cursor, suppress);
 	}
 }
 
 static void
-pqTraceOutput_ErrorResponse(FILE *f, const char *message, int *cursor, bool regress)
+pqTraceOutput_ErrorResponse(PQExpBuffer buf, const char *message, int *cursor, bool regress)
 {
-	pqTraceOutputNR(f, "ErrorResponse", message, cursor, regress);
+	pqTraceOutputNR(buf, "ErrorResponse", message, cursor, regress);
 }
 
 static void
-pqTraceOutput_NoticeResponse(FILE *f, const char *message, int *cursor, bool regress)
+pqTraceOutput_NoticeResponse(PQExpBuffer buf, const char *message, int *cursor, bool regress)
 {
-	pqTraceOutputNR(f, "NoticeResponse", message, cursor, regress);
+	pqTraceOutputNR(buf, "NoticeResponse", message, cursor, regress);
 }
 
 static void
-pqTraceOutput_Execute(FILE *f, const char *message, int *cursor, bool regress)
+pqTraceOutput_Execute(PQExpBuffer buf, const char *message, int *cursor, bool regress)
 {
-	fprintf(f, "Execute\t");
-	pqTraceOutputString(f, message, cursor, false);
-	pqTraceOutputInt32(f, message, cursor, false);
+	appendPQExpBufferStr(buf, "Execute\t");
+	pqTraceOutputString(buf, message, cursor, false);
+	pqTraceOutputInt32(buf, message, cursor, false);
 }
 
 static void
-pqTraceOutput_CopyFail(FILE *f, const char *message, int *cursor)
+pqTraceOutput_CopyFail(PQExpBuffer buf, const char *message, int *cursor)
 {
-	fprintf(f, "CopyFail\t");
-	pqTraceOutputString(f, message, cursor, false);
+	appendPQExpBufferStr(buf, "CopyFail\t");
+	pqTraceOutputString(buf, message, cursor, false);
 }
 
 static void
-pqTraceOutput_GSSResponse(FILE *f, const char *message, int *cursor,
+pqTraceOutput_GSSResponse(PQExpBuffer buf, const char *message, int *cursor,
 						  int length, bool regress)
 {
-	fprintf(f, "GSSResponse\t");
-	pqTraceOutputNchar(f, length - *cursor + 1, message, cursor, regress);
+	appendPQExpBufferStr(buf, "GSSResponse\t");
+	pqTraceOutputNchar(buf, length - *cursor + 1, message, cursor, regress);
 }
 
 static void
-pqTraceOutput_PasswordMessage(FILE *f, const char *message, int *cursor)
+pqTraceOutput_PasswordMessage(PQExpBuffer buf, const char *message, int *cursor)
 {
-	fprintf(f, "PasswordMessage\t");
-	pqTraceOutputString(f, message, cursor, false);
+	appendPQExpBufferStr(buf, "PasswordMessage\t");
+	pqTraceOutputString(buf, message, cursor, false);
 }
 
 static void
-pqTraceOutput_SASLInitialResponse(FILE *f, const char *message, int *cursor,
+pqTraceOutput_SASLInitialResponse(PQExpBuffer buf, const char *message, int *cursor,
 								  bool regress)
 {
 	int			initialResponse;
 
-	fprintf(f, "SASLInitialResponse\t");
-	pqTraceOutputString(f, message, cursor, false);
-	initialResponse = pqTraceOutputInt32(f, message, cursor, false);
+	appendPQExpBufferStr(buf, "SASLInitialResponse\t");
+	pqTraceOutputString(buf, message, cursor, false);
+	initialResponse = pqTraceOutputInt32(buf, message, cursor, false);
 	if (initialResponse != -1)
-		pqTraceOutputNchar(f, initialResponse, message, cursor, regress);
+		pqTraceOutputNchar(buf, initialResponse, message, cursor, regress);
 }
 
 static void
-pqTraceOutput_SASLResponse(FILE *f, const char *message, int *cursor,
+pqTraceOutput_SASLResponse(PQExpBuffer buf, const char *message, int *cursor,
 						   int length, bool regress)
 {
-	fprintf(f, "SASLResponse\t");
-	pqTraceOutputNchar(f, length - *cursor + 1, message, cursor, regress);
+	appendPQExpBufferStr(buf, "SASLResponse\t");
+	pqTraceOutputNchar(buf, length - *cursor + 1, message, cursor, regress);
 }
 
 static void
-pqTraceOutput_FunctionCall(FILE *f, const char *message, int *cursor, bool regress)
+pqTraceOutput_FunctionCall(PQExpBuffer buf, const char *message, int *cursor, bool regress)
 {
 	int			nfields;
 	int			nbytes;
 
-	fprintf(f, "FunctionCall\t");
-	pqTraceOutputInt32(f, message, cursor, regress);
-	nfields = pqTraceOutputInt16(f, message, cursor);
+	appendPQExpBufferStr(buf, "FunctionCall\t");
+	pqTraceOutputInt32(buf, message, cursor, regress);
+	nfields = pqTraceOutputInt16(buf, message, cursor);
 
 	for (int i = 0; i < nfields; i++)
-		pqTraceOutputInt16(f, message, cursor);
+		pqTraceOutputInt16(buf, message, cursor);
 
-	nfields = pqTraceOutputInt16(f, message, cursor);
+	nfields = pqTraceOutputInt16(buf, message, cursor);
 
 	for (int i = 0; i < nfields; i++)
 	{
-		nbytes = pqTraceOutputInt32(f, message, cursor, false);
+		nbytes = pqTraceOutputInt32(buf, message, cursor, false);
 		if (nbytes == -1)
 			continue;
-		pqTraceOutputNchar(f, nbytes, message, cursor, false);
+		pqTraceOutputNchar(buf, nbytes, message, cursor, false);
 	}
 
-	pqTraceOutputInt16(f, message, cursor);
+	pqTraceOutputInt16(buf, message, cursor);
 }
 
 static void
-pqTraceOutput_CopyInResponse(FILE *f, const char *message, int *cursor)
+pqTraceOutput_CopyInResponse(PQExpBuffer buf, const char *message, int *cursor)
 {
 	int			nfields;
 
-	fprintf(f, "CopyInResponse\t");
-	pqTraceOutputByte1(f, message, cursor);
-	nfields = pqTraceOutputInt16(f, message, cursor);
+	appendPQExpBufferStr(buf, "CopyInResponse\t");
+	pqTraceOutputByte1(buf, message, cursor);
+	nfields = pqTraceOutputInt16(buf, message, cursor);
 
 	for (int i = 0; i < nfields; i++)
-		pqTraceOutputInt16(f, message, cursor);
+		pqTraceOutputInt16(buf, message, cursor);
 }
 
 static void
-pqTraceOutput_CopyOutResponse(FILE *f, const char *message, int *cursor)
+pqTraceOutput_CopyOutResponse(PQExpBuffer buf, const char *message, int *cursor)
 {
 	int			nfields;
 
-	fprintf(f, "CopyOutResponse\t");
-	pqTraceOutputByte1(f, message, cursor);
-	nfields = pqTraceOutputInt16(f, message, cursor);
+	appendPQExpBufferStr(buf, "CopyOutResponse\t");
+	pqTraceOutputByte1(buf, message, cursor);
+	nfields = pqTraceOutputInt16(buf, message, cursor);
 
 	for (int i = 0; i < nfields; i++)
-		pqTraceOutputInt16(f, message, cursor);
+		pqTraceOutputInt16(buf, message, cursor);
 }
 
 static void
-pqTraceOutput_BackendKeyData(FILE *f, const char *message, int *cursor, int length,
+pqTraceOutput_BackendKeyData(PQExpBuffer buf, const char *message, int *cursor, int length,
 							 bool regress)
 {
-	fprintf(f, "BackendKeyData\t");
-	pqTraceOutputInt32(f, message, cursor, regress);
-	pqTraceOutputNbyte(f, length - *cursor + 1, message, cursor, regress);
+	appendPQExpBufferStr(buf, "BackendKeyData\t");
+	pqTraceOutputInt32(buf, message, cursor, regress);
+	pqTraceOutputNbyte(buf, length - *cursor + 1, message, cursor, regress);
 }
 
 static void
-pqTraceOutput_Parse(FILE *f, const char *message, int *cursor, bool regress)
+pqTraceOutput_Parse(PQExpBuffer buf, const char *message, int *cursor, bool regress)
 {
 	int			nparams;
 
-	fprintf(f, "Parse\t");
-	pqTraceOutputString(f, message, cursor, false);
-	pqTraceOutputString(f, message, cursor, false);
-	nparams = pqTraceOutputInt16(f, message, cursor);
+	appendPQExpBufferStr(buf, "Parse\t");
+	pqTraceOutputString(buf, message, cursor, false);
+	pqTraceOutputString(buf, message, cursor, false);
+	nparams = pqTraceOutputInt16(buf, message, cursor);
 
 	for (int i = 0; i < nparams; i++)
-		pqTraceOutputInt32(f, message, cursor, regress);
+		pqTraceOutputInt32(buf, message, cursor, regress);
 }
 
 static void
-pqTraceOutput_Query(FILE *f, const char *message, int *cursor)
+pqTraceOutput_Query(PQExpBuffer buf, const char *message, int *cursor)
 {
-	fprintf(f, "Query\t");
-	pqTraceOutputString(f, message, cursor, false);
+	appendPQExpBufferStr(buf, "Query\t");
+	pqTraceOutputString(buf, message, cursor, false);
 }
 
 static void
-pqTraceOutput_Authentication(FILE *f, const char *message, int *cursor,
+pqTraceOutput_Authentication(PQExpBuffer buf, const char *message, int *cursor,
 							 int length, bool suppress)
 {
 	int			authType = 0;
@@ -520,129 +517,129 @@ pqTraceOutput_Authentication(FILE *f, const char *message, int *cursor,
 	switch (authType)
 	{
 		case AUTH_REQ_OK:
-			fprintf(f, "AuthenticationOk");
+			appendPQExpBufferStr(buf, "AuthenticationOk");
 			break;
 			/* AUTH_REQ_KRB4 not supported */
 			/* AUTH_REQ_KRB5 not supported */
 		case AUTH_REQ_PASSWORD:
-			fprintf(f, "AuthenticationCleartextPassword");
+			appendPQExpBufferStr(buf, "AuthenticationCleartextPassword");
 			break;
 			/* AUTH_REQ_CRYPT not supported */
 		case AUTH_REQ_MD5:
-			fprintf(f, "AuthenticationMD5Password");
+			appendPQExpBufferStr(buf, "AuthenticationMD5Password");
 			break;
 		case AUTH_REQ_GSS:
-			fprintf(f, "AuthenticationGSS");
+			appendPQExpBufferStr(buf, "AuthenticationGSS");
 			break;
 		case AUTH_REQ_GSS_CONT:
-			fprintf(f, "AuthenticationGSSContinue\t");
-			pqTraceOutputNchar(f, length - *cursor + 1, message, cursor,
+			appendPQExpBufferStr(buf, "AuthenticationGSSContinue\t");
+			pqTraceOutputNchar(buf, length - *cursor + 1, message, cursor,
 							   suppress);
 			break;
 		case AUTH_REQ_SSPI:
-			fprintf(f, "AuthenticationSSPI");
+			appendPQExpBufferStr(buf, "AuthenticationSSPI");
 			break;
 		case AUTH_REQ_SASL:
-			fprintf(f, "AuthenticationSASL\t");
+			appendPQExpBufferStr(buf, "AuthenticationSASL\t");
 			while (message[*cursor] != '\0')
-				pqTraceOutputString(f, message, cursor, false);
-			pqTraceOutputString(f, message, cursor, false);
+				pqTraceOutputString(buf, message, cursor, false);
+			pqTraceOutputString(buf, message, cursor, false);
 			break;
 		case AUTH_REQ_SASL_CONT:
-			fprintf(f, "AuthenticationSASLContinue\t");
-			pqTraceOutputNchar(f, length - *cursor + 1, message, cursor,
+			appendPQExpBufferStr(buf, "AuthenticationSASLContinue\t");
+			pqTraceOutputNchar(buf, length - *cursor + 1, message, cursor,
 							   suppress);
 			break;
 		case AUTH_REQ_SASL_FIN:
-			fprintf(f, "AuthenticationSASLFinal\t");
-			pqTraceOutputNchar(f, length - *cursor + 1, message, cursor,
+			appendPQExpBufferStr(buf, "AuthenticationSASLFinal\t");
+			pqTraceOutputNchar(buf, length - *cursor + 1, message, cursor,
 							   suppress);
 			break;
 		default:
-			fprintf(f, "Unknown authentication message %d", authType);
+			appendPQExpBuffer(buf, "Unknown authentication message %d", authType);
 	}
 }
 
 static void
-pqTraceOutput_ParameterStatus(FILE *f, const char *message, int *cursor, bool regress)
+pqTraceOutput_ParameterStatus(PQExpBuffer buf, const char *message, int *cursor, bool regress)
 {
-	fprintf(f, "ParameterStatus\t");
-	pqTraceOutputString(f, message, cursor, false);
-	pqTraceOutputString(f, message, cursor, regress);
+	appendPQExpBufferStr(buf, "ParameterStatus\t");
+	pqTraceOutputString(buf, message, cursor, false);
+	pqTraceOutputString(buf, message, cursor, regress);
 }
 
 static void
-pqTraceOutput_ParameterDescription(FILE *f, const char *message, int *cursor, bool regress)
+pqTraceOutput_ParameterDescription(PQExpBuffer buf, const char *message, int *cursor, bool regress)
 {
 	int			nfields;
 
-	fprintf(f, "ParameterDescription\t");
-	nfields = pqTraceOutputInt16(f, message, cursor);
+	appendPQExpBufferStr(buf, "ParameterDescription\t");
+	nfields = pqTraceOutputInt16(buf, message, cursor);
 
 	for (int i = 0; i < nfields; i++)
-		pqTraceOutputInt32(f, message, cursor, regress);
+		pqTraceOutputInt32(buf, message, cursor, regress);
 }
 
 static void
-pqTraceOutput_RowDescription(FILE *f, const char *message, int *cursor, bool regress)
+pqTraceOutput_RowDescription(PQExpBuffer buf, const char *message, int *cursor, bool regress)
 {
 	int			nfields;
 
-	fprintf(f, "RowDescription\t");
-	nfields = pqTraceOutputInt16(f, message, cursor);
+	appendPQExpBufferStr(buf, "RowDescription\t");
+	nfields = pqTraceOutputInt16(buf, message, cursor);
 
 	for (int i = 0; i < nfields; i++)
 	{
-		pqTraceOutputString(f, message, cursor, false);
-		pqTraceOutputInt32(f, message, cursor, regress);
-		pqTraceOutputInt16(f, message, cursor);
-		pqTraceOutputInt32(f, message, cursor, regress);
-		pqTraceOutputInt16(f, message, cursor);
-		pqTraceOutputInt32(f, message, cursor, false);
-		pqTraceOutputInt16(f, message, cursor);
+		pqTraceOutputString(buf, message, cursor, false);
+		pqTraceOutputInt32(buf, message, cursor, regress);
+		pqTraceOutputInt16(buf, message, cursor);
+		pqTraceOutputInt32(buf, message, cursor, regress);
+		pqTraceOutputInt16(buf, message, cursor);
+		pqTraceOutputInt32(buf, message, cursor, false);
+		pqTraceOutputInt16(buf, message, cursor);
 	}
 }
 
 static void
-pqTraceOutput_NegotiateProtocolVersion(FILE *f, const char *message, int *cursor)
+pqTraceOutput_NegotiateProtocolVersion(PQExpBuffer buf, const char *message, int *cursor)
 {
 	int			nparams;
 
-	fprintf(f, "NegotiateProtocolVersion\t");
-	pqTraceOutputInt32(f, message, cursor, false);
-	nparams = pqTraceOutputInt32(f, message, cursor, false);
+	appendPQExpBufferStr(buf, "NegotiateProtocolVersion\t");
+	pqTraceOutputInt32(buf, message, cursor, false);
+	nparams = pqTraceOutputInt32(buf, message, cursor, false);
 	for (int i = 0; i < nparams; i++)
 	{
-		pqTraceOutputString(f, message, cursor, false);
+		pqTraceOutputString(buf, message, cursor, false);
 	}
 }
 
 static void
-pqTraceOutput_FunctionCallResponse(FILE *f, const char *message, int *cursor)
+pqTraceOutput_FunctionCallResponse(PQExpBuffer buf, const char *message, int *cursor)
 {
 	int			len;
 
-	fprintf(f, "FunctionCallResponse\t");
-	len = pqTraceOutputInt32(f, message, cursor, false);
+	appendPQExpBufferStr(buf, "FunctionCallResponse\t");
+	len = pqTraceOutputInt32(buf, message, cursor, false);
 	if (len != -1)
-		pqTraceOutputNchar(f, len, message, cursor, false);
+		pqTraceOutputNchar(buf, len, message, cursor, false);
 }
 
 static void
-pqTraceOutput_CopyBothResponse(FILE *f, const char *message, int *cursor, int length)
+pqTraceOutput_CopyBothResponse(PQExpBuffer buf, const char *message, int *cursor, int length)
 {
-	fprintf(f, "CopyBothResponse\t");
-	pqTraceOutputByte1(f, message, cursor);
+	appendPQExpBufferStr(buf, "CopyBothResponse\t");
+	pqTraceOutputByte1(buf, message, cursor);
 
 	while (length > *cursor)
-		pqTraceOutputInt16(f, message, cursor);
+		pqTraceOutputInt16(buf, message, cursor);
 }
 
 static void
-pqTraceOutput_ReadyForQuery(FILE *f, const char *message, int *cursor)
+pqTraceOutput_ReadyForQuery(PQExpBuffer buf, const char *message, int *cursor)
 {
-	fprintf(f, "ReadyForQuery\t");
-	pqTraceOutputByte1(f, message, cursor);
+	appendPQExpBufferStr(buf, "ReadyForQuery\t");
+	pqTraceOutputByte1(buf, message, cursor);
 }
 
 /*
@@ -656,13 +653,16 @@ pqTraceOutputMessage(PGconn *conn, const char *message, bool toServer)
 	char	   *prefix = toServer ? "F" : "B";
 	int			logCursor = 0;
 	bool		regress;
+	PQExpBufferData buf;
+
+	initPQExpBuffer(&buf);
 
 	if ((conn->traceFlags & PQTRACE_SUPPRESS_TIMESTAMPS) == 0)
 	{
 		char		timestr[128];
 
 		pqTraceFormatTimestamp(timestr, sizeof(timestr));
-		fprintf(conn->Pfdebug, "%s\t", timestr);
+		appendPQExpBuffer(&buf, "%s\t", timestr);
 	}
 	regress = (conn->traceFlags & PQTRACE_REGRESS_MODE) != 0;
 
@@ -683,64 +683,64 @@ pqTraceOutputMessage(PGconn *conn, const char *message, bool toServer)
 	if (regress && !toServer && (id == PqMsg_ErrorResponse
 								 || id == PqMsg_NoticeResponse
 								 || id == PqMsg_ParameterStatus))
-		fprintf(conn->Pfdebug, "%s\tNN\t", prefix);
+		appendPQExpBuffer(&buf, "%s\tNN\t", prefix);
 	else
-		fprintf(conn->Pfdebug, "%s\t%d\t", prefix, length);
+		appendPQExpBuffer(&buf, "%s\t%d\t", prefix, length);
 
 	switch (id)
 	{
 		case PqMsg_ParseComplete:
-			fprintf(conn->Pfdebug, "ParseComplete");
+			appendPQExpBufferStr(&buf, "ParseComplete");
 			/* No message content */
 			break;
 		case PqMsg_BindComplete:
-			fprintf(conn->Pfdebug, "BindComplete");
+			appendPQExpBufferStr(&buf, "BindComplete");
 			/* No message content */
 			break;
 		case PqMsg_CloseComplete:
-			fprintf(conn->Pfdebug, "CloseComplete");
+			appendPQExpBufferStr(&buf, "CloseComplete");
 			/* No message content */
 			break;
 		case PqMsg_NotificationResponse:
-			pqTraceOutput_NotificationResponse(conn->Pfdebug, message, &logCursor, regress);
+			pqTraceOutput_NotificationResponse(&buf, message, &logCursor, regress);
 			break;
 		case PqMsg_Bind:
-			pqTraceOutput_Bind(conn->Pfdebug, message, &logCursor);
+			pqTraceOutput_Bind(&buf, message, &logCursor);
 			break;
 		case PqMsg_CopyDone:
-			fprintf(conn->Pfdebug, "CopyDone");
+			appendPQExpBufferStr(&buf, "CopyDone");
 			/* No message content */
 			break;
 		case PqMsg_CommandComplete:
 			/* Close(F) and CommandComplete(B) use the same identifier. */
 			Assert(PqMsg_Close == PqMsg_CommandComplete);
 			if (toServer)
-				pqTraceOutput_Close(conn->Pfdebug, message, &logCursor);
+				pqTraceOutput_Close(&buf, message, &logCursor);
 			else
-				pqTraceOutput_CommandComplete(conn->Pfdebug, message, &logCursor);
+				pqTraceOutput_CommandComplete(&buf, message, &logCursor);
 			break;
 		case PqMsg_CopyData:
-			pqTraceOutput_CopyData(conn->Pfdebug, message, &logCursor,
+			pqTraceOutput_CopyData(&buf, message, &logCursor,
 								   length, regress);
 			break;
 		case PqMsg_Describe:
 			/* Describe(F) and DataRow(B) use the same identifier. */
 			Assert(PqMsg_Describe == PqMsg_DataRow);
 			if (toServer)
-				pqTraceOutput_Describe(conn->Pfdebug, message, &logCursor);
+				pqTraceOutput_Describe(&buf, message, &logCursor);
 			else
-				pqTraceOutput_DataRow(conn->Pfdebug, message, &logCursor);
+				pqTraceOutput_DataRow(&buf, message, &logCursor);
 			break;
 		case PqMsg_Execute:
 			/* Execute(F) and ErrorResponse(B) use the same identifier. */
 			Assert(PqMsg_Execute == PqMsg_ErrorResponse);
 			if (toServer)
-				pqTraceOutput_Execute(conn->Pfdebug, message, &logCursor, regress);
+				pqTraceOutput_Execute(&buf, message, &logCursor, regress);
 			else
-				pqTraceOutput_ErrorResponse(conn->Pfdebug, message, &logCursor, regress);
+				pqTraceOutput_ErrorResponse(&buf, message, &logCursor, regress);
 			break;
 		case PqMsg_CopyFail:
-			pqTraceOutput_CopyFail(conn->Pfdebug, message, &logCursor);
+			pqTraceOutput_CopyFail(&buf, message, &logCursor);
 			break;
 		case PqMsg_GSSResponse:
 			Assert(PqMsg_GSSResponse == PqMsg_PasswordMessage);
@@ -754,106 +754,106 @@ pqTraceOutputMessage(PGconn *conn, const char *message, bool toServer)
 			switch (conn->current_auth_response)
 			{
 				case AUTH_RESPONSE_GSS:
-					pqTraceOutput_GSSResponse(conn->Pfdebug, message,
+					pqTraceOutput_GSSResponse(&buf, message,
 											  &logCursor, length, regress);
 					break;
 				case AUTH_RESPONSE_PASSWORD:
-					pqTraceOutput_PasswordMessage(conn->Pfdebug, message,
+					pqTraceOutput_PasswordMessage(&buf, message,
 												  &logCursor);
 					break;
 				case AUTH_RESPONSE_SASL_INITIAL:
-					pqTraceOutput_SASLInitialResponse(conn->Pfdebug, message,
+					pqTraceOutput_SASLInitialResponse(&buf, message,
 													  &logCursor, regress);
 					break;
 				case AUTH_RESPONSE_SASL:
-					pqTraceOutput_SASLResponse(conn->Pfdebug, message,
+					pqTraceOutput_SASLResponse(&buf, message,
 											   &logCursor, length, regress);
 					break;
 				default:
-					fprintf(conn->Pfdebug, "UnknownAuthenticationResponse");
+					appendPQExpBufferStr(&buf, "UnknownAuthenticationResponse");
 					break;
 			}
 			conn->current_auth_response = '\0';
 			break;
 		case PqMsg_FunctionCall:
-			pqTraceOutput_FunctionCall(conn->Pfdebug, message, &logCursor, regress);
+			pqTraceOutput_FunctionCall(&buf, message, &logCursor, regress);
 			break;
 		case PqMsg_CopyInResponse:
-			pqTraceOutput_CopyInResponse(conn->Pfdebug, message, &logCursor);
+			pqTraceOutput_CopyInResponse(&buf, message, &logCursor);
 			break;
 		case PqMsg_Flush:
 			/* Flush(F) and CopyOutResponse(B) use the same identifier */
 			Assert(PqMsg_CopyOutResponse == PqMsg_Flush);
 			if (toServer)
-				fprintf(conn->Pfdebug, "Flush");	/* no message content */
+				appendPQExpBufferStr(&buf, "Flush");	/* no message content */
 			else
-				pqTraceOutput_CopyOutResponse(conn->Pfdebug, message, &logCursor);
+				pqTraceOutput_CopyOutResponse(&buf, message, &logCursor);
 			break;
 		case PqMsg_EmptyQueryResponse:
-			fprintf(conn->Pfdebug, "EmptyQueryResponse");
+			appendPQExpBufferStr(&buf, "EmptyQueryResponse");
 			/* No message content */
 			break;
 		case PqMsg_BackendKeyData:
-			pqTraceOutput_BackendKeyData(conn->Pfdebug, message, &logCursor,
+			pqTraceOutput_BackendKeyData(&buf, message, &logCursor,
 										 length, regress);
 			break;
 		case PqMsg_NoData:
-			fprintf(conn->Pfdebug, "NoData");
+			appendPQExpBufferStr(&buf, "NoData");
 			/* No message content */
 			break;
 		case PqMsg_NoticeResponse:
-			pqTraceOutput_NoticeResponse(conn->Pfdebug, message, &logCursor, regress);
+			pqTraceOutput_NoticeResponse(&buf, message, &logCursor, regress);
 			break;
 		case PqMsg_Parse:
-			pqTraceOutput_Parse(conn->Pfdebug, message, &logCursor, regress);
+			pqTraceOutput_Parse(&buf, message, &logCursor, regress);
 			break;
 		case PqMsg_Query:
-			pqTraceOutput_Query(conn->Pfdebug, message, &logCursor);
+			pqTraceOutput_Query(&buf, message, &logCursor);
 			break;
 		case PqMsg_AuthenticationRequest:
-			pqTraceOutput_Authentication(conn->Pfdebug, message, &logCursor,
+			pqTraceOutput_Authentication(&buf, message, &logCursor,
 										 length, regress);
 			break;
 		case PqMsg_PortalSuspended:
-			fprintf(conn->Pfdebug, "PortalSuspended");
+			appendPQExpBufferStr(&buf, "PortalSuspended");
 			/* No message content */
 			break;
 		case PqMsg_Sync:
 			/* ParameterStatus(B) and Sync(F) use the same identifier */
 			Assert(PqMsg_ParameterStatus == PqMsg_Sync);
 			if (toServer)
-				fprintf(conn->Pfdebug, "Sync"); /* no message content */
+				appendPQExpBufferStr(&buf, "Sync"); /* no message content */
 			else
-				pqTraceOutput_ParameterStatus(conn->Pfdebug, message, &logCursor, regress);
+				pqTraceOutput_ParameterStatus(&buf, message, &logCursor, regress);
 			break;
 		case PqMsg_ParameterDescription:
-			pqTraceOutput_ParameterDescription(conn->Pfdebug, message, &logCursor, regress);
+			pqTraceOutput_ParameterDescription(&buf, message, &logCursor, regress);
 			break;
 		case PqMsg_RowDescription:
-			pqTraceOutput_RowDescription(conn->Pfdebug, message, &logCursor, regress);
+			pqTraceOutput_RowDescription(&buf, message, &logCursor, regress);
 			break;
 		case PqMsg_NegotiateProtocolVersion:
-			pqTraceOutput_NegotiateProtocolVersion(conn->Pfdebug, message, &logCursor);
+			pqTraceOutput_NegotiateProtocolVersion(&buf, message, &logCursor);
 			break;
 		case PqMsg_FunctionCallResponse:
-			pqTraceOutput_FunctionCallResponse(conn->Pfdebug, message, &logCursor);
+			pqTraceOutput_FunctionCallResponse(&buf, message, &logCursor);
 			break;
 		case PqMsg_CopyBothResponse:
-			pqTraceOutput_CopyBothResponse(conn->Pfdebug, message, &logCursor, length);
+			pqTraceOutput_CopyBothResponse(&buf, message, &logCursor, length);
 			break;
 		case PqMsg_Terminate:
-			fprintf(conn->Pfdebug, "Terminate");
+			appendPQExpBufferStr(&buf, "Terminate");
 			/* No message content */
 			break;
 		case PqMsg_ReadyForQuery:
-			pqTraceOutput_ReadyForQuery(conn->Pfdebug, message, &logCursor);
+			pqTraceOutput_ReadyForQuery(&buf, message, &logCursor);
 			break;
 		default:
-			fprintf(conn->Pfdebug, "Unknown message: %02x", id);
+			appendPQExpBuffer(&buf, "Unknown message: %02x", id);
 			break;
 	}
 
-	fputc('\n', conn->Pfdebug);
+	appendPQExpBufferChar(&buf, '\n');
 
 	/*
 	 * Verify the printing routine did it right.  Note that the one-byte
@@ -861,9 +861,12 @@ pqTraceOutputMessage(PGconn *conn, const char *message, bool toServer)
 	 * include it.
 	 */
 	if (logCursor - 1 != length)
-		fprintf(conn->Pfdebug,
-				"mismatched message length: consumed %d, expected %d\n",
-				logCursor - 1, length);
+		appendPQExpBuffer(&buf,
+						  "mismatched message length: consumed %d, expected %d\n",
+						  logCursor - 1, length);
+
+	fwrite(buf.data, 1, buf.len, conn->Pfdebug);
+	termPQExpBuffer(&buf);
 }
 
 /*
@@ -877,7 +880,9 @@ pqTraceOutputNoTypeByteMessage(PGconn *conn, const char *message)
 	int			version;
 	bool		regress;
 	int			logCursor = 0;
+	PQExpBufferData buf;
 
+	initPQExpBuffer(&buf);
 	regress = (conn->traceFlags & PQTRACE_REGRESS_MODE) != 0;
 
 	if ((conn->traceFlags & PQTRACE_SUPPRESS_TIMESTAMPS) == 0)
@@ -885,7 +890,7 @@ pqTraceOutputNoTypeByteMessage(PGconn *conn, const char *message)
 		char		timestr[128];
 
 		pqTraceFormatTimestamp(timestr, sizeof(timestr));
-		fprintf(conn->Pfdebug, "%s\t", timestr);
+		appendPQExpBuffer(&buf, "%s\t", timestr);
 	}
 
 	memcpy(&length, message + logCursor, 4);
@@ -894,7 +899,9 @@ pqTraceOutputNoTypeByteMessage(PGconn *conn, const char *message)
 
 	if (length < 8)
 	{
-		fprintf(conn->Pfdebug, "F\t%d\tUnknown message\n", length);
+		appendPQExpBuffer(&buf, "F\t%d\tUnknown message\n", length);
+		fprintf(conn->Pfdebug, "%s", buf.data);
+		termPQExpBuffer(&buf);
 		return;
 	}
 
@@ -909,40 +916,40 @@ pqTraceOutputNoTypeByteMessage(PGconn *conn, const char *message)
 	if (regress && (version != CANCEL_REQUEST_CODE
 					&& version != NEGOTIATE_SSL_CODE
 					&& version != NEGOTIATE_GSS_CODE))
-		fprintf(conn->Pfdebug, "F\tNN\t");
+		appendPQExpBufferStr(&buf, "F\tNN\t");
 	else
-		fprintf(conn->Pfdebug, "F\t%d\t", length);
+		appendPQExpBuffer(&buf, "F\t%d\t", length);
 
 	if (version == CANCEL_REQUEST_CODE && length >= 16)
 	{
-		fprintf(conn->Pfdebug, "CancelRequest\t");
-		pqTraceOutputInt16(conn->Pfdebug, message, &logCursor);
-		pqTraceOutputInt16(conn->Pfdebug, message, &logCursor);
-		pqTraceOutputInt32(conn->Pfdebug, message, &logCursor, regress);
-		pqTraceOutputNbyte(conn->Pfdebug, length - logCursor, message,
+		appendPQExpBufferStr(&buf, "CancelRequest\t");
+		pqTraceOutputInt16(&buf, message, &logCursor);
+		pqTraceOutputInt16(&buf, message, &logCursor);
+		pqTraceOutputInt32(&buf, message, &logCursor, regress);
+		pqTraceOutputNbyte(&buf, length - logCursor, message,
 						   &logCursor, regress);
 	}
 	else if (version == NEGOTIATE_SSL_CODE)
 	{
-		fprintf(conn->Pfdebug, "SSLRequest\t");
-		pqTraceOutputInt16(conn->Pfdebug, message, &logCursor);
-		pqTraceOutputInt16(conn->Pfdebug, message, &logCursor);
+		appendPQExpBufferStr(&buf, "SSLRequest\t");
+		pqTraceOutputInt16(&buf, message, &logCursor);
+		pqTraceOutputInt16(&buf, message, &logCursor);
 	}
 	else if (version == NEGOTIATE_GSS_CODE)
 	{
-		fprintf(conn->Pfdebug, "GSSENCRequest\t");
-		pqTraceOutputInt16(conn->Pfdebug, message, &logCursor);
-		pqTraceOutputInt16(conn->Pfdebug, message, &logCursor);
+		appendPQExpBufferStr(&buf, "GSSENCRequest\t");
+		pqTraceOutputInt16(&buf, message, &logCursor);
+		pqTraceOutputInt16(&buf, message, &logCursor);
 	}
 	else
 	{
-		fprintf(conn->Pfdebug, "StartupMessage\t");
-		pqTraceOutputInt16(conn->Pfdebug, message, &logCursor);
-		pqTraceOutputInt16(conn->Pfdebug, message, &logCursor);
+		appendPQExpBufferStr(&buf, "StartupMessage\t");
+		pqTraceOutputInt16(&buf, message, &logCursor);
+		pqTraceOutputInt16(&buf, message, &logCursor);
 		while (message[logCursor] != '\0')
 		{
-			pqTraceOutputString(conn->Pfdebug, message, &logCursor, false);
-			pqTraceOutputString(conn->Pfdebug, message, &logCursor, regress);
+			pqTraceOutputString(&buf, message, &logCursor, false);
+			pqTraceOutputString(&buf, message, &logCursor, regress);
 		}
 
 		/*
@@ -952,16 +959,19 @@ pqTraceOutputNoTypeByteMessage(PGconn *conn, const char *message)
 		logCursor++;
 	}
 
-	fputc('\n', conn->Pfdebug);
+	appendPQExpBufferChar(&buf, '\n');
 
 	/*
 	 * Verify the printing routine did it right. There's no one-byte message
 	 * identifier here, so logCursor should match the length
 	 */
 	if (logCursor != length)
-		fprintf(conn->Pfdebug,
-				"mismatched message length: consumed %d, expected %d\n",
-				logCursor, length);
+		appendPQExpBuffer(&buf,
+						  "mismatched message length: consumed %d, expected %d\n",
+						  logCursor, length);
+
+	fwrite(buf.data, 1, buf.len, conn->Pfdebug);
+	termPQExpBuffer(&buf);
 }
 
 /*
@@ -973,13 +983,20 @@ void
 pqTraceOutputCharResponse(PGconn *conn, const char *responseType,
 						  char response)
 {
+	PQExpBufferData buf;
+
+	initPQExpBuffer(&buf);
+
 	if ((conn->traceFlags & PQTRACE_SUPPRESS_TIMESTAMPS) == 0)
 	{
 		char		timestr[128];
 
 		pqTraceFormatTimestamp(timestr, sizeof(timestr));
-		fprintf(conn->Pfdebug, "%s\t", timestr);
+		appendPQExpBuffer(&buf, "%s\t", timestr);
 	}
 
-	fprintf(conn->Pfdebug, "B\t1\t%s\t %c\n", responseType, response);
+	appendPQExpBuffer(&buf, "B\t1\t%s\t %c\n", responseType, response);
+
+	fprintf(conn->Pfdebug, "%s", buf.data);
+	termPQExpBuffer(&buf);
 }
-- 
2.55.0

