v24-libpq-trace-log.patch

application/octet-stream

Filename: v24-libpq-trace-log.patch
Type: application/octet-stream
Part: 0
Message: RE: libpq debug log
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 910e9a8..eb0c588 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -5941,12 +5941,28 @@ 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.
+      Only available when protocol version 3 or higher is used.
 <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,
+      and <literal>&lt;</literal> for messages from server to client),
+      message type, message length, and message contents.
+      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.
+      For numerical quantities, 16-bit values are preceded with <literal>#</literal>
+      and 32-bit values are printed without a prefix.
+      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
@@ -5961,6 +5977,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 2aca882..2311399 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
 
@@ -113,6 +114,7 @@ install: all installdirs install-lib
 	$(INSTALL_DATA) $(srcdir)/libpq-fe.h '$(DESTDIR)$(includedir)'
 	$(INSTALL_DATA) $(srcdir)/libpq-events.h '$(DESTDIR)$(includedir)'
 	$(INSTALL_DATA) $(srcdir)/libpq-int.h '$(DESTDIR)$(includedir_internal)'
+	$(INSTALL_DATA) $(srcdir)/libpq-trace.h '$(DESTDIR)$(includedir_internal)'
 	$(INSTALL_DATA) $(srcdir)/pqexpbuffer.h '$(DESTDIR)$(includedir_internal)'
 	$(INSTALL_DATA) $(srcdir)/pg_service.conf.sample '$(DESTDIR)$(datadir)/pg_service.conf.sample'
 
@@ -126,6 +128,7 @@ uninstall: uninstall-lib
 	rm -f '$(DESTDIR)$(includedir)/libpq-fe.h'
 	rm -f '$(DESTDIR)$(includedir)/libpq-events.h'
 	rm -f '$(DESTDIR)$(includedir_internal)/libpq-int.h'
+	rm -f '$(DESTDIR)$(includedir_internal)/libpq-trace.h'
 	rm -f '$(DESTDIR)$(includedir_internal)/pqexpbuffer.h'
 	rm -f '$(DESTDIR)$(datadir)/pg_service.conf.sample'
 
diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt
index bbc1f90..09f1111 100644
--- a/src/interfaces/libpq/exports.txt
+++ b/src/interfaces/libpq/exports.txt
@@ -179,3 +179,4 @@ PQgetgssctx               176
 PQsetSSLKeyPassHook_OpenSSL         177
 PQgetSSLKeyPassHook_OpenSSL         178
 PQdefaultSSLKeyPassHook_OpenSSL     179
+PQtraceSetFlags           180
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 29054ba..c711669 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -6810,27 +6810,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 9a03804..5009e98 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -966,10 +966,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 ce2d24b..bfa730f 100644
--- a/src/interfaces/libpq/fe-misc.c
+++ b/src/interfaces/libpq/fe-misc.c
@@ -49,6 +49,7 @@
 
 #include "libpq-fe.h"
 #include "libpq-int.h"
+#include "libpq-trace.h"
 #include "mb/pg_wchar.h"
 #include "pg_config_paths.h"
 #include "port/pg_bswap.h"
@@ -84,9 +85,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 +98,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 +133,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 +158,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 +176,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 +193,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 +208,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 +245,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 +278,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 +486,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 +520,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 +529,10 @@ pqPutMsgEnd(PGconn *conn)
 		memcpy(conn->outBuffer + conn->outMsgStart, &msgLen, 4);
 	}
 
+	if(conn->Pfdebug)
+		pqTraceOutputMsg(conn, conn->outMsgStart, MSGDIR_FROM_FRONTEND);
+
+
 	/* Make message eligible to send */
 	conn->outCount = conn->outMsgEnd;
 
@@ -1002,11 +959,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 2ca8c05..07b4b3b 100644
--- a/src/interfaces/libpq/fe-protocol3.c
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -28,6 +28,7 @@
 
 #include "libpq-fe.h"
 #include "libpq-int.h"
+#include "libpq-trace.h"
 #include "mb/pg_wchar.h"
 #include "port/pg_bswap.h"
 
