From f239da90f069b19e1c2fa6b69bc7c6fd4be826b9 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Fri, 26 Mar 2021 11:10:52 -0300
Subject: [PATCH v30 1/7] libpq trace v29

---
 doc/src/sgml/libpq.sgml             |  39 +-
 src/interfaces/libpq/Makefile       |   1 +
 src/interfaces/libpq/exports.txt    |   1 +
 src/interfaces/libpq/fe-connect.c   |  21 -
 src/interfaces/libpq/fe-exec.c      |   4 -
 src/interfaces/libpq/fe-misc.c      |  66 +--
 src/interfaces/libpq/fe-protocol3.c |   8 +
 src/interfaces/libpq/libpq-fe.h     |   2 +
 src/interfaces/libpq/libpq-int.h    |   7 +
 src/interfaces/libpq/libpq-trace.c  | 717 ++++++++++++++++++++++++++++
 10 files changed, 789 insertions(+), 77 deletions(-)
 create mode 100644 src/interfaces/libpq/libpq-trace.c

diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index be674fbaa9..838e394d54 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -6459,12 +6459,27 @@ PGContextVisibility PQsetErrorContextVisibility(PGconn *conn, PGContextVisibilit
 
     <listitem>
      <para>
-      Enables  tracing of the client/server communication to a debugging file stream.
+      Enables tracing of the client/server communication to a debugging file
+      stream.
 <synopsis>
 void PQtrace(PGconn *conn, FILE *stream);
 </synopsis>
      </para>
 
+     <para>
+      Each line consists of: an optional timestamp, a direction indicator
+      (<literal>&gt;</literal> for messages from client to server
+      or <literal>&lt;</literal> for messages from server to client),
+      message length, message type, and message contents.
+      Non-message contents fields (timestamp, direction, length and message type)
+      are separated by a tab. Message contents are separated by a space.
+      Protocol strings are enclosed in double quotes, while strings used as data
+      values are enclosed in single quotes.  Non-printable chars are printed as
+      hexadecimal escapes.
+      Further message-type-specific detail can be found in
+      <xref linkend="protocol-message-formats"/>.
+     </para>
+
      <note>
       <para>
        On Windows, if the <application>libpq</application> library and an application are
@@ -6479,6 +6494,28 @@ void PQtrace(PGconn *conn, FILE *stream);
     </listitem>
    </varlistentry>
 
+   <varlistentry id="libpq-PQtraceSetFlags">
+    <term><function>PQtraceSetFlags</function><indexterm><primary>PQtraceSetFlags</primary></indexterm></term>
+
+    <listitem>
+     <para>
+      Controls the tracing behavior of client/server communication.
+<synopsis>
+void PQtraceSetFlags(PGconn *conn, int flags);
+</synopsis>
+     </para>
+
+     <para>
+      <literal>flags</literal> contains flag bits describing the operating mode
+      of tracing.
+      If <literal>flags</literal> contains <literal>PQTRACE_SUPPRESS_TIMESTAMPS</literal>,
+      then the timestamp is not included when printing each message.
+      This function must be called after calling <function>PQtrace</function>.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
    <varlistentry id="libpq-PQuntrace">
     <term><function>PQuntrace</function><indexterm><primary>PQuntrace</primary></indexterm></term>
 
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index 2aca882a2b..0424523492 100644
--- a/src/interfaces/libpq/Makefile
+++ b/src/interfaces/libpq/Makefile
@@ -41,6 +41,7 @@ OBJS = \
 	fe-secure.o \
 	legacy-pqsignal.o \
 	libpq-events.o \
+	libpq-trace.o \
 	pqexpbuffer.o \
 	fe-auth.o
 
diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt
index 5c48c14191..a00701f2c5 100644
--- a/src/interfaces/libpq/exports.txt
+++ b/src/interfaces/libpq/exports.txt
@@ -183,3 +183,4 @@ PQenterPipelineMode       180
 PQexitPipelineMode        181
 PQpipelineSync            182
 PQpipelineStatus          183
+PQtraceSetFlags           184
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 53b354abb2..a90bdb8ab6 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -6859,27 +6859,6 @@ PQsetErrorContextVisibility(PGconn *conn, PGContextVisibility show_context)
 	return old;
 }
 
