mirror of https://github.com/postgres/postgres
libpq failed to cope with COPY FROM STDIN if the command was issued
via extended query protocol, because it sends Sync right after Execute without realizing that the command to be executed is COPY. There seems to be no reasonable way for it to realize that, either, so the best fix seems to be to make the backend ignore Sync during copy-in mode. Bit of a wart on the protocol, but little alternative. Also, libpq must send another Sync after terminating the COPY, if the command was issued via Execute.
This commit is contained in:
parent
0be731ad44
commit
c01641f8ae
|
@ -1,4 +1,4 @@
|
||||||
<!-- $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">
|
<chapter id="protocol">
|
||||||
<title>Frontend/Backend Protocol</title>
|
<title>Frontend/Backend Protocol</title>
|
||||||
|
@ -919,8 +919,7 @@
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
In the event of a backend-detected error during copy-in mode (including
|
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
|
receipt of a CopyFail message), the backend will issue an ErrorResponse
|
||||||
CopyData or CopyDone), the backend will issue an ErrorResponse
|
|
||||||
message. If the <command>COPY</> command was issued via an extended-query
|
message. If the <command>COPY</> command was issued via an extended-query
|
||||||
message, the backend will now discard frontend messages until a Sync
|
message, the backend will now discard frontend messages until a Sync
|
||||||
message is received, then it will issue ReadyForQuery and return to normal
|
message is received, then it will issue ReadyForQuery and return to normal
|
||||||
|
@ -930,6 +929,15 @@
|
||||||
messages issued by the frontend will simply be dropped.
|
messages issued by the frontend will simply be dropped.
|
||||||
</para>
|
</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>
|
<para>
|
||||||
Copy-out mode (data transfer from the server) is initiated when the
|
Copy-out mode (data transfer from the server) is initiated when the
|
||||||
backend executes a <command>COPY TO STDOUT</> SQL statement. The backend
|
backend executes a <command>COPY TO STDOUT</> SQL statement. The backend
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -386,6 +386,7 @@ CopyGetData(void *databuf, int datasize)
|
||||||
/* Try to receive another message */
|
/* Try to receive another message */
|
||||||
int mtype;
|
int mtype;
|
||||||
|
|
||||||
|
readmessage:
|
||||||
mtype = pq_getbyte();
|
mtype = pq_getbyte();
|
||||||
if (mtype == EOF)
|
if (mtype == EOF)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
|
@ -409,6 +410,15 @@ CopyGetData(void *databuf, int datasize)
|
||||||
errmsg("COPY from stdin failed: %s",
|
errmsg("COPY from stdin failed: %s",
|
||||||
pq_getmsgstring(copy_msgbuf))));
|
pq_getmsgstring(copy_msgbuf))));
|
||||||
break;
|
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:
|
default:
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_PROTOCOL_VIOLATION),
|
(errcode(ERRCODE_PROTOCOL_VIOLATION),
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -647,6 +647,9 @@ PQsendQuery(PGconn *conn, const char *query)
|
||||||
return 0;
|
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
|
* Give the data a push. In nonblock mode, don't complain if we're
|
||||||
* unable to send it all; PQgetResult() will do any additional
|
* unable to send it all; PQgetResult() will do any additional
|
||||||
|
@ -901,6 +904,9 @@ PQsendQueryGuts(PGconn *conn,
|
||||||
pqPutMsgEnd(conn) < 0)
|
pqPutMsgEnd(conn) < 0)
|
||||||
goto sendFailed;
|
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
|
* Give the data a push. In nonblock mode, don't complain if we're
|
||||||
* unable to send it all; PQgetResult() will do any additional
|
* unable to send it all; PQgetResult() will do any additional
|
||||||
|
@ -1187,29 +1193,28 @@ PQexecStart(PGconn *conn)
|
||||||
*/
|
*/
|
||||||
while ((result = PQgetResult(conn)) != NULL)
|
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)
|
if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
|
||||||
{
|
{
|
||||||
/* In protocol 3, we can get out of a COPY IN state */
|
/* In protocol 3, we can get out of a COPY IN state */
|
||||||
if (PQputCopyEnd(conn,
|
if (PQputCopyEnd(conn,
|
||||||
libpq_gettext("COPY terminated by new PQexec")) < 0)
|
libpq_gettext("COPY terminated by new PQexec")) < 0)
|
||||||
{
|
|
||||||
PQclear(result);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
/* keep waiting to swallow the copy's failure message */
|
/* keep waiting to swallow the copy's failure message */
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* In older protocols we have to punt */
|
/* In older protocols we have to punt */
|
||||||
PQclear(result);
|
|
||||||
printfPQExpBuffer(&conn->errorMessage,
|
printfPQExpBuffer(&conn->errorMessage,
|
||||||
libpq_gettext("COPY IN state must be terminated first\n"));
|
libpq_gettext("COPY IN state must be terminated first\n"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (result->resultStatus == PGRES_COPY_OUT)
|
else if (resultStatus == PGRES_COPY_OUT)
|
||||||
{
|
{
|
||||||
if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
|
if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
|
||||||
{
|
{
|
||||||
|
@ -1224,13 +1229,11 @@ PQexecStart(PGconn *conn)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* In older protocols we have to punt */
|
/* In older protocols we have to punt */
|
||||||
PQclear(result);
|
|
||||||
printfPQExpBuffer(&conn->errorMessage,
|
printfPQExpBuffer(&conn->errorMessage,
|
||||||
libpq_gettext("COPY OUT state must be terminated first\n"));
|
libpq_gettext("COPY OUT state must be terminated first\n"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PQclear(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* OK to send a command */
|
/* OK to send a command */
|
||||||
|
@ -1409,6 +1412,16 @@ PQputCopyEnd(PGconn *conn, const char *errormsg)
|
||||||
pqPutMsgEnd(conn) < 0)
|
pqPutMsgEnd(conn) < 0)
|
||||||
return -1;
|
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
|
else
|
||||||
{
|
{
|
||||||
|
@ -2055,12 +2068,15 @@ PQgetisnull(const PGresult *res, int tup_num, int field_num)
|
||||||
int
|
int
|
||||||
PQsetnonblocking(PGconn *conn, int arg)
|
PQsetnonblocking(PGconn *conn, int arg)
|
||||||
{
|
{
|
||||||
|
bool barg;
|
||||||
|
|
||||||
if (!conn || conn->status == CONNECTION_BAD)
|
if (!conn || conn->status == CONNECTION_BAD)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
arg = (arg == TRUE) ? 1 : 0;
|
barg = (arg ? TRUE : FALSE);
|
||||||
|
|
||||||
/* early out if the socket is already in the state requested */
|
/* early out if the socket is already in the state requested */
|
||||||
if (arg == conn->nonblocking)
|
if (barg == conn->nonblocking)
|
||||||
return (0);
|
return (0);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2074,7 +2090,7 @@ PQsetnonblocking(PGconn *conn, int arg)
|
||||||
if (pqFlush(conn))
|
if (pqFlush(conn))
|
||||||
return (-1);
|
return (-1);
|
||||||
|
|
||||||
conn->nonblocking = arg;
|
conn->nonblocking = barg;
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -1086,6 +1086,16 @@ pqEndcopy3(PGconn *conn)
|
||||||
if (pqPutMsgStart('c', false, conn) < 0 ||
|
if (pqPutMsgStart('c', false, conn) < 0 ||
|
||||||
pqPutMsgEnd(conn) < 0)
|
pqPutMsgEnd(conn) < 0)
|
||||||
return 1;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -262,8 +262,10 @@ struct pg_conn
|
||||||
PGAsyncStatusType asyncStatus;
|
PGAsyncStatusType asyncStatus;
|
||||||
PGTransactionStatusType xactStatus;
|
PGTransactionStatusType xactStatus;
|
||||||
/* note: xactStatus never changes to ACTIVE */
|
/* note: xactStatus never changes to ACTIVE */
|
||||||
int nonblocking; /* whether this connection is using a
|
bool nonblocking; /* whether this connection is using
|
||||||
* blocking socket to the backend or not */
|
* 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 */
|
char copy_is_binary; /* 1 = copy binary, 0 = copy text */
|
||||||
int copy_already_done; /* # bytes already returned in
|
int copy_already_done; /* # bytes already returned in
|
||||||
* COPY OUT */
|
* COPY OUT */
|
||||||
|
|
Loading…
Reference in New Issue