TLA Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * pqformat.c
4 : * Routines for formatting and parsing frontend/backend messages
5 : *
6 : * Outgoing messages are built up in a StringInfo buffer (which is expansible)
7 : * and then sent in a single call to pq_putmessage. This module provides data
8 : * formatting/conversion routines that are needed to produce valid messages.
9 : * Note in particular the distinction between "raw data" and "text"; raw data
10 : * is message protocol characters and binary values that are not subject to
11 : * character set conversion, while text is converted by character encoding
12 : * rules.
13 : *
14 : * Incoming messages are similarly read into a StringInfo buffer, via
15 : * pq_getmessage, and then parsed and converted from that using the routines
16 : * in this module.
17 : *
18 : * These same routines support reading and writing of external binary formats
19 : * (typsend/typreceive routines). The conversion routines for individual
20 : * data types are exactly the same, only initialization and completion
21 : * are different.
22 : *
23 : *
24 : * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
25 : * Portions Copyright (c) 1994, Regents of the University of California
26 : *
27 : * src/backend/libpq/pqformat.c
28 : *
29 : *-------------------------------------------------------------------------
30 : */
31 : /*
32 : * INTERFACE ROUTINES
33 : * Message assembly and output:
34 : * pq_beginmessage - initialize StringInfo buffer
35 : * pq_sendbyte - append a raw byte to a StringInfo buffer
36 : * pq_sendint - append a binary integer to a StringInfo buffer
37 : * pq_sendint64 - append a binary 8-byte int to a StringInfo buffer
38 : * pq_sendfloat4 - append a float4 to a StringInfo buffer
39 : * pq_sendfloat8 - append a float8 to a StringInfo buffer
40 : * pq_sendbytes - append raw data to a StringInfo buffer
41 : * pq_sendcountedtext - append a counted text string (with character set conversion)
42 : * pq_sendtext - append a text string (with conversion)
43 : * pq_sendstring - append a null-terminated text string (with conversion)
44 : * pq_send_ascii_string - append a null-terminated text string (without conversion)
45 : * pq_endmessage - send the completed message to the frontend
46 : * Note: it is also possible to append data to the StringInfo buffer using
47 : * the regular StringInfo routines, but this is discouraged since required
48 : * character set conversion may not occur.
49 : *
50 : * typsend support (construct a bytea value containing external binary data):
51 : * pq_begintypsend - initialize StringInfo buffer
52 : * pq_endtypsend - return the completed string as a "bytea*"
53 : *
54 : * Special-case message output:
55 : * pq_puttextmessage - generate a character set-converted message in one step
56 : * pq_putemptymessage - convenience routine for message with empty body
57 : *
58 : * Message parsing after input:
59 : * pq_getmsgbyte - get a raw byte from a message buffer
60 : * pq_getmsgint - get a binary integer from a message buffer
61 : * pq_getmsgint64 - get a binary 8-byte int from a message buffer
62 : * pq_getmsgfloat4 - get a float4 from a message buffer
63 : * pq_getmsgfloat8 - get a float8 from a message buffer
64 : * pq_getmsgbytes - get raw data from a message buffer
65 : * pq_copymsgbytes - copy raw data from a message buffer
66 : * pq_getmsgtext - get a counted text string (with conversion)
67 : * pq_getmsgstring - get a null-terminated text string (with conversion)
68 : * pq_getmsgrawstring - get a null-terminated text string - NO conversion
69 : * pq_getmsgend - verify message fully consumed
70 : */
71 :
72 : #include "postgres.h"
73 :
74 : #include <sys/param.h>
75 :
76 : #include "libpq/libpq.h"
77 : #include "libpq/pqformat.h"
78 : #include "mb/pg_wchar.h"
79 : #include "port/pg_bswap.h"
80 : #include "varatt.h"
81 :
82 :
83 : /* --------------------------------
84 : * pq_beginmessage - initialize for sending a message
85 : * --------------------------------
86 : */
87 : void
88 GIC 430651 : pq_beginmessage(StringInfo buf, char msgtype)
89 ECB : {
90 GIC 430651 : initStringInfo(buf);
91 ECB :
92 : /*
93 : * We stash the message type into the buffer's cursor field, expecting
94 : * that the pq_sendXXX routines won't touch it. We could alternatively
95 : * make it the first byte of the buffer contents, but this seems easier.
96 : */
97 GIC 430651 : buf->cursor = msgtype;
98 CBC 430651 : }
99 ECB :
100 : /* --------------------------------
101 :
102 : * pq_beginmessage_reuse - initialize for sending a message, reuse buffer
103 : *
104 : * This requires the buffer to be allocated in a sufficiently long-lived
105 : * memory context.
106 : * --------------------------------
107 : */
108 : void
109 GIC 3932911 : pq_beginmessage_reuse(StringInfo buf, char msgtype)
110 ECB : {
111 GIC 3932911 : resetStringInfo(buf);
112 ECB :
113 : /*
114 : * We stash the message type into the buffer's cursor field, expecting
115 : * that the pq_sendXXX routines won't touch it. We could alternatively
116 : * make it the first byte of the buffer contents, but this seems easier.
117 : */
118 GIC 3932911 : buf->cursor = msgtype;
119 CBC 3932911 : }
120 ECB :
121 : /* --------------------------------
122 : * pq_sendbytes - append raw data to a StringInfo buffer
123 : * --------------------------------
124 : */
125 : void
126 GNC 123978 : pq_sendbytes(StringInfo buf, const void *data, int datalen)
127 ECB : {
128 : /* use variant that maintains a trailing null-byte, out of caution */
129 GIC 123978 : appendBinaryStringInfo(buf, data, datalen);
130 CBC 123978 : }
131 ECB :
132 : /* --------------------------------
133 : * pq_sendcountedtext - append a counted text string (with character set conversion)
134 : *
135 : * The data sent to the frontend by this routine is a 4-byte count field
136 : * followed by the string. The count includes itself or not, as per the
137 : * countincludesself flag (pre-3.0 protocol requires it to include itself).
138 : * The passed text string need not be null-terminated, and the data sent
139 : * to the frontend isn't either.
140 : * --------------------------------
141 : */
142 : void
143 GIC 13453671 : pq_sendcountedtext(StringInfo buf, const char *str, int slen,
144 ECB : bool countincludesself)
145 : {
146 GIC 13453671 : int extra = countincludesself ? 4 : 0;
147 ECB : char *p;
148 :
149 GIC 13453671 : p = pg_server_to_client(str, slen);
150 CBC 13453671 : if (p != str) /* actual conversion has been done? */
151 ECB : {
152 GIC 25 : slen = strlen(p);
153 CBC 25 : pq_sendint32(buf, slen + extra);
154 25 : appendBinaryStringInfoNT(buf, p, slen);
155 25 : pfree(p);
156 ECB : }
157 : else
158 : {
159 GIC 13453646 : pq_sendint32(buf, slen + extra);
160 CBC 13453646 : appendBinaryStringInfoNT(buf, str, slen);
161 ECB : }
162 GIC 13453671 : }
163 ECB :
164 : /* --------------------------------
165 : * pq_sendtext - append a text string (with conversion)
166 : *
167 : * The passed text string need not be null-terminated, and the data sent
168 : * to the frontend isn't either. Note that this is not actually useful
169 : * for direct frontend transmissions, since there'd be no way for the
170 : * frontend to determine the string length. But it is useful for binary
171 : * format conversions.
172 : * --------------------------------
173 : */
174 : void
175 GIC 34118 : pq_sendtext(StringInfo buf, const char *str, int slen)
176 ECB : {
177 : char *p;
178 :
179 GIC 34118 : p = pg_server_to_client(str, slen);
180 CBC 34118 : if (p != str) /* actual conversion has been done? */
181 ECB : {
182 UIC 0 : slen = strlen(p);
183 UBC 0 : appendBinaryStringInfo(buf, p, slen);
184 0 : pfree(p);
185 EUB : }
186 : else
187 GIC 34118 : appendBinaryStringInfo(buf, str, slen);
188 CBC 34118 : }
189 ECB :
190 : /* --------------------------------
191 : * pq_sendstring - append a null-terminated text string (with conversion)
192 : *
193 : * NB: passed text string must be null-terminated, and so is the data
194 : * sent to the frontend.
195 : * --------------------------------
196 : */
197 : void
198 GIC 475037 : pq_sendstring(StringInfo buf, const char *str)
199 ECB : {
200 GIC 475037 : int slen = strlen(str);
201 ECB : char *p;
202 :
203 GIC 475037 : p = pg_server_to_client(str, slen);
204 CBC 475037 : if (p != str) /* actual conversion has been done? */
205 ECB : {
206 GIC 102 : slen = strlen(p);
207 CBC 102 : appendBinaryStringInfoNT(buf, p, slen + 1);
208 102 : pfree(p);
209 ECB : }
210 : else
211 GIC 474935 : appendBinaryStringInfoNT(buf, str, slen + 1);
212 CBC 475037 : }
213 ECB :
214 : /* --------------------------------
215 : * pq_send_ascii_string - append a null-terminated text string (without conversion)
216 : *
217 : * This function intentionally bypasses encoding conversion, instead just
218 : * silently replacing any non-7-bit-ASCII characters with question marks.
219 : * It is used only when we are having trouble sending an error message to
220 : * the client with normal localization and encoding conversion. The caller
221 : * should already have taken measures to ensure the string is just ASCII;
222 : * the extra work here is just to make certain we don't send a badly encoded
223 : * string to the client (which might or might not be robust about that).
224 : *
225 : * NB: passed text string must be null-terminated, and so is the data
226 : * sent to the frontend.
227 : * --------------------------------
228 : */
229 : void
230 UIC 0 : pq_send_ascii_string(StringInfo buf, const char *str)
231 EUB : {
232 UIC 0 : while (*str)
233 EUB : {
234 UIC 0 : char ch = *str++;
235 EUB :
236 UIC 0 : if (IS_HIGHBIT_SET(ch))
237 UBC 0 : ch = '?';
238 0 : appendStringInfoCharMacro(buf, ch);
239 EUB : }
240 UIC 0 : appendStringInfoChar(buf, '\0');
241 UBC 0 : }
242 EUB :
243 : /* --------------------------------
244 : * pq_sendfloat4 - append a float4 to a StringInfo buffer
245 : *
246 : * The point of this routine is to localize knowledge of the external binary
247 : * representation of float4, which is a component of several datatypes.
248 : *
249 : * We currently assume that float4 should be byte-swapped in the same way
250 : * as int4. This rule is not perfect but it gives us portability across
251 : * most IEEE-float-using architectures.
252 : * --------------------------------
253 : */
254 : void
255 GIC 3246 : pq_sendfloat4(StringInfo buf, float4 f)
256 ECB : {
257 : union
258 : {
259 : float4 f;
260 : uint32 i;
261 : } swap;
262 :
263 GIC 3246 : swap.f = f;
264 CBC 3246 : pq_sendint32(buf, swap.i);
265 3246 : }
266 ECB :
267 : /* --------------------------------
268 : * pq_sendfloat8 - append a float8 to a StringInfo buffer
269 : *
270 : * The point of this routine is to localize knowledge of the external binary
271 : * representation of float8, which is a component of several datatypes.
272 : *
273 : * We currently assume that float8 should be byte-swapped in the same way
274 : * as int8. This rule is not perfect but it gives us portability across
275 : * most IEEE-float-using architectures.
276 : * --------------------------------
277 : */
278 : void
279 GIC 2596 : pq_sendfloat8(StringInfo buf, float8 f)
280 ECB : {
281 : union
282 : {
283 : float8 f;
284 : int64 i;
285 : } swap;
286 :
287 GIC 2596 : swap.f = f;
288 CBC 2596 : pq_sendint64(buf, swap.i);
289 2596 : }
290 ECB :
291 : /* --------------------------------
292 : * pq_endmessage - send the completed message to the frontend
293 : *
294 : * The data buffer is pfree()d, but if the StringInfo was allocated with
295 : * makeStringInfo then the caller must still pfree it.
296 : * --------------------------------
297 : */
298 : void
299 GIC 430651 : pq_endmessage(StringInfo buf)
300 ECB : {
301 : /* msgtype was saved in cursor field */
302 GIC 430651 : (void) pq_putmessage(buf->cursor, buf->data, buf->len);
303 ECB : /* no need to complain about any failure, since pqcomm.c already did */
304 GIC 430651 : pfree(buf->data);
305 CBC 430651 : buf->data = NULL;
306 430651 : }
307 ECB :
308 : /* --------------------------------
309 : * pq_endmessage_reuse - send the completed message to the frontend
310 : *
311 : * The data buffer is *not* freed, allowing to reuse the buffer with
312 : * pq_beginmessage_reuse.
313 : --------------------------------
314 : */
315 :
316 : void
317 GIC 3932911 : pq_endmessage_reuse(StringInfo buf)
318 ECB : {
319 : /* msgtype was saved in cursor field */
320 GIC 3932911 : (void) pq_putmessage(buf->cursor, buf->data, buf->len);
321 CBC 3932911 : }
322 ECB :
323 :
324 : /* --------------------------------
325 : * pq_begintypsend - initialize for constructing a bytea result
326 : * --------------------------------
327 : */
328 : void
329 GIC 126774 : pq_begintypsend(StringInfo buf)
330 ECB : {
331 GIC 126774 : initStringInfo(buf);
332 ECB : /* Reserve four bytes for the bytea length word */
333 GIC 126774 : appendStringInfoCharMacro(buf, '\0');
334 CBC 126774 : appendStringInfoCharMacro(buf, '\0');
335 126774 : appendStringInfoCharMacro(buf, '\0');
336 126774 : appendStringInfoCharMacro(buf, '\0');
337 126774 : }
338 ECB :
339 : /* --------------------------------
340 : * pq_endtypsend - finish constructing a bytea result
341 : *
342 : * The data buffer is returned as the palloc'd bytea value. (We expect
343 : * that it will be suitably aligned for this because it has been palloc'd.)
344 : * We assume the StringInfoData is just a local variable in the caller and
345 : * need not be pfree'd.
346 : * --------------------------------
347 : */
348 : bytea *
349 GIC 126774 : pq_endtypsend(StringInfo buf)
350 ECB : {
351 GIC 126774 : bytea *result = (bytea *) buf->data;
352 ECB :
353 : /* Insert correct length into bytea length word */
354 GIC 126774 : Assert(buf->len >= VARHDRSZ);
355 CBC 126774 : SET_VARSIZE(result, buf->len);
356 ECB :
357 GIC 126774 : return result;
358 ECB : }
359 :
360 :
361 : /* --------------------------------
362 : * pq_puttextmessage - generate a character set-converted message in one step
363 : *
364 : * This is the same as the pqcomm.c routine pq_putmessage, except that
365 : * the message body is a null-terminated string to which encoding
366 : * conversion applies.
367 : * --------------------------------
368 : */
369 : void
370 GIC 372 : pq_puttextmessage(char msgtype, const char *str)
371 ECB : {
372 GIC 372 : int slen = strlen(str);
373 ECB : char *p;
374 :
375 GIC 372 : p = pg_server_to_client(str, slen);
376 CBC 372 : if (p != str) /* actual conversion has been done? */
377 ECB : {
378 UIC 0 : (void) pq_putmessage(msgtype, p, strlen(p) + 1);
379 UBC 0 : pfree(p);
380 0 : return;
381 EUB : }
382 GIC 372 : (void) pq_putmessage(msgtype, str, slen + 1);
383 ECB : }
384 :
385 :
386 : /* --------------------------------
387 : * pq_putemptymessage - convenience routine for message with empty body
388 : * --------------------------------
389 : */
390 : void
391 GIC 22265 : pq_putemptymessage(char msgtype)
392 ECB : {
393 GIC 22265 : (void) pq_putmessage(msgtype, NULL, 0);
394 CBC 22265 : }
395 ECB :
396 :
397 : /* --------------------------------
398 : * pq_getmsgbyte - get a raw byte from a message buffer
399 : * --------------------------------
400 : */
401 : int
402 GIC 1235704 : pq_getmsgbyte(StringInfo msg)
403 ECB : {
404 GIC 1235704 : if (msg->cursor >= msg->len)
405 LBC 0 : ereport(ERROR,
406 EUB : (errcode(ERRCODE_PROTOCOL_VIOLATION),
407 : errmsg("no data left in message")));
408 GIC 1235704 : return (unsigned char) msg->data[msg->cursor++];
409 ECB : }
410 :
411 : /* --------------------------------
412 : * pq_getmsgint - get a binary integer from a message buffer
413 : *
414 : * Values are treated as unsigned.
415 : * --------------------------------
416 : */
417 : unsigned int
418 GIC 1368193 : pq_getmsgint(StringInfo msg, int b)
419 ECB : {
420 : unsigned int result;
421 : unsigned char n8;
422 : uint16 n16;
423 : uint32 n32;
424 :
425 GIC 1368193 : switch (b)
426 ECB : {
427 GIC 17 : case 1:
428 CBC 17 : pq_copymsgbytes(msg, (char *) &n8, 1);
429 17 : result = n8;
430 17 : break;
431 518262 : case 2:
432 518262 : pq_copymsgbytes(msg, (char *) &n16, 2);
433 518262 : result = pg_ntoh16(n16);
434 518262 : break;
435 849914 : case 4:
436 849914 : pq_copymsgbytes(msg, (char *) &n32, 4);
437 849914 : result = pg_ntoh32(n32);
438 849914 : break;
439 LBC 0 : default:
440 UBC 0 : elog(ERROR, "unsupported integer size %d", b);
441 EUB : result = 0; /* keep compiler quiet */
442 : break;
443 : }
444 GIC 1368193 : return result;
445 ECB : }
446 :
447 : /* --------------------------------
448 : * pq_getmsgint64 - get a binary 8-byte int from a message buffer
449 : *
450 : * It is tempting to merge this with pq_getmsgint, but we'd have to make the
451 : * result int64 for all data widths --- that could be a big performance
452 : * hit on machines where int64 isn't efficient.
453 : * --------------------------------
454 : */
455 : int64
456 GIC 987883 : pq_getmsgint64(StringInfo msg)
457 ECB : {
458 : uint64 n64;
459 :
460 GIC 987883 : pq_copymsgbytes(msg, (char *) &n64, sizeof(n64));
461 ECB :
462 GIC 987883 : return pg_ntoh64(n64);
463 ECB : }
464 :
465 : /* --------------------------------
466 : * pq_getmsgfloat4 - get a float4 from a message buffer
467 : *
468 : * See notes for pq_sendfloat4.
469 : * --------------------------------
470 : */
471 : float4
472 UIC 0 : pq_getmsgfloat4(StringInfo msg)
473 EUB : {
474 : union
475 : {
476 : float4 f;
477 : uint32 i;
478 : } swap;
479 :
480 UIC 0 : swap.i = pq_getmsgint(msg, 4);
481 UBC 0 : return swap.f;
482 EUB : }
483 :
484 : /* --------------------------------
485 : * pq_getmsgfloat8 - get a float8 from a message buffer
486 : *
487 : * See notes for pq_sendfloat8.
488 : * --------------------------------
489 : */
490 : float8
491 GIC 31 : pq_getmsgfloat8(StringInfo msg)
492 ECB : {
493 : union
494 : {
495 : float8 f;
496 : int64 i;
497 : } swap;
498 :
499 GIC 31 : swap.i = pq_getmsgint64(msg);
500 CBC 31 : return swap.f;
501 ECB : }
502 :
503 : /* --------------------------------
504 : * pq_getmsgbytes - get raw data from a message buffer
505 : *
506 : * Returns a pointer directly into the message buffer; note this
507 : * may not have any particular alignment.
508 : * --------------------------------
509 : */
510 : const char *
511 GIC 85378 : pq_getmsgbytes(StringInfo msg, int datalen)
512 ECB : {
513 : const char *result;
514 :
515 GIC 85378 : if (datalen < 0 || datalen > (msg->len - msg->cursor))
516 LBC 0 : ereport(ERROR,
517 EUB : (errcode(ERRCODE_PROTOCOL_VIOLATION),
518 : errmsg("insufficient data left in message")));
519 GIC 85378 : result = &msg->data[msg->cursor];
520 CBC 85378 : msg->cursor += datalen;
521 85378 : return result;
522 ECB : }
523 :
524 : /* --------------------------------
525 : * pq_copymsgbytes - copy raw data from a message buffer
526 : *
527 : * Same as above, except data is copied to caller's buffer.
528 : * --------------------------------
529 : */
530 : void
531 GIC 2809246 : pq_copymsgbytes(StringInfo msg, char *buf, int datalen)
532 ECB : {
533 GIC 2809246 : if (datalen < 0 || datalen > (msg->len - msg->cursor))
534 LBC 0 : ereport(ERROR,
535 EUB : (errcode(ERRCODE_PROTOCOL_VIOLATION),
536 : errmsg("insufficient data left in message")));
537 GIC 2809246 : memcpy(buf, &msg->data[msg->cursor], datalen);
538 CBC 2809246 : msg->cursor += datalen;
539 2809246 : }
540 ECB :
541 : /* --------------------------------
542 : * pq_getmsgtext - get a counted text string (with conversion)
543 : *
544 : * Always returns a pointer to a freshly palloc'd result.
545 : * The result has a trailing null, *and* we return its strlen in *nbytes.
546 : * --------------------------------
547 : */
548 : char *
549 GIC 53364 : pq_getmsgtext(StringInfo msg, int rawbytes, int *nbytes)
550 ECB : {
551 : char *str;
552 : char *p;
553 :
554 GIC 53364 : if (rawbytes < 0 || rawbytes > (msg->len - msg->cursor))
555 LBC 0 : ereport(ERROR,
556 EUB : (errcode(ERRCODE_PROTOCOL_VIOLATION),
557 : errmsg("insufficient data left in message")));
558 GIC 53364 : str = &msg->data[msg->cursor];
559 CBC 53364 : msg->cursor += rawbytes;
560 ECB :
561 GIC 53364 : p = pg_client_to_server(str, rawbytes);
562 CBC 53364 : if (p != str) /* actual conversion has been done? */
563 LBC 0 : *nbytes = strlen(p);
564 EUB : else
565 : {
566 GIC 53364 : p = (char *) palloc(rawbytes + 1);
567 CBC 53364 : memcpy(p, str, rawbytes);
568 53364 : p[rawbytes] = '\0';
569 53364 : *nbytes = rawbytes;
570 ECB : }
571 GIC 53364 : return p;
572 ECB : }
573 :
574 : /* --------------------------------
575 : * pq_getmsgstring - get a null-terminated text string (with conversion)
576 : *
577 : * May return a pointer directly into the message buffer, or a pointer
578 : * to a palloc'd conversion result.
579 : * --------------------------------
580 : */
581 : const char *
582 GIC 492670 : pq_getmsgstring(StringInfo msg)
583 ECB : {
584 : char *str;
585 : int slen;
586 :
587 GIC 492670 : str = &msg->data[msg->cursor];
588 ECB :
589 : /*
590 : * It's safe to use strlen() here because a StringInfo is guaranteed to
591 : * have a trailing null byte. But check we found a null inside the
592 : * message.
593 : */
594 GIC 492670 : slen = strlen(str);
595 CBC 492670 : if (msg->cursor + slen >= msg->len)
596 LBC 0 : ereport(ERROR,
597 EUB : (errcode(ERRCODE_PROTOCOL_VIOLATION),
598 : errmsg("invalid string in message")));
599 GIC 492670 : msg->cursor += slen + 1;
600 ECB :
601 GIC 492670 : return pg_client_to_server(str, slen);
602 ECB : }
603 :
604 : /* --------------------------------
605 : * pq_getmsgrawstring - get a null-terminated text string - NO conversion
606 : *
607 : * Returns a pointer directly into the message buffer.
608 : * --------------------------------
609 : */
610 : const char *
611 GIC 62 : pq_getmsgrawstring(StringInfo msg)
612 ECB : {
613 : char *str;
614 : int slen;
615 :
616 GIC 62 : str = &msg->data[msg->cursor];
617 ECB :
618 : /*
619 : * It's safe to use strlen() here because a StringInfo is guaranteed to
620 : * have a trailing null byte. But check we found a null inside the
621 : * message.
622 : */
623 GIC 62 : slen = strlen(str);
624 CBC 62 : if (msg->cursor + slen >= msg->len)
625 LBC 0 : ereport(ERROR,
626 EUB : (errcode(ERRCODE_PROTOCOL_VIOLATION),
627 : errmsg("invalid string in message")));
628 GIC 62 : msg->cursor += slen + 1;
629 ECB :
630 GIC 62 : return str;
631 ECB : }
632 :
633 : /* --------------------------------
634 : * pq_getmsgend - verify message fully consumed
635 : * --------------------------------
636 : */
637 : void
638 GIC 489944 : pq_getmsgend(StringInfo msg)
639 ECB : {
640 GIC 489944 : if (msg->cursor != msg->len)
641 LBC 0 : ereport(ERROR,
642 EUB : (errcode(ERRCODE_PROTOCOL_VIOLATION),
643 : errmsg("invalid message format")));
644 GIC 489944 : }
|