-void
-PQtrace(PGconn *conn, FILE *debug_port)
-{
-	if (conn == NULL)
-		return;
-	PQuntrace(conn);
-	conn->Pfdebug = debug_port;
-}
-
-void
-PQuntrace(PGconn *conn)
-{
-	if (conn == NULL)
-		return;
-	if (conn->Pfdebug)
-	{
-		fflush(conn->Pfdebug);
-		conn->Pfdebug = NULL;
-	}
-}
-
 PQnoticeReceiver
 PQsetNoticeReceiver(PGconn *conn, PQnoticeReceiver proc, void *arg)
 {
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index f143b8b7fb..03592bdce9 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -970,10 +970,6 @@ pqSaveParameterStatus(PGconn *conn, const char *name, const char *value)
 	pgParameterStatus *pstatus;
 	pgParameterStatus *prev;
 
-	if (conn->Pfdebug)
-		fprintf(conn->Pfdebug, "pqSaveParameterStatus: '%s' = '%s'\n",
-				name, value);
-
 	/*
 	 * Forget any old information about the parameter
 	 */
diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c
index ce2d24b91f..54d6a76246 100644
--- a/src/interfaces/libpq/fe-misc.c
+++ b/src/interfaces/libpq/fe-misc.c
@@ -84,9 +84,6 @@ pqGetc(char *result, PGconn *conn)
 
 	*result = conn->inBuffer[conn->inCursor++];
 
-	if (conn->Pfdebug)
-		fprintf(conn->Pfdebug, "From backend> %c\n", *result);
-
 	return 0;
 }
 
@@ -100,9 +97,6 @@ pqPutc(char c, PGconn *conn)
 	if (pqPutMsgBytes(&c, 1, conn))
 		return EOF;
 
-	if (conn->Pfdebug)
-		fprintf(conn->Pfdebug, "To backend> %c\n", c);
-
 	return 0;
 }
 
@@ -138,10 +132,6 @@ pqGets_internal(PQExpBuffer buf, PGconn *conn, bool resetbuffer)
 
 	conn->inCursor = ++inCursor;
 
-	if (conn->Pfdebug)
-		fprintf(conn->Pfdebug, "From backend> \"%s\"\n",
-				buf->data);
-
 	return 0;
 }
 
@@ -167,9 +157,6 @@ pqPuts(const char *s, PGconn *conn)
 	if (pqPutMsgBytes(s, strlen(s) + 1, conn))
 		return EOF;
 
-	if (conn->Pfdebug)
-		fprintf(conn->Pfdebug, "To backend> \"%s\"\n", s);
-
 	return 0;
 }
 
@@ -188,13 +175,6 @@ pqGetnchar(char *s, size_t len, PGconn *conn)
 
 	conn->inCursor += len;
 
-	if (conn->Pfdebug)
-	{
-		fprintf(conn->Pfdebug, "From backend (%lu)> ", (unsigned long) len);
-		fwrite(s, 1, len, conn->Pfdebug);
-		fprintf(conn->Pfdebug, "\n");
-	}
-
 	return 0;
 }
 
@@ -212,13 +192,6 @@ pqSkipnchar(size_t len, PGconn *conn)
 	if (len > (size_t) (conn->inEnd - conn->inCursor))
 		return EOF;
 
-	if (conn->Pfdebug)
-	{
-		fprintf(conn->Pfdebug, "From backend (%lu)> ", (unsigned long) len);
-		fwrite(conn->inBuffer + conn->inCursor, 1, len, conn->Pfdebug);
-		fprintf(conn->Pfdebug, "\n");
-	}
-
 	conn->inCursor += len;
 
 	return 0;
@@ -234,13 +207,6 @@ pqPutnchar(const char *s, size_t len, PGconn *conn)
 	if (pqPutMsgBytes(s, len, conn))
 		return EOF;
 
-	if (conn->Pfdebug)
-	{
-		fprintf(conn->Pfdebug, "To backend> ");
-		fwrite(s, 1, len, conn->Pfdebug);
-		fprintf(conn->Pfdebug, "\n");
-	}
-
 	return 0;
 }
 
@@ -278,9 +244,6 @@ pqGetInt(int *result, size_t bytes, PGconn *conn)
 			return EOF;
 	}
 
-	if (conn->Pfdebug)
-		fprintf(conn->Pfdebug, "From backend (#%lu)> %d\n", (unsigned long) bytes, *result);
-
 	return 0;
 }
 
@@ -314,9 +277,6 @@ pqPutInt(int value, size_t bytes, PGconn *conn)
 			return EOF;
 	}
 
-	if (conn->Pfdebug)
-		fprintf(conn->Pfdebug, "To backend (%lu#)> %d\n", (unsigned long) bytes, value);
-
 	return 0;
 }
 
@@ -525,10 +485,6 @@ pqPutMsgStart(char msg_type, PGconn *conn)
 	conn->outMsgEnd = endPos;
 	/* length word, if needed, will be filled in by pqPutMsgEnd */
 
