aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2008-01-14 18:46:49 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2008-01-14 18:46:49 +0000
commitd85096ce12965d5b642d524b3b930caa0e932dec (patch)
treec19464a65d1425c170df7dac146de15ef5833e86 /src
parentec59b6deb8f5cee83d2f6f3155855a06c77a050c (diff)
downloadpostgresql-d85096ce12965d5b642d524b3b930caa0e932dec.tar.gz
postgresql-d85096ce12965d5b642d524b3b930caa0e932dec.zip
Fix an ancient oversight in libpq's handling of V3-protocol COPY OUT mode:
we need to be able to swallow NOTICE messages, and potentially also ParameterStatus messages (although the latter would be a bit weird), without exiting COPY OUT state. Fix it, and adjust the protocol documentation to emphasize the need for this. Per off-list report from Alexander Galler.
Diffstat (limited to 'src')
-rw-r--r--src/interfaces/libpq/fe-protocol3.c136
1 files changed, 92 insertions, 44 deletions
diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
index 690bef2e9cf..a98fb8c3b1a 100644
--- a/src/interfaces/libpq/fe-protocol3.c
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-protocol3.c,v 1.9.2.1 2003/12/28 17:44:05 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-protocol3.c,v 1.9.2.2 2008/01/14 18:46:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -874,16 +874,13 @@ getReadyForQuery(PGconn *conn)
}
/*
- * PQgetCopyData - read a row of data from the backend during COPY OUT
+ * getCopyDataMessage - fetch next CopyData message, process async messages
*
- * If successful, sets *buffer to point to a malloc'd row of data, and
- * returns row length (always > 0) as result.
- * Returns 0 if no row available yet (only possible if async is true),
- * -1 if end of copy (consult PQgetResult), or -2 if error (consult
- * PQerrorMessage).
+ * Returns length word of CopyData message (> 0), or 0 if no complete
+ * message available, -1 if end of copy, -2 if error.
*/
-int
-pqGetCopyData3(PGconn *conn, char **buffer, int async)
+static int
+getCopyDataMessage(PGconn *conn)
{
char id;
int msgLength;
@@ -898,22 +895,94 @@ pqGetCopyData3(PGconn *conn, char **buffer, int async)
*/
conn->inCursor = conn->inStart;
if (pqGetc(&id, conn))
- goto nodata;
+ return 0;
if (pqGetInt(&msgLength, 4, conn))
- goto nodata;
+ return 0;
+ if (msgLength < 4)
+ {
+ handleSyncLoss(conn, id, msgLength);
+ return -2;
+ }
avail = conn->inEnd - conn->inCursor;
if (avail < msgLength - 4)
- goto nodata;
+ return 0;
/*
- * If it's anything except Copy Data, exit COPY_OUT mode and let
- * caller read status with PQgetResult(). The normal case is that
- * it's Copy Done, but we let parseInput read that.
+ * If it's a legitimate async message type, process it. (NOTIFY
+ * messages are not currently possible here, but we handle them for
+ * completeness. NOTICE is definitely possible, and ParameterStatus
+ * could probably be made to happen.) Otherwise, if it's anything
+ * except Copy Data, report end-of-copy.
*/
- if (id != 'd')
+ switch (id)
{
- conn->asyncStatus = PGASYNC_BUSY;
- return -1;
+ case 'A': /* NOTIFY */
+ if (getNotify(conn))
+ return 0;
+ break;
+ case 'N': /* NOTICE */
+ if (pqGetErrorNotice3(conn, false))
+ return 0;
+ break;
+ case 'S': /* ParameterStatus */
+ if (getParameterStatus(conn))
+ return 0;
+ break;
+ case 'd': /* Copy Data, pass it back to caller */
+ return msgLength;
+ default: /* treat as end of copy */
+ return -1;
+ }
+
+ /* Drop the processed message and loop around for another */
+ conn->inStart = conn->inCursor;
+ }
+}
+
+/*
+ * PQgetCopyData - read a row of data from the backend during COPY OUT
+ *
+ * If successful, sets *buffer to point to a malloc'd row of data, and
+ * returns row length (always > 0) as result.
+ * Returns 0 if no row available yet (only possible if async is true),
+ * -1 if end of copy (consult PQgetResult), or -2 if error (consult
+ * PQerrorMessage).
+ */
+int
+pqGetCopyData3(PGconn *conn, char **buffer, int async)
+{
+ int msgLength;
+
+ for (;;)
+ {
+ /*
+ * Collect 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.
+ */
+ msgLength = getCopyDataMessage(conn);
+ if (msgLength < 0)
+ {
+ /*
+ * On end-of-copy, exit COPY_OUT mode and let caller read status
+ * with PQgetResult(). The normal case is that it's Copy Done,
+ * but we let parseInput read that. If error, we expect the
+ * state was already changed.
+ */
+ if (msgLength == -1)
+ conn->asyncStatus = PGASYNC_BUSY;
+ return msgLength; /* end-of-copy or error */
+ }
+ if (msgLength == 0)
+ {
+ /* Don't block if async read requested */
+ if (async)
+ return 0;
+ /* Need to load more data */
+ if (pqWait(TRUE, FALSE, conn) ||
+ pqReadData(conn) < 0)
+ return -2;
+ continue;
}
/*
@@ -941,16 +1010,6 @@ pqGetCopyData3(PGconn *conn, char **buffer, int async)
/* Empty, so drop it and loop around for another */
conn->inStart = conn->inCursor;
- continue;
-
-nodata:
- /* Don't block if async read requested */
- if (async)
- return 0;
- /* Need to load more data */
- if (pqWait(TRUE, FALSE, conn) ||
- pqReadData(conn) < 0)
- return -2;
}
}
@@ -1013,7 +1072,6 @@ pqGetline3(PGconn *conn, char *s, int maxlen)
int
pqGetlineAsync3(PGconn *conn, char *buffer, int bufsize)
{
- char id;
int msgLength;
int avail;
@@ -1026,21 +1084,11 @@ pqGetlineAsync3(PGconn *conn, char *buffer, int bufsize)
* 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')
- return -1;
+ msgLength = getCopyDataMessage(conn);
+ if (msgLength < 0)
+ return -1; /* end-of-copy or error */
+ if (msgLength == 0)
+ return 0; /* no data yet */
/*
* Move data from libpq's buffer to the caller's. In the case where a