/*------------------------------------------------------------------------- * * 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-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * $PostgreSQL: pgsql/src/backend/libpq/pqformat.c,v 1.48 2009/01/01 17:23:42 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 #include #include #include "libpq/libpq.h" #include "libpq/pqformat.h" #include "mb/pg_wchar.h" /* -------------------------------- * pq_beginmessage - initialize for sending a message * -------------------------------- */ void pq_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 * -------------------------------- */ void pq_sendbyte(StringInfo buf, int byt) { appendStringInfoCharMacro(buf, byt); } /* -------------------------------- * pq_sendbytes - append raw data to a StringInfo buffer * -------------------------------- */ void pq_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. * -------------------------------- */ void pq_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. * -------------------------------- */ void pq_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. * -------------------------------- */ void pq_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 * -------------------------------- */ void pq_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. * -------------------------------- */ void pq_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. * -------------------------------- */ void pq_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. * -------------------------------- */ void pq_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]); #ifdef WORDS_BIGENDIAN /* 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); #endif #else /* INT64 works */ 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. * -------------------------------- */ void pq_endmessage(StringInfo buf) { /* msgtype was saved in cursor field */ (void) pq_putmessage(buf->cursor, buf->data, buf->len); /* no need to complain about any failure, since pqcomm.c already did */ pfree(buf->data); buf->data = NULL; } /* -------------------------------- * pq_begintypsend - initialize for constructing a bytea result * -------------------------------- */ void pq_begintypsend(StringInfo buf) { initStringInfo(buf); /* Reserve four bytes for the bytea length word */ appendStringInfoCharMacro(buf, '\0'); appendStringInfoCharMacro(buf, '\0'); appendStringInfoCharMacro(buf, '\0'); appendStringInfoCharMacro(buf, '\0'); } /* -------------------------------- * pq_endtypsend - finish constructing a bytea result * * The data buffer is returned as the palloc'd bytea value. (We expect * that it will be suitably aligned for this because it has been palloc'd.) * We assume the StringInfoData is just a local variable in the caller and * need not be pfree'd. * -------------------------------- */ bytea * pq_endtypsend(StringInfo buf) { bytea *result = (bytea *) buf->data; /* Insert correct length into bytea length word */ Assert(buf->len >= VARHDRSZ); SET_VARSIZE(result, buf->len); return result; } /* -------------------------------- * pq_puttextmessage - generate a character set-converted message in one step * * This is the same as the pqcomm.c routine pq_putmessage, except that * the message body is a null-terminated string to which encoding * conversion applies. * -------------------------------- */ void pq_puttextmessage(char msgtype, const char *str) { int slen = strlen(str); char *p; p = pg_server_to_client(str, slen); if (p != str) /* actual conversion has been done? */ { (void) pq_putmessage(msgtype, p, strlen(p) + 1); pfree(p); return; } (void) pq_putmessage(msgtype, str, slen + 1); } /* -------------------------------- * pq_putemptymessage - convenience routine for message with empty body * -------------------------------- */ void pq_putemptymessage(char msgtype) { (void) pq_putmessage(msgtype, NULL, 0); } /* -------------------------------- * pq_getmsgbyte - get a raw byte from a message buffer * -------------------------------- */ int pq_getmsgbyte(StringInfo msg) { if (msg->cursor >= msg->len) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("no data left in message"))); return (unsigned char) msg->data[msg->cursor++]; } /* -------------------------------- * pq_getmsgint - get a binary integer from a message buffer * * Values are treated as unsigned. * -------------------------------- */ unsigned int pq_getmsgint(StringInfo msg, int b) { unsigned int result; unsigned char n8; uint16 n16; uint32 n32; switch (b) { case 1: pq_copymsgbytes(msg, (char *) &n8, 1); result = n8; break; case 2: pq_copymsgbytes(msg, (char *) &n16, 2); result = ntohs(n16); break; case 4: pq_copymsgbytes(msg, (char *) &n32, 4); result = ntohl(n32); break; default: elog(ERROR, "unsupported integer size %d", b); result = 0; /* keep compiler quiet */ break; } return result; } /* -------------------------------- * pq_getmsgint64 - get a binary 8-byte int from a message buffer * * It is tempting to merge this with pq_getmsgint, but we'd have to make the * result int64 for all data widths --- that could be a big performance * hit on machines where int64 isn't efficient. * -------------------------------- */ int64 pq_getmsgint64(StringInfo msg) { int64 result; uint32 h32; uint32 l32; pq_copymsgbytes(msg, (char *) &h32, 4); pq_copymsgbytes(msg, (char *) &l32, 4); h32 = ntohl(h32); l32 = ntohl(l32); #ifdef INT64_IS_BUSTED /* error out if incoming value is wider than 32 bits */ result = l32; if ((result < 0) ? (h32 != -1) : (h32 != 0)) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("binary value is out of range for type bigint"))); #else result = h32; result <<= 32; result |= l32; #endif return result; } /* -------------------------------- * pq_getmsgfloat4 - get a float4 from a message buffer * * See notes for pq_sendfloat4. * -------------------------------- */ float4 pq_getmsgfloat4(StringInfo msg) { union { float4 f; uint32 i; } swap; swap.i = pq_getmsgint(msg, 4); return swap.f; } /* -------------------------------- * pq_getmsgfloat8 - get a float8 from a message buffer * * See notes for pq_sendfloat8. * -------------------------------- */ float8 pq_getmsgfloat8(StringInfo msg) { #ifdef INT64_IS_BUSTED union { float8 f; uint32 h[2]; } swap; #ifdef WORDS_BIGENDIAN /* machine seems to be big-endian, receive h[0] first */ swap.h[0] = pq_getmsgint(msg, 4); swap.h[1] = pq_getmsgint(msg, 4); #else /* machine seems to be little-endian, receive h[1] first */ swap.h[1] = pq_getmsgint(msg, 4); swap.h[0] = pq_getmsgint(msg, 4); #endif return swap.f; #else /* INT64 works */ union { float8 f; int64 i; } swap; swap.i = pq_getmsgint64(msg); return swap.f; #endif } /* -------------------------------- * pq_getmsgbytes - get raw data from a message buffer * * Returns a pointer directly into the message buffer; note this * may not have any particular alignment. * -------------------------------- */ const char * pq_getmsgbytes(StringInfo msg, int datalen) { const char *result; if (datalen < 0 || datalen > (msg->len - msg->cursor)) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("insufficient data left in message"))); result = &msg->data[msg->cursor]; msg->cursor += datalen; return result; } /* -------------------------------- * pq_copymsgbytes - copy raw data from a message buffer * * Same as above, except data is copied to caller's buffer. * -------------------------------- */ void pq_copymsgbytes(StringInfo msg, char *buf, int datalen) { if (datalen < 0 || datalen > (msg->len - msg->cursor)) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("insufficient data left in message"))); memcpy(buf, &msg->data[msg->cursor], datalen); msg->cursor += datalen; } /* -------------------------------- * pq_getmsgtext - get a counted text string (with conversion) * * Always returns a pointer to a freshly palloc'd result. * The result has a trailing null, *and* we return its strlen in *nbytes. * -------------------------------- */ char * pq_getmsgtext(StringInfo msg, int rawbytes, int *nbytes) { char *str; char *p; if (rawbytes < 0 || rawbytes > (msg->len - msg->cursor)) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("insufficient data left in message"))); str = &msg->data[msg->cursor]; msg->cursor += rawbytes; p = pg_client_to_server(str, rawbytes); if (p != str) /* actual conversion has been done? */ *nbytes = strlen(p); else { p = (char *) palloc(rawbytes + 1); memcpy(p, str, rawbytes); p[rawbytes] = '\0'; *nbytes = rawbytes; } return p; } /* -------------------------------- * pq_getmsgstring - get a null-terminated text string (with conversion) * * May return a pointer directly into the message buffer, or a pointer * to a palloc'd conversion result. * -------------------------------- */ const char * pq_getmsgstring(StringInfo msg) { char *str; int slen; str = &msg->data[msg->cursor]; /* * It's safe to use strlen() here because a StringInfo is guaranteed to * have a trailing null byte. But check we found a null inside the * message. */ slen = strlen(str); if (msg->cursor + slen >= msg->len) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("invalid string in message"))); msg->cursor += slen + 1; return pg_client_to_server(str, slen); } /* -------------------------------- * pq_getmsgend - verify message fully consumed * -------------------------------- */ void pq_getmsgend(StringInfo msg) { if (msg->cursor != msg->len) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("invalid message format"))); }