-	if (conn->Pfdebug)
-		fprintf(conn->Pfdebug, "To backend> Msg %c\n",
-				msg_type ? msg_type : ' ');
-
 	return 0;
 }
 
@@ -563,10 +519,6 @@ pqPutMsgBytes(const void *buf, size_t len, PGconn *conn)
 int
 pqPutMsgEnd(PGconn *conn)
 {
-	if (conn->Pfdebug)
-		fprintf(conn->Pfdebug, "To backend> Msg complete, length %u\n",
-				conn->outMsgEnd - conn->outCount);
-
 	/* Fill in length word if needed */
 	if (conn->outMsgStart >= 0)
 	{
@@ -576,6 +528,16 @@ pqPutMsgEnd(PGconn *conn)
 		memcpy(conn->outBuffer + conn->outMsgStart, &msgLen, 4);
 	}
 
+	/* Trace message only when there is first 1 byte */
+	if (conn->Pfdebug)
+	{
+		if (conn->outCount < conn->outMsgStart)
+			pqTraceOutputMessage(conn, conn->outBuffer + conn->outCount, true);
+		else
+			pqTraceOutputNoTypeByteMessage(conn,
+										conn->outBuffer + conn->outMsgStart);
+	}
+
 	/* Make message eligible to send */
 	conn->outCount = conn->outMsgEnd;
 
@@ -1002,11 +964,13 @@ pqSendSome(PGconn *conn, int len)
 int
 pqFlush(PGconn *conn)
 {
-	if (conn->Pfdebug)
-		fflush(conn->Pfdebug);
-
 	if (conn->outCount > 0)
+	{
+		if (conn->Pfdebug)
+			fflush(conn->Pfdebug);
+
 		return pqSendSome(conn, conn->outCount);
+	}
 
 	return 0;
 }
diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
index 306e89acfd..de77c06874 100644
--- a/src/interfaces/libpq/fe-protocol3.c
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -457,6 +457,9 @@ pqParseInput3(PGconn *conn)
 		/* Successfully consumed this message */
 		if (conn->inCursor == conn->inStart + 5 + msgLength)
 		{
+			if(conn->Pfdebug)
+				pqTraceOutputMessage(conn, conn->inBuffer + conn->inStart, false);
+
 			/* Normal case: parsing agrees with specified length */
 			conn->inStart = conn->inCursor;
 		}
@@ -1660,6 +1663,9 @@ getCopyDataMessage(PGconn *conn)
 				return -1;
 		}
 
+		if(conn->Pfdebug)
+			pqTraceOutputMessage(conn, conn->inBuffer + conn->inStart, false);
+
 		/* Drop the processed message and loop around for another */
 		conn->inStart = conn->inCursor;
 	}
@@ -2121,6 +2127,8 @@ pqFunctionCall3(PGconn *conn, Oid fnid,
 		}
 		/* Completed this message, keep going */
 		/* trust the specified message length as what to skip */
+		if(conn->Pfdebug)
+			pqTraceOutputMessage(conn, conn->inBuffer + conn->inStart, false);
 		conn->inStart += 5 + msgLength;
 		needInput = false;
 	}
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index cee42d4843..d0decde2da 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -376,7 +376,9 @@ extern PGContextVisibility PQsetErrorContextVisibility(PGconn *conn,
 													   PGContextVisibility show_context);
 
 /* Enable/disable tracing */
+#define PQTRACE_SUPPRESS_TIMESTAMPS		1
 extern void PQtrace(PGconn *conn, FILE *debug_port);
+extern void PQtraceSetFlags(PGconn *conn, int flags);
 extern void PQuntrace(PGconn *conn);
 
 /* Override default notice handling routines */
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 6374ec657a..1a1d0e1414 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -394,6 +394,7 @@ struct pg_conn
 
 	/* Optional file to write trace info to */
 	FILE	   *Pfdebug;
+	int			traceFlags;
 
 	/* Callback procedures for notice message processing */
 	PGNoticeHooks noticeHooks;
@@ -818,6 +819,12 @@ extern ssize_t pg_GSS_write(PGconn *conn, const void *ptr, size_t len);
 extern ssize_t pg_GSS_read(PGconn *conn, void *ptr, size_t len);
 #endif
 
