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 - 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. void PQtrace(PGconn *conn, FILE *stream); + + Each line consists of: an optional timestamp, a direction indicator + (> for messages from client to server, + and < 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 # + and 32-bit values are printed without a prefix. + Further message-type-specific detail can be found in + . + + On Windows, if the libpq library and an application are @@ -5961,6 +5977,28 @@ void PQtrace(PGconn *conn, FILE *stream); + + PQtraceSetFlagsPQtraceSetFlags + + + + Controls the tracing behavior of client/server communication. + +void PQtraceSetFlags(PGconn *conn, int flags); + + + + + flags contains flag bits describing the operating mode + of tracing. + If flags contains PQTRACE_SUPPRESS_TIMESTAMPS, + then the timestamp is not included when printing each message. + This function must be called after calling PQtrace. + + + + + PQuntracePQuntrace 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 +#include + +#ifdef WIN32 +#include "win32.h" +#else +#include +#include +#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