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-2025, 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 1357972 : pq_beginmessage(StringInfo buf, char msgtype)
89 : {
90 1357972 : initStringInfo(buf);
91 :
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 1357972 : buf->cursor = msgtype;
98 1357972 : }
99 :
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 7294508 : pq_beginmessage_reuse(StringInfo buf, char msgtype)
110 : {
111 7294508 : resetStringInfo(buf);
112 :
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 7294508 : buf->cursor = msgtype;
119 7294508 : }
120 :
121 : /* --------------------------------
122 : * pq_sendbytes - append raw data to a StringInfo buffer
123 : * --------------------------------
124 : */
125 : void
126 274222 : pq_sendbytes(StringInfo buf, const void *data, int datalen)
127 : {
128 : /* use variant that maintains a trailing null-byte, out of caution */
129 274222 : appendBinaryStringInfo(buf, data, datalen);
130 274222 : }
131 :
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 does not include itself, as required by
137 : * protocol version 3.0. The passed text string need not be null-terminated,
138 : * and the data sent to the frontend isn't either.
139 : * --------------------------------
140 : */
141 : void
142 33162768 : pq_sendcountedtext(StringInfo buf, const char *str, int slen)
143 : {
144 : char *p;
145 :
146 33162768 : p = pg_server_to_client(str, slen);
147 33162768 : if (p != str) /* actual conversion has been done? */
148 : {
149 56 : slen = strlen(p);
150 56 : pq_sendint32(buf, slen);
151 56 : appendBinaryStringInfoNT(buf, p, slen);
152 56 : pfree(p);
153 : }
154 : else
155 : {
156 33162712 : pq_sendint32(buf, slen);
157 33162712 : appendBinaryStringInfoNT(buf, str, slen);
158 : }
159 33162768 : }
160 :
161 : /* --------------------------------
162 : * pq_sendtext - append a text string (with conversion)
163 : *
164 : * The passed text string need not be null-terminated, and the data sent
165 : * to the frontend isn't either. Note that this is not actually useful
166 : * for direct frontend transmissions, since there'd be no way for the
167 : * frontend to determine the string length. But it is useful for binary
168 : * format conversions.
169 : * --------------------------------
170 : */
171 : void
172 4760 : pq_sendtext(StringInfo buf, const char *str, int slen)
173 : {
174 : char *p;
175 :
176 4760 : p = pg_server_to_client(str, slen);
177 4760 : if (p != str) /* actual conversion has been done? */
178 : {
179 0 : slen = strlen(p);
180 0 : appendBinaryStringInfo(buf, p, slen);
181 0 : pfree(p);
182 : }
183 : else
184 4760 : appendBinaryStringInfo(buf, str, slen);
185 4760 : }
186 :
187 : /* --------------------------------
188 : * pq_sendstring - append a null-terminated text string (with conversion)
189 : *
190 : * NB: passed text string must be null-terminated, and so is the data
191 : * sent to the frontend.
192 : * --------------------------------
193 : */
194 : void
195 2445822 : pq_sendstring(StringInfo buf, const char *str)
196 : {
197 2445822 : int slen = strlen(str);
198 : char *p;
199 :
200 2445822 : p = pg_server_to_client(str, slen);
201 2445822 : if (p != str) /* actual conversion has been done? */
202 : {
203 294 : slen = strlen(p);
204 294 : appendBinaryStringInfoNT(buf, p, slen + 1);
205 294 : pfree(p);
206 : }
207 : else
208 2445528 : appendBinaryStringInfoNT(buf, str, slen + 1);
209 2445822 : }
210 :
211 : /* --------------------------------
212 : * pq_send_ascii_string - append a null-terminated text string (without conversion)
213 : *
214 : * This function intentionally bypasses encoding conversion, instead just
215 : * silently replacing any non-7-bit-ASCII characters with question marks.
216 : * It is used only when we are having trouble sending an error message to
217 : * the client with normal localization and encoding conversion. The caller
218 : * should already have taken measures to ensure the string is just ASCII;
219 : * the extra work here is just to make certain we don't send a badly encoded
220 : * string to the client (which might or might not be robust about that).
221 : *
222 : * NB: passed text string must be null-terminated, and so is the data
223 : * sent to the frontend.
224 : * --------------------------------
225 : */
226 : void
227 0 : pq_send_ascii_string(StringInfo buf, const char *str)
228 : {
229 0 : while (*str)
230 : {
231 0 : char ch = *str++;
232 :
233 0 : if (IS_HIGHBIT_SET(ch))
234 0 : ch = '?';
235 0 : appendStringInfoCharMacro(buf, ch);
236 : }
237 0 : appendStringInfoChar(buf, '\0');
238 0 : }
239 :
240 : /* --------------------------------
241 : * pq_sendfloat4 - append a float4 to a StringInfo buffer
242 : *
243 : * The point of this routine is to localize knowledge of the external binary
244 : * representation of float4, which is a component of several datatypes.
245 : *
246 : * We currently assume that float4 should be byte-swapped in the same way
247 : * as int4. This rule is not perfect but it gives us portability across
248 : * most IEEE-float-using architectures.
249 : * --------------------------------
250 : */
251 : void
252 6492 : pq_sendfloat4(StringInfo buf, float4 f)
253 : {
254 : union
255 : {
256 : float4 f;
257 : uint32 i;
258 : } swap;
259 :
260 6492 : swap.f = f;
261 6492 : pq_sendint32(buf, swap.i);
262 6492 : }
263 :
264 : /* --------------------------------
265 : * pq_sendfloat8 - append a float8 to a StringInfo buffer
266 : *
267 : * The point of this routine is to localize knowledge of the external binary
268 : * representation of float8, which is a component of several datatypes.
269 : *
270 : * We currently assume that float8 should be byte-swapped in the same way
271 : * as int8. This rule is not perfect but it gives us portability across
272 : * most IEEE-float-using architectures.
273 : * --------------------------------
274 : */
275 : void
276 5192 : pq_sendfloat8(StringInfo buf, float8 f)
277 : {
278 : union
279 : {
280 : float8 f;
281 : int64 i;
282 : } swap;
283 :
284 5192 : swap.f = f;
285 5192 : pq_sendint64(buf, swap.i);
286 5192 : }
287 :
288 : /* --------------------------------
289 : * pq_endmessage - send the completed message to the frontend
290 : *
291 : * The data buffer is pfree()d, but if the StringInfo was allocated with
292 : * makeStringInfo then the caller must still pfree it.
293 : * --------------------------------
294 : */
295 : void
296 1357950 : pq_endmessage(StringInfo buf)
297 : {
298 : /* msgtype was saved in cursor field */
299 1357950 : (void) pq_putmessage(buf->cursor, buf->data, buf->len);
300 : /* no need to complain about any failure, since pqcomm.c already did */
301 1357950 : pfree(buf->data);
302 1357950 : buf->data = NULL;
303 1357950 : }
304 :
305 : /* --------------------------------
306 : * pq_endmessage_reuse - send the completed message to the frontend
307 : *
308 : * The data buffer is *not* freed, allowing to reuse the buffer with
309 : * pq_beginmessage_reuse.
310 : * --------------------------------
311 : */
312 : void
313 7294410 : pq_endmessage_reuse(StringInfo buf)
314 : {
315 : /* msgtype was saved in cursor field */
316 7294410 : (void) pq_putmessage(buf->cursor, buf->data, buf->len);
317 7294410 : }
318 :
319 :
320 : /* --------------------------------
321 : * pq_begintypsend - initialize for constructing a bytea result
322 : * --------------------------------
323 : */
324 : void
325 231080 : pq_begintypsend(StringInfo buf)
326 : {
327 231080 : initStringInfo(buf);
328 : /* Reserve four bytes for the bytea length word */
329 231080 : appendStringInfoCharMacro(buf, '\0');
330 231080 : appendStringInfoCharMacro(buf, '\0');
331 231080 : appendStringInfoCharMacro(buf, '\0');
332 231080 : appendStringInfoCharMacro(buf, '\0');
333 231080 : }
334 :
335 : /* --------------------------------
336 : * pq_endtypsend - finish constructing a bytea result
337 : *
338 : * The data buffer is returned as the palloc'd bytea value. (We expect
339 : * that it will be suitably aligned for this because it has been palloc'd.)
340 : * We assume the StringInfoData is just a local variable in the caller and
341 : * need not be pfree'd.
342 : * --------------------------------
343 : */
344 : bytea *
345 231080 : pq_endtypsend(StringInfo buf)
346 : {
347 231080 : bytea *result = (bytea *) buf->data;
348 :
349 : /* Insert correct length into bytea length word */
350 : Assert(buf->len >= VARHDRSZ);
351 231080 : SET_VARSIZE(result, buf->len);
352 :
353 231080 : return result;
354 : }
355 :
356 :
357 : /* --------------------------------
358 : * pq_puttextmessage - generate a character set-converted message in one step
359 : *
360 : * This is the same as the pqcomm.c routine pq_putmessage, except that
361 : * the message body is a null-terminated string to which encoding
362 : * conversion applies.
363 : * --------------------------------
364 : */
365 : void
366 968 : pq_puttextmessage(char msgtype, const char *str)
367 : {
368 968 : int slen = strlen(str);
369 : char *p;
370 :
371 968 : p = pg_server_to_client(str, slen);
372 968 : if (p != str) /* actual conversion has been done? */
373 : {
374 0 : (void) pq_putmessage(msgtype, p, strlen(p) + 1);
375 0 : pfree(p);
376 0 : return;
377 : }
378 968 : (void) pq_putmessage(msgtype, str, slen + 1);
379 : }
380 :
381 :
382 : /* --------------------------------
383 : * pq_putemptymessage - convenience routine for message with empty body
384 : * --------------------------------
385 : */
386 : void
387 57146 : pq_putemptymessage(char msgtype)
388 : {
389 57146 : (void) pq_putmessage(msgtype, NULL, 0);
390 57146 : }
391 :
392 :
393 : /* --------------------------------
394 : * pq_getmsgbyte - get a raw byte from a message buffer
395 : * --------------------------------
396 : */
397 : int
398 2630048 : pq_getmsgbyte(StringInfo msg)
399 : {
400 2630048 : if (msg->cursor >= msg->len)
401 0 : ereport(ERROR,
402 : (errcode(ERRCODE_PROTOCOL_VIOLATION),
403 : errmsg("no data left in message")));
404 2630048 : return (unsigned char) msg->data[msg->cursor++];
405 : }
406 :
407 : /* --------------------------------
408 : * pq_getmsgint - get a binary integer from a message buffer
409 : *
410 : * Values are treated as unsigned.
411 : * --------------------------------
412 : */
413 : unsigned int
414 2615896 : pq_getmsgint(StringInfo msg, int b)
415 : {
416 : unsigned int result;
417 : unsigned char n8;
418 : uint16 n16;
419 : uint32 n32;
420 :
421 2615896 : switch (b)
422 : {
423 40 : case 1:
424 40 : pq_copymsgbytes(msg, &n8, 1);
425 40 : result = n8;
426 40 : break;
427 1052048 : case 2:
428 1052048 : pq_copymsgbytes(msg, &n16, 2);
429 1052048 : result = pg_ntoh16(n16);
430 1052048 : break;
431 1563808 : case 4:
432 1563808 : pq_copymsgbytes(msg, &n32, 4);
433 1563808 : result = pg_ntoh32(n32);
434 1563808 : break;
435 0 : default:
436 0 : elog(ERROR, "unsupported integer size %d", b);
437 : result = 0; /* keep compiler quiet */
438 : break;
439 : }
440 2615896 : return result;
441 : }
442 :
443 : /* --------------------------------
444 : * pq_getmsgint64 - get a binary 8-byte int from a message buffer
445 : *
446 : * It is tempting to merge this with pq_getmsgint, but we'd have to make the
447 : * result int64 for all data widths --- that could be a big performance
448 : * hit on machines where int64 isn't efficient.
449 : * --------------------------------
450 : */
451 : int64
452 2734680 : pq_getmsgint64(StringInfo msg)
453 : {
454 : uint64 n64;
455 :
456 2734680 : pq_copymsgbytes(msg, &n64, sizeof(n64));
457 :
458 2734680 : return pg_ntoh64(n64);
459 : }
460 :
461 : /* --------------------------------
462 : * pq_getmsgfloat4 - get a float4 from a message buffer
463 : *
464 : * See notes for pq_sendfloat4.
465 : * --------------------------------
466 : */
467 : float4
468 0 : pq_getmsgfloat4(StringInfo msg)
469 : {
470 : union
471 : {
472 : float4 f;
473 : uint32 i;
474 : } swap;
475 :
476 0 : swap.i = pq_getmsgint(msg, 4);
477 0 : return swap.f;
478 : }
479 :
480 : /* --------------------------------
481 : * pq_getmsgfloat8 - get a float8 from a message buffer
482 : *
483 : * See notes for pq_sendfloat8.
484 : * --------------------------------
485 : */
486 : float8
487 62 : pq_getmsgfloat8(StringInfo msg)
488 : {
489 : union
490 : {
491 : float8 f;
492 : int64 i;
493 : } swap;
494 :
495 62 : swap.i = pq_getmsgint64(msg);
496 62 : return swap.f;
497 : }
498 :
499 : /* --------------------------------
500 : * pq_getmsgbytes - get raw data from a message buffer
501 : *
502 : * Returns a pointer directly into the message buffer; note this
503 : * may not have any particular alignment.
504 : * --------------------------------
505 : */
506 : const char *
507 33132 : pq_getmsgbytes(StringInfo msg, int datalen)
508 : {
509 : const char *result;
510 :
511 33132 : if (datalen < 0 || datalen > (msg->len - msg->cursor))
512 0 : ereport(ERROR,
513 : (errcode(ERRCODE_PROTOCOL_VIOLATION),
514 : errmsg("insufficient data left in message")));
515 33132 : result = &msg->data[msg->cursor];
516 33132 : msg->cursor += datalen;
517 33132 : return result;
518 : }
519 :
520 : /* --------------------------------
521 : * pq_copymsgbytes - copy raw data from a message buffer
522 : *
523 : * Same as above, except data is copied to caller's buffer.
524 : * --------------------------------
525 : */
526 : void
527 6365244 : pq_copymsgbytes(StringInfo msg, void *buf, int datalen)
528 : {
529 6365244 : if (datalen < 0 || datalen > (msg->len - msg->cursor))
530 0 : ereport(ERROR,
531 : (errcode(ERRCODE_PROTOCOL_VIOLATION),
532 : errmsg("insufficient data left in message")));
533 6365244 : memcpy(buf, &msg->data[msg->cursor], datalen);
534 6365244 : msg->cursor += datalen;
535 6365244 : }
536 :
537 : /* --------------------------------
538 : * pq_getmsgtext - get a counted text string (with conversion)
539 : *
540 : * Always returns a pointer to a freshly palloc'd result.
541 : * The result has a trailing null, *and* we return its strlen in *nbytes.
542 : * --------------------------------
543 : */
544 : char *
545 56 : pq_getmsgtext(StringInfo msg, int rawbytes, int *nbytes)
546 : {
547 : char *str;
548 : char *p;
549 :
550 56 : if (rawbytes < 0 || rawbytes > (msg->len - msg->cursor))
551 0 : ereport(ERROR,
552 : (errcode(ERRCODE_PROTOCOL_VIOLATION),
553 : errmsg("insufficient data left in message")));
554 56 : str = &msg->data[msg->cursor];
555 56 : msg->cursor += rawbytes;
556 :
557 56 : p = pg_client_to_server(str, rawbytes);
558 56 : if (p != str) /* actual conversion has been done? */
559 0 : *nbytes = strlen(p);
560 : else
561 : {
562 56 : p = (char *) palloc(rawbytes + 1);
563 56 : memcpy(p, str, rawbytes);
564 56 : p[rawbytes] = '\0';
565 56 : *nbytes = rawbytes;
566 : }
567 56 : return p;
568 : }
569 :
570 : /* --------------------------------
571 : * pq_getmsgstring - get a null-terminated text string (with conversion)
572 : *
573 : * May return a pointer directly into the message buffer, or a pointer
574 : * to a palloc'd conversion result.
575 : * --------------------------------
576 : */
577 : const char *
578 800942 : pq_getmsgstring(StringInfo msg)
579 : {
580 : char *str;
581 : int slen;
582 :
583 800942 : str = &msg->data[msg->cursor];
584 :
585 : /*
586 : * It's safe to use strlen() here because a StringInfo is guaranteed to
587 : * have a trailing null byte. But check we found a null inside the
588 : * message.
589 : */
590 800942 : slen = strlen(str);
591 800942 : if (msg->cursor + slen >= msg->len)
592 0 : ereport(ERROR,
593 : (errcode(ERRCODE_PROTOCOL_VIOLATION),
594 : errmsg("invalid string in message")));
595 800942 : msg->cursor += slen + 1;
596 :
597 800942 : return pg_client_to_server(str, slen);
598 : }
599 :
600 : /* --------------------------------
601 : * pq_getmsgrawstring - get a null-terminated text string - NO conversion
602 : *
603 : * Returns a pointer directly into the message buffer.
604 : * --------------------------------
605 : */
606 : const char *
607 242 : pq_getmsgrawstring(StringInfo msg)
608 : {
609 : char *str;
610 : int slen;
611 :
612 242 : str = &msg->data[msg->cursor];
613 :
614 : /*
615 : * It's safe to use strlen() here because a StringInfo is guaranteed to
616 : * have a trailing null byte. But check we found a null inside the
617 : * message.
618 : */
619 242 : slen = strlen(str);
620 242 : if (msg->cursor + slen >= msg->len)
621 0 : ereport(ERROR,
622 : (errcode(ERRCODE_PROTOCOL_VIOLATION),
623 : errmsg("invalid string in message")));
624 242 : msg->cursor += slen + 1;
625 :
626 242 : return str;
627 : }
628 :
629 : /* --------------------------------
630 : * pq_getmsgend - verify message fully consumed
631 : * --------------------------------
632 : */
633 : void
634 790370 : pq_getmsgend(StringInfo msg)
635 : {
636 790370 : if (msg->cursor != msg->len)
637 0 : ereport(ERROR,
638 : (errcode(ERRCODE_PROTOCOL_VIOLATION),
639 : errmsg("invalid message format")));
640 790370 : }
|