pqformat.c 7.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
/*-------------------------------------------------------------------------
 *
 * 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
11
 * character set conversion, while text is converted by character encoding rules.
12 13 14 15
 *
 * Incoming messages are read directly off the wire, as it were, but there
 * are still data-conversion tasks to be performed.
 *
B
Bruce Momjian 已提交
16
 * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
B
Add:  
Bruce Momjian 已提交
17
 * Portions Copyright (c) 1994, Regents of the University of California
18
 *
19
 *	$Id: pqformat.c,v 1.25 2002/09/04 23:31:35 tgl Exp $
20 21 22 23 24
 *
 *-------------------------------------------------------------------------
 */
/*
 * INTERFACE ROUTINES
25
 * Message assembly and output:
B
Bruce Momjian 已提交
26
 *		pq_beginmessage - initialize StringInfo buffer
27 28 29
 *		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
30 31
 *		pq_sendcountedtext - append a text string (with character set conversion)
 *		pq_sendstring	- append a null-terminated text string (with conversion)
32 33 34
 *		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
35
 * character set conversion may not occur.
36
 *
37
 * Special-case message output:
38
 *		pq_puttextmessage - generate a character set-converted message in one step
39
 *
40
 * Message input:
41 42 43 44
 *		pq_getint			- get an integer from connection
 *		pq_getstr_bounded	- get a null terminated string from connection
 * pq_getstr_bounded performs character set conversion on the collected
 * string.  Use the raw pqcomm.c routines pq_getstring or pq_getbytes
45 46
 * to fetch data without conversion.
 */
47

48 49
#include "postgres.h"

50 51 52
#include <errno.h>
#include <sys/param.h>

53
#include "libpq/libpq.h"
B
Bruce Momjian 已提交
54 55
#include "libpq/pqformat.h"
#include "mb/pg_wchar.h"
56
#ifdef HAVE_ENDIAN_H
57
#include <endian.h>
58 59 60 61 62 63 64 65 66 67
#endif


/* --------------------------------
 *		pq_sendbyte		- append a raw byte to a StringInfo buffer
 * --------------------------------
 */
void
pq_sendbyte(StringInfo buf, int byt)
{
68
	appendStringInfoCharMacro(buf, byt);
69 70 71 72 73 74 75 76 77 78 79 80 81
}

/* --------------------------------
 *		pq_sendbytes	- append raw data to a StringInfo buffer
 * --------------------------------
 */
void
pq_sendbytes(StringInfo buf, const char *data, int datalen)
{
	appendBinaryStringInfo(buf, data, datalen);
}

/* --------------------------------
82
 *		pq_sendcountedtext - append a text string (with character set conversion)
83
 *
84 85 86 87
 * 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.
88 89 90
 * --------------------------------
 */
void
91
pq_sendcountedtext(StringInfo buf, const char *str, int slen)
92
{
93
	char	   *p;
B
Bruce Momjian 已提交
94

95
	p = (char *) pg_server_to_client((unsigned char *) str, slen);
96 97
	if (p != str)				/* actual conversion has been done? */
	{
98 99 100 101 102
		slen = strlen(p);
		pq_sendint(buf, slen + 4, 4);
		appendBinaryStringInfo(buf, p, slen);
		pfree(p);
		return;
103 104
	}
	pq_sendint(buf, slen + 4, 4);
105 106 107 108
	appendBinaryStringInfo(buf, str, slen);
}

/* --------------------------------
109
 *		pq_sendstring	- append a null-terminated text string (with conversion)
110
 *
111 112
 * NB: passed text string must be null-terminated, and so is the data
 * sent to the frontend.
113 114 115
 * --------------------------------
 */
void
116
pq_sendstring(StringInfo buf, const char *str)
117
{
B
Bruce Momjian 已提交
118
	int			slen = strlen(str);
119

120
	char	   *p;
B
Bruce Momjian 已提交
121

122
	p = (char *) pg_server_to_client((unsigned char *) str, slen);
123 124
	if (p != str)				/* actual conversion has been done? */
	{
125 126 127 128
		slen = strlen(p);
		appendBinaryStringInfo(buf, p, slen + 1);
		pfree(p);
		return;
129
	}
B
Bruce Momjian 已提交
130
	appendBinaryStringInfo(buf, str, slen + 1);
131 132 133 134 135 136 137 138 139
}

