336 lines
8.6 KiB
C
336 lines
8.6 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
|
|
* MULTIBYTE conversion, while text is converted by MULTIBYTE rules.
|
|
*
|
|
* Incoming messages are read directly off the wire, as it were, but there
|
|
* are still data-conversion tasks to be performed.
|
|
*
|
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
* $Id: pqformat.c,v 1.15 2000/07/12 22:58:59 petere 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_sendbytes - append raw data to a StringInfo buffer
|
|
* pq_sendcountedtext - append a text string (with MULTIBYTE conversion)
|
|
* pq_sendstring - append a null-terminated text string (with MULTIBYTE)
|
|
* 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
|
|
* MULTIBYTE conversion may not occur.
|
|
*
|
|
* Special-case message output:
|
|
* pq_puttextmessage - generate a MULTIBYTE-converted message in one step
|
|
*
|
|
* Message input:
|
|
* pq_getint - get an integer from connection
|
|
* pq_getstr - get a null terminated string from connection
|
|
* pq_getstr performs MULTIBYTE conversion on the collected string.
|
|
* Use the raw pqcomm.c routines pq_getstring or pq_getbytes
|
|
* to fetch data without conversion.
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
#include <errno.h>
|
|
#include <sys/param.h>
|
|
|
|
#include "libpq/libpq.h"
|
|
#include "libpq/pqformat.h"
|
|
#ifdef MULTIBYTE
|
|
#include "mb/pg_wchar.h"
|
|
#endif
|
|
#ifdef HAVE_ENDIAN_H
|
|
#include <endian.h>
|
|
#endif
|
|
|
|
#ifndef BYTE_ORDER
|
|
#error BYTE_ORDER must be defined as LITTLE_ENDIAN, BIG_ENDIAN or PDP_ENDIAN
|
|
#endif
|
|
|
|
#if BYTE_ORDER == LITTLE_ENDIAN
|
|
|
|
#define ntoh_s(n) n
|
|
#define ntoh_l(n) n
|
|
#define hton_s(n) n
|
|
#define hton_l(n) n
|
|
|
|
#else
|
|
#if BYTE_ORDER == BIG_ENDIAN
|
|
|
|
#define ntoh_s(n) (uint16)((((uint16)n & 0x00ff) << 8) | \
|
|
(((uint16)n & 0xff00) >> 8))
|
|
#define ntoh_l(n) (uint32)((((uint32)n & 0x000000ff) << 24) | \
|
|
(((uint32)n & 0x0000ff00) << 8) | \
|
|
(((uint32)n & 0x00ff0000) >> 8) | \
|
|
(((uint32)n & 0xff000000) >> 24))
|
|
#define hton_s(n) (ntoh_s(n))
|
|
#define hton_l(n) (ntoh_l(n))
|
|
|
|
#else
|
|
#if BYTE_ORDER == PDP_ENDIAN
|
|
|
|
#error PDP_ENDIAN macros not written yet
|
|
|
|
#else
|
|
|
|
#error BYTE_ORDER not defined as anything understood
|
|
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
|
|
/* --------------------------------
|
|
* pq_sendbyte - append a raw byte to a StringInfo buffer
|
|
* --------------------------------
|
|
*/
|
|
void
|
|
pq_sendbyte(StringInfo buf, int byt)
|
|
{
|
|
appendStringInfoChar(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 text string (with MULTIBYTE conversion)
|
|
*
|
|
* The data sent to the frontend by this routine is a 4-byte count field
|
|
* (the count includes itself, by convention) followed by the string.
|
|
* 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)
|
|
{
|
|
#ifdef MULTIBYTE
|
|
char *p;
|
|
|
|
p = (char *) pg_server_to_client((unsigned char *) str, slen);
|
|
if (p != str) /* actual conversion has been done? */
|
|
{
|
|
slen = strlen(p);
|
|
pq_sendint(buf, slen + 4, 4);
|
|
appendBinaryStringInfo(buf, p, slen);
|
|
pfree(p);
|
|
return;
|
|
}
|
|
#endif
|
|
pq_sendint(buf, slen + 4, 4);
|
|
appendBinaryStringInfo(buf, str, slen);
|
|
}
|
|
|
|
/* --------------------------------
|
|
* pq_sendstring - append a null-terminated text string (with MULTIBYTE)
|
|
*
|
|
* 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);
|
|
|
|
#ifdef MULTIBYTE
|
|
char *p;
|
|
|
|
p = (char *) pg_server_to_client((unsigned char *) str, slen);
|
|
if (p != str) /* actual conversion has been done? */
|
|
{
|
|
slen = strlen(p);
|
|
appendBinaryStringInfo(buf, p, slen + 1);
|
|
pfree(p);
|
|
return;
|
|
}
|
|
#endif
|
|
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 = ((PG_PROTOCOL_MAJOR(FrontendProtocol) == 0) ? hton_s(i) : htons((uint16) i));
|
|
appendBinaryStringInfo(buf, (char *) &n16, 2);
|
|
break;
|
|
case 4:
|
|
n32 = ((PG_PROTOCOL_MAJOR(FrontendProtocol) == 0) ? hton_l(i) : htonl((uint32) i));
|
|
appendBinaryStringInfo(buf, (char *) &n32, 4);
|
|
break;
|
|
default:
|
|
elog(ERROR, "pq_sendint: unsupported size %d", b);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* --------------------------------
|
|
* 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)
|
|
{
|
|
if (pq_putmessage('\0', buf->data, buf->len))
|
|
{
|
|
snprintf(PQerrormsg, PQERRORMSG_LENGTH,
|
|
"FATAL: pq_endmessage failed: errno=%d\n", errno);
|
|
fputs(PQerrormsg, stderr);
|
|
pqdebug("%s", PQerrormsg);
|
|
}
|
|
pfree(buf->data);
|
|
buf->data = NULL;
|
|
}
|
|
|
|
/* --------------------------------
|
|
* pq_puttextmessage - generate a MULTIBYTE-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 MULTIBYTE
|
|
* conversion applies.
|
|
*
|
|
* returns 0 if OK, EOF if trouble
|
|
* --------------------------------
|
|
*/
|
|
int
|
|
pq_puttextmessage(char msgtype, const char *str)
|
|
{
|
|
int slen = strlen(str);
|
|
|
|
#ifdef MULTIBYTE
|
|
char *p;
|
|
|
|
p = (char *) pg_server_to_client((unsigned char *) str, slen);
|
|
if (p != str) /* actual conversion has been done? */
|
|
{
|
|
int result = pq_putmessage(msgtype, p, strlen(p) + 1);
|
|
|
|
pfree(p);
|
|
return result;
|
|
}
|
|
#endif
|
|
return pq_putmessage(msgtype, str, slen + 1);
|
|
}
|
|
|
|
/* --------------------------------
|
|
* pq_getint - get an integer from connection
|
|
*
|
|
* returns 0 if OK, EOF if trouble
|
|
* --------------------------------
|
|
*/
|
|
int
|
|
pq_getint(int *result, int b)
|
|
{
|
|
int status;
|
|
unsigned char n8;
|
|
uint16 n16;
|
|
uint32 n32;
|
|
|
|
switch (b)
|
|
{
|
|
case 1:
|
|
status = pq_getbytes((char *) &n8, 1);
|
|
*result = (int) n8;
|
|
break;
|
|
case 2:
|
|
status = pq_getbytes((char *) &n16, 2);
|
|
*result = (int) ((PG_PROTOCOL_MAJOR(FrontendProtocol) == 0) ?
|
|
ntoh_s(n16) : ntohs(n16));
|
|
break;
|
|
case 4:
|
|
status = pq_getbytes((char *) &n32, 4);
|
|
*result = (int) ((PG_PROTOCOL_MAJOR(FrontendProtocol) == 0) ?
|
|
ntoh_l(n32) : ntohl(n32));
|
|
break;
|
|
default:
|
|
|
|
/*
|
|
* if we elog(ERROR) here, we will lose sync with the
|
|
* frontend, so just complain to postmaster log instead...
|
|
*/
|
|
fprintf(stderr, "pq_getint: unsupported size %d\n", b);
|
|
status = EOF;
|
|
*result = 0;
|
|
break;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/* --------------------------------
|
|
* pq_getstr - get a null terminated string from connection
|
|
*
|
|
* The return value is placed in an expansible StringInfo.
|
|
* Note that space allocation comes from the current memory context!
|
|
*
|
|
* returns 0 if OK, EOF if trouble
|
|
* --------------------------------
|
|
*/
|
|
int
|
|
pq_getstr(StringInfo s)
|
|
{
|
|
int result;
|
|
|
|
#ifdef MULTIBYTE
|
|
char *p;
|
|
|
|
#endif
|
|
|
|
result = pq_getstring(s);
|
|
|
|
#ifdef MULTIBYTE
|
|
p = (char *) pg_client_to_server((unsigned char *) s->data, s->len);
|
|
if (p != s->data) /* actual conversion has been done? */
|
|
{
|
|
/* reset s to empty, and append the new string p */
|
|
s->len = 0;
|
|
s->data[0] = '\0';
|
|
appendBinaryStringInfo(s, p, strlen(p));
|
|
pfree(p);
|
|
}
|
|
#endif
|
|
|
|
return result;
|
|
}
|