+/* === in libpq-trace.c === */
+
+extern void pqTraceOutputMessage(PGconn *conn, const char *message,
+															bool toServer);
+extern void pqTraceOutputNoTypeByteMessage(PGconn *conn, const char *message);
+
 /* === miscellaneous macros === */
 
 /*
diff --git a/src/interfaces/libpq/libpq-trace.c b/src/interfaces/libpq/libpq-trace.c
new file mode 100644
index 0000000000..bacb7903b9
--- /dev/null
+++ b/src/interfaces/libpq/libpq-trace.c
@@ -0,0 +1,717 @@
+/*-------------------------------------------------------------------------
+ *
+ *	libpq-trace.c
+ *	  functions for libpq protocol tracing
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/interfaces/libpq/libpq-trace.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres_fe.h"
+
+#include <limits.h>
+#include <time.h>
+
+#ifdef WIN32
+#include "win32.h"
+#else
+#include <unistd.h>
+#include <sys/time.h>
+#endif
+
+#include "libpq-fe.h"
+#include "libpq-int.h"
+#include "pgtime.h"
+#include "port/pg_bswap.h"
+
+void
+PQtrace(PGconn *conn, FILE *debug_port)
+{
+	if (conn == NULL)
+		return;
+	PQuntrace(conn);
+	if (debug_port == NULL)
+		return;
+
+	setvbuf(debug_port, NULL, _IOLBF, 0);
+	conn->Pfdebug = debug_port;
+	conn->traceFlags = 0;
+}
+
+void
+PQuntrace(PGconn *conn)
+{
+	if (conn == NULL)
+		return;
+	if (conn->Pfdebug)
+	{
+		fflush(conn->Pfdebug);
+		conn->Pfdebug = NULL;
+	}
+
+	conn->traceFlags = 0;
+}
+
+void
+PQtraceSetFlags(PGconn *conn, int flags)
+{
+	if (conn == NULL)
+		return;
+	/* If PQtrace() failed, do nothing. */
+	if (conn->Pfdebug == NULL)
+		return;
+	conn->traceFlags = flags;
+}
+
+/*
+ * Print the current time, with microseconds, into a caller-supplied
+ * buffer.
+ * Cribbed from setup_formatted_log_time, but much simpler.
+ */
+static void
+pqTraceFormatTimestamp(char *timestr, size_t ts_len)
+{
+	struct timeval tval;
+	pg_time_t	stamp_time;
+
+	gettimeofday(&tval, NULL);
+	stamp_time = (pg_time_t) tval.tv_sec;
+
+	strftime(timestr, ts_len,
+			 "%Y-%m-%d %H:%M:%S",
+			 localtime(&stamp_time));
+	/* append microseconds */
+	sprintf(timestr + strlen(timestr), ".%06d", (int) (tval.tv_usec));
+}
+
+/*
+ *   pqTraceOutputByte1: output 1 char message to the log
+ */
+static void
+pqTraceOutputByte1(const char *data, int *cursor, FILE *pfdebug)
+{
+	const char *v = data + *cursor;
+
+	/*
+	 * Show non-printable data in hex format, including the
+	 * terminating \0 that completes ErrorResponse and NoticeResponse
+	 * messages.
+	 */
+	if (!isprint(*v))
+		fprintf(pfdebug, " \\x%02x", *v);
+	else
+		fprintf(pfdebug, " %c", *v);
+	++*cursor;
+}
+
+/*
+ *   pqTraceOutputInt16: output a 2-byte integer message to the log
+ */
+static int
+pqTraceOutputInt16(const char *data, int *cursor, FILE *pfdebug)
+{
+	uint16		tmp;
+	int			result;
+
+	memcpy(&tmp, data + *cursor , 2);
+	*cursor += 2;
+	result = (int) pg_ntoh16(tmp);
+	fprintf(pfdebug, " %d", result);
+
+	return result;
+}
+
+/*
+ *   pqTraceOutputInt32: output a 4-byte integer message to the log
+ */
+static int
+pqTraceOutputInt32(const char *data, int *cursor, FILE *pfdebug)
+{
+	int			result;
+
+	memcpy(&result, data + *cursor, 4);
+	*cursor += 4;
+	result = (int) pg_ntoh32(result);
+	fprintf(pfdebug, " %d", result);
+
+	return result;
+}
+
+/*
+ *   pqTraceOutputString: output a string message to the log
+ */
+static void
+pqTraceOutputString(const char *data, int *cursor, FILE *pfdebug)
+{
+	int	len;
+
+	len = fprintf(pfdebug, " \"%s\"", data + *cursor);
+
+	/*
+	 * This is null-terminated string. So add 1 after subtracting 3
+	 * which is the double quotes and space length from len.
+	 */
+	*cursor += (len - 3 + 1);
+}
+
+/*
+ * pqTraceOutputNchar: output a string of exactly len bytes message to the log
+ */
+static void
+pqTraceOutputNchar(const char *data, int *cursor, FILE *pfdebug, int len)
+{
+	int			i,
+				next;			/* first char not yet printed */
+	const char	*v = data + *cursor;
+
+	fprintf(pfdebug, " \'");
+
+	for (next = i = 0; i < len; ++i)
+	{
+		if (isprint(v[i]))
+			continue;
+		else
+		{
+			fwrite(v + next, 1, i - next, pfdebug);
+			fprintf(pfdebug, "\\x%02x", v[i]);
+			next = i + 1;
+		}
+	}
+	if (next < len)
+		fwrite(v + next, 1, len - next, pfdebug);
+
+	fprintf(pfdebug, "\'");
+	*cursor += len;
+}
+
+/*
+ * Output functions by protocol message type
+ */
+
+/* Authentication */
+static void
+pqTraceOutputR(const char *message, FILE *f)
+{
+	int	cursor = 0;
+
+	fprintf(f, "Authentication\t");
+	pqTraceOutputInt32(message, &cursor, f);
+}
+
+/* BackendKeyData */
+static void
+pqTraceOutputK(const char *message, FILE *f)
+{
+	int	cursor = 0;
+
+	fprintf(f, "BackendKeyData\t");
+	pqTraceOutputInt32(message, &cursor, f);
+	pqTraceOutputInt32(message, &cursor, f);
+}
+
+/* Bind */
+static void
+pqTraceOutputB(const char *message, int end, FILE *f)
+{
+	int cursor = 0;
+	int nparams;
+	int nbytes;
+	int i;
+
+	fprintf(f, "Bind\t");
+	pqTraceOutputString(message, &cursor, f);
+	pqTraceOutputString(message, &cursor, f);
+	nparams = pqTraceOutputInt16(message, &cursor, f);
+
+	for (i = 0; i < nparams; i++)
+		pqTraceOutputInt16(message, &cursor, f);
+
+	nparams = pqTraceOutputInt16(message, &cursor, f);
+
+	for (i = 0; i < nparams; i++)
+	{
+		nbytes = pqTraceOutputInt32(message, &cursor, f);
+		if (nbytes == -1)
+			continue;
+		pqTraceOutputNchar(message, &cursor, f, nbytes);
+	}
+
+	nparams = pqTraceOutputInt16(message, &cursor, f);
+	for (i = 0; i < nparams; i++)
+		pqTraceOutputInt16(message, &cursor, f);
+}
+
+/* Close(F) or CommandComplete(B) */
+static void
+pqTraceOutputC(const char *message, int end, FILE *f, bool toServer)
+{
+	int	cursor = 0;
+
+	if (toServer)
+	{
+		fprintf(f, "Close\t");
+		pqTraceOutputByte1(message, &cursor, f);
+		pqTraceOutputString(message, &cursor, f);
+	}
+	else
+	{
+		fprintf(f, "CommandComplete\t");
+		pqTraceOutputString(message, &cursor, f);
+	}
+}
+
+/* CopyFail */
+static void
+pqTraceOutputf(const char *message, int end, FILE *f)
+{
+	int	cursor = 0;
+
+	fprintf(f, "CopyFail\t");
+	pqTraceOutputString(message, &cursor, f);
+}
+
+/* CopyInResponse */
+static void
+pqTraceOutputG(const char *message, int end, FILE *f)
+{
+	int	cursor = 0;
+	int	nfields;
+	int	i;
+
+	fprintf(f, "CopyInResponse\t");
+	pqTraceOutputByte1(message, &cursor, f);
+	nfields = pqTraceOutputInt16(message, &cursor, f);
+
+	for (i = 0; i < nfields; i++)
+		pqTraceOutputInt16(message, &cursor, f);
+}
+
+/* CopyOutResponse */
+static void
+pqTraceOutputH(const char *message, int end, FILE *f)
+{
+	int	cursor = 0;
+	int	nfields;
+	int	i;
+
+	fprintf(f, "CopyOutResponse\t");
+	pqTraceOutputByte1(message, &cursor, f);
+	nfields = pqTraceOutputInt16(message, &cursor, f);
+
+	for (i = 0; i < nfields; i++)
+		pqTraceOutputInt16(message, &cursor, f);
+}
+
+/* CopyBothResponse */
+static void
+pqTraceOutputW(const char *message, int end, FILE *f)
+{
+	int	cursor = 0;
+
+	fprintf(f, "CopyBothResponse\t");
+	pqTraceOutputByte1(message, &cursor, f);
+
+	while (end > cursor)
+		pqTraceOutputInt16(message, &cursor, f);
+}
+
+/* Describe(F) or DataRow(B) */
+static void
+pqTraceOutputD(const char *message, int end, FILE *f, bool toServer)
+{
+	int	cursor = 0;
+
+	if (toServer)
+	{
+		fprintf(f, "Describe\t");
+		pqTraceOutputByte1(message, &cursor, f);
+		pqTraceOutputString(message, &cursor, f);
+	}
+	else
+	{
+		int		nfields;
+		int		len;
+		int		i;
+
+		fprintf(f, "DataRow\t");
+		nfields = pqTraceOutputInt16(message, &cursor, f);
+		for (i = 0; i < nfields; i++)
+		{
+			len = pqTraceOutputInt32(message, &cursor, f);
+			if (len == -1)
+				continue;
+			pqTraceOutputNchar(message, &cursor, f, len);
+		}
+	}
+}
+
+/* Execute(F) or ErrorResponse(B) */
+static void
+pqTraceOutputE(const char *message, int end, FILE *f, bool toServer)
+{
+	int	cursor = 0;
+
+	if (toServer)
+	{
+		fprintf(f, "Execute\t");
+		pqTraceOutputString(message, &cursor, f);
+		pqTraceOutputInt32(message, &cursor, f);
+	}
+	else
+	{
+		fprintf(f, "ErrorResponse\t");
+		while (end > cursor)
+		{
+			pqTraceOutputByte1(message, &cursor, f);
+			if (message[cursor] == '\0')
+				continue;
+			pqTraceOutputString(message, &cursor, f);
+		}
+	}
+}
+
+/* FunctionCall */
+static void
+pqTraceOutputF(const char *message, FILE *f)
+{
+	int	cursor = 0;
+	int nfields;
+	int nbytes;
+	int	i;
+
+	fprintf(f, "FunctionCall\t");
+	pqTraceOutputInt32(message, &cursor, f);
+	nfields = pqTraceOutputInt16(message, &cursor, f);
+
+	for (i = 0; i < nfields; i++)
+		pqTraceOutputInt16(message, &cursor, f);
+
+	nfields = pqTraceOutputInt16(message, &cursor, f);
+
+	for (i = 0; i < nfields; i++)
+	{
+		nbytes = pqTraceOutputInt32(message, &cursor, f);
+		if (nbytes == -1)
+			continue;
+		pqTraceOutputNchar(message, &cursor, f, nbytes);
+	}
+
+	pqTraceOutputInt16(message, &cursor, f);
+}
+
+/* FunctionCallResponse */
+static void
+pqTraceOutputV(const char *message, FILE *f)
+{
+	int	cursor = 0;
+	int		len;
+
+	fprintf(f, "FunctionCallResponse\t");
+	len = pqTraceOutputInt32(message, &cursor, f);
+	if (len != -1)
+		pqTraceOutputNchar(message, &cursor, f, len);
+}
+
+/* NegotiateProtocolVersion */
+static void
+pqTraceOutputv(const char *message, FILE *f)
+{
+	int	cursor = 0;
+
+	fprintf(f, "NegotiateProtocolVersion\t");
+	pqTraceOutputInt32(message, &cursor, f);
+	pqTraceOutputInt32(message, &cursor, f);
+}
+
+/* NoticeResponse */
+static void
+pqTraceOutputN(const char *message, int end, FILE *f)
+{
+	int	cursor = 0;
+
+	fprintf(f, "NoticeResponse\t");
+	while (end > cursor)
+	{
+		pqTraceOutputByte1(message, &cursor, f);
+		if (message[cursor] == '\0')
+			continue;
+		pqTraceOutputString(message, &cursor, f);
+	}
+}
+
+/* NotificationResponse */
+static void
+pqTraceOutputA(const char *message, int end, FILE *f)
+{
+	int	cursor = 0;
+
+	fprintf(f, "NotificationResponse\t");
+	pqTraceOutputInt32(message, &cursor, f);
+	pqTraceOutputString(message, &cursor, f);
+	pqTraceOutputString(message, &cursor, f);
+}
+
+/* ParameterDescription */
+static void
+pqTraceOutputt(const char *message, FILE *f)
+{
+	int	cursor = 0;
+	int	nfields;
+	int	i;
+
+	fprintf(f, "ParameterDescription\t");
+	nfields = pqTraceOutputInt16(message, &cursor, f);
+
+	for (i = 0; i < nfields; i++)
+		pqTraceOutputInt32(message, &cursor, f);
+}
+
+/* ParameterStatus */
+static void
+pqTraceOutputS(const char *message, int end, FILE *f)
+{
+	int	cursor = 0;
+
+	fprintf(f, "ParameterStatus\t");
+	pqTraceOutputString(message, &cursor, f);
+	pqTraceOutputString(message, &cursor, f);
+}
+
+/* Parse */
+static void
+pqTraceOutputP(const char *message, int end, FILE *f)
+{
+	int	cursor = 0;
+	int nparams;
+	int i;
+
+	fprintf(f, "Parse\t");
+	pqTraceOutputString(message, &cursor, f);
+	pqTraceOutputString(message, &cursor, f);
+	nparams = pqTraceOutputInt16(message, &cursor, f);
+
+	for (i = 0; i < nparams; i++)
+		pqTraceOutputInt32(message, &cursor, f);
+}
+
+/* Query */
+static void
+pqTraceOutputQ(const char *message, int end, FILE *f)
+{
+	int	cursor = 0;
+
+	fprintf(f, "Query\t");
+	pqTraceOutputString(message, &cursor, f);
+}
+
+/* ReadyForQuery */
+static void
+pqTraceOutputZ(const char *message, FILE *f)
+{
+	int	cursor = 0;
+
+	fprintf(f, "ReadyForQuery\t");
+	pqTraceOutputByte1(message, &cursor, f);
+}
+
+/* RowDescription */
+static void
+pqTraceOutputT(const char *message, int end, FILE *f)
+{
+	int	cursor = 0;
+	int nfields;
+	int	i;
+
+	fprintf(f, "RowDescription\t");
+	nfields = pqTraceOutputInt16(message, &cursor, f);
+
+	for (i = 0; i < nfields; i++)
+	{
+		pqTraceOutputString(message, &cursor, f);
+		pqTraceOutputInt32(message, &cursor, f);
+		pqTraceOutputInt16(message, &cursor, f);
+		pqTraceOutputInt32(message, &cursor, f);
+		pqTraceOutputInt16(message, &cursor, f);
+		pqTraceOutputInt32(message, &cursor, f);
+		pqTraceOutputInt16(message, &cursor, f);
+	}
+}
+
+void
+pqTraceOutputMessage(PGconn *conn, const char *message, bool toServer)
+{
+	char		timestr[128];
+	char 		id;
+	int			length;
+	char	   *prefix = toServer ? ">" : "<";
+	int			LogCursor = 0;
+	int			LogEnd;
+
+	if ((conn->traceFlags & PQTRACE_SUPPRESS_TIMESTAMPS) == 0)
+		pqTraceFormatTimestamp(timestr, sizeof(timestr));
+	else
+		timestr[0] = '\0';
+
+	id = message[LogCursor++];
+
+	memcpy(&length, message + LogCursor , 4);
+	length = (int) pg_ntoh32(length);
+	LogCursor += 4;
+	LogEnd = length - 4;
+
+	fprintf(conn->Pfdebug, "%s\t%s\t%d\t", timestr, prefix, length);
+
+	switch(id)
+	{
+		case 'R':	/* Authentication */
+			pqTraceOutputR(message + LogCursor, conn->Pfdebug);
+			break;
+		case 'K':	/* secret key data from the backend */
+			pqTraceOutputK(message + LogCursor, conn->Pfdebug);
+			break;
+		case 'B':	/* Bind */
+			pqTraceOutputB(message + LogCursor, LogEnd, conn->Pfdebug);
+			break;
+		case 'C':	/* Close(F) or Command Complete(B) */
+			pqTraceOutputC(message + LogCursor, LogEnd, conn->Pfdebug, toServer);
+			break;
+		case 'd':	/* Copy Data */
+			/* Drop COPY data to reduce the overhead of logging. */
+			break;
+		case 'f':	/* Copy Fail */
+			pqTraceOutputf(message + LogCursor, LogEnd, conn->Pfdebug);
+			break;
+		case 'G':	/* Start Copy In */
+			pqTraceOutputG(message + LogCursor, LogEnd, conn->Pfdebug);
+			break;
+		case 'H':	/* Flush(F) or Start Copy Out(B) */
+			if (!toServer)
+				pqTraceOutputH(message + LogCursor, LogEnd, conn->Pfdebug);
+			else
+				fprintf(conn->Pfdebug, "Flush");
+			break;
+		case 'W':	/* Start Copy Both */
+			pqTraceOutputW(message + LogCursor, LogEnd, conn->Pfdebug);
+			break;
+		case 'D':	/* Describe(F) or Data Row(B) */
+			pqTraceOutputD(message + LogCursor, LogEnd, conn->Pfdebug, toServer);
+			break;
+		case 'E':	/* Execute(F) or Error Response(B) */
+			pqTraceOutputE(message + LogCursor, LogEnd, conn->Pfdebug, toServer);
+			break;
+		case 'F':	/* Function Call */
+			pqTraceOutputF(message + LogCursor, conn->Pfdebug);
+			break;
+		case 'V':	/* Function Call response */
+			pqTraceOutputV(message + LogCursor, conn->Pfdebug);
+			break;
+		case 'v':	/* Negotiate Protocol Version */
+			pqTraceOutputv(message + LogCursor, conn->Pfdebug);
+			break;
+		case 'N':	/* Notice Response */
+			pqTraceOutputN(message + LogCursor, LogEnd, conn->Pfdebug);
+			break;
+		case 'A':	/* Notification Response */
+			pqTraceOutputA(message + LogCursor, LogEnd, conn->Pfdebug);
+			break;
+		case 't':	/* Parameter Description */
+			pqTraceOutputt(message + LogCursor, conn->Pfdebug);
+			break;
+		case 'S':	/* Parameter Status(B) or Sync(F) */
+			if (!toServer)
+				pqTraceOutputS(message + LogCursor, LogEnd, conn->Pfdebug);
+			else
+				fprintf(conn->Pfdebug, "Sync");
+			break;
+		case 'P':	/* Parse */
+			pqTraceOutputP(message + LogCursor, LogEnd, conn->Pfdebug);
+			break;
+		case 'Q':	/* Query */
+			pqTraceOutputQ(message + LogCursor, LogEnd, conn->Pfdebug);
+			break;
+		case 'Z':	/* Ready For Query */
+			pqTraceOutputZ(message + LogCursor, conn->Pfdebug);
+			break;
+		case 'T':	/* Row Description */
+			pqTraceOutputT(message + LogCursor, LogEnd, conn->Pfdebug);
+			break;
+		case '2':	/* Bind Complete */
+			fprintf(conn->Pfdebug, "BindComplete");
+			/* No message content */
+			break;
+		case '3':	/* Close Complete */
+			fprintf(conn->Pfdebug, "CloseComplete");
+			/* No message content */
+			break;
+		case 'c':	/* Copy Done */
+			fprintf(conn->Pfdebug, "CopyDone");
+			/* No message content */
+			break;
+		case 'I':	/* Empty Query Response */
+			fprintf(conn->Pfdebug, "EmptyQueryResponse");
+			/* No message content */
+			break;
+		case 'n':	/* No Data */
+			fprintf(conn->Pfdebug, "NoData");
+			/* No message content */
+			break;
+		case '1':	/* Parse Complete */
+			fprintf(conn->Pfdebug, "ParseComplete");
+			/* No message content */
+			break;
+		case 's':	/* Portal Suspended */
+			fprintf(conn->Pfdebug, "PortalSuspended");
+			/* No message content */
+			break;
+		case 'X':	/* Terminate */
+			fprintf(conn->Pfdebug, "Terminate");
+			/* No message content */
+			break;
+		default:
+			fprintf(conn->Pfdebug, "Unknown message: %02x", id);
+			break;
+	}
+
+	fputc('\n', conn->Pfdebug);
+}
+
+void
+pqTraceOutputNoTypeByteMessage(PGconn *conn, const char *message)
+{
+	char		timestr[128];
+	int			length;
+	int			LogCursor = 0;
+
+	if ((conn->traceFlags & PQTRACE_SUPPRESS_TIMESTAMPS) == 0)
+		pqTraceFormatTimestamp(timestr, sizeof(timestr));
+	else
+		timestr[0] = '\0';
+
+	memcpy(&length, message + LogCursor , 4);
+	length = (int) pg_ntoh32(length);
+	LogCursor += 4;
+
+	fprintf(conn->Pfdebug, "%s\t>\t%d\t", timestr, length);
+
+	switch(length)
+	{
+		case 16:	/* CancelRequest */
+			fprintf(conn->Pfdebug, "CancelRequest\t");
+			pqTraceOutputInt32(message, &LogCursor, conn->Pfdebug);
+			pqTraceOutputInt32(message, &LogCursor, conn->Pfdebug);
+			pqTraceOutputInt32(message, &LogCursor, conn->Pfdebug);
+			break;
+		case 8 :	/* GSSENCRequest or SSLRequest */
+			/* These messages do not reach here. */
+		default:
+			fprintf(conn->Pfdebug, "Unknown message: length is %d", length);
+			break;
+	}
+
+	fputc('\n', conn->Pfdebug);
+}
-- 
2.20.1

