pqformat.c 9.0 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 12
 * character set conversion, while text is converted by character encoding
 * rules.
13
 *
14 15 16
 * 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.
17
 *
B
Bruce Momjian 已提交
18
 * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
B
Add:  
Bruce Momjian 已提交
19
 * Portions Copyright (c) 1994, Regents of the University of California
20
 *
21
 *	$Header: /cvsroot/pgsql/src/backend/libpq/pqformat.c,v 1.27 2003/04/19 00:02:29 tgl Exp $
22 23 24 25 26
 *
 *-------------------------------------------------------------------------
 */
/*
 * INTERFACE ROUTINES
27
 * Message assembly and output:
B
Bruce Momjian 已提交
28
 *		pq_beginmessage - initialize StringInfo buffer
29 30 31
 *		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
32 33
 *		pq_sendcountedtext - append a text string (with character set conversion)
 *		pq_sendstring	- append a null-terminated text string (with conversion)
34 35 36
 *		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
37
 * character set conversion may not occur.
38
 *
39
 * Special-case message output:
40
 *		pq_puttextmessage - generate a character set-converted message in one step
41
 *
42 43 44 45 46 47 48
 * 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_getmsgbytes	- get raw data from a message buffer
 *		pq_copymsgbytes	- copy raw data from a message buffer
 *		pq_getmsgstring	- get a null-terminated text string (with conversion)
 *		pq_getmsgend	- verify message fully consumed
49
 */
50

51 52
#include "postgres.h"

53 54
#include <errno.h>
#include <sys/param.h>
55 56 57 58 59
#include <netinet/in.h>
#include <arpa/inet.h>
#ifdef HAVE_ENDIAN_H
#include <endian.h>
#endif
60

61
#include "libpq/libpq.h"
B
Bruce Momjian 已提交
62 63
#include "libpq/pqformat.h"
#include "mb/pg_wchar.h"
64 65 66 67 68 69 70 71 72


/* --------------------------------
 *		pq_sendbyte		- append a raw byte to a StringInfo buffer
 * --------------------------------
 */
void
pq_sendbyte(StringInfo buf, int byt)
{
73
	appendStringInfoCharMacro(buf, byt);
74 75 76 77 78 79 80 81 82 83 84 85 86
}

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

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

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

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

125
	char	   *p;
B
Bruce Momjian 已提交
126

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

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

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

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

201
	p = (char *) pg_server_to_client((unsigned char *) str, slen);
202 203
	if (p != str)				/* actual conversion has been done? */
	{
204 205
		int			result = pq_putmessage(msgtype, p, strlen(p) + 1);

206 207
		pfree(p);
		return result;
208
	}
B
Bruce Momjian 已提交
209
	return pq_putmessage(msgtype, str, slen + 1);
210 211
}

212

213
/* --------------------------------
214
 *		pq_getmsgbyte	- get a raw byte from a message buffer
215 216 217
 * --------------------------------
 */
int
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
pq_getmsgbyte(StringInfo msg)
{
	if (msg->cursor >= msg->len)
		elog(ERROR, "pq_getmsgbyte: 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)
233
{
234
	unsigned int result;
B
Bruce Momjian 已提交
235 236 237
	unsigned char n8;
	uint16		n16;
	uint32		n32;
238 239 240 241

	switch (b)
	{
		case 1:
242 243
			pq_copymsgbytes(msg, (char *) &n8, 1);
			result = n8;
244 245
			break;
		case 2:
246 247
			pq_copymsgbytes(msg, (char *) &n16, 2);
			result = ntohs(n16);
248 249
			break;
		case 4:
250 251
			pq_copymsgbytes(msg, (char *) &n32, 4);
			result = ntohl(n32);
252 253
			break;
		default:
254 255
			elog(ERROR, "pq_getmsgint: unsupported size %d", b);
			result = 0;			/* keep compiler quiet */
256 257
			break;
	}
258
	return result;
259 260 261
}

/* --------------------------------
262
 *		pq_getmsgbytes	- get raw data from a message buffer
263
 *
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
 *		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 > (msg->len - msg->cursor))
		elog(ERROR, "pq_getmsgbytes: insufficient data left in message");
	result = &msg->data[msg->cursor];
	msg->cursor += datalen;
	return result;
}

/* --------------------------------
 *		pq_copymsgbytes	- copy raw data from a message buffer
282
 *
283 284 285 286 287 288 289 290 291 292 293 294 295 296
 *		Same as above, except data is copied to caller's buffer.
 * --------------------------------
 */
void
pq_copymsgbytes(StringInfo msg, char *buf, int datalen)
{
	if (datalen > (msg->len - msg->cursor))
		elog(ERROR, "pq_copymsgbytes: insufficient data left in message");
	memcpy(buf, &msg->data[msg->cursor], datalen);
	msg->cursor += datalen;
}

/* --------------------------------
 *		pq_getmsgstring	- get a null-terminated text string (with conversion)
297
 *
298 299
 *		May return a pointer directly into the message buffer, or a pointer
 *		to a palloc'd conversion result.
300 301
 * --------------------------------
 */
302 303
const char *
pq_getmsgstring(StringInfo msg)
304
{
305 306
	char   *str;
	int		slen;
307

308 309 310 311 312 313 314 315 316 317
	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)
		elog(ERROR, "pq_getmsgstring: invalid string in message");
	msg->cursor += slen + 1;
318

319 320
	return (const char *) pg_client_to_server((unsigned char *) str, slen);
}
321

322 323 324 325 326 327 328 329 330
/* --------------------------------
 *		pq_getmsgend	- verify message fully consumed
 * --------------------------------
 */
void
pq_getmsgend(StringInfo msg)
{
	if (msg->cursor != msg->len)
		elog(ERROR, "pq_getmsgend: invalid message format");
331
}