@@ -421,6 +422,9 @@ pqParseInput3(PGconn *conn)
 		{
 			/* Normal case: parsing agrees with specified length */
 			conn->inStart = conn->inCursor;
+
+			if(conn->Pfdebug)
+				pqTraceOutputMsg(conn, conn->inStart - 5 - msgLength, MSGDIR_FROM_BACKEND);
 		}
 		else
 		{
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index fa9b62a..59ea464 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -362,7 +362,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 adf149a..e35ad19 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -376,6 +376,7 @@ struct pg_conn
 
 	/* Optional file to write trace info to */
 	FILE	   *Pfdebug;
+	int			traceFlags;
 
 	/* Callback procedures for notice message processing */
 	PGNoticeHooks noticeHooks;
diff --git a/src/interfaces/libpq/libpq-trace.c b/src/interfaces/libpq/libpq-trace.c
new file mode 100644
index 0000000..ef294fa
--- /dev/null
+++ b/src/interfaces/libpq/libpq-trace.c
@@ -0,0 +1,689 @@
+/*-------------------------------------------------------------------------
+ *
+ *	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 "libpq-trace.h"
+#include "pgtime.h"
+#include "port/pg_bswap.h"
+
+/*
+ * protocol message types:
+ *
+ * protocol_message_type_b[]: message types sent by a backend
+ * protocol_message_type_f[]: message types sent by a frontend
+ */
+static const char *const protocol_message_type_b[] = {
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* \x00 ... \x0f */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* \x10 ... \x1f */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* \x20 ... \x2f */
+	0,							/* 0 */
+	"ParseComplete",			/* 1 */
+	"BindComplete",				/* 2 */
+	"CloseComplete",			/* 3 */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* \x34 ... \x3f */
+	0,							/* @ */
+	"NotificationResponse",		/* A */
+	0,							/* B */
+	"CommandComplete",			/* C */
+	"DataRow",					/* D */
+	"ErrorResponse",			/* E */
+	0,							/* F */
+	"CopyInResponse",			/* G */
+	"CopyOutResponse",			/* H */
+	"EmptyQueryResponse",		/* I */
+	0,							/* J */
+	"BackendKeyData",			/* K */
+	0,							/* L */
+	0,							/* M */
+	"NoticeResponse",			/* N */
+	0,							/* O */
+	0,							/* P */
+	0,							/* Q */
+	"Authentication",			/* R */
+	"ParameterStatus",			/* S */
+	"RowDescription",			/* T */
+	0,							/* U */
+	"FunctionCallResponse",		/* V */
+	"CopyBothResponse",			/* W */
+	0,							/* X */
+	0,							/* Y */
+	"ReadyForQuery",			/* Z */
+	0, 0, 0, 0, 0,				/* \x5b ... \x5f */
+	0,							/* ` */
+	0,							/* a */
+	0,							/* b */
+	"CopyDone",					/* c */
+	"CopyData",					/* d */
+	0, 0, 0, 0, 0, 0, 0, 0, 0,	/* \x65 ... \0x6d */
+	"NoData",					/* n */
+	0,							/* o */
+	0,							/* p */
+	0,							/* q */
+	0,							/* r */
+	"PortalSuspended",			/* s */
+	"ParameterDescription",		/* t */
+	0,							/* u */
+	"NegotiateProtocolVersion", /* v */
+};
+
+static const char *const protocol_message_type_f[] = {
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* \x00 ... \x0f */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* \x10 ... \x1f */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* \x20 ... \x2f */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* \x30 ... \x3f */
+	0,							/* @ */
+	0,							/* A */
+	"Bind",						/* B */
+	"Close",					/* C */
+	"Describe",					/* D */
+	"Execute",					/* E */
+	"FunctionCall",				/* F */
+	0,							/* G */
+	"Flush",					/* H */
+	0, 0, 0, 0, 0, 0, 0,		/* I ... O */
+	"Parse",					/* P */
+	"Query",					/* Q */
+	0,							/* R */
+	"Sync",						/* S */
+	0, 0, 0, 0,					/* T ... W */
+	"Terminate",				/* X */
+	0, 0, 0, 0, 0, 0, 0,		/* \x59 ... \x5f */
+	0,							/* ` */
+	0,							/* a */
+	0,							/* b */
+	"CopyDone",					/* c */
+	"CopyData",					/* d */
+	0,							/* e */
+	"CopyFail",					/* f */
+	0, 0, 0, 0, 0, 0, 0, 0, 0,	/* g ... o */
+	"AuthenticationResponse",	/* p */
+};
+
+#define FORMATTED_TS_LEN 128	/* formatted timestamp length */
+
+static const char *pqGetProtocolMsgType(unsigned char c,
+										PGCommSource commsource);
+static void pqTraceOutputByte1(int LogEnd, int *cursor, FILE *pfdebug, char v);
+static int pqTraceOutputInt(char *buf, int LogEnd,int *cursor,
+													FILE *pfdebug, int length);
+static int pqTraceOutputString(char *buf, int LogEnd,int *cursor, FILE *pfdebug);
+static void pqTraceOutputBinary(char *v, int length, FILE *pfdebug);
+static void pqTraceOutputNchar(char *buf, int LogEnd, int *cursor,
+													FILE *pfdebug, int len);
+
+/* -------------------------
+ * FE/BE trace support
+ *
+ * We accumulate frontend message pieces in an array as the libpq code writes
+ * them, and log the complete message when pqTraceOutputFeMsg is called.
+ * For backend, we print the pieces as soon as we receive them from the server.
+ * -------------------------
+ */
+
+void
+PQtrace(PGconn *conn, FILE *debug_port)
+{
+	if (conn == NULL)
+		return;
+	/* Protocol 2.0 does not support tracing. */
+	if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
+		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;
+	/* Protocol 2.0 is not supported. */
+	if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
+		return;
+	/* If PQtrace() failed, do nothing. */
+	if (conn->Pfdebug == NULL)
+		return;
+	conn->traceFlags = flags;
+}
+
+/*
+ * pqGetProtocolMsgType:
+ *		Get a protocol type from first byte identifier
+ */
+static const char *
+pqGetProtocolMsgType(unsigned char c, PGCommSource commsource)
+{
+	if (commsource == MSGDIR_FROM_BACKEND &&
+		c < lengthof(protocol_message_type_b) &&
+		protocol_message_type_b[c] != NULL)
+		return protocol_message_type_b[c];
+
+	if (commsource == MSGDIR_FROM_FRONTEND &&
+		c < lengthof(protocol_message_type_f) &&
+		protocol_message_type_f[c] != NULL)
+		return protocol_message_type_f[c];
+
+	return "UnknownMessage:";
+}
+
+/*
+ * 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(int LogEnd, int *cursor, FILE *pfdebug, char v)
+{
+	if (cursor == NULL)
+		return;
+	if (*cursor > LogEnd)
+		return;
+
+	/*
+	 * 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);
+
+	if (*cursor < LogEnd)
+		fprintf(pfdebug, " ");
+	else
+		fprintf(pfdebug, "\n");
+}
+
+/*
+ *   pqTraceOutputInt: output a 2- or 4-byte integer message to the log
+ */
+static int
+pqTraceOutputInt(char *buf, int LogEnd,int *cursor, FILE *pfdebug, int length)
+{
+	char	   *prefix = length == 4 ? "" : "#";
+	uint16		tmp2;
+	uint32		tmp4;
+	int			result;
+
+
+	if (*cursor + length > LogEnd)
+		return EOF;
+
+	switch(length)
+	{
+		case 2:
+			memcpy(&tmp2, buf + *cursor , 2);
+			result = (int) pg_ntoh16(tmp2);
+			*cursor += 2;
+			break;
+		case 4:
+			memcpy(&tmp4, buf + *cursor , 4);
+			result = (int) pg_ntoh32(tmp4);
+			*cursor += 4;
+			break;
+		default:
+			fprintf(pfdebug,
+			"integer of size %d not supported by pqTraceOutputInt", length);
+			return EOF;
+	}
+	fprintf(pfdebug, "%s%d", prefix, result);
+
+	if (*cursor < LogEnd)
+		fprintf(pfdebug, " ");
+	else
+		fprintf(pfdebug, "\n");
+
+	return (int) result;
+}
+
+/*
+ * pqTraceOutputString: output a null-terminated string to the log
+ */
+static int
+pqTraceOutputString(char *buf, int LogEnd,int *cursor, FILE *pfdebug)
+{
+	int		csr = *cursor;
+	int		slen;
+	char	*s;
+
+	while (csr < LogEnd && buf[csr])
+		csr++;
+
+	if (csr >= LogEnd)
+		return EOF;
+
+	slen = csr - *cursor;
+
+	s = (char *) malloc(sizeof(char) * (slen + 1));
+	memcpy(s, buf + *cursor, slen);
+	s[slen] = '\0';
+
+	fprintf(pfdebug, "\"%s\"", s);
+
+	*cursor = ++csr;
+
+	if (*cursor < LogEnd)
+		fprintf(pfdebug, " ");
+	else
+		fprintf(pfdebug, "\n");
+
+	return 0;
+}
+
+/*
+ * pqTraceOutputBinary: output a string possibly consisting of
+ * non-printable characters. Hex representation is used for such
+ * chars; others are printed normally.
+ *
+ * XXX this probably doesn't do a great job with multibyte chars, but then we
+ * don't know what is text and what encoding it'd be in.
+ */
+static void
+pqTraceOutputBinary(char *v, int length, FILE *pfdebug)
+{
+	int			i,
+				next;			/* first char not yet printed */
+
+	for (next = i = 0; i < length; ++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 < length)
+		fwrite(v + next, 1, length - next, pfdebug);
+}
+
+/*
+ * pqTraceOutputNchar: output a string of exactly len bytes message to the log
+ */
+static void
+pqTraceOutputNchar(char *buf, int LogEnd, int *cursor, FILE *pfdebug, int len)
+{
+	char	*v = '\0';
+
+	memcpy(v, buf + *cursor, len);
+	*cursor += len;
+
+	fprintf(pfdebug, "\'");
+	pqTraceOutputBinary(v, len, pfdebug);
+	fprintf(pfdebug, "\'");
+
+	if (*cursor < LogEnd)
+		fprintf(pfdebug, " ");
+	else
+		fprintf(pfdebug, "\n");
+}
+
+int
+pqTraceOutputMsg(PGconn *conn, int msgCursor, PGCommSource commsource)
+{
+	char		timestr[FORMATTED_TS_LEN];
+	char 		id = '\0';
+	uint32		length32;
+	int			length;
+	char	   *prefix = commsource == MSGDIR_FROM_BACKEND ? "<" : ">";
+	int			LogCount = conn->outCount;
+	int			LogEnd = commsource == MSGDIR_FROM_BACKEND ? conn->inCursor : conn->outMsgEnd;
+	int			LogCursor = msgCursor;
+	char		*logBuffer = commsource == MSGDIR_FROM_BACKEND ? conn->inBuffer : conn->outBuffer;
+	const char	*message_type = "UnkownMessage";
+
+	if ((conn->traceFlags & PQTRACE_SUPPRESS_TIMESTAMPS) == 0)
+		pqTraceFormatTimestamp(timestr, sizeof(timestr));
+	else
+		timestr[0] = '\0';
+
+	if (commsource == MSGDIR_FROM_BACKEND)
+		id = logBuffer[LogCursor++];
+	else
+	{
+		if (LogCursor > LogCount)
+			id = logBuffer[LogCount];
+	}
+
+	if (LogCursor + 4 > LogEnd)
+		return EOF;
+	memcpy(&length32, logBuffer + LogCursor , 4);
+	length = (int) pg_ntoh32(length32);
+	LogCursor += 4;
+
+	if (id == '\0')
+	{
+		switch(length)
+		{
+			case 8:
+			{
+				uint32		result32;
+				int			result;
+				memcpy(&result32, logBuffer + LogCursor, 4);
+				result = (int) pg_ntoh32(result32);
+				if (result == NEGOTIATE_SSL_CODE)
+					message_type = "SSLRequest";
+				else if (result == NEGOTIATE_GSS_CODE)
+					message_type = "GSSRequest";
+				break;
+			}
+			case 16:
+				message_type = "CancelRequest";
+				break;
+			default :
+				message_type = "StartupMessage";
+				break;
+		}
+	}
+	else
+		message_type =
+			pqGetProtocolMsgType(id, commsource);
+
+	fprintf(conn->Pfdebug, "%s\t%s\t%s\t%d ", timestr, prefix, message_type, length);
+
+	switch(id)
+	{
+		case 'R':	/* Authentication */
+			pqTraceOutputInt(logBuffer, LogEnd, &LogCursor, conn->Pfdebug, 4);
+			break;
+		case 'K':	/* secret key data from the backend */
+			pqTraceOutputInt(logBuffer, LogEnd, &LogCursor, conn->Pfdebug, 4);
+			pqTraceOutputInt(logBuffer, LogEnd, &LogCursor, conn->Pfdebug, 4);
+			break;
+		case 'B':	/* Bind */
+		{
+			int nparams;
+			int nbytes;
+			int i;
+			pqTraceOutputString(logBuffer, LogEnd, &LogCursor,conn->Pfdebug);
+			pqTraceOutputString(logBuffer, LogEnd, &LogCursor,conn->Pfdebug);
+			nparams = pqTraceOutputInt(logBuffer, LogEnd, &LogCursor,
+															conn->Pfdebug, 2);
+			for (i = 0; i < nparams; i++)
+				pqTraceOutputInt(logBuffer, LogEnd, &LogCursor,
+															conn->Pfdebug, 2);
+			nparams = pqTraceOutputInt(logBuffer, LogEnd, &LogCursor,
+															conn->Pfdebug, 2);
+			for (i = 0; i < nparams; i++)
+			{
+				nbytes = pqTraceOutputInt(logBuffer, LogEnd, &LogCursor,
+															conn->Pfdebug, 4);
+				pqTraceOutputNchar(logBuffer, LogEnd, &LogCursor,
+														conn->Pfdebug, nbytes);
+			}
+			pqTraceOutputInt(logBuffer, LogEnd, &LogCursor, conn->Pfdebug, 2);
+			pqTraceOutputInt(logBuffer, LogEnd, &LogCursor, conn->Pfdebug, 2);
+			break;
+		}
+		case 'C':	/* Close(F) or Command Complete(B) */
+			if (commsource == MSGDIR_FROM_BACKEND)
+				fprintf(conn->Pfdebug, "\"%s\"\n", logBuffer + LogCursor);
+			else
+			{
+				pqTraceOutputByte1(LogEnd, &LogCursor, conn->Pfdebug,
+													logBuffer[LogCursor++]);
+				pqTraceOutputString(logBuffer, LogEnd, &LogCursor,conn->Pfdebug);
+			}
+			break;
+		case 'd':	/* Copy Data */
+			/* Drop COPY data to reduce the overhead of logging. */
+			fprintf(conn->Pfdebug, "\n");
+			break;
+		case 'f':	/* Copy Fail */
+			fprintf(conn->Pfdebug, "\"%s\"\n", logBuffer + LogCursor);
+			pqTraceOutputString(logBuffer, LogEnd, &LogCursor,conn->Pfdebug);
+			break;
+		case 'G':	/* Start Copy In */
+			pqTraceOutputByte1(LogEnd, &LogCursor,
+									conn->Pfdebug, logBuffer[LogCursor++]);
+			while (LogEnd > LogCursor)
+				pqTraceOutputInt(logBuffer, LogEnd, &LogCursor, conn->Pfdebug, 2);
+			break;
+		case 'H':	/* Flush(F) or Start Copy Out(B) */
+			if (commsource == MSGDIR_FROM_BACKEND)
+			{
+				pqTraceOutputByte1(LogEnd, &LogCursor,
+										conn->Pfdebug, logBuffer[LogCursor++]);
+				while (LogEnd > LogCursor)
+					pqTraceOutputInt(logBuffer, LogEnd, &LogCursor, conn->Pfdebug, 2);
+			}
+			else
+				fprintf(conn->Pfdebug, "\n");
+			break;
+		case 'W':	/* Start Copy Both */
+			pqTraceOutputByte1(LogEnd, &LogCursor,
+									conn->Pfdebug, logBuffer[LogCursor++]);
+			while (LogEnd > LogCursor)
+				pqTraceOutputInt(logBuffer, LogEnd, &LogCursor, conn->Pfdebug, 2);
+			break;
+		case 'D':	/* Describe(F) or Data Row(B) */
+			if (commsource == MSGDIR_FROM_BACKEND)
+			{
+				int		len;
+				pqTraceOutputInt(logBuffer, LogEnd, &LogCursor, conn->Pfdebug, 2);
+				while (LogEnd > LogCursor)
+				{
+					len = pqTraceOutputInt(logBuffer, LogEnd, &LogCursor,
+																conn->Pfdebug, 4);
+					pqTraceOutputNchar(logBuffer, LogEnd, &LogCursor,
+														conn->Pfdebug, len);
+				}
+			}
+			else
+			{
+				pqTraceOutputByte1(LogEnd, &LogCursor, conn->Pfdebug,
+													logBuffer[LogCursor++]);
+				pqTraceOutputString(logBuffer, LogEnd, &LogCursor,conn->Pfdebug);
+			}
+			break;
+		case 'E':	/* Execute(F) or Error Return(B) */
+			if (commsource == MSGDIR_FROM_BACKEND)
+			{
+				while (LogEnd > LogCursor)
+				{
+					pqTraceOutputByte1(LogEnd, &LogCursor, conn->Pfdebug,
+													logBuffer[LogCursor++]);
+					if (logBuffer[LogCursor] == '\0')
+						break;
+					pqTraceOutputString(logBuffer, LogEnd, &LogCursor,conn->Pfdebug);
+				}
+			}
+			else
+			{
+				pqTraceOutputString(logBuffer, LogEnd, &LogCursor,conn->Pfdebug);
+				pqTraceOutputInt(logBuffer, LogEnd, &LogCursor, conn->Pfdebug, 4);
+			}
+			break;
+		case 'F':	/* Function Call */
+			{
+				int nargs;
+				int nbytes;
+				int	i;
+				pqTraceOutputInt(logBuffer, LogEnd, &LogCursor, conn->Pfdebug, 4);
+				pqTraceOutputInt(logBuffer, LogEnd, &LogCursor, conn->Pfdebug, 2);
+				pqTraceOutputInt(logBuffer, LogEnd, &LogCursor, conn->Pfdebug, 2);
+				nargs = pqTraceOutputInt(logBuffer, LogEnd, &LogCursor,
+																conn->Pfdebug, 2);
+				for (i = 0; i < nargs; i++)
+				{
+					nbytes = pqTraceOutputInt(logBuffer, LogEnd, &LogCursor,
+																conn->Pfdebug, 4);
+					pqTraceOutputNchar(logBuffer, LogEnd, &LogCursor,
+														conn->Pfdebug, nbytes);
+				}
+				pqTraceOutputInt(logBuffer, LogEnd, &LogCursor, conn->Pfdebug, 2);
+				break;
+			}
+		case 'V':	/* Function Call response */
+			{
+				int		len;
+				len = pqTraceOutputInt(logBuffer, LogEnd, &LogCursor, conn->Pfdebug, 4);
+				pqTraceOutputNchar(logBuffer, LogEnd, &LogCursor, conn->Pfdebug, len);
+			break;
+			}
+		case 'v':	/* Negotiate Protocol Version */
+			pqTraceOutputInt(logBuffer, LogEnd, &LogCursor, conn->Pfdebug, 4);
+			pqTraceOutputInt(logBuffer, LogEnd, &LogCursor, conn->Pfdebug, 4);
+			break;
+		case 'N':	/* Notice Response */
+			while (LogEnd > LogCursor)
+			{
+				pqTraceOutputByte1(LogEnd, &LogCursor, conn->Pfdebug,
+														logBuffer[LogCursor++]);
+				if (logBuffer[LogCursor] == '\0')
+					break;
+				pqTraceOutputString(logBuffer, LogEnd, &LogCursor,conn->Pfdebug);
+			}
+			break;
+		case 'A':	/* Notification Response */
+			pqTraceOutputInt(logBuffer, LogEnd, &LogCursor, conn->Pfdebug, 4);
+			pqTraceOutputString(logBuffer, LogEnd, &LogCursor,conn->Pfdebug);
+			pqTraceOutputString(logBuffer, LogEnd, &LogCursor,conn->Pfdebug);
+			break;
+		case 't':	/* Parameter Description */
+			pqTraceOutputInt(logBuffer, LogEnd, &LogCursor, conn->Pfdebug, 2);
+			while (LogEnd > LogCursor)
+				pqTraceOutputInt(logBuffer, LogEnd, &LogCursor, conn->Pfdebug, 4);
+			break;
+		case 'S':	/* Parameter Status(B) or Sync(F) */
+			if (commsource == MSGDIR_FROM_BACKEND)
+			{
+				pqTraceOutputString(logBuffer, LogEnd, &LogCursor,conn->Pfdebug);
+				pqTraceOutputString(logBuffer, LogEnd, &LogCursor,conn->Pfdebug);
+			}
+			else
+				fprintf(conn->Pfdebug, "\n");
+			break;
+		case 'P':	/* Parse */
+			{
+				int nparams;
+				int i;
+				pqTraceOutputString(logBuffer, LogEnd, &LogCursor,conn->Pfdebug);
+				pqTraceOutputString(logBuffer, LogEnd, &LogCursor,conn->Pfdebug);
+				nparams = pqTraceOutputInt(logBuffer, LogEnd, &LogCursor,
+																conn->Pfdebug, 2);
+				for (i = 0; i < nparams; i++)
+					pqTraceOutputInt(logBuffer, LogEnd, &LogCursor, conn->Pfdebug, 4);
+				break;
+			}
+		case 'Q':	/* Query */
+			pqTraceOutputString(logBuffer, LogEnd, &LogCursor,conn->Pfdebug);
+			break;
+		case 'Z':	/* Ready For Query */
+			pqTraceOutputByte1(LogEnd, &LogCursor, conn->Pfdebug,
+													logBuffer[LogCursor++]);
+			break;
+		case 'T':	/* Row Description */
+			{
+				int nfields;
+				int	i;
+				nfields = pqTraceOutputInt(logBuffer, LogEnd, &LogCursor, conn->Pfdebug, 2);
+				for (i = 0; i < nfields; i++)
+				{
+					pqTraceOutputString(logBuffer, LogEnd, &LogCursor,conn->Pfdebug);
+					pqTraceOutputInt(logBuffer, LogEnd, &LogCursor, conn->Pfdebug, 4);
+					pqTraceOutputInt(logBuffer, LogEnd, &LogCursor, conn->Pfdebug, 2);
+					pqTraceOutputInt(logBuffer, LogEnd, &LogCursor, conn->Pfdebug, 4);
+					pqTraceOutputInt(logBuffer, LogEnd, &LogCursor, conn->Pfdebug, 2);
+					pqTraceOutputInt(logBuffer, LogEnd, &LogCursor, conn->Pfdebug, 4);
+					pqTraceOutputInt(logBuffer, LogEnd, &LogCursor, conn->Pfdebug, 2);
+				}
+				break;
+			}
+		case '2':	/* Bind Complete */
+		case '3':	/* Close Complete */
+		case 'c':	/* Copy Done */
+		case 'I':	/* empty query */
+		case 'n':	/* No Data */
+		case '1':	/* Parse Complete */
+		case 's':	/* Portal Suspended */
+		case 'X':	/* Terminate */
+			/* No message content */
+			fprintf(conn->Pfdebug, "\n");
+			break;
+		case '\0' :
+			/* CancelRequest SSLRequest. GSSENCRequest and StartupMessage */
+			pqTraceOutputInt(logBuffer, LogEnd, &LogCursor, conn->Pfdebug, 4);
+			if (length == 8)
+			{
+				pqTraceOutputInt(logBuffer, LogEnd, &LogCursor, conn->Pfdebug, 4);
+				pqTraceOutputInt(logBuffer, LogEnd, &LogCursor, conn->Pfdebug, 4);
+			}
+			else
+			{
+				while (LogEnd > LogCursor)
+					pqTraceOutputString(logBuffer, LogEnd, &LogCursor,conn->Pfdebug);
+			}
+			break;
+		default:
+			fprintf(conn->Pfdebug, "Invalid Protocol:%c\n", id);
+			break;
+	}
+
+	return 0;
+}
diff --git a/src/interfaces/libpq/libpq-trace.h b/src/interfaces/libpq/libpq-trace.h
new file mode 100644
index 0000000..71b66da
--- /dev/null
+++ b/src/interfaces/libpq/libpq-trace.h
@@ -0,0 +1,39 @@
+/*-------------------------------------------------------------------------
+ *
+ * libpq-trace.h
+ *	  This file contains definitions for structures and
+ *	  externs for functions used by libpq protocol tracing.
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/interfaces/libpq/libpq-trace.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef LIBPQ_TRACE_H
+#define LIBPQ_TRACE_H
+
+#include "libpq-fe.h"
+#include "libpq-int.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/* Log message source */
+typedef enum
+{
+	MSGDIR_FROM_BACKEND,
+	MSGDIR_FROM_FRONTEND
+} PGCommSource;
+
+extern int pqTraceOutputMsg(PGconn *conn, int msgCursor, PGCommSource commsource);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif							/* LIBPQ_TRACE_H */
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 8bd95ae..18a692d 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -1523,6 +1523,7 @@ PGAlignedXLogBlock
 PGAsyncStatusType
 PGCALL2
 PGChecksummablePage
+PGCommSource
 PGContextVisibility
 PGEvent
 PGEventConnDestroy