📄 pqformat.c
字号:
/*------------------------------------------------------------------------- * * pqformat.c * Routines for formatting and parsing frontend/backend messages * * Outgoing messages are built up in a StringInfo buffer (which is expansible) * and then sent in a single call to pq_putmessage. This module provides data * formatting/conversion routines that are needed to produce valid messages. * Note in particular the distinction between "raw data" and "text"; raw data * is message protocol characters and binary values that are not subject to * character set conversion, while text is converted by character encoding * rules. * * Incoming messages are similarly read into a StringInfo buffer, via * pq_getmessage, and then parsed and converted from that using the routines * in this module. * * These same routines support reading and writing of external binary formats * (typsend/typreceive routines). The conversion routines for individual * data types are exactly the same, only initialization and completion * are different. * * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * $PostgreSQL: pgsql/src/backend/libpq/pqformat.c,v 1.40 2005/10/15 02:49:18 momjian Exp $ * *------------------------------------------------------------------------- *//* * INTERFACE ROUTINES * Message assembly and output: * pq_beginmessage - initialize StringInfo buffer * pq_sendbyte - append a raw byte to a StringInfo buffer * pq_sendint - append a binary integer to a StringInfo buffer * pq_sendint64 - append a binary 8-byte int to a StringInfo buffer * pq_sendfloat4 - append a float4 to a StringInfo buffer * pq_sendfloat8 - append a float8 to a StringInfo buffer * pq_sendbytes - append raw data to a StringInfo buffer * pq_sendcountedtext - append a counted text string (with character set conversion) * pq_sendtext - append a text string (with conversion) * pq_sendstring - append a null-terminated text string (with conversion) * pq_endmessage - send the completed message to the frontend * Note: it is also possible to append data to the StringInfo buffer using * the regular StringInfo routines, but this is discouraged since required * character set conversion may not occur. * * typsend support (construct a bytea value containing external binary data): * pq_begintypsend - initialize StringInfo buffer * pq_endtypsend - return the completed string as a "bytea*" * * Special-case message output: * pq_puttextmessage - generate a character set-converted message in one step * pq_putemptymessage - convenience routine for message with empty body * * Message parsing after input: * pq_getmsgbyte - get a raw byte from a message buffer * pq_getmsgint - get a binary integer from a message buffer * pq_getmsgint64 - get a binary 8-byte int from a message buffer * pq_getmsgfloat4 - get a float4 from a message buffer * pq_getmsgfloat8 - get a float8 from a message buffer * pq_getmsgbytes - get raw data from a message buffer * pq_copymsgbytes - copy raw data from a message buffer * pq_getmsgtext - get a counted text string (with conversion) * pq_getmsgstring - get a null-terminated text string (with conversion) * pq_getmsgend - verify message fully consumed */#include "postgres.h"#include <errno.h>#include <sys/param.h>#include <netinet/in.h>#include <arpa/inet.h>#ifdef HAVE_ENDIAN_H#include <endian.h>#endif#include "libpq/libpq.h"#include "libpq/pqformat.h"#include "mb/pg_wchar.h"/* -------------------------------- * pq_beginmessage - initialize for sending a message * -------------------------------- */voidpq_beginmessage(StringInfo buf, char msgtype){ initStringInfo(buf); /* * We stash the message type into the buffer's cursor field, expecting * that the pq_sendXXX routines won't touch it. We could alternatively * make it the first byte of the buffer contents, but this seems easier. */ buf->cursor = msgtype;}/* -------------------------------- * pq_sendbyte - append a raw byte to a StringInfo buffer * -------------------------------- */voidpq_sendbyte(StringInfo buf, int byt){ appendStringInfoCharMacro(buf, byt);}/* -------------------------------- * pq_sendbytes - append raw data to a StringInfo buffer * -------------------------------- */voidpq_sendbytes(StringInfo buf, const char *data, int datalen){ appendBinaryStringInfo(buf, data, datalen);}/* -------------------------------- * pq_sendcountedtext - append a counted text string (with character set conversion) * * The data sent to the frontend by this routine is a 4-byte count field * followed by the string. The count includes itself or not, as per the * countincludesself flag (pre-3.0 protocol requires it to include itself). * The passed text string need not be null-terminated, and the data sent * to the frontend isn't either. * -------------------------------- */voidpq_sendcountedtext(StringInfo buf, const char *str, int slen, bool countincludesself){ int extra = countincludesself ? 4 : 0; char *p; p = pg_server_to_client(str, slen); if (p != str) /* actual conversion has been done? */ { slen = strlen(p); pq_sendint(buf, slen + extra, 4); appendBinaryStringInfo(buf, p, slen); pfree(p); } else { pq_sendint(buf, slen + extra, 4); appendBinaryStringInfo(buf, str, slen); }}/* -------------------------------- * pq_sendtext - append a text string (with conversion) * * The passed text string need not be null-terminated, and the data sent * to the frontend isn't either. Note that this is not actually useful * for direct frontend transmissions, since there'd be no way for the * frontend to determine the string length. But it is useful for binary * format conversions. * -------------------------------- */voidpq_sendtext(StringInfo buf, const char *str, int slen){ char *p; p = pg_server_to_client(str, slen); if (p != str) /* actual conversion has been done? */ { slen = strlen(p); appendBinaryStringInfo(buf, p, slen); pfree(p); } else appendBinaryStringInfo(buf, str, slen);}/* -------------------------------- * pq_sendstring - append a null-terminated text string (with conversion) * * NB: passed text string must be null-terminated, and so is the data * sent to the frontend. * -------------------------------- */voidpq_sendstring(StringInfo buf, const char *str){ int slen = strlen(str); char *p; p = pg_server_to_client(str, slen); if (p != str) /* actual conversion has been done? */ { slen = strlen(p); appendBinaryStringInfo(buf, p, slen + 1); pfree(p); } else appendBinaryStringInfo(buf, str, slen + 1);}/* -------------------------------- * pq_sendint - append a binary integer to a StringInfo buffer * -------------------------------- */voidpq_sendint(StringInfo buf, int i, int b){ unsigned char n8; uint16 n16; uint32 n32; switch (b) { case 1: n8 = (unsigned char) i; appendBinaryStringInfo(buf, (char *) &n8, 1); break; case 2: n16 = htons((uint16) i); appendBinaryStringInfo(buf, (char *) &n16, 2); break; case 4: n32 = htonl((uint32) i); appendBinaryStringInfo(buf, (char *) &n32, 4); break; default: elog(ERROR, "unsupported integer size %d", b); break; }}/* -------------------------------- * pq_sendint64 - append a binary 8-byte int to a StringInfo buffer * * It is tempting to merge this with pq_sendint, but we'd have to make the * argument int64 for all data widths --- that could be a big performance * hit on machines where int64 isn't efficient. * -------------------------------- */voidpq_sendint64(StringInfo buf, int64 i){ uint32 n32; /* High order half first, since we're doing MSB-first */#ifdef INT64_IS_BUSTED /* don't try a right shift of 32 on a 32-bit word */ n32 = (i < 0) ? -1 : 0;#else n32 = (uint32) (i >> 32);#endif n32 = htonl(n32); appendBinaryStringInfo(buf, (char *) &n32, 4); /* Now the low order half */ n32 = (uint32) i; n32 = htonl(n32); appendBinaryStringInfo(buf, (char *) &n32, 4);}/* -------------------------------- * pq_sendfloat4 - append a float4 to a StringInfo buffer * * The point of this routine is to localize knowledge of the external binary * representation of float4, which is a component of several datatypes. * * We currently assume that float4 should be byte-swapped in the same way * as int4. This rule is not perfect but it gives us portability across * most IEEE-float-using architectures. * -------------------------------- */voidpq_sendfloat4(StringInfo buf, float4 f){ union { float4 f; uint32 i; } swap; swap.f = f; swap.i = htonl(swap.i); appendBinaryStringInfo(buf, (char *) &swap.i, 4);}/* -------------------------------- * pq_sendfloat8 - append a float8 to a StringInfo buffer * * The point of this routine is to localize knowledge of the external binary * representation of float8, which is a component of several datatypes. * * We currently assume that float8 should be byte-swapped in the same way * as int8. This rule is not perfect but it gives us portability across * most IEEE-float-using architectures. * -------------------------------- */voidpq_sendfloat8(StringInfo buf, float8 f){#ifdef INT64_IS_BUSTED union { float8 f; uint32 h[2]; } swap; swap.f = f; swap.h[0] = htonl(swap.h[0]); swap.h[1] = htonl(swap.h[1]); /* Have to figure out endianness by testing... */ if (((uint32) 1) == htonl((uint32) 1)) { /* machine seems to be big-endian, send h[0] first */ appendBinaryStringInfo(buf, (char *) &swap.h[0], 4); appendBinaryStringInfo(buf, (char *) &swap.h[1], 4); } else { /* machine seems to be little-endian, send h[1] first */ appendBinaryStringInfo(buf, (char *) &swap.h[1], 4); appendBinaryStringInfo(buf, (char *) &swap.h[0], 4); }#else union { float8 f; int64 i; } swap; swap.f = f; pq_sendint64(buf, swap.i);#endif}/* -------------------------------- * pq_endmessage - send the completed message to the frontend * * The data buffer is pfree()d, but if the StringInfo was allocated with * makeStringInfo then the caller must still pfree it. * --------------------------------
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -