mirror of
https://github.com/postgres/postgres.git
synced 2025-06-13 07:41:39 +03:00
Revise backend libpq interfaces so that messages to the frontend
can be generated in a buffer and then sent to the frontend in a single libpq call. This solves problems with NOTICE and ERROR messages generated in the middle of a data message or COPY OUT operation.
This commit is contained in:
@ -1,125 +1,171 @@
|
||||
/*
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* stringinfo.c
|
||||
* These are routines that can be used to write informations to a string,
|
||||
* without having to worry about string lengths, space allocation etc.
|
||||
* Ideally the interface should look like the file i/o interface,
|
||||
*
|
||||
* StringInfo provides an indefinitely-extensible string data type.
|
||||
* It can be used to buffer either ordinary C strings (null-terminated text)
|
||||
* or arbitrary binary data. All storage is allocated with palloc().
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: stringinfo.c,v 1.14 1999/02/13 23:15:36 momjian Exp $
|
||||
* $Id: stringinfo.c,v 1.15 1999/04/25 03:19:25 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <postgres.h>
|
||||
|
||||
#include <nodes/pg_list.h>
|
||||
#include <lib/stringinfo.h>
|
||||
#include "postgres.h"
|
||||
#include "lib/stringinfo.h"
|
||||
|
||||
/*
|
||||
* makeStringInfo
|
||||
*
|
||||
* Create a StringInfoData & return a pointer to it.
|
||||
*
|
||||
* Create an empty 'StringInfoData' & return a pointer to it.
|
||||
*/
|
||||
StringInfo
|
||||
makeStringInfo()
|
||||
makeStringInfo(void)
|
||||
{
|
||||
StringInfo res;
|
||||
int size;
|
||||
|
||||
res = (StringInfo) palloc(sizeof(StringInfoData));
|
||||
if (res == NULL)
|
||||
elog(ERROR, "makeStringInfo: Out of memory!");
|
||||
elog(ERROR, "makeStringInfo: Out of memory");
|
||||
|
||||
size = 256; /* initial default size */
|
||||
res->data = palloc(size);
|
||||
if (res->data == NULL)
|
||||
{
|
||||
elog(ERROR,
|
||||
"makeStringInfo: Out of memory! (%d bytes requested)", size);
|
||||
}
|
||||
res->maxlen = size;
|
||||
res->len = 0;
|
||||
/* Make sure the string is empty initially. */
|
||||
res->data[0] = '\0';
|
||||
initStringInfo(res);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* appendStringInfo
|
||||
*
|
||||
* append to the current 'StringInfo' a new string.
|
||||
* If there is not enough space in the current 'data', then reallocate
|
||||
* some more...
|
||||
*
|
||||
* NOTE: if we reallocate space, we pfree the old one!
|
||||
* initStringInfo
|
||||
*
|
||||
* Initialize a StringInfoData struct (with previously undefined contents)
|
||||
* to describe an empty string.
|
||||
*/
|
||||
void
|
||||
appendStringInfo(StringInfo str, const char *fmt,...)
|
||||
initStringInfo(StringInfo str)
|
||||
{
|
||||
int buflen,
|
||||
newlen,
|
||||
needed;
|
||||
char *s,
|
||||
buffer[512];
|
||||
int size = 256; /* initial default buffer size */
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
buflen = vsnprintf(buffer, 512, fmt, args);
|
||||
va_end(args);
|
||||
str->data = palloc(size);
|
||||
if (str->data == NULL)
|
||||
elog(ERROR,
|
||||
"initStringInfo: Out of memory (%d bytes requested)", size);
|
||||
str->maxlen = size;
|
||||
str->len = 0;
|
||||
str->data[0] = '\0';
|
||||
}
|
||||
|
||||
/*
|
||||
* enlargeStringInfo
|
||||
*
|
||||
* Internal routine: make sure there is enough space for 'needed' more bytes
|
||||
* ('needed' does not include the terminating null).
|
||||
*/
|
||||
static void
|
||||
enlargeStringInfo(StringInfo str, int needed)
|
||||
{
|
||||
int newlen;
|
||||
char *newdata;
|
||||
|
||||
needed += str->len + 1; /* total space required now */
|
||||
if (needed <= str->maxlen)
|
||||
return; /* got enough space already */
|
||||
|
||||
/*
|
||||
* We don't want to allocate just a little more space with each append;
|
||||
* for efficiency, double the buffer size each time it overflows.
|
||||
* Actually, we might need to more than double it if 'needed' is big...
|
||||
*/
|
||||
newlen = 2 * str->maxlen;
|
||||
while (needed > newlen)
|
||||
newlen = 2 * newlen;
|
||||
|
||||
newdata = palloc(newlen);
|
||||
if (newdata == NULL)
|
||||
elog(ERROR,
|
||||
"enlargeStringInfo: Out of memory (%d bytes requested)", newlen);
|
||||
|
||||
/* OK, transfer data into new buffer, and release old buffer */
|
||||
memcpy(newdata, str->data, str->len + 1);
|
||||
pfree(str->data);
|
||||
str->data = newdata;
|
||||
str->maxlen = newlen;
|
||||
}
|
||||
|
||||
/*
|
||||
* appendStringInfo
|
||||
*
|
||||
* Format text data under the control of fmt (an sprintf-like format string)
|
||||
* and append it to whatever is already in str. More space is allocated
|
||||
* to str if necessary. This is sort of like a combination of sprintf and
|
||||
* strcat.
|
||||
*
|
||||
* CAUTION: the current implementation has a 1K limit on the amount of text
|
||||
* generated in a single call (not on the total string length).
|
||||
*/
|
||||
void
|
||||
appendStringInfo(StringInfo str, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
char buffer[1024];
|
||||
int buflen;
|
||||
|
||||
Assert(str != NULL);
|
||||
if (buflen == 0)
|
||||
strcpy(buffer, "<>");
|
||||
|
||||
/*
|
||||
* do we have enough space to append the new string? (don't forget to
|
||||
* count the null string terminating char!) If no, then reallocate
|
||||
* some more.
|
||||
*/
|
||||
needed = str->len + buflen + 1;
|
||||
if (needed > str->maxlen)
|
||||
{
|
||||
va_start(args, fmt);
|
||||
buflen = vsnprintf(buffer, sizeof(buffer), fmt, args);
|
||||
va_end(args);
|
||||
|
||||
/*
|
||||
* how much more space to allocate ? Let's say double the current
|
||||
* space... However we must check if this is enough!
|
||||
*/
|
||||
newlen = 2 * str->maxlen;
|
||||
while (needed > newlen)
|
||||
newlen = 2 * newlen;
|
||||
/* Make more room if needed */
|
||||
enlargeStringInfo(str, buflen);
|
||||
|
||||
/*
|
||||
* allocate enough space.
|
||||
*/
|
||||
s = palloc(newlen);
|
||||
if (s == NULL)
|
||||
{
|
||||
elog(ERROR,
|
||||
"appendStringInfo: Out of memory (%d bytes requested)", newlen);
|
||||
}
|
||||
/*
|
||||
* transfer the data. strcpy() would work, but is probably a tad
|
||||
* slower than memcpy(), and since we know the string length...
|
||||
*/
|
||||
memcpy(s, str->data, str->len + 1);
|
||||
pfree(str->data);
|
||||
str->maxlen = newlen;
|
||||
str->data = s;
|
||||
}
|
||||
|
||||
/*
|
||||
* OK, we have enough space now, append 'buffer' at the end of the
|
||||
* string & update the string length. NOTE: strcat() would work,
|
||||
* but is certainly slower than just memcpy'ing the data to the right
|
||||
* place.
|
||||
*/
|
||||
/* OK, append the data, including the trailing null */
|
||||
memcpy(str->data + str->len, buffer, buflen + 1);
|
||||
str->len += buflen;
|
||||
}
|
||||
|
||||
/*------------------------
|
||||
* appendStringInfoChar
|
||||
* Append a single byte to str.
|
||||
* Like appendStringInfo(str, "%c", ch) but much faster.
|
||||
*/
|
||||
void
|
||||
appendStringInfoChar(StringInfo str, char ch)
|
||||
{
|
||||
Assert(str != NULL);
|
||||
|
||||
/* Make more room if needed */
|
||||
enlargeStringInfo(str, 1);
|
||||
|
||||
/* OK, append the character */
|
||||
str->data[str->len] = ch;
|
||||
str->len++;
|
||||
str->data[str->len] = '\0';
|
||||
}
|
||||
|
||||
/*
|
||||
* appendBinaryStringInfo
|
||||
*
|
||||
* Append arbitrary binary data to a StringInfo, allocating more space
|
||||
* if necessary.
|
||||
*/
|
||||
void
|
||||
appendBinaryStringInfo(StringInfo str, const char *data, int datalen)
|
||||
{
|
||||
Assert(str != NULL);
|
||||
|
||||
/* Make more room if needed */
|
||||
enlargeStringInfo(str, datalen);
|
||||
|
||||
/* OK, append the data */
|
||||
memcpy(str->data + str->len, data, datalen);
|
||||
str->len += datalen;
|
||||
|
||||
/* Keep a trailing null in place, even though it's probably useless
|
||||
* for binary data...
|
||||
*/
|
||||
str->data[str->len] = '\0';
|
||||
}
|
||||
|
Reference in New Issue
Block a user