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