mirror of
				https://github.com/postgres/postgres.git
				synced 2025-11-03 09:13:20 +03:00 
			
		
		
		
	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.
This commit is contained in:
		@@ -8,7 +8,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 * IDENTIFICATION
 | 
			
		||||
 *	  $PostgreSQL: pgsql/src/interfaces/libpq/fe-protocol3.c,v 1.22.2.1 2005/11/22 18:23:30 momjian Exp $
 | 
			
		||||
 *	  $PostgreSQL: pgsql/src/interfaces/libpq/fe-protocol3.c,v 1.22.2.2 2008/01/14 18:46:33 tgl Exp $
 | 
			
		||||
 *
 | 
			
		||||
 *-------------------------------------------------------------------------
 | 
			
		||||
 */
 | 
			
		||||
@@ -921,16 +921,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;
 | 
			
		||||
@@ -945,22 +942,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;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
@@ -988,16 +1057,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;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1060,7 +1119,6 @@ pqGetline3(PGconn *conn, char *s, int maxlen)
 | 
			
		||||
int
 | 
			
		||||
pqGetlineAsync3(PGconn *conn, char *buffer, int bufsize)
 | 
			
		||||
{
 | 
			
		||||
	char		id;
 | 
			
		||||
	int			msgLength;
 | 
			
		||||
	int			avail;
 | 
			
		||||
 | 
			
		||||
@@ -1071,22 +1129,13 @@ pqGetlineAsync3(PGconn *conn, char *buffer, int bufsize)
 | 
			
		||||
	 * 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.
 | 
			
		||||
	 * (Note: unlike pqGetCopyData3, we do not change asyncStatus here.)
 | 
			
		||||
	 */
 | 
			
		||||
	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
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user