mirror of
https://github.com/postgres/postgres.git
synced 2025-04-25 21:42:33 +03:00
693 lines
18 KiB
C
693 lines
18 KiB
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.39 2005/09/24 17:53:14 tgl 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
|
|
* --------------------------------
|
|
*/
|
|
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]);
|
|
|
|
/* 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.
|
|
* --------------------------------
|
|
*/
|
|
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);
|
|
VARATT_SIZEP(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;
|
|
|
|
/* Have to figure out endianness by testing... */
|
|
if (((uint32) 1) == htonl((uint32) 1))
|
|
{
|
|
/* 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);
|
|
}
|
|
return swap.f;
|
|
#else
|
|
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")));
|
|
}
|