aboutsummaryrefslogtreecommitdiff
path: root/src/interfaces/libpq/fe-exec.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/interfaces/libpq/fe-exec.c')
-rw-r--r--src/interfaces/libpq/fe-exec.c1228
1 files changed, 146 insertions, 1082 deletions
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index df43f2cad2b..0ea46ca2894 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.136 2003/05/26 20:05:20 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.137 2003/06/08 17:43:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -42,22 +42,8 @@ char *const pgresStatus[] = {
};
-/* Note: DONOTICE macro will work if applied to either PGconn or PGresult */
-#define DONOTICE(conn,message) \
- ((*(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);
-static void handleSyncLoss(PGconn *conn, char id, int msgLength);
-static int getRowDescriptions(PGconn *conn);
-static int getAnotherTuple(PGconn *conn, int msgLength);
-static int getParameterStatus(PGconn *conn);
-static int getNotify(PGconn *conn);
/* ----------------
@@ -142,7 +128,6 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
result = (PGresult *) malloc(sizeof(PGresult));
- result->xconn = conn; /* might be NULL */
result->ntups = 0;
result->numAttributes = 0;
result->attDescs = NULL;
@@ -336,7 +321,7 @@ pqSetResultError(PGresult *res, const char *msg)
* pqCatenateResultError -
* concatenate a new error message to the one already in a PGresult
*/
-static void
+void
pqCatenateResultError(PGresult *res, const char *msg)
{
PQExpBufferData errorBuf;
@@ -403,8 +388,8 @@ pqClearAsyncResult(PGconn *conn)
* and immediately closes the connection --- we want to report both the
* backend error and the connection closure error.)
*/
-static void
-saveErrorResult(PGconn *conn)
+void
+pqSaveErrorResult(PGconn *conn)
{
/*
* If no old async result, just let PQmakeEmptyPGresult make one.
@@ -431,8 +416,8 @@ saveErrorResult(PGconn *conn)
* result storage and make sure PQerrorMessage will agree with the result's
* error string.
*/
-static PGresult *
-prepareAsyncResult(PGconn *conn)
+PGresult *
+pqPrepareAsyncResult(PGconn *conn)
{
PGresult *res;
@@ -460,12 +445,12 @@ prepareAsyncResult(PGconn *conn)
}
/*
- * addTuple
+ * pqAddTuple
* add a row pointer to the PGresult structure, growing it if necessary
* Returns TRUE if OK, FALSE if not enough memory to add the row
*/
-static int
-addTuple(PGresult *res, PGresAttValue * tup)
+int
+pqAddTuple(PGresult *res, PGresAttValue *tup)
{
if (res->ntups >= res->tupArrSize)
{
@@ -501,6 +486,85 @@ addTuple(PGresult *res, PGresAttValue * tup)
/*
+ * pqSaveParameterStatus - remember parameter status sent by backend
+ */
+void
+pqSaveParameterStatus(PGconn *conn, const char *name, const char *value)
+{
+ pgParameterStatus *pstatus;
+ pgParameterStatus *prev;
+
+ if (conn->Pfdebug)
+ fprintf(conn->Pfdebug, "pqSaveParameterStatus: '%s' = '%s'\n",
+ name, value);
+
+ /*
+ * Forget any old information about the parameter
+ */
+ for (pstatus = conn->pstatus, prev = NULL;
+ pstatus != NULL;
+ prev = pstatus, pstatus = pstatus->next)
+ {
+ if (strcmp(pstatus->name, name) == 0)
+ {
+ if (prev)
+ prev->next = pstatus->next;
+ else
+ conn->pstatus = pstatus->next;
+ free(pstatus); /* frees name and value strings too */
+ break;
+ }
+ }
+ /*
+ * Store new info as a single malloc block
+ */
+ pstatus = (pgParameterStatus *) malloc(sizeof(pgParameterStatus) +
+ strlen(name) + strlen(value) + 2);
+ if (pstatus)
+ {
+ char *ptr;
+
+ ptr = ((char *) pstatus) + sizeof(pgParameterStatus);
+ pstatus->name = ptr;
+ strcpy(ptr, name);
+ ptr += strlen(name) + 1;
+ pstatus->value = ptr;
+ strcpy(ptr, value);
+ pstatus->next = conn->pstatus;
+ conn->pstatus = pstatus;
+ }
+ /*
+ * Special hacks: remember client_encoding as a numeric value, and
+ * remember at least the first few bytes of server version.
+ */
+ if (strcmp(name, "client_encoding") == 0)
+ conn->client_encoding = pg_char_to_encoding(value);
+ if (strcmp(name, "server_version") == 0)
+ StrNCpy(conn->sversion, value, sizeof(conn->sversion));
+}
+
+/*
+ * pqGetParameterStatus - fetch parameter value, if available
+ *
+ * Returns NULL if info not available
+ *
+ * XXX this probably should be exported for client use
+ */
+const char *
+pqGetParameterStatus(PGconn *conn, const char *name)
+{
+ pgParameterStatus *pstatus;
+
+ for (pstatus = conn->pstatus; pstatus != NULL; pstatus = pstatus->next)
+ {
+ if (strcmp(pstatus->name, name) == 0)
+ return pstatus->value;
+ }
+ return NULL;
+}
+
+
+/*
* PQsendQuery
* Submit a query, but don't wait for it to finish
*
@@ -543,11 +607,11 @@ PQsendQuery(PGconn *conn, const char *query)
conn->curTuple = NULL;
/* construct the outgoing Query message */
- if (pqPutMsgStart('Q', conn) < 0 ||
+ if (pqPutMsgStart('Q', false, conn) < 0 ||
pqPuts(query, conn) < 0 ||
pqPutMsgEnd(conn) < 0)
{
- handleSendFailure(conn);
+ pqHandleSendFailure(conn);
return 0;
}
@@ -558,7 +622,7 @@ PQsendQuery(PGconn *conn, const char *query)
*/
if (pqFlush(conn) < 0)
{
- handleSendFailure(conn);
+ pqHandleSendFailure(conn);
return 0;
}
@@ -568,15 +632,15 @@ PQsendQuery(PGconn *conn, const char *query)
}
/*
- * handleSendFailure: try to clean up after failure to send command.
+ * pqHandleSendFailure: try to clean up after failure to send command.
*
* Primarily, what we want to accomplish here is to process an async
* NOTICE message that the backend might have sent just before it died.
*
* NOTE: this routine should only be called in PGASYNC_IDLE state.
*/
-static void
-handleSendFailure(PGconn *conn)
+void
+pqHandleSendFailure(PGconn *conn)
{
/*
* Accept any available input data, ignoring errors. Note that if
@@ -634,499 +698,15 @@ PQconsumeInput(PGconn *conn)
* until input is exhausted or a stopping state is reached.
* Note that this function will NOT attempt to read more data from the backend.
*/
-
static void
parseInput(PGconn *conn)
{
- char id;
- int msgLength;
- int avail;
- char noticeWorkspace[128];
-
- /*
- * Loop to parse successive complete messages available in the buffer.
- */
- for (;;)
- {
- /*
- * Try to read a message. First get the type code and length.
- * Return if not enough data.
- */
- conn->inCursor = conn->inStart;
- if (pqGetc(&id, conn))
- return;
- if (pqGetInt(&msgLength, 4, conn))
- return;
-
- /*
- * Try to validate message type/length here. A length less than 4
- * is definitely broken. Large lengths should only be believed
- * for a few message types.
- */
- if (msgLength < 4)
- {
- handleSyncLoss(conn, id, msgLength);
- return;
- }
- if (msgLength > 30000 &&
- !(id == 'T' || id == 'D' || id == 'd'))
- {
- handleSyncLoss(conn, id, msgLength);
- return;
- }
-
- /*
- * Can't process if message body isn't all here yet.
- */
- msgLength -= 4;
- avail = conn->inEnd - conn->inCursor;
- if (avail < msgLength)
- {
- /*
- * Before returning, enlarge the input buffer if needed to hold
- * the whole message. This is better than leaving it to
- * pqReadData because we can avoid multiple cycles of realloc()
- * when the message is large; also, we can implement a reasonable
- * recovery strategy if we are unable to make the buffer big
- * enough.
- */
- if (pqCheckInBufferSpace(conn->inCursor + msgLength, conn))
- {
- /*
- * XXX add some better recovery code... plan is to skip
- * over the message using its length, then report an error.
- * For the moment, just treat this like loss of sync (which
- * indeed it might be!)
- */
- handleSyncLoss(conn, id, msgLength);
- }
- return;
- }
-
- /*
- * NOTIFY and NOTICE messages can happen in any state; always process
- * them right away.
- *
- * Most other messages should only be processed while in BUSY state.
- * (In particular, in READY state we hold off further parsing
- * until the application collects the current PGresult.)
- *
- * However, if the state is IDLE then we got trouble; we need to deal
- * with the unexpected message somehow.
- *
- * ParameterStatus ('S') messages are a special case: in IDLE state
- * we must process 'em (this case could happen if a new value was
- * adopted from config file due to SIGHUP), but otherwise we hold
- * off until BUSY state.
- */
- if (id == 'A')
- {
- if (getNotify(conn))
- return;
- }
- else if (id == 'N')
- {
- if (pqGetErrorNotice(conn, false))
- return;
- }
- else if (conn->asyncStatus != PGASYNC_BUSY)
- {
- /* If not IDLE state, just wait ... */
- if (conn->asyncStatus != PGASYNC_IDLE)
- return;
-
- /*
- * Unexpected message in IDLE state; need to recover somehow.
- * ERROR messages are displayed using the notice processor;
- * ParameterStatus is handled normally;
- * anything else is just dropped on the floor after displaying
- * a suitable warning notice. (An ERROR is very possibly the
- * backend telling us why it is about to close the connection,
- * so we don't want to just discard it...)
- */
- if (id == 'E')
- {
- if (pqGetErrorNotice(conn, false /* treat as notice */))
- return;
- }
- else if (id == 'S')
- {
- if (getParameterStatus(conn))
- return;
- }
- else
- {
- snprintf(noticeWorkspace, sizeof(noticeWorkspace),
- libpq_gettext("message type 0x%02x arrived from server while idle\n"),
- id);
- DONOTICE(conn, noticeWorkspace);
- /* Discard the unexpected message */
- conn->inCursor += msgLength;
- }
- }
- else
- {
- /*
- * In BUSY state, we can process everything.
- */
- switch (id)
- {
- case 'C': /* command complete */
- if (pqGets(&conn->workBuffer, conn))
- return;
- if (conn->result == NULL)
- conn->result = PQmakeEmptyPGresult(conn,
- PGRES_COMMAND_OK);
- strncpy(conn->result->cmdStatus, conn->workBuffer.data,
- CMDSTATUS_LEN);
- conn->asyncStatus = PGASYNC_READY;
- break;
- case 'E': /* error return */
- if (pqGetErrorNotice(conn, true))
- return;
- conn->asyncStatus = PGASYNC_READY;
- break;
- case 'Z': /* backend is ready for new query */
- if (pqGetc(&conn->xact_status, conn))
- return;
- conn->asyncStatus = PGASYNC_IDLE;
- break;
- case 'I': /* empty query */
- if (conn->result == NULL)
- conn->result = PQmakeEmptyPGresult(conn,
- PGRES_EMPTY_QUERY);
- conn->asyncStatus = PGASYNC_READY;
- break;
- case 'S': /* parameter status */
- if (getParameterStatus(conn))
- return;
- break;
- case 'K': /* secret key data from the backend */
-
- /*
- * This is expected only during backend startup, but
- * it's just as easy to handle it as part of the main
- * loop. Save the data and continue processing.
- */
- if (pqGetInt(&(conn->be_pid), 4, conn))
- return;
- if (pqGetInt(&(conn->be_key), 4, conn))
- return;
- break;
- case 'T': /* row descriptions (start of query
- * results) */
- if (conn->result == NULL)
- {
- /* First 'T' in a query sequence */
- if (getRowDescriptions(conn))
- return;
- }
- else
- {
- /*
- * A new 'T' message is treated as the start of
- * another PGresult. (It is not clear that this
- * is really possible with the current backend.)
- * We stop parsing until the application accepts
- * the current result.
- */
- conn->asyncStatus = PGASYNC_READY;
- return;
- }
- break;
- case 'D': /* Data Row */
- if (conn->result != NULL &&
- conn->result->resultStatus == PGRES_TUPLES_OK)
- {
- /* Read another tuple of a normal query response */
- if (getAnotherTuple(conn, msgLength))
- return;
- }
- else if (conn->result != NULL &&
- conn->result->resultStatus == PGRES_FATAL_ERROR)
- {
- /*
- * We've already choked for some reason. Just discard
- * tuples till we get to the end of the query.
- */
- conn->inCursor += msgLength;
- }
- else
- {
- /* Set up to report error at end of query */
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("server sent data (\"D\" message) without prior row description (\"T\" message)\n"));
- saveErrorResult(conn);
- /* Discard the unexpected message */
- conn->inCursor += msgLength;
- }
- break;
- case 'G': /* Start Copy In */
- if (pqGetc(&conn->copy_is_binary, conn))
- return;
- /* XXX we currently ignore the rest of the message */
- conn->inCursor = conn->inStart + 5 + msgLength;
- conn->asyncStatus = PGASYNC_COPY_IN;
- break;
- case 'H': /* Start Copy Out */
- if (pqGetc(&conn->copy_is_binary, conn))
- return;
- /* XXX we currently ignore the rest of the message */
- conn->inCursor = conn->inStart + 5 + msgLength;
- conn->asyncStatus = PGASYNC_COPY_OUT;
- conn->copy_already_done = 0;
- break;
- case 'd': /* Copy Data */
- /*
- * If we see Copy Data, just silently drop it. This
- * would only occur if application exits COPY OUT mode
- * too early.
- */
- conn->inCursor += msgLength;
- break;
- case 'c': /* Copy Done */
- /*
- * If we see Copy Done, just silently drop it. This
- * is the normal case during PQendcopy. We will keep
- * swallowing data, expecting to see command-complete
- * for the COPY command.
- */
- break;
- default:
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext(
- "unexpected response from server; first received character was \"%c\"\n"),
- id);
- /* build an error result holding the error message */
- saveErrorResult(conn);
- /* not sure if we will see more, so go to ready state */
- conn->asyncStatus = PGASYNC_READY;
- /* Discard the unexpected message */
- conn->inCursor += msgLength;
- break;
- } /* switch on protocol character */
- }
- /* Successfully consumed this message */
- if (conn->inCursor == conn->inStart + 5 + msgLength)
- {
- /* Normal case: parsing agrees with specified length */
- conn->inStart = conn->inCursor;
- }
- else
- {
- /* Trouble --- report it */
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("Message contents do not agree with length in message type \"%c\"\n"),
- id);
- /* build an error result holding the error message */
- saveErrorResult(conn);
- conn->asyncStatus = PGASYNC_READY;
- /* trust the specified message length as what to skip */
- conn->inStart += 5 + msgLength;
- }
- }
-}
-
-/*
- * handleSyncLoss: clean up after loss of message-boundary sync
- *
- * There isn't really a lot we can do here except abandon the connection.
- */
-static void
-handleSyncLoss(PGconn *conn, char id, int msgLength)
-{
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext(
- "lost synchronization with server: got message type \"%c\", length %d\n"),
- id, msgLength);
- conn->status = CONNECTION_BAD; /* No more connection to backend */
- pqsecure_close(conn);
- closesocket(conn->sock);
- conn->sock = -1;
- conn->asyncStatus = PGASYNC_READY; /* drop out of GetResult wait loop */
-}
-
-/*
- * parseInput subroutine to read a 'T' (row descriptions) message.
- * We build a PGresult structure containing the attribute data.
- * Returns: 0 if completed message, EOF if not enough data yet.
- *
- * Note that if we run out of data, we have to release the partially
- * constructed PGresult, and rebuild it again next time. Fortunately,
- * that shouldn't happen often, since 'T' messages usually fit in a packet.
- */
-
-static int
-getRowDescriptions(PGconn *conn)
-{
- PGresult *result;
- int nfields;
- int i;
-
- result = PQmakeEmptyPGresult(conn, PGRES_TUPLES_OK);
-
- /* parseInput already read the 'T' label and message length. */
- /* the next two bytes are the number of fields */
- if (pqGetInt(&(result->numAttributes), 2, conn))
- {
- PQclear(result);
- return EOF;
- }
- nfields = result->numAttributes;
-
- /* allocate space for the attribute descriptors */
- if (nfields > 0)
- {
- result->attDescs = (PGresAttDesc *)
- pqResultAlloc(result, nfields * sizeof(PGresAttDesc), TRUE);
- MemSet((char *) result->attDescs, 0, nfields * sizeof(PGresAttDesc));
- }
-
- /* get type info */
- for (i = 0; i < nfields; i++)
- {
- int tableid;
- int columnid;
- int typid;
- int typlen;
- int atttypmod;
- int format;
-
- if (pqGets(&conn->workBuffer, conn) ||
- pqGetInt(&tableid, 4, conn) ||
- pqGetInt(&columnid, 2, conn) ||
- pqGetInt(&typid, 4, conn) ||
- pqGetInt(&typlen, 2, conn) ||
- pqGetInt(&atttypmod, 4, conn) ||
- pqGetInt(&format, 2, conn))
- {
- PQclear(result);
- return EOF;
- }
-
- /*
- * Since pqGetInt treats 2-byte integers as unsigned, we need to
- * coerce these results to signed form.
- */
- columnid = (int) ((int16) columnid);
- typlen = (int) ((int16) typlen);
- format = (int) ((int16) format);
-
- result->attDescs[i].name = pqResultStrdup(result,
- conn->workBuffer.data);
- result->attDescs[i].typid = typid;
- result->attDescs[i].typlen = typlen;
- result->attDescs[i].atttypmod = atttypmod;
- /* XXX todo: save tableid/columnid, format too */
- }
-
- /* Success! */
- conn->result = result;
- return 0;
-}
-
-/*
- * parseInput subroutine to read a 'D' (row data) message.
- * We add another tuple to the existing PGresult structure.
- * Returns: 0 if completed message, EOF if error or not enough data yet.
- *
- * Note that if we run out of data, we have to suspend and reprocess
- * the message after more data is received. We keep a partially constructed
- * tuple in conn->curTuple, and avoid reallocating already-allocated storage.
- */
-
-static int
-getAnotherTuple(PGconn *conn, int msgLength)
-{
- PGresult *result = conn->result;
- int nfields = result->numAttributes;
- PGresAttValue *tup;
- int tupnfields; /* # fields from tuple */
- int vlen; /* length of the current field value */
- int i;
-
- /* Allocate tuple space if first time for this data message */
- if (conn->curTuple == NULL)
- {
- conn->curTuple = (PGresAttValue *)
- pqResultAlloc(result, nfields * sizeof(PGresAttValue), TRUE);
- if (conn->curTuple == NULL)
- goto outOfMemory;
- MemSet((char *) conn->curTuple, 0, nfields * sizeof(PGresAttValue));
- }
- tup = conn->curTuple;
-
- /* Get the field count and make sure it's what we expect */
- if (pqGetInt(&tupnfields, 2, conn))
- return EOF;
-
- if (tupnfields != nfields)
- {
- /* Replace partially constructed result with an error result */
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("unexpected field count in D message\n"));
- saveErrorResult(conn);
- /* Discard the failed message by pretending we read it */
- conn->inCursor = conn->inStart + 5 + msgLength;
- return 0;
- }
-
- /* Scan the fields */
- for (i = 0; i < nfields; i++)
- {
- /* get the value length */
- if (pqGetInt(&vlen, 4, conn))
- return EOF;
- if (vlen == -1)
- {
- /* null field */
- tup[i].value = result->null_field;
- tup[i].len = NULL_LEN;
- continue;
- }
- if (vlen < 0)
- vlen = 0;
- if (tup[i].value == NULL)
- {
- tup[i].value = (char *) pqResultAlloc(result, vlen + 1, false);
- if (tup[i].value == NULL)
- goto outOfMemory;
- }
- tup[i].len = vlen;
- /* read in the value */
- if (vlen > 0)
- if (pqGetnchar((char *) (tup[i].value), vlen, conn))
- return EOF;
- /* we have to terminate this ourselves */
- tup[i].value[vlen] = '\0';
- }
-
- /* Success! Store the completed tuple in the result */
- if (!addTuple(result, tup))
- goto outOfMemory;
- /* and reset for a new message */
- conn->curTuple = NULL;
-
- 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. Instead,
- * discard the old result first to try to win back some memory.
- */
- pqClearAsyncResult(conn);
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("out of memory for query result\n"));
- conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
- /* Discard the failed message by pretending we read it */
- conn->inCursor = conn->inStart + 5 + msgLength;
- return 0;
+ if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
+ pqParseInput3(conn);
+ else
+ pqParseInput2(conn);
}
-
/*
* PQisBusy
* Return TRUE if PQgetResult would block waiting for input.
@@ -1174,9 +754,9 @@ PQgetResult(PGconn *conn)
* conn->errorMessage has been set by pqWait or pqReadData. We
* want to append it to any already-received error message.
*/
- saveErrorResult(conn);
+ pqSaveErrorResult(conn);
conn->asyncStatus = PGASYNC_IDLE;
- return prepareAsyncResult(conn);
+ return pqPrepareAsyncResult(conn);
}
/* Parse it. */
parseInput(conn);
@@ -1189,7 +769,7 @@ PQgetResult(PGconn *conn)
res = NULL; /* query is complete */
break;
case PGASYNC_READY:
- res = prepareAsyncResult(conn);
+ res = pqPrepareAsyncResult(conn);
/* Set the state back to BUSY, allowing parsing to proceed. */
conn->asyncStatus = PGASYNC_BUSY;
break;
@@ -1304,205 +884,6 @@ errout:
return NULL;
}
-
-/*
- * Attempt to read an Error or Notice response message.
- * This is possible in several places, so we break it out as a subroutine.
- * Entry: 'E' or 'N' message type and length have already been consumed.
- * Exit: returns 0 if successfully consumed message.
- * returns EOF if not enough data.
- */
-int
-pqGetErrorNotice(PGconn *conn, bool isError)
-{
- PGresult *res;
- PQExpBufferData workBuf;
- char id;
-
- /*
- * Make a PGresult to hold the accumulated fields. We temporarily
- * lie about the result status, so that PQmakeEmptyPGresult doesn't
- * uselessly copy conn->errorMessage.
- */
- res = PQmakeEmptyPGresult(conn, PGRES_EMPTY_QUERY);
- res->resultStatus = PGRES_FATAL_ERROR;
- /*
- * Since the fields 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. We shouldn't
- * use conn->errorMessage either, since this might be only a notice.
- */
- initPQExpBuffer(&workBuf);
-
- /*
- * Read the fields and save into res.
- */
- for (;;)
- {
- if (pqGetc(&id, conn))
- goto fail;
- if (id == '\0')
- break; /* terminator found */
- if (pqGets(&workBuf, conn))
- goto fail;
- switch (id)
- {
- case 'S':
- res->errSeverity = pqResultStrdup(res, workBuf.data);
- break;
- case 'C':
- res->errCode = pqResultStrdup(res, workBuf.data);
- break;
- case 'M':
- res->errPrimary = pqResultStrdup(res, workBuf.data);
- break;
- case 'D':
- res->errDetail = pqResultStrdup(res, workBuf.data);
- break;
- case 'H':
- res->errHint = pqResultStrdup(res, workBuf.data);
- break;
- case 'P':
- res->errPosition = pqResultStrdup(res, workBuf.data);
- break;
- case 'W':
- res->errContext = pqResultStrdup(res, workBuf.data);
- break;
- case 'F':
- res->errFilename = pqResultStrdup(res, workBuf.data);
- break;
- case 'L':
- res->errLineno = pqResultStrdup(res, workBuf.data);
- break;
- case 'R':
- res->errFuncname = pqResultStrdup(res, workBuf.data);
- break;
- default:
- /* silently ignore any other field type */
- break;
- }
- }
-
- /*
- * Now build the "overall" error message for PQresultErrorMessage.
- *
- * XXX this should be configurable somehow.
- */
- resetPQExpBuffer(&workBuf);
- if (res->errSeverity)
- appendPQExpBuffer(&workBuf, "%s: ", res->errSeverity);
- if (res->errPrimary)
- appendPQExpBufferStr(&workBuf, res->errPrimary);
- /* translator: %s represents a digit string */
- if (res->errPosition)
- appendPQExpBuffer(&workBuf, libpq_gettext(" at character %s"),
- res->errPosition);
- appendPQExpBufferChar(&workBuf, '\n');
- if (res->errDetail)
- appendPQExpBuffer(&workBuf, libpq_gettext("DETAIL: %s\n"),
- res->errDetail);
- if (res->errHint)
- appendPQExpBuffer(&workBuf, libpq_gettext("HINT: %s\n"),
- res->errHint);
- if (res->errContext)
- appendPQExpBuffer(&workBuf, libpq_gettext("CONTEXT: %s\n"),
- res->errContext);
-
- /*
- * Either save error as current async result, or just emit the notice.
- */
- if (isError)
- {
- res->errMsg = pqResultStrdup(res, workBuf.data);
- pqClearAsyncResult(conn);
- conn->result = res;
- resetPQExpBuffer(&conn->errorMessage);
- appendPQExpBufferStr(&conn->errorMessage, workBuf.data);
- }
- else
- {
- DONOTICE(conn, workBuf.data);
- PQclear(res);
- }
-
- termPQExpBuffer(&workBuf);
- return 0;
-
-fail:
- PQclear(res);
- termPQExpBuffer(&workBuf);
- return EOF;
-}
-
-/*
- * Attempt to read a ParameterStatus message.
- * This is possible in several places, so we break it out as a subroutine.
- * Entry: 'S' message type and length have already been consumed.
- * Exit: returns 0 if successfully consumed message.
- * returns EOF if not enough data.
- */
-static int
-getParameterStatus(PGconn *conn)
-{
- /* Get the parameter name */
- if (pqGets(&conn->workBuffer, conn))
- return EOF;
- /* Is it one we care about? */
- if (strcmp(conn->workBuffer.data, "client_encoding") == 0)
- {
- if (pqGets(&conn->workBuffer, conn))
- return EOF;
- conn->client_encoding = pg_char_to_encoding(conn->workBuffer.data);
- }
- else
- {
- /* Uninteresting parameter, ignore it */
- if (pqGets(&conn->workBuffer, conn))
- return EOF;
- }
- return 0;
-}
-
-/*
- * Attempt to read a Notify response message.
- * This is possible in several places, so we break it out as a subroutine.
- * Entry: 'A' message type and length have already been consumed.
- * Exit: returns 0 if successfully consumed Notify message.
- * returns EOF if not enough data.
- */
-static int
-getNotify(PGconn *conn)
-{
- int be_pid;
- PGnotify *newNotify;
-
- if (pqGetInt(&be_pid, 4, conn))
- return EOF;
- if (pqGets(&conn->workBuffer, conn))
- return EOF;
-
- /*
- * Store the relation name right after the PQnotify structure so it
- * can all be freed at once. We don't use NAMEDATALEN because we
- * don't want to tie this interface to a specific server name length.
- */
- newNotify = (PGnotify *) malloc(sizeof(PGnotify) +
- strlen(conn->workBuffer.data) +1);
- if (newNotify)
- {
- newNotify->relname = (char *) newNotify + sizeof(PGnotify);
- strcpy(newNotify->relname, conn->workBuffer.data);
- newNotify->be_pid = be_pid;
- DLAddTail(conn->notifyList, DLNewElem(newNotify));
- }
-
- /* Swallow extra string (not presently used) */
- if (pqGets(&conn->workBuffer, conn))
- return EOF;
-
- return 0;
-}
-
/*
* PQnotifies
* returns a PGnotify* structure of the latest async notification
@@ -1561,51 +942,20 @@ PQnotifies(PGconn *conn)
int
PQgetline(PGconn *conn, char *s, int maxlen)
{
- int status;
-
+ if (!s || maxlen <= 0)
+ return EOF;
+ *s = '\0';
/* maxlen must be at least 3 to hold the \. terminator! */
- if (!conn || !s || maxlen < 3)
+ if (maxlen < 3)
return EOF;
- if (conn->sock < 0 ||
- conn->asyncStatus != PGASYNC_COPY_OUT ||
- conn->copy_is_binary)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("PQgetline: not doing text COPY OUT\n"));
- *s = '\0';
+ if (!conn)
return EOF;
- }
-
- while ((status = PQgetlineAsync(conn, s, maxlen-1)) == 0)
- {
- /* need to load more data */
- if (pqWait(TRUE, FALSE, conn) ||
- pqReadData(conn) < 0)
- {
- *s = '\0';
- return EOF;
- }
- }
- if (status < 0)
- {
- /* End of copy detected; gin up old-style terminator */
- strcpy(s, "\\.");
- return 0;
- }
-
- /* Add null terminator, and strip trailing \n if present */
- if (s[status-1] == '\n')
- {
- s[status-1] = '\0';
- return 0;
- }
+ if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
+ return pqGetline3(conn, s, maxlen);
else
- {
- s[status] = '\0';
- return 1;
- }
+ return pqGetline2(conn, s, maxlen);
}
/*
@@ -1642,60 +992,13 @@ PQgetline(PGconn *conn, char *s, int maxlen)
int
PQgetlineAsync(PGconn *conn, char *buffer, int bufsize)
{
- char id;
- int msgLength;
- int avail;
-
- if (!conn || conn->asyncStatus != PGASYNC_COPY_OUT)
- return -1; /* we are not doing a copy... */
-
- /*
- * Recognize the next input message. To make life simpler for async
- * callers, we keep returning 0 until the next message is fully available
- * even if it is not Copy Data. This should keep PQendcopy from blocking.
- */
- conn->inCursor = conn->inStart;
- if (pqGetc(&id, conn))
- return 0;
- if (pqGetInt(&msgLength, 4, conn))
- return 0;
- avail = conn->inEnd - conn->inCursor;
- if (avail < msgLength - 4)
- return 0;
-
- /*
- * Cannot proceed unless it's a Copy Data message. Anything else means
- * end of copy mode.
- */
- if (id != 'd')
+ if (!conn)
return -1;
- /*
- * Move data from libpq's buffer to the caller's. In the case where
- * a prior call found the caller's buffer too small, we use
- * conn->copy_already_done to remember how much of the row was already
- * returned to the caller.
- */
- conn->inCursor += conn->copy_already_done;
- avail = msgLength - 4 - conn->copy_already_done;
- if (avail <= bufsize)
- {
- /* Able to consume the whole message */
- memcpy(buffer, &conn->inBuffer[conn->inCursor], avail);
- /* Mark message consumed */
- conn->inStart = conn->inCursor + avail;
- /* Reset state for next time */
- conn->copy_already_done = 0;
- return avail;
- }
+ if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
+ return pqGetlineAsync3(conn, buffer, bufsize);
else
- {
- /* We must return a partial message */
- memcpy(buffer, &conn->inBuffer[conn->inCursor], bufsize);
- /* The message is NOT consumed from libpq's buffer */
- conn->copy_already_done += bufsize;
- return bufsize;
- }
+ return pqGetlineAsync2(conn, buffer, bufsize);
}
/*
@@ -1722,10 +1025,21 @@ PQputnbytes(PGconn *conn, const char *buffer, int nbytes)
return EOF;
if (nbytes > 0)
{
- if (pqPutMsgStart('d', conn) < 0 ||
- pqPutnchar(buffer, nbytes, conn) < 0 ||
- pqPutMsgEnd(conn) < 0)
- return EOF;
+ /* This is too simple to bother with separate subroutines */
+ if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
+ {
+ if (pqPutMsgStart('d', false, conn) < 0 ||
+ pqPutnchar(buffer, nbytes, conn) < 0 ||
+ pqPutMsgEnd(conn) < 0)
+ return EOF;
+ }
+ else
+ {
+ if (pqPutMsgStart(0, false, conn) < 0 ||
+ pqPutnchar(buffer, nbytes, conn) < 0 ||
+ pqPutMsgEnd(conn) < 0)
+ return EOF;
+ }
}
return 0;
}
@@ -1742,72 +1056,13 @@ PQputnbytes(PGconn *conn, const char *buffer, int nbytes)
int
PQendcopy(PGconn *conn)
{
- PGresult *result;
-
if (!conn)
return 0;
- if (conn->asyncStatus != PGASYNC_COPY_IN &&
- conn->asyncStatus != PGASYNC_COPY_OUT)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("no COPY in progress\n"));
- return 1;
- }
-
- /* Send the CopyDone message if needed */
- if (conn->asyncStatus == PGASYNC_COPY_IN)
- {
- if (pqPutMsgStart('c', conn) < 0 ||
- pqPutMsgEnd(conn) < 0)
- return 1;
- }
-
- /*
- * make sure no data is waiting to be sent, abort if we are
- * non-blocking and the flush fails
- */
- if (pqFlush(conn) && pqIsnonblocking(conn))
- return (1);
-
- /* Return to active duty */
- conn->asyncStatus = PGASYNC_BUSY;
- resetPQExpBuffer(&conn->errorMessage);
-
- /*
- * Non blocking connections may have to abort at this point. If everyone
- * played the game there should be no problem, but in error scenarios
- * the expected messages may not have arrived yet. (We are assuming that
- * the backend's packetizing will ensure that CommandComplete arrives
- * along with the CopyDone; are there corner cases where that doesn't
- * happen?)
- */
- if (pqIsnonblocking(conn) && PQisBusy(conn))
- return (1);
-
- /* Wait for the completion response */
- result = PQgetResult(conn);
-
- /* Expecting a successful result */
- if (result && result->resultStatus == PGRES_COMMAND_OK)
- {
- PQclear(result);
- return 0;
- }
-
- /*
- * Trouble. For backwards-compatibility reasons, we issue the error
- * message as if it were a notice (would be nice to get rid of this
- * silliness, but too many apps probably don't handle errors from
- * PQendcopy reasonably). Note that the app can still obtain the
- * error status from the PGconn object.
- */
- if (conn->errorMessage.len > 0)
- DONOTICE(conn, conn->errorMessage.data);
-
- PQclear(result);
-
- return 1;
+ if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
+ return pqEndcopy3(conn);
+ else
+ return pqEndcopy2(conn);
}
@@ -1843,13 +1098,6 @@ PQfn(PGconn *conn,
const PQArgBlock *args,
int nargs)
{
- bool needInput = false;
- ExecStatusType status = PGRES_FATAL_ERROR;
- char id;
- int msgLength;
- int avail;
- int i;
-
*actual_result_len = 0;
if (!conn)
@@ -1866,200 +1114,16 @@ PQfn(PGconn *conn,
return NULL;
}
- if (pqPutMsgStart('F', conn) < 0 || /* function call msg */
- pqPutInt(fnid, 4, conn) < 0 || /* function id */
- pqPutInt(1, 2, conn) < 0 || /* # of format codes */
- pqPutInt(1, 2, conn) < 0 || /* format code: BINARY */
- pqPutInt(nargs, 2, conn) < 0) /* # of args */
- {
- handleSendFailure(conn);
- return NULL;
- }
-
- for (i = 0; i < nargs; ++i)
- { /* len.int4 + contents */
- if (pqPutInt(args[i].len, 4, conn))
- {
- handleSendFailure(conn);
- return NULL;
- }
- if (args[i].len == -1)
- continue; /* it's NULL */
-
- if (args[i].isint)
- {
- if (pqPutInt(args[i].u.integer, args[i].len, conn))
- {
- handleSendFailure(conn);
- return NULL;
- }
- }
- else
- {
- if (pqPutnchar((char *) args[i].u.ptr, args[i].len, conn))
- {
- handleSendFailure(conn);
- return NULL;
- }
- }
- }
-
- if (pqPutInt(1, 2, conn) < 0) /* result format code: BINARY */
- {
- handleSendFailure(conn);
- return NULL;
- }
-
- if (pqPutMsgEnd(conn) < 0 ||
- pqFlush(conn))
- {
- handleSendFailure(conn);
- return NULL;
- }
-
- for (;;)
- {
- if (needInput)
- {
- /* Wait for some data to arrive (or for the channel to close) */
- if (pqWait(TRUE, FALSE, conn) ||
- pqReadData(conn) < 0)
- break;
- }
-
- /*
- * Scan the message. If we run out of data, loop around to try
- * again.
- */
- needInput = true;
-
- conn->inCursor = conn->inStart;
- if (pqGetc(&id, conn))
- continue;
- if (pqGetInt(&msgLength, 4, conn))
- continue;
-
- /*
- * Try to validate message type/length here. A length less than 4
- * is definitely broken. Large lengths should only be believed
- * for a few message types.
- */
- if (msgLength < 4)
- {
- handleSyncLoss(conn, id, msgLength);
- break;
- }
- if (msgLength > 30000 &&
- !(id == 'T' || id == 'D' || id == 'd' || id == 'V'))
- {
- handleSyncLoss(conn, id, msgLength);
- break;
- }
-
- /*
- * Can't process if message body isn't all here yet.
- */
- msgLength -= 4;
- avail = conn->inEnd - conn->inCursor;
- if (avail < msgLength)
- {
- /*
- * Before looping, enlarge the input buffer if needed to hold
- * the whole message. See notes in parseInput.
- */
- if (pqCheckInBufferSpace(conn->inCursor + msgLength, conn))
- {
- /*
- * XXX add some better recovery code... plan is to skip
- * over the message using its length, then report an error.
- * For the moment, just treat this like loss of sync (which
- * indeed it might be!)
- */
- handleSyncLoss(conn, id, msgLength);
- break;
- }
- continue;
- }
-
- /*
- * We should see V or E response to the command, but might get N
- * and/or A notices first. We also need to swallow the final Z
- * before returning.
- */
- switch (id)
- {
- case 'V': /* function result */
- if (pqGetInt(actual_result_len, 4, conn))
- continue;
- if (*actual_result_len != -1)
- {
- if (result_is_int)
- {
- if (pqGetInt(result_buf, *actual_result_len, conn))
- continue;
- }
- else
- {
- if (pqGetnchar((char *) result_buf,
- *actual_result_len,
- conn))
- continue;
- }
- }
- /* correctly finished function result message */
- status = PGRES_COMMAND_OK;
- break;
- case 'E': /* error return */
- if (pqGetErrorNotice(conn, true))
- continue;
- status = PGRES_FATAL_ERROR;
- break;
- case 'A': /* notify message */
- /* handle notify and go back to processing return values */
- if (getNotify(conn))
- continue;
- break;
- case 'N': /* notice */
- /* handle notice and go back to processing return values */
- if (pqGetErrorNotice(conn, false))
- continue;
- break;
- case 'Z': /* backend is ready for new query */
- if (pqGetc(&conn->xact_status, conn))
- continue;
- /* consume the message and exit */
- conn->inStart += 5 + msgLength;
- /* if we saved a result object (probably an error), use it */
- if (conn->result)
- return prepareAsyncResult(conn);
- return PQmakeEmptyPGresult(conn, status);
- case 'S': /* parameter status */
- if (getParameterStatus(conn))
- continue;
- break;
- default:
- /* The backend violates the protocol. */
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("protocol error: id=0x%x\n"),
- id);
- saveErrorResult(conn);
- /* trust the specified message length as what to skip */
- conn->inStart += 5 + msgLength;
- return prepareAsyncResult(conn);
- }
- /* Completed this message, keep going */
- /* trust the specified message length as what to skip */
- conn->inStart += 5 + msgLength;
- needInput = false;
- }
-
- /*
- * 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);
+ if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
+ return pqFunctionCall3(conn, fnid,
+ result_buf, actual_result_len,
+ result_is_int,
+ args, nargs);
+ else
+ return pqFunctionCall2(conn, fnid,
+ result_buf, actual_result_len,
+ result_is_int,
+ args, nargs);
}
@@ -2132,7 +1196,7 @@ check_field_number(const PGresult *res, int field_num)
snprintf(noticeBuf, sizeof(noticeBuf),
libpq_gettext("column number %d is out of range 0..%d\n"),
field_num, res->numAttributes - 1);
- DONOTICE(res, noticeBuf);
+ PGDONOTICE(res, noticeBuf);
}
return FALSE;
}
@@ -2154,7 +1218,7 @@ check_tuple_field_number(const PGresult *res,
snprintf(noticeBuf, sizeof(noticeBuf),
libpq_gettext("row number %d is out of range 0..%d\n"),
tup_num, res->ntups - 1);
- DONOTICE(res, noticeBuf);
+ PGDONOTICE(res, noticeBuf);
}
return FALSE;
}
@@ -2165,7 +1229,7 @@ check_tuple_field_number(const PGresult *res,
snprintf(noticeBuf, sizeof(noticeBuf),
libpq_gettext("column number %d is out of range 0..%d\n"),
field_num, res->numAttributes - 1);
- DONOTICE(res, noticeBuf);
+ PGDONOTICE(res, noticeBuf);
}
return FALSE;
}
@@ -2367,7 +1431,7 @@ PQcmdTuples(PGresult *res)
snprintf(noticeBuf, sizeof(noticeBuf),
libpq_gettext("could not interpret result from server: %s\n"),
res->cmdStatus);
- DONOTICE(res, noticeBuf);
+ PGDONOTICE(res, noticeBuf);
}
return "";
}