-<!-- $Header: /cvsroot/pgsql/doc/src/sgml/protocol.sgml,v 1.39 2003/06/27 19:08:37 tgl Exp $ -->
+<!-- $Header: /cvsroot/pgsql/doc/src/sgml/protocol.sgml,v 1.40 2003/08/13 18:56:21 tgl Exp $ -->
<chapter id="protocol">
<title>Frontend/Backend Protocol</title>
<para>
In the event of a backend-detected error during copy-in mode (including
- receipt of a CopyFail message, or indeed any frontend message other than
- CopyData or CopyDone), the backend will issue an ErrorResponse
+ receipt of a CopyFail message), the backend will issue an ErrorResponse
message. If the <command>COPY</> command was issued via an extended-query
message, the backend will now discard frontend messages until a Sync
message is received, then it will issue ReadyForQuery and return to normal
messages issued by the frontend will simply be dropped.
</para>
+ <para>
+ The backend will ignore Flush and Sync messages received during copy-in
+ mode. Receipt of any other non-copy message type constitutes an error
+ that will abort the copy-in state as described above. (The exception for
+ Flush and Sync is for the convenience of client libraries that always
+ send Flush or Sync after an Execute message, without checking whether
+ the command to be executed is a <command>COPY FROM STDIN</>.)
+ </para>
+
<para>
Copy-out mode (data transfer from the server) is initiated when the
backend executes a <command>COPY TO STDOUT</> SQL statement. The backend
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.208 2003/08/08 21:41:30 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.209 2003/08/13 18:56:21 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/* Try to receive another message */
int mtype;
+ readmessage:
mtype = pq_getbyte();
if (mtype == EOF)
ereport(ERROR,
errmsg("COPY from stdin failed: %s",
pq_getmsgstring(copy_msgbuf))));
break;
+ case 'H': /* Flush */
+ case 'S': /* Sync */
+ /*
+ * Ignore Flush/Sync for the convenience of
+ * client libraries (such as libpq) that may
+ * send those without noticing that the command
+ * they just sent was COPY.
+ */
+ goto readmessage;
default:
ereport(ERROR,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.144 2003/08/13 16:29:03 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.145 2003/08/13 18:56:21 tgl Exp $
*
*-------------------------------------------------------------------------
*/
return 0;
}
+ /* remember we are using simple query protocol */
+ conn->ext_query = false;
+
/*
* Give the data a push. In nonblock mode, don't complain if we're
* unable to send it all; PQgetResult() will do any additional
pqPutMsgEnd(conn) < 0)
goto sendFailed;
+ /* remember we are using extended query protocol */
+ conn->ext_query = true;
+
/*
* Give the data a push. In nonblock mode, don't complain if we're
* unable to send it all; PQgetResult() will do any additional
*/
while ((result = PQgetResult(conn)) != NULL)
{
- if (result->resultStatus == PGRES_COPY_IN)
+ ExecStatusType resultStatus = result->resultStatus;
+
+ PQclear(result); /* only need its status */
+ if (resultStatus == PGRES_COPY_IN)
{
if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
{
/* In protocol 3, we can get out of a COPY IN state */
if (PQputCopyEnd(conn,
libpq_gettext("COPY terminated by new PQexec")) < 0)
- {
- PQclear(result);
return false;
- }
/* keep waiting to swallow the copy's failure message */
}
else
{
/* In older protocols we have to punt */
- PQclear(result);
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("COPY IN state must be terminated first\n"));
return false;
}
}
- else if (result->resultStatus == PGRES_COPY_OUT)
+ else if (resultStatus == PGRES_COPY_OUT)
{
if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
{
else
{
/* In older protocols we have to punt */
- PQclear(result);
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("COPY OUT state must be terminated first\n"));
return false;
}
}
- PQclear(result);
}
/* OK to send a command */
pqPutMsgEnd(conn) < 0)
return -1;
}
+ /*
+ * If we sent the COPY command in extended-query mode, we must
+ * issue a Sync as well.
+ */
+ if (conn->ext_query)
+ {
+ if (pqPutMsgStart('S', false, conn) < 0 ||
+ pqPutMsgEnd(conn) < 0)
+ return -1;
+ }
}
else
{
int
PQsetnonblocking(PGconn *conn, int arg)
{
+ bool barg;
+
if (!conn || conn->status == CONNECTION_BAD)
return -1;
- arg = (arg == TRUE) ? 1 : 0;
+ barg = (arg ? TRUE : FALSE);
+
/* early out if the socket is already in the state requested */
- if (arg == conn->nonblocking)
+ if (barg == conn->nonblocking)
return (0);
/*
if (pqFlush(conn))
return (-1);
- conn->nonblocking = arg;
+ conn->nonblocking = barg;
return (0);
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-protocol3.c,v 1.7 2003/08/12 21:34:44 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-protocol3.c,v 1.8 2003/08/13 18:56:21 tgl Exp $
*
*-------------------------------------------------------------------------
*/
if (pqPutMsgStart('c', false, conn) < 0 ||
pqPutMsgEnd(conn) < 0)
return 1;
+ /*
+ * If we sent the COPY command in extended-query mode, we must
+ * issue a Sync as well.
+ */
+ if (conn->ext_query)
+ {
+ if (pqPutMsgStart('S', false, conn) < 0 ||
+ pqPutMsgEnd(conn) < 0)
+ return 1;
+ }
}
/*
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: libpq-int.h,v 1.80 2003/08/04 02:40:20 momjian Exp $
+ * $Id: libpq-int.h,v 1.81 2003/08/13 18:56:21 tgl Exp $
*
*-------------------------------------------------------------------------
*/
PGAsyncStatusType asyncStatus;
PGTransactionStatusType xactStatus;
/* note: xactStatus never changes to ACTIVE */
- int nonblocking; /* whether this connection is using a
- * blocking socket to the backend or not */
+ bool nonblocking; /* whether this connection is using
+ * nonblock sending semantics */
+ bool ext_query; /* was our last query sent with extended
+ * query protocol? */
char copy_is_binary; /* 1 = copy binary, 0 = copy text */
int copy_already_done; /* # bytes already returned in
* COPY OUT */