1999-08-31 03:37:37 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* pqexpbuffer.c
|
|
|
|
*
|
|
|
|
* PQExpBuffer 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 malloc().
|
|
|
|
*
|
|
|
|
* This module is essentially the same as the backend's StringInfo data type,
|
|
|
|
* but it is intended for use in frontend libpq and client applications.
|
2013-10-25 23:42:26 +02:00
|
|
|
* Thus, it does not rely on palloc() nor elog(), nor psprintf.c which
|
|
|
|
* will exit() on error.
|
2000-01-17 03:59:46 +01:00
|
|
|
*
|
|
|
|
* It does rely on vsnprintf(); if configure finds that libc doesn't provide
|
|
|
|
* a usable vsnprintf(), then a copy of our own implementation of it will
|
|
|
|
* be linked into libpq.
|
1999-08-31 03:37:37 +02:00
|
|
|
*
|
2014-01-07 22:05:30 +01:00
|
|
|
* Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
|
2000-01-26 06:58:53 +01:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
1999-08-31 03:37:37 +02:00
|
|
|
*
|
2010-09-20 22:08:53 +02:00
|
|
|
* src/interfaces/libpq/pqexpbuffer.c
|
1999-08-31 03:37:37 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
2001-02-10 03:31:31 +01:00
|
|
|
#include "postgres_fe.h"
|
|
|
|
|
2004-05-14 02:20:38 +02:00
|
|
|
#include <limits.h>
|
|
|
|
|
1999-08-31 03:37:37 +02:00
|
|
|
#include "pqexpbuffer.h"
|
|
|
|
|
2000-01-18 20:05:31 +01:00
|
|
|
#ifdef WIN32
|
|
|
|
#include "win32.h"
|
|
|
|
#endif
|
|
|
|
|
2008-11-26 01:26:23 +01:00
|
|
|
|
|
|
|
/* All "broken" PQExpBuffers point to this string. */
|
|
|
|
static const char oom_buffer[1] = "";
|
|
|
|
|
2013-10-25 23:42:26 +02:00
|
|
|
static bool
|
|
|
|
appendPQExpBufferVA(PQExpBuffer str, const char *fmt, va_list args)
|
|
|
|
__attribute__((format(PG_PRINTF_ATTRIBUTE, 2, 0)));
|
|
|
|
|
2008-11-26 01:26:23 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* markPQExpBufferBroken
|
|
|
|
*
|
|
|
|
* Put a PQExpBuffer in "broken" state if it isn't already.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
markPQExpBufferBroken(PQExpBuffer str)
|
|
|
|
{
|
|
|
|
if (str->data != oom_buffer)
|
|
|
|
free(str->data);
|
2009-06-11 16:49:15 +02:00
|
|
|
|
2008-11-26 01:26:23 +01:00
|
|
|
/*
|
2009-06-11 16:49:15 +02:00
|
|
|
* Casting away const here is a bit ugly, but it seems preferable to not
|
|
|
|
* marking oom_buffer const. We want to do that to encourage the compiler
|
|
|
|
* to put oom_buffer in read-only storage, so that anyone who tries to
|
|
|
|
* scribble on a broken PQExpBuffer will get a failure.
|
2008-11-26 01:26:23 +01:00
|
|
|
*/
|
|
|
|
str->data = (char *) oom_buffer;
|
|
|
|
str->len = 0;
|
|
|
|
str->maxlen = 0;
|
|
|
|
}
|
|
|
|
|
1999-08-31 03:37:37 +02:00
|
|
|
/*
|
|
|
|
* createPQExpBuffer
|
|
|
|
*
|
|
|
|
* Create an empty 'PQExpBufferData' & return a pointer to it.
|
|
|
|
*/
|
|
|
|
PQExpBuffer
|
|
|
|
createPQExpBuffer(void)
|
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
PQExpBuffer res;
|
1999-08-31 03:37:37 +02:00
|
|
|
|
|
|
|
res = (PQExpBuffer) malloc(sizeof(PQExpBufferData));
|
|
|
|
if (res != NULL)
|
|
|
|
initPQExpBuffer(res);
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* initPQExpBuffer
|
|
|
|
*
|
|
|
|
* Initialize a PQExpBufferData struct (with previously undefined contents)
|
|
|
|
* to describe an empty string.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
initPQExpBuffer(PQExpBuffer str)
|
|
|
|
{
|
|
|
|
str->data = (char *) malloc(INITIAL_EXPBUFFER_SIZE);
|
|
|
|
if (str->data == NULL)
|
|
|
|
{
|
2008-11-26 01:26:23 +01:00
|
|
|
str->data = (char *) oom_buffer; /* see comment above */
|
1999-08-31 03:37:37 +02:00
|
|
|
str->maxlen = 0;
|
|
|
|
str->len = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
str->maxlen = INITIAL_EXPBUFFER_SIZE;
|
|
|
|
str->len = 0;
|
|
|
|
str->data[0] = '\0';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2001-08-17 17:11:15 +02:00
|
|
|
/*
|
1999-08-31 03:37:37 +02:00
|
|
|
* destroyPQExpBuffer(str);
|
2001-08-17 17:11:15 +02:00
|
|
|
*
|
1999-08-31 03:37:37 +02:00
|
|
|
* free()s both the data buffer and the PQExpBufferData.
|
|
|
|
* This is the inverse of createPQExpBuffer().
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
destroyPQExpBuffer(PQExpBuffer str)
|
|
|
|
{
|
|
|
|
if (str)
|
|
|
|
{
|
|
|
|
termPQExpBuffer(str);
|
|
|
|
free(str);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2001-08-17 17:11:15 +02:00
|
|
|
/*
|
1999-08-31 03:37:37 +02:00
|
|
|
* termPQExpBuffer(str)
|
|
|
|
* free()s the data buffer but not the PQExpBufferData itself.
|
|
|
|
* This is the inverse of initPQExpBuffer().
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
termPQExpBuffer(PQExpBuffer str)
|
|
|
|
{
|
2008-11-26 01:26:23 +01:00
|
|
|
if (str->data != oom_buffer)
|
1999-08-31 03:37:37 +02:00
|
|
|
free(str->data);
|
2001-01-19 20:39:23 +01:00
|
|
|
/* just for luck, make the buffer validly empty. */
|
2009-06-11 16:49:15 +02:00
|
|
|
str->data = (char *) oom_buffer; /* see comment above */
|
2001-01-19 20:39:23 +01:00
|
|
|
str->maxlen = 0;
|
|
|
|
str->len = 0;
|
1999-08-31 03:37:37 +02:00
|
|
|
}
|
|
|
|
|
2001-08-17 17:11:15 +02:00
|
|
|
/*
|
1999-08-31 03:37:37 +02:00
|
|
|
* resetPQExpBuffer
|
|
|
|
* Reset a PQExpBuffer to empty
|
2008-11-26 01:26:23 +01:00
|
|
|
*
|
|
|
|
* Note: if possible, a "broken" PQExpBuffer is returned to normal.
|
1999-08-31 03:37:37 +02:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
resetPQExpBuffer(PQExpBuffer str)
|
|
|
|
{
|
|
|
|
if (str)
|
|
|
|
{
|
2008-11-26 01:26:23 +01:00
|
|
|
if (str->data != oom_buffer)
|
|
|
|
{
|
|
|
|
str->len = 0;
|
1999-08-31 03:37:37 +02:00
|
|
|
str->data[0] = '\0';
|
2008-11-26 01:26:23 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* try to reinitialize to valid state */
|
|
|
|
initPQExpBuffer(str);
|
|
|
|
}
|
1999-08-31 03:37:37 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2001-08-17 17:11:15 +02:00
|
|
|
/*
|
1999-08-31 03:37:37 +02:00
|
|
|
* enlargePQExpBuffer
|
|
|
|
* Make sure there is enough space for 'needed' more bytes in the buffer
|
|
|
|
* ('needed' does not include the terminating null).
|
|
|
|
*
|
2009-06-11 16:49:15 +02:00
|
|
|
* Returns 1 if OK, 0 if failed to enlarge buffer. (In the latter case
|
2008-11-26 01:26:23 +01:00
|
|
|
* the buffer is left in "broken" state.)
|
1999-08-31 03:37:37 +02:00
|
|
|
*/
|
|
|
|
int
|
2000-02-08 00:10:11 +01:00
|
|
|
enlargePQExpBuffer(PQExpBuffer str, size_t needed)
|
1999-08-31 03:37:37 +02:00
|
|
|
{
|
2000-02-08 00:10:11 +01:00
|
|
|
size_t newlen;
|
1999-08-31 03:37:37 +02:00
|
|
|
char *newdata;
|
|
|
|
|
2008-11-26 01:26:23 +01:00
|
|
|
if (PQExpBufferBroken(str))
|
|
|
|
return 0; /* already failed */
|
|
|
|
|
2004-05-14 02:20:38 +02:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Guard against ridiculous "needed" values, which can occur if we're fed
|
|
|
|
* bogus data. Without this, we can get an overflow or infinite loop in
|
|
|
|
* the following.
|
2004-05-14 02:20:38 +02:00
|
|
|
*/
|
|
|
|
if (needed >= ((size_t) INT_MAX - str->len))
|
2008-11-26 01:26:23 +01:00
|
|
|
{
|
|
|
|
markPQExpBufferBroken(str);
|
2004-05-14 02:20:38 +02:00
|
|
|
return 0;
|
2008-11-26 01:26:23 +01:00
|
|
|
}
|
2004-05-14 02:20:38 +02:00
|
|
|
|
1999-08-31 03:37:37 +02:00
|
|
|
needed += str->len + 1; /* total space required now */
|
2004-05-14 02:20:38 +02:00
|
|
|
|
|
|
|
/* Because of the above test, we now have needed <= INT_MAX */
|
|
|
|
|
1999-08-31 03:37:37 +02:00
|
|
|
if (needed <= str->maxlen)
|
|
|
|
return 1; /* got enough space already */
|
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* 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...
|
1999-08-31 03:37:37 +02:00
|
|
|
*/
|
2001-01-19 20:39:23 +01:00
|
|
|
newlen = (str->maxlen > 0) ? (2 * str->maxlen) : 64;
|
1999-08-31 03:37:37 +02:00
|
|
|
while (needed > newlen)
|
|
|
|
newlen = 2 * newlen;
|
|
|
|
|
2004-05-14 02:20:38 +02:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Clamp to INT_MAX in case we went past it. Note we are assuming here
|
|
|
|
* that INT_MAX <= UINT_MAX/2, else the above loop could overflow. We
|
|
|
|
* will still have newlen >= needed.
|
2004-05-14 02:20:38 +02:00
|
|
|
*/
|
|
|
|
if (newlen > (size_t) INT_MAX)
|
|
|
|
newlen = (size_t) INT_MAX;
|
|
|
|
|
1999-08-31 03:37:37 +02:00
|
|
|
newdata = (char *) realloc(str->data, newlen);
|
|
|
|
if (newdata != NULL)
|
|
|
|
{
|
|
|
|
str->data = newdata;
|
|
|
|
str->maxlen = newlen;
|
|
|
|
return 1;
|
|
|
|
}
|
2008-11-26 01:26:23 +01:00
|
|
|
|
|
|
|
markPQExpBufferBroken(str);
|
1999-08-31 03:37:37 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2001-08-17 17:11:15 +02:00
|
|
|
/*
|
1999-08-31 03:37:37 +02:00
|
|
|
* printfPQExpBuffer
|
|
|
|
* Format text data under the control of fmt (an sprintf-like format string)
|
2000-04-12 19:17:23 +02:00
|
|
|
* and insert it into str. More space is allocated to str if necessary.
|
1999-08-31 03:37:37 +02:00
|
|
|
* This is a convenience routine that does the same thing as
|
|
|
|
* resetPQExpBuffer() followed by appendPQExpBuffer().
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
|
|
|
|
{
|
|
|
|
va_list args;
|
2013-10-25 23:42:26 +02:00
|
|
|
bool done;
|
1999-08-31 03:37:37 +02:00
|
|
|
|
|
|
|
resetPQExpBuffer(str);
|
2000-01-17 03:59:46 +01:00
|
|
|
|
2008-11-26 01:26:23 +01:00
|
|
|
if (PQExpBufferBroken(str))
|
|
|
|
return; /* already failed */
|
|
|
|
|
2013-10-25 23:42:26 +02:00
|
|
|
/* Loop in case we have to retry after enlarging the buffer. */
|
|
|
|
do
|
2000-01-17 03:59:46 +01:00
|
|
|
{
|
2013-10-25 23:42:26 +02:00
|
|
|
va_start(args, fmt);
|
|
|
|
done = appendPQExpBufferVA(str, fmt, args);
|
|
|
|
va_end(args);
|
|
|
|
} while (!done);
|
1999-08-31 03:37:37 +02:00
|
|
|
}
|
|
|
|
|
2001-08-17 17:11:15 +02:00
|
|
|
/*
|
1999-08-31 03:37:37 +02:00
|
|
|
* appendPQExpBuffer
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
|
|
|
|
{
|
|
|
|
va_list args;
|
2013-10-25 23:42:26 +02:00
|
|
|
bool done;
|
1999-08-31 03:37:37 +02:00
|
|
|
|
2008-11-26 01:26:23 +01:00
|
|
|
if (PQExpBufferBroken(str))
|
|
|
|
return; /* already failed */
|
|
|
|
|
2013-10-25 23:42:26 +02:00
|
|
|
/* Loop in case we have to retry after enlarging the buffer. */
|
|
|
|
do
|
|
|
|
{
|
|
|
|
va_start(args, fmt);
|
|
|
|
done = appendPQExpBufferVA(str, fmt, args);
|
|
|
|
va_end(args);
|
|
|
|
} while (!done);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* appendPQExpBufferVA
|
|
|
|
* Shared guts of printfPQExpBuffer/appendPQExpBuffer.
|
|
|
|
* Attempt to format data and append it to str. Returns true if done
|
|
|
|
* (either successful or hard failure), false if need to retry.
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
appendPQExpBufferVA(PQExpBuffer str, const char *fmt, va_list args)
|
|
|
|
{
|
|
|
|
size_t avail;
|
|
|
|
size_t needed;
|
|
|
|
int nprinted;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Try to format the given string into the available space; but if there's
|
|
|
|
* hardly any space, don't bother trying, just enlarge the buffer first.
|
|
|
|
*/
|
|
|
|
if (str->maxlen > str->len + 16)
|
2000-01-17 03:59:46 +01:00
|
|
|
{
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
2013-10-25 23:42:26 +02:00
|
|
|
* Note: we intentionally leave one byte unused, as a guard against
|
|
|
|
* old broken versions of vsnprintf.
|
2000-01-17 03:59:46 +01:00
|
|
|
*/
|
2013-10-25 23:42:26 +02:00
|
|
|
avail = str->maxlen - str->len - 1;
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
|
|
|
|
nprinted = vsnprintf(str->data + str->len, avail, fmt, args);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If vsnprintf reports an error other than ENOMEM, fail.
|
|
|
|
*/
|
|
|
|
if (nprinted < 0 && errno != 0 && errno != ENOMEM)
|
2000-01-17 03:59:46 +01:00
|
|
|
{
|
2013-10-25 23:42:26 +02:00
|
|
|
markPQExpBufferBroken(str);
|
|
|
|
return true;
|
|
|
|
}
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2013-10-25 23:42:26 +02:00
|
|
|
/*
|
|
|
|
* Note: some versions of vsnprintf return the number of chars
|
|
|
|
* actually stored, not the total space needed as C99 specifies. And
|
|
|
|
* at least one returns -1 on failure. Be conservative about
|
|
|
|
* believing whether the print worked.
|
|
|
|
*/
|
|
|
|
if (nprinted >= 0 && (size_t) nprinted < avail - 1)
|
|
|
|
{
|
|
|
|
/* Success. Note nprinted does not include trailing null. */
|
|
|
|
str->len += nprinted;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nprinted >= 0 && (size_t) nprinted > avail)
|
|
|
|
{
|
2000-01-17 03:59:46 +01:00
|
|
|
/*
|
2013-10-25 23:42:26 +02:00
|
|
|
* This appears to be a C99-compliant vsnprintf, so believe its
|
|
|
|
* estimate of the required space. (If it's wrong, the logic will
|
|
|
|
* still work, but we may loop multiple times.) Note that the
|
|
|
|
* space needed should be only nprinted+1 bytes, but we'd better
|
|
|
|
* allocate one more than that so that the test above will succeed
|
|
|
|
* next time.
|
|
|
|
*
|
|
|
|
* In the corner case where the required space just barely
|
|
|
|
* overflows, fail.
|
2000-01-17 03:59:46 +01:00
|
|
|
*/
|
2013-10-25 23:42:26 +02:00
|
|
|
if (nprinted > INT_MAX - 2)
|
2000-01-17 03:59:46 +01:00
|
|
|
{
|
2013-10-25 23:42:26 +02:00
|
|
|
markPQExpBufferBroken(str);
|
|
|
|
return true;
|
2000-01-17 03:59:46 +01:00
|
|
|
}
|
2013-10-25 23:42:26 +02:00
|
|
|
needed = nprinted + 2;
|
2000-01-17 03:59:46 +01:00
|
|
|
}
|
2013-10-25 23:42:26 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Buffer overrun, and we don't know how much space is needed.
|
|
|
|
* Estimate twice the previous buffer size, but not more than
|
|
|
|
* INT_MAX.
|
|
|
|
*/
|
|
|
|
if (avail >= INT_MAX / 2)
|
|
|
|
needed = INT_MAX;
|
|
|
|
else
|
|
|
|
needed = avail * 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* We have to guess at how much to enlarge, since we're skipping the
|
|
|
|
* formatting work.
|
|
|
|
*/
|
|
|
|
needed = 32;
|
2000-01-17 03:59:46 +01:00
|
|
|
}
|
2013-10-25 23:42:26 +02:00
|
|
|
|
|
|
|
/* Increase the buffer size and try again. */
|
|
|
|
if (!enlargePQExpBuffer(str, needed))
|
|
|
|
return true; /* oops, out of memory */
|
|
|
|
|
|
|
|
return false;
|
1999-08-31 03:37:37 +02:00
|
|
|
}
|
|
|
|
|
2001-08-17 17:11:15 +02:00
|
|
|
/*
|
1999-08-31 03:37:37 +02:00
|
|
|
* appendPQExpBufferStr
|
|
|
|
* Append the given string to a PQExpBuffer, allocating more space
|
|
|
|
* if necessary.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
appendPQExpBufferStr(PQExpBuffer str, const char *data)
|
|
|
|
{
|
|
|
|
appendBinaryPQExpBuffer(str, data, strlen(data));
|
|
|
|
}
|
|
|
|
|
2001-08-17 17:11:15 +02:00
|
|
|
/*
|
1999-08-31 03:37:37 +02:00
|
|
|
* appendPQExpBufferChar
|
|
|
|
* Append a single byte to str.
|
|
|
|
* Like appendPQExpBuffer(str, "%c", ch) but much faster.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
appendPQExpBufferChar(PQExpBuffer str, char ch)
|
|
|
|
{
|
|
|
|
/* Make more room if needed */
|
2000-04-12 19:17:23 +02:00
|
|
|
if (!enlargePQExpBuffer(str, 1))
|
1999-08-31 03:37:37 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
/* OK, append the character */
|
|
|
|
str->data[str->len] = ch;
|
|
|
|
str->len++;
|
|
|
|
str->data[str->len] = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* appendBinaryPQExpBuffer
|
|
|
|
*
|
|
|
|
* Append arbitrary binary data to a PQExpBuffer, allocating more space
|
|
|
|
* if necessary.
|
|
|
|
*/
|
|
|
|
void
|
2000-02-08 00:10:11 +01:00
|
|
|
appendBinaryPQExpBuffer(PQExpBuffer str, const char *data, size_t datalen)
|
1999-08-31 03:37:37 +02:00
|
|
|
{
|
|
|
|
/* Make more room if needed */
|
2000-04-12 19:17:23 +02:00
|
|
|
if (!enlargePQExpBuffer(str, datalen))
|
1999-08-31 03:37:37 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
/* OK, append the data */
|
|
|
|
memcpy(str->data + str->len, data, datalen);
|
|
|
|
str->len += datalen;
|
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Keep a trailing null in place, even though it's probably useless for
|
|
|
|
* binary data...
|
1999-08-31 03:37:37 +02:00
|
|
|
*/
|
|
|
|
str->data[str->len] = '\0';
|
|
|
|
}
|