LCOV - code coverage report
Current view: top level - src/interfaces/libpq - fe-trace.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 220 484 45.5 %
Date: 2025-04-01 14:15:22 Functions: 23 42 54.8 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  *  fe-trace.c
       4             :  *    functions for libpq protocol tracing
       5             :  *
       6             :  * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  * IDENTIFICATION
      10             :  *    src/interfaces/libpq/fe-trace.c
      11             :  *
      12             :  *-------------------------------------------------------------------------
      13             :  */
      14             : 
      15             : #include "postgres_fe.h"
      16             : 
      17             : #include <ctype.h>
      18             : #include <limits.h>
      19             : #include <sys/time.h>
      20             : #include <time.h>
      21             : 
      22             : #ifdef WIN32
      23             : #include "win32.h"
      24             : #else
      25             : #include <unistd.h>
      26             : #endif
      27             : 
      28             : #include "libpq-fe.h"
      29             : #include "libpq-int.h"
      30             : #include "port/pg_bswap.h"
      31             : 
      32             : 
      33             : /* Enable tracing */
      34             : void
      35          18 : PQtrace(PGconn *conn, FILE *debug_port)
      36             : {
      37          18 :     if (conn == NULL)
      38           0 :         return;
      39          18 :     PQuntrace(conn);
      40          18 :     if (debug_port == NULL)
      41           0 :         return;
      42             : 
      43          18 :     conn->Pfdebug = debug_port;
      44          18 :     conn->traceFlags = 0;
      45             : }
      46             : 
      47             : /* Disable tracing */
      48             : void
      49          18 : PQuntrace(PGconn *conn)
      50             : {
      51          18 :     if (conn == NULL)
      52           0 :         return;
      53          18 :     if (conn->Pfdebug)
      54             :     {
      55           0 :         fflush(conn->Pfdebug);
      56           0 :         conn->Pfdebug = NULL;
      57             :     }
      58             : 
      59          18 :     conn->traceFlags = 0;
      60             : }
      61             : 
      62             : /* Set flags for current tracing session */
      63             : void
      64          18 : PQsetTraceFlags(PGconn *conn, int flags)
      65             : {
      66          18 :     if (conn == NULL)
      67           0 :         return;
      68             :     /* If PQtrace() failed, do nothing. */
      69          18 :     if (conn->Pfdebug == NULL)
      70           0 :         return;
      71          18 :     conn->traceFlags = flags;
      72             : }
      73             : 
      74             : /*
      75             :  * Print the current time, with microseconds, into a caller-supplied
      76             :  * buffer.
      77             :  * Cribbed from get_formatted_log_time, but much simpler.
      78             :  */
      79             : static void
      80           0 : pqTraceFormatTimestamp(char *timestr, size_t ts_len)
      81             : {
      82             :     struct timeval tval;
      83             :     time_t      now;
      84             :     struct tm   tmbuf;
      85             : 
      86           0 :     gettimeofday(&tval, NULL);
      87             : 
      88             :     /*
      89             :      * MSVC's implementation of timeval uses a long for tv_sec, however,
      90             :      * localtime() expects a time_t pointer.  Here we'll assign tv_sec to a
      91             :      * local time_t variable so that we pass localtime() the correct pointer
      92             :      * type.
      93             :      */
      94           0 :     now = tval.tv_sec;
      95           0 :     strftime(timestr, ts_len,
      96             :              "%Y-%m-%d %H:%M:%S",
      97           0 :              localtime_r(&now, &tmbuf));
      98             :     /* append microseconds */
      99           0 :     snprintf(timestr + strlen(timestr), ts_len - strlen(timestr),
     100           0 :              ".%06u", (unsigned int) (tval.tv_usec));
     101           0 : }
     102             : 
     103             : /*
     104             :  *   pqTraceOutputByte1: output a 1-char message to the log
     105             :  */
     106             : static void
     107         310 : pqTraceOutputByte1(FILE *pfdebug, const char *data, int *cursor)
     108             : {
     109         310 :     const char *v = data + *cursor;
     110             : 
     111             :     /*
     112             :      * Show non-printable data in hex format, including the terminating \0
     113             :      * that completes ErrorResponse and NoticeResponse messages.
     114             :      */
     115         310 :     if (!isprint((unsigned char) *v))
     116          20 :         fprintf(pfdebug, " \\x%02x", *v);
     117             :     else
     118         290 :         fprintf(pfdebug, " %c", *v);
     119         310 :     *cursor += 1;
     120         310 : }
     121             : 
     122             : /*
     123             :  *   pqTraceOutputInt16: output a 2-byte integer message to the log
     124             :  */
     125             : static int
     126         696 : pqTraceOutputInt16(FILE *pfdebug, const char *data, int *cursor)
     127             : {
     128             :     uint16      tmp;
     129             :     int         result;
     130             : 
     131         696 :     memcpy(&tmp, data + *cursor, 2);
     132         696 :     *cursor += 2;
     133         696 :     result = (int) pg_ntoh16(tmp);
     134         696 :     fprintf(pfdebug, " %d", result);
     135             : 
     136         696 :     return result;
     137             : }
     138             : 
     139             : /*
     140             :  *   pqTraceOutputInt32: output a 4-byte integer message to the log
     141             :  *
     142             :  * If 'suppress' is true, print a literal NNNN instead of the actual number.
     143             :  */
     144             : static int
     145         390 : pqTraceOutputInt32(FILE *pfdebug, const char *data, int *cursor, bool suppress)
     146             : {
     147             :     int         result;
     148             : 
     149         390 :     memcpy(&result, data + *cursor, 4);
     150         390 :     *cursor += 4;
     151         390 :     result = (int) pg_ntoh32(result);
     152         390 :     if (suppress)
     153         148 :         fprintf(pfdebug, " NNNN");
     154             :     else
     155         242 :         fprintf(pfdebug, " %d", result);
     156             : 
     157         390 :     return result;
     158             : }
     159             : 
     160             : /*
     161             :  *   pqTraceOutputString: output a string message to the log
     162             :  *
     163             :  * If 'suppress' is true, print a literal "SSSS" instead of the actual string.
     164             :  */
     165             : static void
     166         746 : pqTraceOutputString(FILE *pfdebug, const char *data, int *cursor, bool suppress)
     167             : {
     168             :     int         len;
     169             : 
     170         746 :     if (suppress)
     171             :     {
     172          60 :         fprintf(pfdebug, " \"SSSS\"");
     173          60 :         *cursor += strlen(data + *cursor) + 1;
     174             :     }
     175             :     else
     176             :     {
     177         686 :         len = fprintf(pfdebug, " \"%s\"", data + *cursor);
     178             : 
     179             :         /*
     180             :          * This is a null-terminated string. So add 1 after subtracting 3
     181             :          * which is the double quotes and space length from len.
     182             :          */
     183         686 :         *cursor += (len - 3 + 1);
     184             :     }
     185         746 : }
     186             : 
     187             : /*
     188             :  * pqTraceOutputNchar: output a string of exactly len bytes message to the log
     189             :  *
     190             :  * If 'suppress' is true, print a literal 'BBBB' instead of the actual bytes.
     191             :  */
     192             : static void
     193         106 : pqTraceOutputNchar(FILE *pfdebug, int len, const char *data, int *cursor, bool suppress)
     194             : {
     195             :     int         i,
     196             :                 next;           /* first char not yet printed */
     197         106 :     const char *v = data + *cursor;
     198             : 
     199         106 :     if (suppress)
     200             :     {
     201           0 :         fprintf(pfdebug, " 'BBBB'");
     202           0 :         *cursor += len;
     203           0 :         return;
     204             :     }
     205             : 
     206         106 :     fprintf(pfdebug, " \'");
     207             : 
     208        1548 :     for (next = i = 0; i < len; ++i)
     209             :     {
     210        1442 :         if (isprint((unsigned char) v[i]))
     211        1442 :             continue;
     212             :         else
     213             :         {
     214           0 :             fwrite(v + next, 1, i - next, pfdebug);
     215           0 :             fprintf(pfdebug, "\\x%02x", v[i]);
     216           0 :             next = i + 1;
     217             :         }
     218             :     }
     219         106 :     if (next < len)
     220         106 :         fwrite(v + next, 1, len - next, pfdebug);
     221             : 
     222         106 :     fprintf(pfdebug, "\'");
     223         106 :     *cursor += len;
     224             : }
     225             : 
     226             : /*
     227             :  * Output functions by protocol message type
     228             :  */
     229             : 
     230             : static void
     231           0 : pqTraceOutput_NotificationResponse(FILE *f, const char *message, int *cursor, bool regress)
     232             : {
     233           0 :     fprintf(f, "NotificationResponse\t");
     234           0 :     pqTraceOutputInt32(f, message, cursor, regress);
     235           0 :     pqTraceOutputString(f, message, cursor, false);
     236           0 :     pqTraceOutputString(f, message, cursor, false);
     237           0 : }
     238             : 
     239             : static void
     240          72 : pqTraceOutput_Bind(FILE *f, const char *message, int *cursor)
     241             : {
     242             :     int         nparams;
     243             : 
     244          72 :     fprintf(f, "Bind\t");
     245          72 :     pqTraceOutputString(f, message, cursor, false);
     246          72 :     pqTraceOutputString(f, message, cursor, false);
     247          72 :     nparams = pqTraceOutputInt16(f, message, cursor);
     248             : 
     249          72 :     for (int i = 0; i < nparams; i++)
     250           0 :         pqTraceOutputInt16(f, message, cursor);
     251             : 
     252          72 :     nparams = pqTraceOutputInt16(f, message, cursor);
     253             : 
     254          94 :     for (int i = 0; i < nparams; i++)
     255             :     {
     256             :         int         nbytes;
     257             : 
     258          22 :         nbytes = pqTraceOutputInt32(f, message, cursor, false);
     259          22 :         if (nbytes == -1)
     260           0 :             continue;
     261          22 :         pqTraceOutputNchar(f, nbytes, message, cursor, false);
     262             :     }
     263             : 
     264          72 :     nparams = pqTraceOutputInt16(f, message, cursor);
     265         144 :     for (int i = 0; i < nparams; i++)
     266          72 :         pqTraceOutputInt16(f, message, cursor);
     267          72 : }
     268             : 
     269             : static void
     270           8 : pqTraceOutput_Close(FILE *f, const char *message, int *cursor)
     271             : {
     272           8 :     fprintf(f, "Close\t");
     273           8 :     pqTraceOutputByte1(f, message, cursor);
     274           8 :     pqTraceOutputString(f, message, cursor, false);
     275           8 : }
     276             : 
     277             : static void
     278          74 : pqTraceOutput_CommandComplete(FILE *f, const char *message, int *cursor)
     279             : {
     280          74 :     fprintf(f, "CommandComplete\t");
     281          74 :     pqTraceOutputString(f, message, cursor, false);
     282          74 : }
     283             : 
     284             : static void
     285           0 : pqTraceOutput_CopyData(FILE *f, const char *message, int *cursor, int length,
     286             :                        bool suppress)
     287             : {
     288           0 :     fprintf(f, "CopyData\t");
     289           0 :     pqTraceOutputNchar(f, length - *cursor + 1, message, cursor, suppress);
     290           0 : }
     291             : 
     292             : static void
     293          84 : pqTraceOutput_DataRow(FILE *f, const char *message, int *cursor)
     294             : {
     295             :     int         nfields;
     296             :     int         len;
     297             :     int         i;
     298             : 
     299          84 :     fprintf(f, "DataRow\t");
     300          84 :     nfields = pqTraceOutputInt16(f, message, cursor);
     301         168 :     for (i = 0; i < nfields; i++)
     302             :     {
     303          84 :         len = pqTraceOutputInt32(f, message, cursor, false);
     304          84 :         if (len == -1)
     305           0 :             continue;
     306          84 :         pqTraceOutputNchar(f, len, message, cursor, false);
     307             :     }
     308          84 : }
     309             : 
     310             : static void
     311          80 : pqTraceOutput_Describe(FILE *f, const char *message, int *cursor)
     312             : {
     313          80 :     fprintf(f, "Describe\t");
     314          80 :     pqTraceOutputByte1(f, message, cursor);
     315          80 :     pqTraceOutputString(f, message, cursor, false);
     316          80 : }
     317             : 
     318             : /* shared code NoticeResponse / ErrorResponse */
     319             : static void
     320          20 : pqTraceOutputNR(FILE *f, const char *type, const char *message, int *cursor,
     321             :                 bool regress)
     322             : {
     323          20 :     fprintf(f, "%s\t", type);
     324             :     for (;;)
     325         144 :     {
     326             :         char        field;
     327             :         bool        suppress;
     328             : 
     329         164 :         pqTraceOutputByte1(f, message, cursor);
     330         164 :         field = message[*cursor - 1];
     331         164 :         if (field == '\0')
     332          20 :             break;
     333             : 
     334         144 :         suppress = regress && (field == 'L' || field == 'F' || field == 'R');
     335         144 :         pqTraceOutputString(f, message, cursor, suppress);
     336             :     }
     337          20 : }
     338             : 
     339             : static void
     340          14 : pqTraceOutput_ErrorResponse(FILE *f, const char *message, int *cursor, bool regress)
     341             : {
     342          14 :     pqTraceOutputNR(f, "ErrorResponse", message, cursor, regress);
     343          14 : }
     344             : 
     345             : static void
     346           6 : pqTraceOutput_NoticeResponse(FILE *f, const char *message, int *cursor, bool regress)
     347             : {
     348           6 :     pqTraceOutputNR(f, "NoticeResponse", message, cursor, regress);
     349           6 : }
     350             : 
     351             : static void
     352          72 : pqTraceOutput_Execute(FILE *f, const char *message, int *cursor, bool regress)
     353             : {
     354          72 :     fprintf(f, "Execute\t");
     355          72 :     pqTraceOutputString(f, message, cursor, false);
     356          72 :     pqTraceOutputInt32(f, message, cursor, false);
     357          72 : }
     358             : 
     359             : static void
     360           0 : pqTraceOutput_CopyFail(FILE *f, const char *message, int *cursor)
     361             : {
     362           0 :     fprintf(f, "CopyFail\t");
     363           0 :     pqTraceOutputString(f, message, cursor, false);
     364           0 : }
     365             : 
     366             : static void
     367           0 : pqTraceOutput_GSSResponse(FILE *f, const char *message, int *cursor,
     368             :                           int length, bool regress)
     369             : {
     370           0 :     fprintf(f, "GSSResponse\t");
     371           0 :     pqTraceOutputNchar(f, length - *cursor + 1, message, cursor, regress);
     372           0 : }
     373             : 
     374             : static void
     375           0 : pqTraceOutput_PasswordMessage(FILE *f, const char *message, int *cursor)
     376             : {
     377           0 :     fprintf(f, "PasswordMessage\t");
     378           0 :     pqTraceOutputString(f, message, cursor, false);
     379           0 : }
     380             : 
     381             : static void
     382           0 : pqTraceOutput_SASLInitialResponse(FILE *f, const char *message, int *cursor,
     383             :                                   bool regress)
     384             : {
     385             :     int         initialResponse;
     386             : 
     387           0 :     fprintf(f, "SASLInitialResponse\t");
     388           0 :     pqTraceOutputString(f, message, cursor, false);
     389           0 :     initialResponse = pqTraceOutputInt32(f, message, cursor, false);
     390           0 :     if (initialResponse != -1)
     391           0 :         pqTraceOutputNchar(f, initialResponse, message, cursor, regress);
     392           0 : }
     393             : 
     394             : static void
     395           0 : pqTraceOutput_SASLResponse(FILE *f, const char *message, int *cursor,
     396             :                            int length, bool regress)
     397             : {
     398           0 :     fprintf(f, "SASLResponse\t");
     399           0 :     pqTraceOutputNchar(f, length - *cursor + 1, message, cursor, regress);
     400           0 : }
     401             : 
     402             : static void
     403           0 : pqTraceOutput_FunctionCall(FILE *f, const char *message, int *cursor, bool regress)
     404             : {
     405             :     int         nfields;
     406             :     int         nbytes;
     407             : 
     408           0 :     fprintf(f, "FunctionCall\t");
     409           0 :     pqTraceOutputInt32(f, message, cursor, regress);
     410           0 :     nfields = pqTraceOutputInt16(f, message, cursor);
     411             : 
     412           0 :     for (int i = 0; i < nfields; i++)
     413           0 :         pqTraceOutputInt16(f, message, cursor);
     414             : 
     415           0 :     nfields = pqTraceOutputInt16(f, message, cursor);
     416             : 
     417           0 :     for (int i = 0; i < nfields; i++)
     418             :     {
     419           0 :         nbytes = pqTraceOutputInt32(f, message, cursor, false);
     420           0 :         if (nbytes == -1)
     421           0 :             continue;
     422           0 :         pqTraceOutputNchar(f, nbytes, message, cursor, false);
     423             :     }
     424             : 
     425           0 :     pqTraceOutputInt16(f, message, cursor);
     426           0 : }
     427             : 
     428             : static void
     429           0 : pqTraceOutput_CopyInResponse(FILE *f, const char *message, int *cursor)
     430             : {
     431             :     int         nfields;
     432             : 
     433           0 :     fprintf(f, "CopyInResponse\t");
     434           0 :     pqTraceOutputByte1(f, message, cursor);
     435           0 :     nfields = pqTraceOutputInt16(f, message, cursor);
     436             : 
     437           0 :     for (int i = 0; i < nfields; i++)
     438           0 :         pqTraceOutputInt16(f, message, cursor);
     439           0 : }
     440             : 
     441             : static void
     442           0 : pqTraceOutput_CopyOutResponse(FILE *f, const char *message, int *cursor)
     443             : {
     444             :     int         nfields;
     445             : 
     446           0 :     fprintf(f, "CopyOutResponse\t");
     447           0 :     pqTraceOutputByte1(f, message, cursor);
     448           0 :     nfields = pqTraceOutputInt16(f, message, cursor);
     449             : 
     450           0 :     for (int i = 0; i < nfields; i++)
     451           0 :         pqTraceOutputInt16(f, message, cursor);
     452           0 : }
     453             : 
     454             : static void
     455           0 : pqTraceOutput_BackendKeyData(FILE *f, const char *message, int *cursor, bool regress)
     456             : {
     457           0 :     fprintf(f, "BackendKeyData\t");
     458           0 :     pqTraceOutputInt32(f, message, cursor, regress);
     459           0 :     pqTraceOutputInt32(f, message, cursor, regress);
     460           0 : }
     461             : 
     462             : static void
     463          72 : pqTraceOutput_Parse(FILE *f, const char *message, int *cursor, bool regress)
     464             : {
     465             :     int         nparams;
     466             : 
     467          72 :     fprintf(f, "Parse\t");
     468          72 :     pqTraceOutputString(f, message, cursor, false);
     469          72 :     pqTraceOutputString(f, message, cursor, false);
     470          72 :     nparams = pqTraceOutputInt16(f, message, cursor);
     471             : 
     472          90 :     for (int i = 0; i < nparams; i++)
     473          18 :         pqTraceOutputInt32(f, message, cursor, regress);
     474          72 : }
     475             : 
     476             : static void
     477          16 : pqTraceOutput_Query(FILE *f, const char *message, int *cursor)
     478             : {
     479          16 :     fprintf(f, "Query\t");
     480          16 :     pqTraceOutputString(f, message, cursor, false);
     481          16 : }
     482             : 
     483             : static void
     484           0 : pqTraceOutput_Authentication(FILE *f, const char *message, int *cursor,
     485             :                              int length, bool suppress)
     486             : {
     487           0 :     int         authType = 0;
     488             : 
     489           0 :     memcpy(&authType, message + *cursor, 4);
     490           0 :     authType = (int) pg_ntoh32(authType);
     491           0 :     *cursor += 4;
     492           0 :     switch (authType)
     493             :     {
     494           0 :         case AUTH_REQ_OK:
     495           0 :             fprintf(f, "AuthenticationOk");
     496           0 :             break;
     497             :             /* AUTH_REQ_KRB4 not supported */
     498             :             /* AUTH_REQ_KRB5 not supported */
     499           0 :         case AUTH_REQ_PASSWORD:
     500           0 :             fprintf(f, "AuthenticationCleartextPassword");
     501           0 :             break;
     502             :             /* AUTH_REQ_CRYPT not supported */
     503           0 :         case AUTH_REQ_MD5:
     504           0 :             fprintf(f, "AuthenticationMD5Password");
     505           0 :             break;
     506           0 :         case AUTH_REQ_GSS:
     507           0 :             fprintf(f, "AuthenticationGSS");
     508           0 :             break;
     509           0 :         case AUTH_REQ_GSS_CONT:
     510           0 :             fprintf(f, "AuthenticationGSSContinue\t");
     511           0 :             pqTraceOutputNchar(f, length - *cursor + 1, message, cursor,
     512             :                                suppress);
     513           0 :             break;
     514           0 :         case AUTH_REQ_SSPI:
     515           0 :             fprintf(f, "AuthenticationSSPI");
     516           0 :             break;
     517           0 :         case AUTH_REQ_SASL:
     518           0 :             fprintf(f, "AuthenticationSASL\t");
     519           0 :             while (message[*cursor] != '\0')
     520           0 :                 pqTraceOutputString(f, message, cursor, false);
     521           0 :             pqTraceOutputString(f, message, cursor, false);
     522           0 :             break;
     523           0 :         case AUTH_REQ_SASL_CONT:
     524           0 :             fprintf(f, "AuthenticationSASLContinue\t");
     525           0 :             pqTraceOutputNchar(f, length - *cursor + 1, message, cursor,
     526             :                                suppress);
     527           0 :             break;
     528           0 :         case AUTH_REQ_SASL_FIN:
     529           0 :             fprintf(f, "AuthenticationSASLFinal\t");
     530           0 :             pqTraceOutputNchar(f, length - *cursor + 1, message, cursor,
     531             :                                suppress);
     532           0 :             break;
     533           0 :         default:
     534           0 :             fprintf(f, "Unknown authentication message %d", authType);
     535             :     }
     536           0 : }
     537             : 
     538             : static void
     539           0 : pqTraceOutput_ParameterStatus(FILE *f, const char *message, int *cursor)
     540             : {
     541           0 :     fprintf(f, "ParameterStatus\t");
     542           0 :     pqTraceOutputString(f, message, cursor, false);
     543           0 :     pqTraceOutputString(f, message, cursor, false);
     544           0 : }
     545             : 
     546             : static void
     547           2 : pqTraceOutput_ParameterDescription(FILE *f, const char *message, int *cursor, bool regress)
     548             : {
     549             :     int         nfields;
     550             : 
     551           2 :     fprintf(f, "ParameterDescription\t");
     552           2 :     nfields = pqTraceOutputInt16(f, message, cursor);
     553             : 
     554           4 :     for (int i = 0; i < nfields; i++)
     555           2 :         pqTraceOutputInt32(f, message, cursor, regress);
     556           2 : }
     557             : 
     558             : static void
     559          58 : pqTraceOutput_RowDescription(FILE *f, const char *message, int *cursor, bool regress)
     560             : {
     561             :     int         nfields;
     562             : 
     563          58 :     fprintf(f, "RowDescription\t");
     564          58 :     nfields = pqTraceOutputInt16(f, message, cursor);
     565             : 
     566         122 :     for (int i = 0; i < nfields; i++)
     567             :     {
     568          64 :         pqTraceOutputString(f, message, cursor, false);
     569          64 :         pqTraceOutputInt32(f, message, cursor, regress);
     570          64 :         pqTraceOutputInt16(f, message, cursor);
     571          64 :         pqTraceOutputInt32(f, message, cursor, regress);
     572          64 :         pqTraceOutputInt16(f, message, cursor);
     573          64 :         pqTraceOutputInt32(f, message, cursor, false);
     574          64 :         pqTraceOutputInt16(f, message, cursor);
     575             :     }
     576          58 : }
     577             : 
     578             : static void
     579           0 : pqTraceOutput_NegotiateProtocolVersion(FILE *f, const char *message, int *cursor)
     580             : {
     581             :     int         nparams;
     582             : 
     583           0 :     fprintf(f, "NegotiateProtocolVersion\t");
     584           0 :     pqTraceOutputInt32(f, message, cursor, false);
     585           0 :     nparams = pqTraceOutputInt32(f, message, cursor, false);
     586           0 :     for (int i = 0; i < nparams; i++)
     587             :     {
     588           0 :         pqTraceOutputString(f, message, cursor, false);
     589             :     }
     590           0 : }
     591             : 
     592             : static void
     593           0 : pqTraceOutput_FunctionCallResponse(FILE *f, const char *message, int *cursor)
     594             : {
     595             :     int         len;
     596             : 
     597           0 :     fprintf(f, "FunctionCallResponse\t");
     598           0 :     len = pqTraceOutputInt32(f, message, cursor, false);
     599           0 :     if (len != -1)
     600           0 :         pqTraceOutputNchar(f, len, message, cursor, false);
     601           0 : }
     602             : 
     603             : static void
     604           0 : pqTraceOutput_CopyBothResponse(FILE *f, const char *message, int *cursor, int length)
     605             : {
     606           0 :     fprintf(f, "CopyBothResponse\t");
     607           0 :     pqTraceOutputByte1(f, message, cursor);
     608             : 
     609           0 :     while (length > *cursor)
     610           0 :         pqTraceOutputInt16(f, message, cursor);
     611           0 : }
     612             : 
     613             : static void
     614          58 : pqTraceOutput_ReadyForQuery(FILE *f, const char *message, int *cursor)
     615             : {
     616          58 :     fprintf(f, "ReadyForQuery\t");
     617          58 :     pqTraceOutputByte1(f, message, cursor);
     618          58 : }
     619             : 
     620             : /*
     621             :  * Print the given message to the trace output stream.
     622             :  */
     623             : void
     624         828 : pqTraceOutputMessage(PGconn *conn, const char *message, bool toServer)
     625             : {
     626             :     char        id;
     627             :     int         length;
     628         828 :     char       *prefix = toServer ? "F" : "B";
     629         828 :     int         logCursor = 0;
     630             :     bool        regress;
     631             : 
     632         828 :     if ((conn->traceFlags & PQTRACE_SUPPRESS_TIMESTAMPS) == 0)
     633             :     {
     634             :         char        timestr[128];
     635             : 
     636           0 :         pqTraceFormatTimestamp(timestr, sizeof(timestr));
     637           0 :         fprintf(conn->Pfdebug, "%s\t", timestr);
     638             :     }
     639         828 :     regress = (conn->traceFlags & PQTRACE_REGRESS_MODE) != 0;
     640             : 
     641         828 :     id = message[logCursor++];
     642             : 
     643         828 :     memcpy(&length, message + logCursor, 4);
     644         828 :     length = (int) pg_ntoh32(length);
     645         828 :     logCursor += 4;
     646             : 
     647             :     /*
     648             :      * In regress mode, suppress the length of ErrorResponse and
     649             :      * NoticeResponse.  The F (file name), L (line number) and R (routine
     650             :      * name) fields can change as server code is modified, and if their
     651             :      * lengths differ from the originals, that would break tests.
     652             :      */
     653         828 :     if (regress && !toServer && (id == PqMsg_ErrorResponse || id == PqMsg_NoticeResponse))
     654          20 :         fprintf(conn->Pfdebug, "%s\tNN\t", prefix);
     655             :     else
     656         808 :         fprintf(conn->Pfdebug, "%s\t%d\t", prefix, length);
     657             : 
     658         828 :     switch (id)
     659             :     {
     660          62 :         case PqMsg_ParseComplete:
     661          62 :             fprintf(conn->Pfdebug, "ParseComplete");
     662             :             /* No message content */
     663          62 :             break;
     664          58 :         case PqMsg_BindComplete:
     665          58 :             fprintf(conn->Pfdebug, "BindComplete");
     666             :             /* No message content */
     667          58 :             break;
     668           8 :         case PqMsg_CloseComplete:
     669           8 :             fprintf(conn->Pfdebug, "CloseComplete");
     670             :             /* No message content */
     671           8 :             break;
     672           0 :         case PqMsg_NotificationResponse:
     673           0 :             pqTraceOutput_NotificationResponse(conn->Pfdebug, message, &logCursor, regress);
     674           0 :             break;
     675          72 :         case PqMsg_Bind:
     676          72 :             pqTraceOutput_Bind(conn->Pfdebug, message, &logCursor);
     677          72 :             break;
     678           0 :         case PqMsg_CopyDone:
     679           0 :             fprintf(conn->Pfdebug, "CopyDone");
     680             :             /* No message content */
     681           0 :             break;
     682          82 :         case PqMsg_CommandComplete:
     683             :             /* Close(F) and CommandComplete(B) use the same identifier. */
     684             :             Assert(PqMsg_Close == PqMsg_CommandComplete);
     685          82 :             if (toServer)
     686           8 :                 pqTraceOutput_Close(conn->Pfdebug, message, &logCursor);
     687             :             else
     688          74 :                 pqTraceOutput_CommandComplete(conn->Pfdebug, message, &logCursor);
     689          82 :             break;
     690           0 :         case PqMsg_CopyData:
     691           0 :             pqTraceOutput_CopyData(conn->Pfdebug, message, &logCursor,
     692             :                                    length, regress);
     693           0 :             break;
     694         164 :         case PqMsg_Describe:
     695             :             /* Describe(F) and DataRow(B) use the same identifier. */
     696             :             Assert(PqMsg_Describe == PqMsg_DataRow);
     697         164 :             if (toServer)
     698          80 :                 pqTraceOutput_Describe(conn->Pfdebug, message, &logCursor);
     699             :             else
     700          84 :                 pqTraceOutput_DataRow(conn->Pfdebug, message, &logCursor);
     701         164 :             break;
     702          86 :         case PqMsg_Execute:
     703             :             /* Execute(F) and ErrorResponse(B) use the same identifier. */
     704             :             Assert(PqMsg_Execute == PqMsg_ErrorResponse);
     705          86 :             if (toServer)
     706          72 :                 pqTraceOutput_Execute(conn->Pfdebug, message, &logCursor, regress);
     707             :             else
     708          14 :                 pqTraceOutput_ErrorResponse(conn->Pfdebug, message, &logCursor, regress);
     709          86 :             break;
     710           0 :         case PqMsg_CopyFail:
     711           0 :             pqTraceOutput_CopyFail(conn->Pfdebug, message, &logCursor);
     712           0 :             break;
     713           0 :         case PqMsg_GSSResponse:
     714             :             Assert(PqMsg_GSSResponse == PqMsg_PasswordMessage);
     715             :             Assert(PqMsg_GSSResponse == PqMsg_SASLInitialResponse);
     716             :             Assert(PqMsg_GSSResponse == PqMsg_SASLResponse);
     717             : 
     718             :             /*
     719             :              * These messages share a common type byte, so we discriminate by
     720             :              * having the code store the auth type separately.
     721             :              */
     722           0 :             switch (conn->current_auth_response)
     723             :             {
     724           0 :                 case AUTH_RESPONSE_GSS:
     725           0 :                     pqTraceOutput_GSSResponse(conn->Pfdebug, message,
     726             :                                               &logCursor, length, regress);
     727           0 :                     break;
     728           0 :                 case AUTH_RESPONSE_PASSWORD:
     729           0 :                     pqTraceOutput_PasswordMessage(conn->Pfdebug, message,
     730             :                                                   &logCursor);
     731           0 :                     break;
     732           0 :                 case AUTH_RESPONSE_SASL_INITIAL:
     733           0 :                     pqTraceOutput_SASLInitialResponse(conn->Pfdebug, message,
     734             :                                                       &logCursor, regress);
     735           0 :                     break;
     736           0 :                 case AUTH_RESPONSE_SASL:
     737           0 :                     pqTraceOutput_SASLResponse(conn->Pfdebug, message,
     738             :                                                &logCursor, length, regress);
     739           0 :                     break;
     740           0 :                 default:
     741           0 :                     fprintf(conn->Pfdebug, "UnknownAuthenticationResponse");
     742           0 :                     break;
     743             :             }
     744           0 :             conn->current_auth_response = '\0';
     745           0 :             break;
     746           0 :         case PqMsg_FunctionCall:
     747           0 :             pqTraceOutput_FunctionCall(conn->Pfdebug, message, &logCursor, regress);
     748           0 :             break;
     749           0 :         case PqMsg_CopyInResponse:
     750           0 :             pqTraceOutput_CopyInResponse(conn->Pfdebug, message, &logCursor);
     751           0 :             break;
     752          14 :         case PqMsg_Flush:
     753             :             /* Flush(F) and CopyOutResponse(B) use the same identifier */
     754             :             Assert(PqMsg_CopyOutResponse == PqMsg_Flush);
     755          14 :             if (toServer)
     756          14 :                 fprintf(conn->Pfdebug, "Flush");   /* no message content */
     757             :             else
     758           0 :                 pqTraceOutput_CopyOutResponse(conn->Pfdebug, message, &logCursor);
     759          14 :             break;
     760           0 :         case PqMsg_EmptyQueryResponse:
     761           0 :             fprintf(conn->Pfdebug, "EmptyQueryResponse");
     762             :             /* No message content */
     763           0 :             break;
     764           0 :         case PqMsg_BackendKeyData:
     765           0 :             pqTraceOutput_BackendKeyData(conn->Pfdebug, message, &logCursor, regress);
     766           0 :             break;
     767          10 :         case PqMsg_NoData:
     768          10 :             fprintf(conn->Pfdebug, "NoData");
     769             :             /* No message content */
     770          10 :             break;
     771           6 :         case PqMsg_NoticeResponse:
     772           6 :             pqTraceOutput_NoticeResponse(conn->Pfdebug, message, &logCursor, regress);
     773           6 :             break;
     774          72 :         case PqMsg_Parse:
     775          72 :             pqTraceOutput_Parse(conn->Pfdebug, message, &logCursor, regress);
     776          72 :             break;
     777          16 :         case PqMsg_Query:
     778          16 :             pqTraceOutput_Query(conn->Pfdebug, message, &logCursor);
     779          16 :             break;
     780           0 :         case PqMsg_AuthenticationRequest:
     781           0 :             pqTraceOutput_Authentication(conn->Pfdebug, message, &logCursor,
     782             :                                          length, regress);
     783           0 :             break;
     784           0 :         case PqMsg_PortalSuspended:
     785           0 :             fprintf(conn->Pfdebug, "PortalSuspended");
     786             :             /* No message content */
     787           0 :             break;
     788          42 :         case PqMsg_Sync:
     789             :             /* ParameterStatus(B) and Sync(F) use the same identifier */
     790             :             Assert(PqMsg_ParameterStatus == PqMsg_Sync);
     791          42 :             if (toServer)
     792          42 :                 fprintf(conn->Pfdebug, "Sync"); /* no message content */
     793             :             else
     794           0 :                 pqTraceOutput_ParameterStatus(conn->Pfdebug, message, &logCursor);
     795          42 :             break;
     796           2 :         case PqMsg_ParameterDescription:
     797           2 :             pqTraceOutput_ParameterDescription(conn->Pfdebug, message, &logCursor, regress);
     798           2 :             break;
     799          58 :         case PqMsg_RowDescription:
     800          58 :             pqTraceOutput_RowDescription(conn->Pfdebug, message, &logCursor, regress);
     801          58 :             break;
     802           0 :         case PqMsg_NegotiateProtocolVersion:
     803           0 :             pqTraceOutput_NegotiateProtocolVersion(conn->Pfdebug, message, &logCursor);
     804           0 :             break;
     805           0 :         case PqMsg_FunctionCallResponse:
     806           0 :             pqTraceOutput_FunctionCallResponse(conn->Pfdebug, message, &logCursor);
     807           0 :             break;
     808           0 :         case PqMsg_CopyBothResponse:
     809           0 :             pqTraceOutput_CopyBothResponse(conn->Pfdebug, message, &logCursor, length);
     810           0 :             break;
     811          18 :         case PqMsg_Terminate:
     812          18 :             fprintf(conn->Pfdebug, "Terminate");
     813             :             /* No message content */
     814          18 :             break;
     815          58 :         case PqMsg_ReadyForQuery:
     816          58 :             pqTraceOutput_ReadyForQuery(conn->Pfdebug, message, &logCursor);
     817          58 :             break;
     818           0 :         default:
     819           0 :             fprintf(conn->Pfdebug, "Unknown message: %02x", id);
     820           0 :             break;
     821             :     }
     822             : 
     823         828 :     fputc('\n', conn->Pfdebug);
     824             : 
     825             :     /*
     826             :      * Verify the printing routine did it right.  Note that the one-byte
     827             :      * message identifier is not included in the length, but our cursor does
     828             :      * include it.
     829             :      */
     830         828 :     if (logCursor - 1 != length)
     831           0 :         fprintf(conn->Pfdebug,
     832             :                 "mismatched message length: consumed %d, expected %d\n",
     833             :                 logCursor - 1, length);
     834         828 : }
     835             : 
     836             : /*
     837             :  * Print special messages (those containing no type byte) to the trace output
     838             :  * stream.
     839             :  */
     840             : void
     841           0 : pqTraceOutputNoTypeByteMessage(PGconn *conn, const char *message)
     842             : {
     843             :     int         length;
     844             :     int         version;
     845             :     bool        regress;
     846           0 :     int         logCursor = 0;
     847             : 
     848           0 :     regress = (conn->traceFlags & PQTRACE_REGRESS_MODE) != 0;
     849             : 
     850           0 :     if ((conn->traceFlags & PQTRACE_SUPPRESS_TIMESTAMPS) == 0)
     851             :     {
     852             :         char        timestr[128];
     853             : 
     854           0 :         pqTraceFormatTimestamp(timestr, sizeof(timestr));
     855           0 :         fprintf(conn->Pfdebug, "%s\t", timestr);
     856             :     }
     857             : 
     858           0 :     memcpy(&length, message + logCursor, 4);
     859           0 :     length = (int) pg_ntoh32(length);
     860           0 :     logCursor += 4;
     861             : 
     862           0 :     fprintf(conn->Pfdebug, "F\t%d\t", length);
     863             : 
     864           0 :     if (length < 8)
     865             :     {
     866           0 :         fprintf(conn->Pfdebug, "Unknown message\n");
     867           0 :         return;
     868             :     }
     869             : 
     870           0 :     memcpy(&version, message + logCursor, 4);
     871           0 :     version = (int) pg_ntoh32(version);
     872             : 
     873           0 :     if (version == CANCEL_REQUEST_CODE && length >= 16)
     874             :     {
     875           0 :         fprintf(conn->Pfdebug, "CancelRequest\t");
     876           0 :         pqTraceOutputInt16(conn->Pfdebug, message, &logCursor);
     877           0 :         pqTraceOutputInt16(conn->Pfdebug, message, &logCursor);
     878           0 :         pqTraceOutputInt32(conn->Pfdebug, message, &logCursor, regress);
     879           0 :         pqTraceOutputInt32(conn->Pfdebug, message, &logCursor, regress);
     880             :     }
     881           0 :     else if (version == NEGOTIATE_SSL_CODE)
     882             :     {
     883           0 :         fprintf(conn->Pfdebug, "SSLRequest\t");
     884           0 :         pqTraceOutputInt16(conn->Pfdebug, message, &logCursor);
     885           0 :         pqTraceOutputInt16(conn->Pfdebug, message, &logCursor);
     886             :     }
     887           0 :     else if (version == NEGOTIATE_GSS_CODE)
     888             :     {
     889           0 :         fprintf(conn->Pfdebug, "GSSENCRequest\t");
     890           0 :         pqTraceOutputInt16(conn->Pfdebug, message, &logCursor);
     891           0 :         pqTraceOutputInt16(conn->Pfdebug, message, &logCursor);
     892             :     }
     893             :     else
     894             :     {
     895           0 :         fprintf(conn->Pfdebug, "StartupMessage\t");
     896           0 :         pqTraceOutputInt16(conn->Pfdebug, message, &logCursor);
     897           0 :         pqTraceOutputInt16(conn->Pfdebug, message, &logCursor);
     898           0 :         while (message[logCursor] != '\0')
     899             :         {
     900             :             /* XXX should we suppress anything in regress mode? */
     901           0 :             pqTraceOutputString(conn->Pfdebug, message, &logCursor, false);
     902           0 :             pqTraceOutputString(conn->Pfdebug, message, &logCursor, false);
     903             :         }
     904             :     }
     905             : 
     906           0 :     fputc('\n', conn->Pfdebug);
     907             : }
     908             : 
     909             : /*
     910             :  * Trace a single-byte backend response received for a known request
     911             :  * type the frontend previously sent.  Only useful for the simplest of
     912             :  * FE/BE interaction workflows such as SSL/GSS encryption requests.
     913             :  */
     914             : void
     915           0 : pqTraceOutputCharResponse(PGconn *conn, const char *responseType,
     916             :                           char response)
     917             : {
     918           0 :     if ((conn->traceFlags & PQTRACE_SUPPRESS_TIMESTAMPS) == 0)
     919             :     {
     920             :         char        timestr[128];
     921             : 
     922           0 :         pqTraceFormatTimestamp(timestr, sizeof(timestr));
     923           0 :         fprintf(conn->Pfdebug, "%s\t", timestr);
     924             :     }
     925             : 
     926           0 :     fprintf(conn->Pfdebug, "B\t1\t%s\t %c\n", responseType, response);
     927           0 : }

Generated by: LCOV version 1.14