/* --------------------------------
 *		pq_sendint		- append a binary integer to a StringInfo buffer
 * --------------------------------
 */
void
pq_sendint(StringInfo buf, int i, int b)
{
B
Bruce Momjian 已提交
140 141 142
	unsigned char n8;
	uint16		n16;
	uint32		n32;
143 144 145 146 147 148 149 150

	switch (b)
	{
		case 1:
			n8 = (unsigned char) i;
			appendBinaryStringInfo(buf, (char *) &n8, 1);
			break;
		case 2:
151
			n16 = htons((uint16) i);
152 153 154
			appendBinaryStringInfo(buf, (char *) &n16, 2);
			break;
		case 4:
155
			n32 = htonl((uint32) i);
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
			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)
{
174 175
	(void) pq_putmessage('\0', buf->data, buf->len);
	/* no need to complain about any failure, since pqcomm.c already did */
176 177 178 179
	pfree(buf->data);
	buf->data = NULL;
}

180
/* --------------------------------
181
 *		pq_puttextmessage - generate a character set-converted message in one step
182 183
 *
 *		This is the same as the pqcomm.c routine pq_putmessage, except that
184
 *		the message body is a null-terminated string to which encoding
185 186 187 188 189 190 191 192
 *		conversion applies.
 *
 *		returns 0 if OK, EOF if trouble
 * --------------------------------
 */
int
pq_puttextmessage(char msgtype, const char *str)
{
B
Bruce Momjian 已提交
193
	int			slen = strlen(str);
194
	char	   *p;
B
Bruce Momjian 已提交
195

196
	p = (char *) pg_server_to_client((unsigned char *) str, slen);
197 198
	if (p != str)				/* actual conversion has been done? */
	{
199 200
		int			result = pq_putmessage(msgtype, p, strlen(p) + 1);

201 202
		pfree(p);
		return result;
203
	}
B
Bruce Momjian 已提交
204
	return pq_putmessage(msgtype, str, slen + 1);
205 206
}

207 208 209 210 211 212 213 214 215
/* --------------------------------
 *		pq_getint - get an integer from connection
 *
 *		returns 0 if OK, EOF if trouble
 * --------------------------------
 */
int
pq_getint(int *result, int b)
{
B
Bruce Momjian 已提交
216 217 218 219
	int			status;
	unsigned char n8;
	uint16		n16;
	uint32		n32;
220 221 222 223 224 225 226 227 228

	switch (b)
	{
		case 1:
			status = pq_getbytes((char *) &n8, 1);
			*result = (int) n8;
			break;
		case 2:
			status = pq_getbytes((char *) &n16, 2);
229
			*result = (int) (ntohs(n16));
230 231 232
			break;
		case 4:
			status = pq_getbytes((char *) &n32, 4);
233
			*result = (int) (ntohl(n32));
234 235
			break;
		default:
B
Bruce Momjian 已提交
236 237 238 239

			/*
			 * if we elog(ERROR) here, we will lose sync with the
			 * frontend, so just complain to postmaster log instead...
240
			 */
241
			elog(COMMERROR, "pq_getint: unsupported size %d", b);
242 243 244 245 246 247 248 249
			status = EOF;
			*result = 0;
			break;
	}
	return status;
}

/* --------------------------------
250
 *		pq_getstr_bounded - get a null terminated string from connection
251
 *
252 253
 *		The return value is placed in an expansible StringInfo.
 *		Note that space allocation comes from the current memory context!
254
 *
255 256
 *		The maxlen parameter is interpreted as per pq_getstring.
 *
257 258 259 260
 *		returns 0 if OK, EOF if trouble
 * --------------------------------
 */
int
261
pq_getstr_bounded(StringInfo s, int maxlen)
262
{
263
	int			result;
264 265
	char	   *p;

266
	result = pq_getstring(s, maxlen);
267

268 269
	p = (char *) pg_client_to_server((unsigned char *) s->data, s->len);
	if (p != s->data)			/* actual conversion has been done? */
270
	{
271 272 273 274
		/* reset s to empty, and append the new string p */
		s->len = 0;
		s->data[0] = '\0';
		appendBinaryStringInfo(s, p, strlen(p));
275
		pfree(p);
276
	}
277

278
	return result;
279
}