aboutsummaryrefslogtreecommitdiff
path: root/src/interfaces/libpq/fe-exec.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>1999-08-31 01:37:37 +0000
committerTom Lane <tgl@sss.pgh.pa.us>1999-08-31 01:37:37 +0000
commitab5cafa5d3f47439d8f65516bc5b88ad04117621 (patch)
tree33724b61ca936a81b37106ffa29ba4e396a543fd /src/interfaces/libpq/fe-exec.c
parent130e372b5d55fcd8201518adc9025a5cb0f13b56 (diff)
downloadpostgresql-ab5cafa5d3f47439d8f65516bc5b88ad04117621.tar.gz
postgresql-ab5cafa5d3f47439d8f65516bc5b88ad04117621.zip
Update frontend libpq to remove limits on query lengths,
error/notice message lengths, and number of fields per tuple. Add pqexpbuffer.c/.h, a frontend version of backend's stringinfo module. This is first step in applying Mike Ansley's long-query patches, even though he didn't do any of these particular changes...
Diffstat (limited to 'src/interfaces/libpq/fe-exec.c')
-rw-r--r--src/interfaces/libpq/fe-exec.c398
1 files changed, 252 insertions, 146 deletions
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index 01613e0db84..24fe9860ebb 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.84 1999/07/19 06:25:39 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.85 1999/08/31 01:37:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -42,6 +42,9 @@ const char *const pgresStatus[] = {
((*(conn)->noticeHook) ((conn)->noticeArg, (message)))
+static void pqCatenateResultError(PGresult *res, const char *msg);
+static void saveErrorResult(PGconn *conn);
+static PGresult *prepareAsyncResult(PGconn *conn);
static int addTuple(PGresult *res, PGresAttValue *tup);
static void parseInput(PGconn *conn);
static void handleSendFailure(PGconn *conn);
@@ -158,7 +161,7 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
/* non-error cases */
break;
default:
- pqSetResultError(result, conn->errorMessage);
+ pqSetResultError(result, conn->errorMessage.data);
break;
}
}
@@ -299,6 +302,25 @@ pqSetResultError(PGresult *res, const char *msg)
}
/*
+ * pqCatenateResultError -
+ * concatenate a new error message to the one already in a PGresult
+ */
+static void
+pqCatenateResultError(PGresult *res, const char *msg)
+{
+ PQExpBufferData errorBuf;
+
+ if (!res || !msg)
+ return;
+ initPQExpBuffer(&errorBuf);
+ if (res->errMsg)
+ appendPQExpBufferStr(&errorBuf, res->errMsg);
+ appendPQExpBufferStr(&errorBuf, msg);
+ pqSetResultError(res, errorBuf.data);
+ termPQExpBuffer(&errorBuf);
+}
+
+/*
* PQclear -
* free's the memory associated with a PGresult
*/
@@ -338,6 +360,72 @@ pqClearAsyncResult(PGconn *conn)
conn->curTuple = NULL;
}
+/*
+ * This subroutine deletes any existing async result, sets conn->result
+ * to a PGresult with status PGRES_FATAL_ERROR, and stores the current
+ * contents of conn->errorMessage into that result. It differs from a
+ * plain call on PQmakeEmptyPGresult() in that if there is already an
+ * async result with status PGRES_FATAL_ERROR, the current error message
+ * is APPENDED to the old error message instead of replacing it. This
+ * behavior lets us report multiple error conditions properly, if necessary.
+ * (An example where this is needed is when the backend sends an 'E' message
+ * and immediately closes the connection --- we want to report both the
+ * backend error and the connection closure error.)
+ */
+static void
+saveErrorResult(PGconn *conn)
+{
+ /* If no old async result, just let PQmakeEmptyPGresult make one.
+ * Likewise if old result is not an error message.
+ */
+ if (conn->result == NULL ||
+ conn->result->resultStatus != PGRES_FATAL_ERROR ||
+ conn->result->errMsg == NULL)
+ {
+ pqClearAsyncResult(conn);
+ conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
+ }
+ else
+ {
+ /* Else, concatenate error message to existing async result. */
+ pqCatenateResultError(conn->result, conn->errorMessage.data);
+ }
+}
+
+/*
+ * This subroutine prepares an async result object for return to the caller.
+ * If there is not already an async result object, build an error object
+ * using whatever is in conn->errorMessage. In any case, clear the async
+ * result storage and make sure PQerrorMessage will agree with the result's
+ * error string.
+ */
+static PGresult *
+prepareAsyncResult(PGconn *conn)
+{
+ PGresult *res;
+
+ /*
+ * conn->result is the PGresult to return. If it is NULL
+ * (which probably shouldn't happen) we assume there is an
+ * appropriate error message in conn->errorMessage.
+ */
+ res = conn->result;
+ conn->result = NULL; /* handing over ownership to caller */
+ conn->curTuple = NULL; /* just in case */
+ if (!res)
+ res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
+ else
+ {
+ /*
+ * Make sure PQerrorMessage agrees with result; it could
+ * be different if we have concatenated messages.
+ */
+ resetPQExpBuffer(&conn->errorMessage);
+ appendPQExpBufferStr(&conn->errorMessage,
+ PQresultErrorMessage(res));
+ }
+ return res;
+}
/*
* addTuple
@@ -394,37 +482,33 @@ PQsendQuery(PGconn *conn, const char *query)
{
if (!conn)
return 0;
+
+ /* clear the error string */
+ resetPQExpBuffer(&conn->errorMessage);
+
if (!query)
{
- sprintf(conn->errorMessage, "PQsendQuery() -- query pointer is null.");
- return 0;
- }
- /* check to see if the query string is too long */
- if (strlen(query) > MAX_MESSAGE_LEN - 2)
- {
- sprintf(conn->errorMessage, "PQsendQuery() -- query is too long. "
- "Maximum length is %d\n", MAX_MESSAGE_LEN - 2);
+ printfPQExpBuffer(&conn->errorMessage,
+ "PQsendQuery() -- query pointer is null.\n");
return 0;
}
/* Don't try to send if we know there's no live connection. */
if (conn->status != CONNECTION_OK)
{
- sprintf(conn->errorMessage, "PQsendQuery() -- There is no connection "
- "to the backend.\n");
+ printfPQExpBuffer(&conn->errorMessage,
+ "PQsendQuery() -- There is no connection "
+ "to the backend.\n");
return 0;
}
/* Can't send while already busy, either. */
if (conn->asyncStatus != PGASYNC_IDLE)
{
- sprintf(conn->errorMessage,
- "PQsendQuery() -- another query already in progress.");
+ printfPQExpBuffer(&conn->errorMessage,
+ "PQsendQuery() -- another query already in progress.\n");
return 0;
}
- /* clear the error string */
- conn->errorMessage[0] = '\0';
-
/* initialize async result-accumulation state */
conn->result = NULL;
conn->curTuple = NULL;
@@ -456,9 +540,6 @@ PQsendQuery(PGconn *conn, const char *query)
static void
handleSendFailure(PGconn *conn)
{
- /* Preserve the error message emitted by the failing output routine */
- char * svErrMsg = strdup(conn->errorMessage);
-
/*
* Accept any available input data, ignoring errors. Note that if
* pqReadData decides the backend has closed the channel, it will
@@ -472,11 +553,6 @@ handleSendFailure(PGconn *conn)
* state, only NOTICE and NOTIFY messages will be eaten.
*/
parseInput(conn);
-
- /* Restore error message generated by output routine, if any. */
- if (*svErrMsg != '\0')
- strcpy(conn->errorMessage, svErrMsg);
- free(svErrMsg);
}
/*
@@ -514,6 +590,7 @@ static void
parseInput(PGconn *conn)
{
char id;
+ char noticeWorkspace[128];
/*
* Loop to parse successive complete messages available in the buffer.
@@ -565,10 +642,10 @@ parseInput(PGconn *conn)
{
if (conn->asyncStatus == PGASYNC_IDLE)
{
- sprintf(conn->errorMessage,
- "Backend message type 0x%02x arrived while idle\n",
+ sprintf(noticeWorkspace,
+ "Backend message type 0x%02x arrived while idle\n",
id);
- DONOTICE(conn, conn->errorMessage);
+ DONOTICE(conn, noticeWorkspace);
/* Discard the unexpected message; good idea?? */
conn->inStart = conn->inEnd;
}
@@ -577,21 +654,20 @@ parseInput(PGconn *conn)
switch (id)
{
case 'C': /* command complete */
+ if (pqGets(&conn->workBuffer, conn))
+ return;
if (conn->result == NULL)
conn->result = PQmakeEmptyPGresult(conn,
- PGRES_COMMAND_OK);
- if (pqGets(conn->result->cmdStatus, CMDSTATUS_LEN, conn))
- return;
+ PGRES_COMMAND_OK);
+ strncpy(conn->result->cmdStatus, conn->workBuffer.data,
+ CMDSTATUS_LEN);
conn->asyncStatus = PGASYNC_READY;
break;
case 'E': /* error return */
- if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, conn))
+ if (pqGets(& conn->errorMessage, conn))
return;
- /* delete any partially constructed result */
- pqClearAsyncResult(conn);
- /* and build an error result holding the error message */
- conn->result = PQmakeEmptyPGresult(conn,
- PGRES_FATAL_ERROR);
+ /* build an error result holding the error message */
+ saveErrorResult(conn);
conn->asyncStatus = PGASYNC_READY;
break;
case 'Z': /* backend is ready for new query */
@@ -603,9 +679,10 @@ parseInput(PGconn *conn)
return;
if (id != '\0')
{
- sprintf(conn->errorMessage,
- "unexpected character %c following 'I'\n", id);
- DONOTICE(conn, conn->errorMessage);
+ sprintf(noticeWorkspace,
+ "unexpected character %c following 'I'\n",
+ id);
+ DONOTICE(conn, noticeWorkspace);
}
if (conn->result == NULL)
conn->result = PQmakeEmptyPGresult(conn,
@@ -625,7 +702,7 @@ parseInput(PGconn *conn)
return;
break;
case 'P': /* synchronous (normal) portal */
- if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, conn))
+ if (pqGets(&conn->workBuffer, conn))
return;
/* We pretty much ignore this message type... */
break;
@@ -660,9 +737,9 @@ parseInput(PGconn *conn)
}
else
{
- sprintf(conn->errorMessage,
- "Backend sent D message without prior T\n");
- DONOTICE(conn, conn->errorMessage);
+ sprintf(noticeWorkspace,
+ "Backend sent D message without prior T\n");
+ DONOTICE(conn, noticeWorkspace);
/* Discard the unexpected message; good idea?? */
conn->inStart = conn->inEnd;
return;
@@ -677,9 +754,9 @@ parseInput(PGconn *conn)
}
else
{
- sprintf(conn->errorMessage,
- "Backend sent B message without prior T\n");
- DONOTICE(conn, conn->errorMessage);
+ sprintf(noticeWorkspace,
+ "Backend sent B message without prior T\n");
+ DONOTICE(conn, noticeWorkspace);
/* Discard the unexpected message; good idea?? */
conn->inStart = conn->inEnd;
return;
@@ -692,18 +769,15 @@ parseInput(PGconn *conn)
conn->asyncStatus = PGASYNC_COPY_OUT;
break;
default:
- sprintf(conn->errorMessage,
- "unknown protocol character '%c' read from backend. "
- "(The protocol character is the first character the "
- "backend sends in response to a query it receives).\n",
- id);
+ printfPQExpBuffer(&conn->errorMessage,
+ "Unknown protocol character '%c' read from backend. "
+ "(The protocol character is the first character the "
+ "backend sends in response to a query it receives).\n",
+ id);
+ /* build an error result holding the error message */
+ saveErrorResult(conn);
/* Discard the unexpected message; good idea?? */
conn->inStart = conn->inEnd;
- /* delete any partially constructed result */
- pqClearAsyncResult(conn);
- /* and build an error result holding the error message */
- conn->result = PQmakeEmptyPGresult(conn,
- PGRES_FATAL_ERROR);
conn->asyncStatus = PGASYNC_READY;
return;
} /* switch on protocol character */
@@ -753,12 +827,11 @@ getRowDescriptions(PGconn *conn)
/* get type info */
for (i = 0; i < nfields; i++)
{
- char typName[MAX_MESSAGE_LEN];
int typid;
int typlen;
int atttypmod;
- if (pqGets(typName, MAX_MESSAGE_LEN, conn) ||
+ if (pqGets(&conn->workBuffer, conn) ||
pqGetInt(&typid, 4, conn) ||
pqGetInt(&typlen, 2, conn) ||
pqGetInt(&atttypmod, 4, conn))
@@ -777,7 +850,8 @@ getRowDescriptions(PGconn *conn)
*/
if (typlen == 0xFFFF)
typlen = -1;
- result->attDescs[i].name = pqResultStrdup(result, typName);
+ result->attDescs[i].name = pqResultStrdup(result,
+ conn->workBuffer.data);
result->attDescs[i].typid = typid;
result->attDescs[i].typlen = typlen;
result->attDescs[i].atttypmod = atttypmod;
@@ -804,8 +878,9 @@ getAnotherTuple(PGconn *conn, int binary)
PGresult *result = conn->result;
int nfields = result->numAttributes;
PGresAttValue *tup;
- char bitmap[MAX_FIELDS]; /* the backend sends us a bitmap
- * of which attributes are null */
+ /* the backend sends us a bitmap of which attributes are null */
+ char std_bitmap[64]; /* used unless it doesn't fit */
+ char *bitmap = std_bitmap;
int i;
int nbytes; /* the number of bytes in bitmap */
char bmap; /* One byte of the bitmap */
@@ -828,21 +903,12 @@ getAnotherTuple(PGconn *conn, int binary)
/* Get the null-value bitmap */
nbytes = (nfields + BYTELEN - 1) / BYTELEN;
- if (nbytes >= MAX_FIELDS)
- {
- /* Replace partially constructed result with an error result */
- pqClearAsyncResult(conn);
- sprintf(conn->errorMessage,
- "getAnotherTuple() -- null-values bitmap is too large\n");
- conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
- conn->asyncStatus = PGASYNC_READY;
- /* Discard the broken message */
- conn->inStart = conn->inEnd;
- return EOF;
- }
+ /* malloc() only for unusually large field counts... */
+ if (nbytes > sizeof(std_bitmap))
+ bitmap = (char *) malloc(nbytes);
if (pqGetnchar(bitmap, nbytes, conn))
- return EOF;
+ goto EOFexit;
/* Scan the fields */
bitmap_index = 0;
@@ -861,7 +927,7 @@ getAnotherTuple(PGconn *conn, int binary)
{
/* get the value length (the first four bytes are for length) */
if (pqGetInt(&vlen, 4, conn))
- return EOF;
+ goto EOFexit;
if (binary == 0)
vlen = vlen - 4;
if (vlen < 0)
@@ -876,7 +942,7 @@ getAnotherTuple(PGconn *conn, int binary)
/* read in the value */
if (vlen > 0)
if (pqGetnchar((char *) (tup[i].value), vlen, conn))
- return EOF;
+ goto EOFexit;
/* we have to terminate this ourselves */
tup[i].value[vlen] = '\0';
}
@@ -897,17 +963,27 @@ getAnotherTuple(PGconn *conn, int binary)
goto outOfMemory;
/* and reset for a new message */
conn->curTuple = NULL;
+
+ if (bitmap != std_bitmap)
+ free(bitmap);
return 0;
outOfMemory:
/* Replace partially constructed result with an error result */
+ /* we do NOT use saveErrorResult() here, because of the likelihood
+ * that there's not enough memory to concatenate messages...
+ */
pqClearAsyncResult(conn);
- sprintf(conn->errorMessage,
- "getAnotherTuple() -- out of memory for result\n");
+ printfPQExpBuffer(&conn->errorMessage,
+ "getAnotherTuple() -- out of memory for result\n");
conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
conn->asyncStatus = PGASYNC_READY;
/* Discard the failed message --- good idea? */
conn->inStart = conn->inEnd;
+
+EOFexit:
+ if (bitmap != std_bitmap)
+ free(bitmap);
return EOF;
}
@@ -955,10 +1031,12 @@ PQgetResult(PGconn *conn)
if (pqWait(TRUE, FALSE, conn) ||
pqReadData(conn) < 0)
{
- pqClearAsyncResult(conn);
+ /* conn->errorMessage has been set by pqWait or pqReadData.
+ * We want to append it to any already-received error message.
+ */
+ saveErrorResult(conn);
conn->asyncStatus = PGASYNC_IDLE;
- /* conn->errorMessage has been set by pqWait or pqReadData. */
- return PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
+ return prepareAsyncResult(conn);
}
/* Parse it. */
parseInput(conn);
@@ -971,28 +1049,7 @@ PQgetResult(PGconn *conn)
res = NULL; /* query is complete */
break;
case PGASYNC_READY:
-
- /*
- * conn->result is the PGresult to return. If it is NULL
- * (which probably shouldn't happen) we assume there is an
- * appropriate error message in conn->errorMessage.
- */
- res = conn->result;
- conn->result = NULL;/* handing over ownership to caller */
- conn->curTuple = NULL; /* just in case */
- if (!res)
- res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
- else
- {
-
- /*
- * Make sure PQerrorMessage agrees with result; it could
- * be that we have done other operations that changed
- * errorMessage since the result's error message was
- * saved.
- */
- strcpy(conn->errorMessage, PQresultErrorMessage(res));
- }
+ res = prepareAsyncResult(conn);
/* Set the state back to BUSY, allowing parsing to proceed. */
conn->asyncStatus = PGASYNC_BUSY;
break;
@@ -1003,9 +1060,9 @@ PQgetResult(PGconn *conn)
res = PQmakeEmptyPGresult(conn, PGRES_COPY_OUT);
break;
default:
- sprintf(conn->errorMessage,
- "PQgetResult: Unexpected asyncStatus %d\n",
- (int) conn->asyncStatus);
+ printfPQExpBuffer(&conn->errorMessage,
+ "PQgetResult: Unexpected asyncStatus %d\n",
+ (int) conn->asyncStatus);
res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
break;
}
@@ -1043,7 +1100,7 @@ PQexec(PGconn *conn, const char *query)
result->resultStatus == PGRES_COPY_OUT)
{
PQclear(result);
- sprintf(conn->errorMessage,
+ printfPQExpBuffer(&conn->errorMessage,
"PQexec: you gotta get out of a COPY state yourself.\n");
return NULL;
}
@@ -1056,14 +1113,30 @@ PQexec(PGconn *conn, const char *query)
/*
* For backwards compatibility, return the last result if there are
- * more than one. We have to stop if we see copy in/out, however. We
- * will resume parsing when application calls PQendcopy.
+ * more than one --- but merge error messages if we get more than one
+ * error result.
+ *
+ * We have to stop if we see copy in/out, however.
+ * We will resume parsing when application calls PQendcopy.
*/
lastResult = NULL;
while ((result = PQgetResult(conn)) != NULL)
{
if (lastResult)
- PQclear(lastResult);
+ {
+ if (lastResult->resultStatus == PGRES_FATAL_ERROR &&
+ result->resultStatus == PGRES_FATAL_ERROR)
+ {
+ pqCatenateResultError(lastResult, result->errMsg);
+ PQclear(result);
+ result = lastResult;
+ /* Make sure PQerrorMessage agrees with catenated result */
+ resetPQExpBuffer(&conn->errorMessage);
+ appendPQExpBufferStr(&conn->errorMessage, result->errMsg);
+ }
+ else
+ PQclear(lastResult);
+ }
lastResult = result;
if (result->resultStatus == PGRES_COPY_IN ||
result->resultStatus == PGRES_COPY_OUT)
@@ -1083,9 +1156,20 @@ PQexec(PGconn *conn, const char *query)
static int
getNotice(PGconn *conn)
{
- if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, conn))
+ /* Since the Notice might be pretty long, we create a temporary
+ * PQExpBuffer rather than using conn->workBuffer. workBuffer is
+ * intended for stuff that is expected to be short.
+ */
+ PQExpBufferData noticeBuf;
+
+ initPQExpBuffer(&noticeBuf);
+ if (pqGets(&noticeBuf, conn))
+ {
+ termPQExpBuffer(&noticeBuf);
return EOF;
- DONOTICE(conn, conn->errorMessage);
+ }
+ DONOTICE(conn, noticeBuf.data);
+ termPQExpBuffer(&noticeBuf);
return 0;
}
@@ -1099,15 +1183,16 @@ getNotice(PGconn *conn)
static int
getNotify(PGconn *conn)
{
- PGnotify tempNotify;
+ int be_pid;
PGnotify *newNotify;
- if (pqGetInt(&(tempNotify.be_pid), 4, conn))
+ if (pqGetInt(&be_pid, 4, conn))
return EOF;
- if (pqGets(tempNotify.relname, NAMEDATALEN, conn))
+ if (pqGets(&conn->workBuffer, conn))
return EOF;
newNotify = (PGnotify *) malloc(sizeof(PGnotify));
- memcpy(newNotify, &tempNotify, sizeof(PGnotify));
+ strncpy(newNotify->relname, conn->workBuffer.data, NAMEDATALEN);
+ newNotify->be_pid = be_pid;
DLAddTail(conn->notifyList, DLNewElem(newNotify));
return 0;
}
@@ -1342,8 +1427,8 @@ PQendcopy(PGconn *conn)
if (conn->asyncStatus != PGASYNC_COPY_IN &&
conn->asyncStatus != PGASYNC_COPY_OUT)
{
- sprintf(conn->errorMessage,
- "PQendcopy() -- I don't think there's a copy in progress.");
+ printfPQExpBuffer(&conn->errorMessage,
+ "PQendcopy() -- I don't think there's a copy in progress.\n");
return 1;
}
@@ -1351,7 +1436,7 @@ PQendcopy(PGconn *conn)
/* Return to active duty */
conn->asyncStatus = PGASYNC_BUSY;
- conn->errorMessage[0] = '\0';
+ resetPQExpBuffer(&conn->errorMessage);
/* Wait for the completion response */
result = PQgetResult(conn);
@@ -1370,8 +1455,8 @@ PQendcopy(PGconn *conn)
*/
PQclear(result);
- if (conn->errorMessage[0])
- DONOTICE(conn, conn->errorMessage);
+ if (conn->errorMessage.len > 0)
+ DONOTICE(conn, conn->errorMessage.data);
DONOTICE(conn, "PQendcopy: resetting connection\n");
@@ -1423,15 +1508,17 @@ PQfn(PGconn *conn,
if (!conn)
return NULL;
- if (conn->sock < 0 || conn->asyncStatus != PGASYNC_IDLE)
+ /* clear the error string */
+ resetPQExpBuffer(&conn->errorMessage);
+
+ if (conn->sock < 0 || conn->asyncStatus != PGASYNC_IDLE ||
+ conn->result != NULL)
{
- sprintf(conn->errorMessage, "PQfn() -- connection in wrong state\n");
+ printfPQExpBuffer(&conn->errorMessage,
+ "PQfn() -- connection in wrong state\n");
return NULL;
}
- /* clear the error string */
- conn->errorMessage[0] = '\0';
-
if (pqPuts("F ", conn) || /* function */
pqPutInt(fnid, 4, conn) || /* function id */
pqPutInt(nargs, 4, conn)) /* # of args */
@@ -1529,15 +1616,19 @@ PQfn(PGconn *conn,
else
{
/* The backend violates the protocol. */
- sprintf(conn->errorMessage,
- "FATAL: PQfn: protocol error: id=%x\n", id);
+ printfPQExpBuffer(&conn->errorMessage,
+ "FATAL: PQfn: protocol error: id=0x%x\n",
+ id);
+ saveErrorResult(conn);
conn->inStart = conn->inCursor;
- return PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
+ return prepareAsyncResult(conn);
}
break;
case 'E': /* error return */
- if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, conn))
+ if (pqGets(&conn->errorMessage, conn))
continue;
+ /* build an error result holding the error message */
+ saveErrorResult(conn);
status = PGRES_FATAL_ERROR;
break;
case 'A': /* notify message */
@@ -1553,21 +1644,30 @@ PQfn(PGconn *conn,
case 'Z': /* backend is ready for new query */
/* consume the message and exit */
conn->inStart = conn->inCursor;
+ /* if we saved a result object (probably an error), use it */
+ if (conn->result)
+ return prepareAsyncResult(conn);
return PQmakeEmptyPGresult(conn, status);
default:
/* The backend violates the protocol. */
- sprintf(conn->errorMessage,
- "FATAL: PQfn: protocol error: id=%x\n", id);
+ printfPQExpBuffer(&conn->errorMessage,
+ "FATAL: PQfn: protocol error: id=0x%x\n",
+ id);
+ saveErrorResult(conn);
conn->inStart = conn->inCursor;
- return PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
+ return prepareAsyncResult(conn);
}
/* Completed this message, keep going */
conn->inStart = conn->inCursor;
needInput = false;
}
- /* we fall out of the loop only upon failing to read data */
- return PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
+ /* We fall out of the loop only upon failing to read data.
+ * conn->errorMessage has been set by pqWait or pqReadData.
+ * We want to append it to any already-received error message.
+ */
+ saveErrorResult(conn);
+ return prepareAsyncResult(conn);
}
@@ -1630,16 +1730,18 @@ PQbinaryTuples(PGresult *res)
static int
check_field_number(const char *routineName, PGresult *res, int field_num)
{
+ char noticeBuf[128];
+
if (!res)
return FALSE; /* no way to display error message... */
if (field_num < 0 || field_num >= res->numAttributes)
{
if (res->conn)
{
- sprintf(res->conn->errorMessage,
+ sprintf(noticeBuf,
"%s: ERROR! field number %d is out of range 0..%d\n",
routineName, field_num, res->numAttributes - 1);
- DONOTICE(res->conn, res->conn->errorMessage);
+ DONOTICE(res->conn, noticeBuf);
}
return FALSE;
}
@@ -1650,16 +1752,18 @@ static int
check_tuple_field_number(const char *routineName, PGresult *res,
int tup_num, int field_num)
{
+ char noticeBuf[128];
+
if (!res)
return FALSE; /* no way to display error message... */
if (tup_num < 0 || tup_num >= res->ntups)
{
if (res->conn)
{
- sprintf(res->conn->errorMessage,
+ sprintf(noticeBuf,
"%s: ERROR! tuple number %d is out of range 0..%d\n",
routineName, tup_num, res->ntups - 1);
- DONOTICE(res->conn, res->conn->errorMessage);
+ DONOTICE(res->conn, noticeBuf);
}
return FALSE;
}
@@ -1667,10 +1771,10 @@ check_tuple_field_number(const char *routineName, PGresult *res,
{
if (res->conn)
{
- sprintf(res->conn->errorMessage,
+ sprintf(noticeBuf,
"%s: ERROR! field number %d is out of range 0..%d\n",
routineName, field_num, res->numAttributes - 1);
- DONOTICE(res->conn, res->conn->errorMessage);
+ DONOTICE(res->conn, noticeBuf);
}
return FALSE;
}
@@ -1830,6 +1934,8 @@ PQoidStatus(PGresult *res)
const char *
PQcmdTuples(PGresult *res)
{
+ char noticeBuf[128];
+
if (!res)
return "";
@@ -1843,10 +1949,10 @@ PQcmdTuples(PGresult *res)
{
if (res->conn)
{
- sprintf(res->conn->errorMessage,
+ sprintf(noticeBuf,
"PQcmdTuples (%s) -- bad input from server\n",
res->cmdStatus);
- DONOTICE(res->conn, res->conn->errorMessage);
+ DONOTICE(res->conn, noticeBuf);
}
return "";
}
@@ -1859,9 +1965,9 @@ PQcmdTuples(PGresult *res)
{
if (res->conn)
{
- sprintf(res->conn->errorMessage,
+ sprintf(noticeBuf,
"PQcmdTuples (INSERT) -- there's no # of tuples\n");
- DONOTICE(res->conn, res->conn->errorMessage);
+ DONOTICE(res->conn, noticeBuf);
}
return "";
}