aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bin/psql/input.c11
-rw-r--r--src/bin/psql/mainloop.c37
-rw-r--r--src/bin/psql/psqlscan.l8
-rw-r--r--src/interfaces/libpq/fe-connect.c10
-rw-r--r--src/interfaces/libpq/pqexpbuffer.c65
-rw-r--r--src/interfaces/libpq/pqexpbuffer.h20
6 files changed, 124 insertions, 27 deletions
diff --git a/src/bin/psql/input.c b/src/bin/psql/input.c
index 6a12dc9ff51..5865a2e4b6e 100644
--- a/src/bin/psql/input.c
+++ b/src/bin/psql/input.c
@@ -3,7 +3,7 @@
*
* Copyright (c) 2000-2008, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/bin/psql/input.c,v 1.64 2008/01/01 19:45:56 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/input.c,v 1.65 2008/11/26 00:26:23 tgl Exp $
*/
#include "postgres_fe.h"
@@ -184,7 +184,8 @@ gets_fromFile(FILE *source)
{
if (ferror(source))
{
- psql_error("could not read from input file: %s\n", strerror(errno));
+ psql_error("could not read from input file: %s\n",
+ strerror(errno));
return NULL;
}
break;
@@ -192,6 +193,12 @@ gets_fromFile(FILE *source)
appendPQExpBufferStr(buffer, line);
+ if (PQExpBufferBroken(buffer))
+ {
+ psql_error("out of memory\n");
+ return NULL;
+ }
+
/* EOL? */
if (buffer->data[buffer->len - 1] == '\n')
{
diff --git a/src/bin/psql/mainloop.c b/src/bin/psql/mainloop.c
index 526674c8181..16532e2d420 100644
--- a/src/bin/psql/mainloop.c
+++ b/src/bin/psql/mainloop.c
@@ -3,7 +3,7 @@
*
* Copyright (c) 2000-2008, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/bin/psql/mainloop.c,v 1.92 2008/06/10 20:58:19 neilc Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/mainloop.c,v 1.93 2008/11/26 00:26:23 tgl Exp $
*/
#include "postgres_fe.h"
#include "mainloop.h"
@@ -26,9 +26,9 @@ int
MainLoop(FILE *source)
{
PsqlScanState scan_state; /* lexer working state */
- PQExpBuffer query_buf; /* buffer for query being accumulated */
- PQExpBuffer previous_buf; /* if there isn't anything in the new buffer
- * yet, use this one for \e, etc. */
+ volatile PQExpBuffer query_buf; /* buffer for query being accumulated */
+ volatile PQExpBuffer previous_buf; /* if there isn't anything in the new
+ * buffer yet, use this one for \e, etc. */
PQExpBuffer history_buf; /* earlier lines of a multi-line command, not
* yet saved to readline history */
char *line; /* current line of input */
@@ -62,7 +62,9 @@ MainLoop(FILE *source)
query_buf = createPQExpBuffer();
previous_buf = createPQExpBuffer();
history_buf = createPQExpBuffer();
- if (!query_buf || !previous_buf || !history_buf)
+ if (PQExpBufferBroken(query_buf) ||
+ PQExpBufferBroken(previous_buf) ||
+ PQExpBufferBroken(history_buf))
{
psql_error("out of memory\n");
exit(EXIT_FAILURE);
@@ -220,6 +222,12 @@ MainLoop(FILE *source)
scan_result = psql_scan(scan_state, query_buf, &prompt_tmp);
prompt_status = prompt_tmp;
+ if (PQExpBufferBroken(query_buf))
+ {
+ psql_error("out of memory\n");
+ exit(EXIT_FAILURE);
+ }
+
/*
* Send command if semicolon found, or if end of line and we're in
* single-line mode.
@@ -242,9 +250,15 @@ MainLoop(FILE *source)
success = SendQuery(query_buf->data);
slashCmdStatus = success ? PSQL_CMD_SEND : PSQL_CMD_ERROR;
- resetPQExpBuffer(previous_buf);
- appendPQExpBufferStr(previous_buf, query_buf->data);
+ /* transfer query to previous_buf by pointer-swapping */
+ {
+ PQExpBuffer swap_buf = previous_buf;
+
+ previous_buf = query_buf;
+ query_buf = swap_buf;
+ }
resetPQExpBuffer(query_buf);
+
added_nl_pos = -1;
/* we need not do psql_scan_reset() here */
}
@@ -294,8 +308,13 @@ MainLoop(FILE *source)
{
success = SendQuery(query_buf->data);
- resetPQExpBuffer(previous_buf);
- appendPQExpBufferStr(previous_buf, query_buf->data);
+ /* transfer query to previous_buf by pointer-swapping */
+ {
+ PQExpBuffer swap_buf = previous_buf;
+
+ previous_buf = query_buf;
+ query_buf = swap_buf;
+ }
resetPQExpBuffer(query_buf);
/* flush any paren nesting info after forced send */
diff --git a/src/bin/psql/psqlscan.l b/src/bin/psql/psqlscan.l
index 02329b9e757..45dfba8d665 100644
--- a/src/bin/psql/psqlscan.l
+++ b/src/bin/psql/psqlscan.l
@@ -33,7 +33,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/bin/psql/psqlscan.l,v 1.26 2008/10/29 08:04:53 petere Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/psqlscan.l,v 1.27 2008/11/26 00:26:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1453,6 +1453,12 @@ psql_scan_slash_option(PsqlScanState state,
error = true;
}
+ if (PQExpBufferBroken(&output))
+ {
+ psql_error("%s: out of memory\n", cmd);
+ error = true;
+ }
+
/* Now done with cmd, transfer result to mybuf */
resetPQExpBuffer(&mybuf);
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 2cfe36e3a22..ce6af2bcd32 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.369 2008/11/25 19:30:42 tgl Exp $
+ * $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.370 2008/11/26 00:26:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -585,7 +585,7 @@ PQconndefaults(void)
PQconninfoOption *connOptions;
initPQExpBuffer(&errorBuf);
- if (errorBuf.data == NULL)
+ if (PQExpBufferBroken(&errorBuf))
return NULL; /* out of memory already :-( */
connOptions = conninfo_parse("", &errorBuf, true);
termPQExpBuffer(&errorBuf);
@@ -1970,8 +1970,8 @@ makeEmptyPGconn(void)
if (conn->inBuffer == NULL ||
conn->outBuffer == NULL ||
- conn->errorMessage.data == NULL ||
- conn->workBuffer.data == NULL)
+ PQExpBufferBroken(&conn->errorMessage) ||
+ PQExpBufferBroken(&conn->workBuffer))
{
/* out of memory already :-( */
freePGconn(conn);
@@ -3151,7 +3151,7 @@ PQconninfoParse(const char *conninfo, char **errmsg)
if (errmsg)
*errmsg = NULL; /* default */
initPQExpBuffer(&errorBuf);
- if (errorBuf.data == NULL)
+ if (PQExpBufferBroken(&errorBuf))
return NULL; /* out of memory already :-( */
connOptions = conninfo_parse(conninfo, &errorBuf, false);
if (connOptions == NULL && errmsg)
diff --git a/src/interfaces/libpq/pqexpbuffer.c b/src/interfaces/libpq/pqexpbuffer.c
index c8e80498d1f..aeb3a21a387 100644
--- a/src/interfaces/libpq/pqexpbuffer.c
+++ b/src/interfaces/libpq/pqexpbuffer.c
@@ -17,7 +17,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/interfaces/libpq/pqexpbuffer.c,v 1.24 2008/01/01 19:46:00 momjian Exp $
+ * $PostgreSQL: pgsql/src/interfaces/libpq/pqexpbuffer.c,v 1.25 2008/11/26 00:26:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -32,6 +32,32 @@
#include "win32.h"
#endif
+
+/* All "broken" PQExpBuffers point to this string. */
+static const char oom_buffer[1] = "";
+
+
+/*
+ * 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);
+ /*
+ * 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.
+ */
+ str->data = (char *) oom_buffer;
+ str->len = 0;
+ str->maxlen = 0;
+}
+
/*
* createPQExpBuffer
*
@@ -61,6 +87,7 @@ initPQExpBuffer(PQExpBuffer str)
str->data = (char *) malloc(INITIAL_EXPBUFFER_SIZE);
if (str->data == NULL)
{
+ str->data = (char *) oom_buffer; /* see comment above */
str->maxlen = 0;
str->len = 0;
}
@@ -96,12 +123,10 @@ destroyPQExpBuffer(PQExpBuffer str)
void
termPQExpBuffer(PQExpBuffer str)
{
- if (str->data)
- {
+ if (str->data != oom_buffer)
free(str->data);
- str->data = NULL;
- }
/* just for luck, make the buffer validly empty. */
+ str->data = (char *) oom_buffer; /* see comment above */
str->maxlen = 0;
str->len = 0;
}
@@ -109,15 +134,24 @@ termPQExpBuffer(PQExpBuffer str)
/*
* resetPQExpBuffer
* Reset a PQExpBuffer to empty
+ *
+ * Note: if possible, a "broken" PQExpBuffer is returned to normal.
*/
void
resetPQExpBuffer(PQExpBuffer str)
{
if (str)
{
- str->len = 0;
- if (str->data)
+ if (str->data != oom_buffer)
+ {
+ str->len = 0;
str->data[0] = '\0';
+ }
+ else
+ {
+ /* try to reinitialize to valid state */
+ initPQExpBuffer(str);
+ }
}
}
@@ -126,7 +160,8 @@ resetPQExpBuffer(PQExpBuffer str)
* Make sure there is enough space for 'needed' more bytes in the buffer
* ('needed' does not include the terminating null).
*
- * Returns 1 if OK, 0 if failed to enlarge buffer.
+ * Returns 1 if OK, 0 if failed to enlarge buffer. (In the latter case
+ * the buffer is left in "broken" state.)
*/
int
enlargePQExpBuffer(PQExpBuffer str, size_t needed)
@@ -134,13 +169,19 @@ enlargePQExpBuffer(PQExpBuffer str, size_t needed)
size_t newlen;
char *newdata;
+ if (PQExpBufferBroken(str))
+ return 0; /* already failed */
+
/*
* 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.
*/
if (needed >= ((size_t) INT_MAX - str->len))
+ {
+ markPQExpBufferBroken(str);
return 0;
+ }
needed += str->len + 1; /* total space required now */
@@ -173,6 +214,8 @@ enlargePQExpBuffer(PQExpBuffer str, size_t needed)
str->maxlen = newlen;
return 1;
}
+
+ markPQExpBufferBroken(str);
return 0;
}
@@ -192,6 +235,9 @@ printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
resetPQExpBuffer(str);
+ if (PQExpBufferBroken(str))
+ return; /* already failed */
+
for (;;)
{
/*
@@ -240,6 +286,9 @@ appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
size_t avail;
int nprinted;
+ if (PQExpBufferBroken(str))
+ return; /* already failed */
+
for (;;)
{
/*
diff --git a/src/interfaces/libpq/pqexpbuffer.h b/src/interfaces/libpq/pqexpbuffer.h
index b834ac48082..3acca24b569 100644
--- a/src/interfaces/libpq/pqexpbuffer.h
+++ b/src/interfaces/libpq/pqexpbuffer.h
@@ -18,7 +18,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/interfaces/libpq/pqexpbuffer.h,v 1.19 2008/01/01 19:46:00 momjian Exp $
+ * $PostgreSQL: pgsql/src/interfaces/libpq/pqexpbuffer.h,v 1.20 2008/11/26 00:26:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -35,6 +35,10 @@
* string size (including the terminating '\0' char) that we can
* currently store in 'data' without having to reallocate
* more space. We must always have maxlen > len.
+ *
+ * An exception occurs if we failed to allocate enough memory for the string
+ * buffer. In that case data points to a statically allocated empty string,
+ * and len = maxlen = 0.
*-------------------------
*/
typedef struct PQExpBufferData
@@ -47,6 +51,15 @@ typedef struct PQExpBufferData
typedef PQExpBufferData *PQExpBuffer;
/*------------------------
+ * Test for a broken (out of memory) PQExpBuffer.
+ * When a buffer is "broken", all operations except resetting or deleting it
+ * are no-ops.
+ *------------------------
+ */
+#define PQExpBufferBroken(str) \
+ (!(str) || (str)->maxlen == 0)
+
+/*------------------------
* Initial size of the data buffer in a PQExpBuffer.
* NB: this must be large enough to hold error messages that might
* be returned by PQrequestCancel().
@@ -103,6 +116,8 @@ extern void termPQExpBuffer(PQExpBuffer str);
/*------------------------
* resetPQExpBuffer
* Reset a PQExpBuffer to empty
+ *
+ * Note: if possible, a "broken" PQExpBuffer is returned to normal.
*/
extern void resetPQExpBuffer(PQExpBuffer str);
@@ -111,7 +126,8 @@ extern void resetPQExpBuffer(PQExpBuffer str);
* Make sure there is enough space for 'needed' more bytes in the buffer
* ('needed' does not include the terminating null).
*
- * Returns 1 if OK, 0 if failed to enlarge buffer.
+ * Returns 1 if OK, 0 if failed to enlarge buffer. (In the latter case
+ * the buffer is left in "broken" state.)
*/
extern int enlargePQExpBuffer(PQExpBuffer str, size_t needed);