From 7f36381318cbafaa97f43d88a13c3b67125bec79 Mon Sep 17 00:00:00 2001
From: Anthonin Bonnefoy <anthonin.bonnefoy@datadoghq.com>
Date: Wed, 8 Jul 2026 10:19:22 +0200
Subject: Add more regress masks in pqtrace

Tracing ParameterStatus or StartupMessage will write parameter values
like username, which is very likely to change depending on the test
environment.

This patch adds additional regress masking, hiding the parameter values
and the packet length of ParameterStatus and StartupMessage to allow
tests to compare traces capturing such messages.

Additionally, a "mismatched length" warning was added to
pqTraceOutputNoTypeByteMessage, to mirror what's done in
pqTraceOutputMessage.
---
 src/interfaces/libpq/fe-trace.c | 52 +++++++++++++++++++++++++--------
 1 file changed, 40 insertions(+), 12 deletions(-)

diff --git a/src/interfaces/libpq/fe-trace.c b/src/interfaces/libpq/fe-trace.c
index 741be09bdd3..e136c77e6b0 100644
--- a/src/interfaces/libpq/fe-trace.c
+++ b/src/interfaces/libpq/fe-trace.c
@@ -564,11 +564,11 @@ pqTraceOutput_Authentication(FILE *f, const char *message, int *cursor,
 }
 
 static void
-pqTraceOutput_ParameterStatus(FILE *f, const char *message, int *cursor)
+pqTraceOutput_ParameterStatus(FILE *f, const char *message, int *cursor, bool regress)
 {
 	fprintf(f, "ParameterStatus\t");
 	pqTraceOutputString(f, message, cursor, false);
-	pqTraceOutputString(f, message, cursor, false);
+	pqTraceOutputString(f, message, cursor, regress);
 }
 
 static void
@@ -673,12 +673,16 @@ pqTraceOutputMessage(PGconn *conn, const char *message, bool toServer)
 	logCursor += 4;
 
 	/*
-	 * In regress mode, suppress the length of ErrorResponse and
-	 * NoticeResponse.  The F (file name), L (line number) and R (routine
+	 * In regress mode, suppress the length of ErrorResponse, NoticeResponse
+	 * and ParameterStatus. The F (file name), L (line number) and R (routine
 	 * name) fields can change as server code is modified, and if their
-	 * lengths differ from the originals, that would break tests.
+	 * lengths differ from the originals, that would break tests. For
+	 * ParameterStatus, the size changes depending on the parameters' value,
+	 * whose values depend on the test environment.
 	 */
-	if (regress && !toServer && (id == PqMsg_ErrorResponse || id == PqMsg_NoticeResponse))
+	if (regress && !toServer && (id == PqMsg_ErrorResponse
+								 || id == PqMsg_NoticeResponse
+								 || id == PqMsg_ParameterStatus))
 		fprintf(conn->Pfdebug, "%s\tNN\t", prefix);
 	else
 		fprintf(conn->Pfdebug, "%s\t%d\t", prefix, length);
@@ -820,7 +824,7 @@ pqTraceOutputMessage(PGconn *conn, const char *message, bool toServer)
 			if (toServer)
 				fprintf(conn->Pfdebug, "Sync"); /* no message content */
 			else
-				pqTraceOutput_ParameterStatus(conn->Pfdebug, message, &logCursor);
+				pqTraceOutput_ParameterStatus(conn->Pfdebug, message, &logCursor, regress);
 			break;
 		case PqMsg_ParameterDescription:
 			pqTraceOutput_ParameterDescription(conn->Pfdebug, message, &logCursor, regress);
@@ -888,17 +892,27 @@ pqTraceOutputNoTypeByteMessage(PGconn *conn, const char *message)
 	length = (int) pg_ntoh32(length);
 	logCursor += 4;
 
-	fprintf(conn->Pfdebug, "F\t%d\t", length);
-
 	if (length < 8)
 	{
-		fprintf(conn->Pfdebug, "Unknown message\n");
+		fprintf(conn->Pfdebug, "F\t%d\tUnknown message\n", length);
 		return;
 	}
 
 	memcpy(&version, message + logCursor, 4);
 	version = (int) pg_ntoh32(version);
 
+	/*
+	 * In regress, suppress the length of StartupMessage. The parameter values
+	 * depend on the test environment, so the test may break depending on
+	 * where it's executed.
+	 */
+	if (regress && (version != CANCEL_REQUEST_CODE
+					&& version != NEGOTIATE_SSL_CODE
+					&& version != NEGOTIATE_GSS_CODE))
+		fprintf(conn->Pfdebug, "F\tNN\t");
+	else
+		fprintf(conn->Pfdebug, "F\t%d\t", length);
+
 	if (version == CANCEL_REQUEST_CODE && length >= 16)
 	{
 		fprintf(conn->Pfdebug, "CancelRequest\t");
@@ -927,13 +941,27 @@ pqTraceOutputNoTypeByteMessage(PGconn *conn, const char *message)
 		pqTraceOutputInt16(conn->Pfdebug, message, &logCursor);
 		while (message[logCursor] != '\0')
 		{
-			/* XXX should we suppress anything in regress mode? */
-			pqTraceOutputString(conn->Pfdebug, message, &logCursor, false);
 			pqTraceOutputString(conn->Pfdebug, message, &logCursor, false);
+			pqTraceOutputString(conn->Pfdebug, message, &logCursor, regress);
 		}
+
+		/*
+		 * Startup messages end with a trailing terminator, advance our cursor
+		 * to include it
+		 */
+		logCursor++;
 	}
 
 	fputc('\n', conn->Pfdebug);
+
+	/*
+	 * 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);
 }
 
 /*
-- 
2.54.0

