1 /*-------------------------------------------------------------------------
2 *
3 * fe-trace.c
4 * functions for libpq protocol tracing
5 *
6 * Portions Copyright (c) 1996-2021, 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
PQtrace(PGconn * conn,FILE * debug_port)35 PQtrace(PGconn *conn, FILE *debug_port)
36 {
37 if (conn == NULL)
38 return;
39 PQuntrace(conn);
40 if (debug_port == NULL)
41 return;
42
43 conn->Pfdebug = debug_port;
44 conn->traceFlags = 0;
45 }
46
47 /* Disable tracing */
48 void
PQuntrace(PGconn * conn)49 PQuntrace(PGconn *conn)
50 {
51 if (conn == NULL)
52 return;
53 if (conn->Pfdebug)
54 {
55 fflush(conn->Pfdebug);
56 conn->Pfdebug = NULL;
57 }
58
59 conn->traceFlags = 0;
60 }
61
62 /* Set flags for current tracing session */
63 void
PQsetTraceFlags(PGconn * conn,int flags)64 PQsetTraceFlags(PGconn *conn, int flags)
65 {
66 if (conn == NULL)
67 return;
68 /* If PQtrace() failed, do nothing. */
69 if (conn->Pfdebug == NULL)
70 return;
71 conn->traceFlags = flags;
72 }
73
74 /*
75 * Print the current time, with microseconds, into a caller-supplied
76 * buffer.
77 * Cribbed from setup_formatted_log_time, but much simpler.
78 */
79 static void
pqTraceFormatTimestamp(char * timestr,size_t ts_len)80 pqTraceFormatTimestamp(char *timestr, size_t ts_len)
81 {
82 struct timeval tval;
83 time_t now;
84
85 gettimeofday(&tval, NULL);
86
87 /*
88 * MSVC's implementation of timeval uses a long for tv_sec, however,
89 * localtime() expects a time_t pointer. Here we'll assign tv_sec to a
90 * local time_t variable so that we pass localtime() the correct pointer
91 * type.
92 */
93 now = tval.tv_sec;
94 strftime(timestr, ts_len,
95 "%Y-%m-%d %H:%M:%S",
96 localtime(&now));
97 /* append microseconds */
98 snprintf(timestr + strlen(timestr), ts_len - strlen(timestr),
99 ".%06u", (unsigned int) (tval.tv_usec));
100 }
101
102 /*
103 * pqTraceOutputByte1: output a 1-char message to the log
104 */
105 static void
pqTraceOutputByte1(FILE * pfdebug,const char * data,int * cursor)106 pqTraceOutputByte1(FILE *pfdebug, const char *data, int *cursor)
107 {
108 const char *v = data + *cursor;
109
110 /*
111 * Show non-printable data in hex format, including the terminating \0
112 * that completes ErrorResponse and NoticeResponse messages.
113 */
114 if (!isprint((unsigned char) *v))
115 fprintf(pfdebug, " \\x%02x", *v);
116 else
117 fprintf(pfdebug, " %c", *v);
118 *cursor += 1;
119 }
120
121 /*
122 * pqTraceOutputInt16: output a 2-byte integer message to the log
123 */
124 static int
pqTraceOutputInt16(FILE * pfdebug,const char * data,int * cursor)125 pqTraceOutputInt16(FILE *pfdebug, const char *data, int *cursor)
126 {
127 uint16 tmp;
128 int result;
129
130 memcpy(&tmp, data + *cursor, 2);
131 *cursor += 2;
132 result = (int) pg_ntoh16(tmp);
133 fprintf(pfdebug, " %d", result);
134
135 return result;
136 }
137
138 /*
139 * pqTraceOutputInt32: output a 4-byte integer message to the log
140 *
141 * If 'suppress' is true, print a literal NNNN instead of the actual number.
142 */
143 static int
pqTraceOutputInt32(FILE * pfdebug,const char * data,int * cursor,bool suppress)144 pqTraceOutputInt32(FILE *pfdebug, const char *data, int *cursor, bool suppress)
145 {
146 int result;
147
148 memcpy(&result, data + *cursor, 4);
149 *cursor += 4;
150 result = (int) pg_ntoh32(result);
151 if (suppress)
152 fprintf(pfdebug, " NNNN");
153 else
154 fprintf(pfdebug, " %d", result);
155
156 return result;
157 }
158
159 /*
160 * pqTraceOutputString: output a string message to the log
161 */
162 static void
pqTraceOutputString(FILE * pfdebug,const char * data,int * cursor,bool suppress)163 pqTraceOutputString(FILE *pfdebug, const char *data, int *cursor, bool suppress)
164 {
165 int len;
166
167 if (suppress)
168 {
169 fprintf(pfdebug, " \"SSSS\"");
170 *cursor += strlen(data + *cursor) + 1;
171 }
172 else
173 {
174 len = fprintf(pfdebug, " \"%s\"", data + *cursor);
175
176 /*
177 * This is a null-terminated string. So add 1 after subtracting 3
178 * which is the double quotes and space length from len.
179 */
180 *cursor += (len - 3 + 1);
181 }
182 }
183
184 /*
185 * pqTraceOutputNchar: output a string of exactly len bytes message to the log
186 */
187 static void
pqTraceOutputNchar(FILE * pfdebug,int len,const char * data,int * cursor)188 pqTraceOutputNchar(FILE *pfdebug, int len, const char *data, int *cursor)
189 {
190 int i,
191 next; /* first char not yet printed */
192 const char *v = data + *cursor;
193
194 fprintf(pfdebug, " \'");
195
196 for (next = i = 0; i < len; ++i)
197 {
198 if (isprint((unsigned char) v[i]))
199 continue;
200 else
201 {
202 fwrite(v + next, 1, i - next, pfdebug);
203 fprintf(pfdebug, "\\x%02x", v[i]);
204 next = i + 1;
205 }
206 }
207 if (next < len)
208 fwrite(v + next, 1, len - next, pfdebug);
209
210 fprintf(pfdebug, "\'");
211 *cursor += len;
212 }
213
214 /*
215 * Output functions by protocol message type
216 */
217
218 /* NotificationResponse */
219 static void
pqTraceOutputA(FILE * f,const char * message,int * cursor,bool regress)220 pqTraceOutputA(FILE *f, const char *message, int *cursor, bool regress)
221 {
222 fprintf(f, "NotificationResponse\t");
223 pqTraceOutputInt32(f, message, cursor, regress);
224 pqTraceOutputString(f, message, cursor, false);
225 pqTraceOutputString(f, message, cursor, false);
226 }
227
228 /* Bind */
229 static void
pqTraceOutputB(FILE * f,const char * message,int * cursor)230 pqTraceOutputB(FILE *f, const char *message, int *cursor)
231 {
232 int nparams;
233
234 fprintf(f, "Bind\t");
235 pqTraceOutputString(f, message, cursor, false);
236 pqTraceOutputString(f, message, cursor, false);
237 nparams = pqTraceOutputInt16(f, message, cursor);
238
239 for (int i = 0; i < nparams; i++)
240 pqTraceOutputInt16(f, message, cursor);
241
242 nparams = pqTraceOutputInt16(f, message, cursor);
243
244 for (int i = 0; i < nparams; i++)
245 {
246 int nbytes;
247
248 nbytes = pqTraceOutputInt32(f, message, cursor, false);
249 if (nbytes == -1)
250 continue;
251 pqTraceOutputNchar(f, nbytes, message, cursor);
252 }
253
254 nparams = pqTraceOutputInt16(f, message, cursor);
255 for (int i = 0; i < nparams; i++)
256 pqTraceOutputInt16(f, message, cursor);
257 }
258
259 /* Close(F) or CommandComplete(B) */
260 static void
pqTraceOutputC(FILE * f,bool toServer,const char * message,int * cursor)261 pqTraceOutputC(FILE *f, bool toServer, const char *message, int *cursor)
262 {
263 if (toServer)
264 {
265 fprintf(f, "Close\t");
266 pqTraceOutputByte1(f, message, cursor);
267 pqTraceOutputString(f, message, cursor, false);
268 }
269 else
270 {
271 fprintf(f, "CommandComplete\t");
272 pqTraceOutputString(f, message, cursor, false);
273 }
274 }
275
276 /* Describe(F) or DataRow(B) */
277 static void
pqTraceOutputD(FILE * f,bool toServer,const char * message,int * cursor)278 pqTraceOutputD(FILE *f, bool toServer, const char *message, int *cursor)
279 {
280 if (toServer)
281 {
282 fprintf(f, "Describe\t");
283 pqTraceOutputByte1(f, message, cursor);
284 pqTraceOutputString(f, message, cursor, false);
285 }
286 else
287 {
288 int nfields;
289 int len;
290 int i;
291
292 fprintf(f, "DataRow\t");
293 nfields = pqTraceOutputInt16(f, message, cursor);
294 for (i = 0; i < nfields; i++)
295 {
296 len = pqTraceOutputInt32(f, message, cursor, false);
297 if (len == -1)
298 continue;
299 pqTraceOutputNchar(f, len, message, cursor);
300 }
301 }
302 }
303
304 /* NoticeResponse / ErrorResponse */
305 static void
pqTraceOutputNR(FILE * f,const char * type,const char * message,int * cursor,bool regress)306 pqTraceOutputNR(FILE *f, const char *type, const char *message, int *cursor,
307 bool regress)
308 {
309 fprintf(f, "%s\t", type);
310 for (;;)
311 {
312 char field;
313 bool suppress;
314
315 pqTraceOutputByte1(f, message, cursor);
316 field = message[*cursor - 1];
317 if (field == '\0')
318 break;
319
320 suppress = regress && (field == 'L' || field == 'F' || field == 'R');
321 pqTraceOutputString(f, message, cursor, suppress);
322 }
323 }
324
325 /* Execute(F) or ErrorResponse(B) */
326 static void
pqTraceOutputE(FILE * f,bool toServer,const char * message,int * cursor,bool regress)327 pqTraceOutputE(FILE *f, bool toServer, const char *message, int *cursor, bool regress)
328 {
329 if (toServer)
330 {
331 fprintf(f, "Execute\t");
332 pqTraceOutputString(f, message, cursor, false);
333 pqTraceOutputInt32(f, message, cursor, false);
334 }
335 else
336 pqTraceOutputNR(f, "ErrorResponse", message, cursor, regress);
337 }
338
339 /* CopyFail */
340 static void
pqTraceOutputf(FILE * f,const char * message,int * cursor)341 pqTraceOutputf(FILE *f, const char *message, int *cursor)
342 {
343 fprintf(f, "CopyFail\t");
344 pqTraceOutputString(f, message, cursor, false);
345 }
346
347 /* FunctionCall */
348 static void
pqTraceOutputF(FILE * f,const char * message,int * cursor,bool regress)349 pqTraceOutputF(FILE *f, const char *message, int *cursor, bool regress)
350 {
351 int nfields;
352 int nbytes;
353
354 fprintf(f, "FunctionCall\t");
355 pqTraceOutputInt32(f, message, cursor, regress);
356 nfields = pqTraceOutputInt16(f, message, cursor);
357
358 for (int i = 0; i < nfields; i++)
359 pqTraceOutputInt16(f, message, cursor);
360
361 nfields = pqTraceOutputInt16(f, message, cursor);
362
363 for (int i = 0; i < nfields; i++)
364 {
365 nbytes = pqTraceOutputInt32(f, message, cursor, false);
366 if (nbytes == -1)
367 continue;
368 pqTraceOutputNchar(f, nbytes, message, cursor);
369 }
370
371 pqTraceOutputInt16(f, message, cursor);
372 }
373
374 /* CopyInResponse */
375 static void
pqTraceOutputG(FILE * f,const char * message,int * cursor)376 pqTraceOutputG(FILE *f, const char *message, int *cursor)
377 {
378 int nfields;
379
380 fprintf(f, "CopyInResponse\t");
381 pqTraceOutputByte1(f, message, cursor);
382 nfields = pqTraceOutputInt16(f, message, cursor);
383
384 for (int i = 0; i < nfields; i++)
385 pqTraceOutputInt16(f, message, cursor);
386 }
387
388 /* CopyOutResponse */
389 static void
pqTraceOutputH(FILE * f,const char * message,int * cursor)390 pqTraceOutputH(FILE *f, const char *message, int *cursor)
391 {
392 int nfields;
393
394 fprintf(f, "CopyOutResponse\t");
395 pqTraceOutputByte1(f, message, cursor);
396 nfields = pqTraceOutputInt16(f, message, cursor);
397
398 for (int i = 0; i < nfields; i++)
399 pqTraceOutputInt16(f, message, cursor);
400 }
401
402 /* BackendKeyData */
403 static void
pqTraceOutputK(FILE * f,const char * message,int * cursor,bool regress)404 pqTraceOutputK(FILE *f, const char *message, int *cursor, bool regress)
405 {
406 fprintf(f, "BackendKeyData\t");
407 pqTraceOutputInt32(f, message, cursor, regress);
408 pqTraceOutputInt32(f, message, cursor, regress);
409 }
410
411 /* Parse */
412 static void
pqTraceOutputP(FILE * f,const char * message,int * cursor,bool regress)413 pqTraceOutputP(FILE *f, const char *message, int *cursor, bool regress)
414 {
415 int nparams;
416
417 fprintf(f, "Parse\t");
418 pqTraceOutputString(f, message, cursor, false);
419 pqTraceOutputString(f, message, cursor, false);
420 nparams = pqTraceOutputInt16(f, message, cursor);
421
422 for (int i = 0; i < nparams; i++)
423 pqTraceOutputInt32(f, message, cursor, regress);
424 }
425
426 /* Query */
427 static void
pqTraceOutputQ(FILE * f,const char * message,int * cursor)428 pqTraceOutputQ(FILE *f, const char *message, int *cursor)
429 {
430 fprintf(f, "Query\t");
431 pqTraceOutputString(f, message, cursor, false);
432 }
433
434 /* Authentication */
435 static void
pqTraceOutputR(FILE * f,const char * message,int * cursor)436 pqTraceOutputR(FILE *f, const char *message, int *cursor)
437 {
438 fprintf(f, "Authentication\t");
439 pqTraceOutputInt32(f, message, cursor, false);
440 }
441
442 /* ParameterStatus */
443 static void
pqTraceOutputS(FILE * f,const char * message,int * cursor)444 pqTraceOutputS(FILE *f, const char *message, int *cursor)
445 {
446 fprintf(f, "ParameterStatus\t");
447 pqTraceOutputString(f, message, cursor, false);
448 pqTraceOutputString(f, message, cursor, false);
449 }
450
451 /* ParameterDescription */
452 static void
pqTraceOutputt(FILE * f,const char * message,int * cursor,bool regress)453 pqTraceOutputt(FILE *f, const char *message, int *cursor, bool regress)
454 {
455 int nfields;
456
457 fprintf(f, "ParameterDescription\t");
458 nfields = pqTraceOutputInt16(f, message, cursor);
459
460 for (int i = 0; i < nfields; i++)
461 pqTraceOutputInt32(f, message, cursor, regress);
462 }
463
464 /* RowDescription */
465 static void
pqTraceOutputT(FILE * f,const char * message,int * cursor,bool regress)466 pqTraceOutputT(FILE *f, const char *message, int *cursor, bool regress)
467 {
468 int nfields;
469
470 fprintf(f, "RowDescription\t");
471 nfields = pqTraceOutputInt16(f, message, cursor);
472
473 for (int i = 0; i < nfields; i++)
474 {
475 pqTraceOutputString(f, message, cursor, false);
476 pqTraceOutputInt32(f, message, cursor, regress);
477 pqTraceOutputInt16(f, message, cursor);
478 pqTraceOutputInt32(f, message, cursor, regress);
479 pqTraceOutputInt16(f, message, cursor);
480 pqTraceOutputInt32(f, message, cursor, false);
481 pqTraceOutputInt16(f, message, cursor);
482 }
483 }
484
485 /* NegotiateProtocolVersion */
486 static void
pqTraceOutputv(FILE * f,const char * message,int * cursor)487 pqTraceOutputv(FILE *f, const char *message, int *cursor)
488 {
489 fprintf(f, "NegotiateProtocolVersion\t");
490 pqTraceOutputInt32(f, message, cursor, false);
491 pqTraceOutputInt32(f, message, cursor, false);
492 }
493
494 /* FunctionCallResponse */
495 static void
pqTraceOutputV(FILE * f,const char * message,int * cursor)496 pqTraceOutputV(FILE *f, const char *message, int *cursor)
497 {
498 int len;
499
500 fprintf(f, "FunctionCallResponse\t");
501 len = pqTraceOutputInt32(f, message, cursor, false);
502 if (len != -1)
503 pqTraceOutputNchar(f, len, message, cursor);
504 }
505
506 /* CopyBothResponse */
507 static void
pqTraceOutputW(FILE * f,const char * message,int * cursor,int length)508 pqTraceOutputW(FILE *f, const char *message, int *cursor, int length)
509 {
510 fprintf(f, "CopyBothResponse\t");
511 pqTraceOutputByte1(f, message, cursor);
512
513 while (length > *cursor)
514 pqTraceOutputInt16(f, message, cursor);
515 }
516
517 /* ReadyForQuery */
518 static void
pqTraceOutputZ(FILE * f,const char * message,int * cursor)519 pqTraceOutputZ(FILE *f, const char *message, int *cursor)
520 {
521 fprintf(f, "ReadyForQuery\t");
522 pqTraceOutputByte1(f, message, cursor);
523 }
524
525 /*
526 * Print the given message to the trace output stream.
527 */
528 void
pqTraceOutputMessage(PGconn * conn,const char * message,bool toServer)529 pqTraceOutputMessage(PGconn *conn, const char *message, bool toServer)
530 {
531 char id;
532 int length;
533 char *prefix = toServer ? "F" : "B";
534 int logCursor = 0;
535 bool regress;
536
537 if ((conn->traceFlags & PQTRACE_SUPPRESS_TIMESTAMPS) == 0)
538 {
539 char timestr[128];
540
541 pqTraceFormatTimestamp(timestr, sizeof(timestr));
542 fprintf(conn->Pfdebug, "%s\t", timestr);
543 }
544 regress = (conn->traceFlags & PQTRACE_REGRESS_MODE) != 0;
545
546 id = message[logCursor++];
547
548 memcpy(&length, message + logCursor, 4);
549 length = (int) pg_ntoh32(length);
550 logCursor += 4;
551
552 /*
553 * In regress mode, suppress the length of ErrorResponse and
554 * NoticeResponse. The F (file name), L (line number) and R (routine
555 * name) fields can change as server code is modified, and if their
556 * lengths differ from the originals, that would break tests.
557 */
558 if (regress && !toServer && (id == 'E' || id == 'N'))
559 fprintf(conn->Pfdebug, "%s\tNN\t", prefix);
560 else
561 fprintf(conn->Pfdebug, "%s\t%d\t", prefix, length);
562
563 switch (id)
564 {
565 case '1':
566 fprintf(conn->Pfdebug, "ParseComplete");
567 /* No message content */
568 break;
569 case '2':
570 fprintf(conn->Pfdebug, "BindComplete");
571 /* No message content */
572 break;
573 case '3':
574 fprintf(conn->Pfdebug, "CloseComplete");
575 /* No message content */
576 break;
577 case 'A': /* Notification Response */
578 pqTraceOutputA(conn->Pfdebug, message, &logCursor, regress);
579 break;
580 case 'B': /* Bind */
581 pqTraceOutputB(conn->Pfdebug, message, &logCursor);
582 break;
583 case 'c':
584 fprintf(conn->Pfdebug, "CopyDone");
585 /* No message content */
586 break;
587 case 'C': /* Close(F) or Command Complete(B) */
588 pqTraceOutputC(conn->Pfdebug, toServer, message, &logCursor);
589 break;
590 case 'd': /* Copy Data */
591 /* Drop COPY data to reduce the overhead of logging. */
592 break;
593 case 'D': /* Describe(F) or Data Row(B) */
594 pqTraceOutputD(conn->Pfdebug, toServer, message, &logCursor);
595 break;
596 case 'E': /* Execute(F) or Error Response(B) */
597 pqTraceOutputE(conn->Pfdebug, toServer, message, &logCursor,
598 regress);
599 break;
600 case 'f': /* Copy Fail */
601 pqTraceOutputf(conn->Pfdebug, message, &logCursor);
602 break;
603 case 'F': /* Function Call */
604 pqTraceOutputF(conn->Pfdebug, message, &logCursor, regress);
605 break;
606 case 'G': /* Start Copy In */
607 pqTraceOutputG(conn->Pfdebug, message, &logCursor);
608 break;
609 case 'H': /* Flush(F) or Start Copy Out(B) */
610 if (!toServer)
611 pqTraceOutputH(conn->Pfdebug, message, &logCursor);
612 else
613 fprintf(conn->Pfdebug, "Flush"); /* no message content */
614 break;
615 case 'I':
616 fprintf(conn->Pfdebug, "EmptyQueryResponse");
617 /* No message content */
618 break;
619 case 'K': /* secret key data from the backend */
620 pqTraceOutputK(conn->Pfdebug, message, &logCursor, regress);
621 break;
622 case 'n':
623 fprintf(conn->Pfdebug, "NoData");
624 /* No message content */
625 break;
626 case 'N':
627 pqTraceOutputNR(conn->Pfdebug, "NoticeResponse", message,
628 &logCursor, regress);
629 break;
630 case 'P': /* Parse */
631 pqTraceOutputP(conn->Pfdebug, message, &logCursor, regress);
632 break;
633 case 'Q': /* Query */
634 pqTraceOutputQ(conn->Pfdebug, message, &logCursor);
635 break;
636 case 'R': /* Authentication */
637 pqTraceOutputR(conn->Pfdebug, message, &logCursor);
638 break;
639 case 's':
640 fprintf(conn->Pfdebug, "PortalSuspended");
641 /* No message content */
642 break;
643 case 'S': /* Parameter Status(B) or Sync(F) */
644 if (!toServer)
645 pqTraceOutputS(conn->Pfdebug, message, &logCursor);
646 else
647 fprintf(conn->Pfdebug, "Sync"); /* no message content */
648 break;
649 case 't': /* Parameter Description */
650 pqTraceOutputt(conn->Pfdebug, message, &logCursor, regress);
651 break;
652 case 'T': /* Row Description */
653 pqTraceOutputT(conn->Pfdebug, message, &logCursor, regress);
654 break;
655 case 'v': /* Negotiate Protocol Version */
656 pqTraceOutputv(conn->Pfdebug, message, &logCursor);
657 break;
658 case 'V': /* Function Call response */
659 pqTraceOutputV(conn->Pfdebug, message, &logCursor);
660 break;
661 case 'W': /* Start Copy Both */
662 pqTraceOutputW(conn->Pfdebug, message, &logCursor, length);
663 break;
664 case 'X':
665 fprintf(conn->Pfdebug, "Terminate");
666 /* No message content */
667 break;
668 case 'Z': /* Ready For Query */
669 pqTraceOutputZ(conn->Pfdebug, message, &logCursor);
670 break;
671 default:
672 fprintf(conn->Pfdebug, "Unknown message: %02x", id);
673 break;
674 }
675
676 fputc('\n', conn->Pfdebug);
677
678 /*
679 * Verify the printing routine did it right. Note that the one-byte
680 * message identifier is not included in the length, but our cursor does
681 * include it.
682 */
683 if (logCursor - 1 != length)
684 fprintf(conn->Pfdebug,
685 "mismatched message length: consumed %d, expected %d\n",
686 logCursor - 1, length);
687 }
688
689 /*
690 * Print special messages (those containing no type byte) to the trace output
691 * stream.
692 */
693 void
pqTraceOutputNoTypeByteMessage(PGconn * conn,const char * message)694 pqTraceOutputNoTypeByteMessage(PGconn *conn, const char *message)
695 {
696 int length;
697 int logCursor = 0;
698
699 if ((conn->traceFlags & PQTRACE_SUPPRESS_TIMESTAMPS) == 0)
700 {
701 char timestr[128];
702
703 pqTraceFormatTimestamp(timestr, sizeof(timestr));
704 fprintf(conn->Pfdebug, "%s\t", timestr);
705 }
706
707 memcpy(&length, message + logCursor, 4);
708 length = (int) pg_ntoh32(length);
709 logCursor += 4;
710
711 fprintf(conn->Pfdebug, "F\t%d\t", length);
712
713 switch (length)
714 {
715 case 16: /* CancelRequest */
716 fprintf(conn->Pfdebug, "CancelRequest\t");
717 pqTraceOutputInt32(conn->Pfdebug, message, &logCursor, false);
718 pqTraceOutputInt32(conn->Pfdebug, message, &logCursor, false);
719 pqTraceOutputInt32(conn->Pfdebug, message, &logCursor, false);
720 break;
721 case 8: /* GSSENCRequest or SSLRequest */
722 /* These messages do not reach here. */
723 default:
724 fprintf(conn->Pfdebug, "Unknown message: length is %d", length);
725 break;
726 }
727
728 fputc('\n', conn->Pfdebug);
729 }
730