diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml index 75fe6b3982e..c4605a13efc 100644 --- a/doc/src/sgml/protocol.sgml +++ b/doc/src/sgml/protocol.sgml @@ -1,4 +1,4 @@ - + Frontend/Backend Protocol @@ -919,8 +919,7 @@ 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 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 @@ -930,6 +929,15 @@ messages issued by the frontend will simply be dropped. + + 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 COPY FROM STDIN.) + + Copy-out mode (data transfer from the server) is initiated when the backend executes a COPY TO STDOUT SQL statement. The backend diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index eb8a796f229..bf798288f1b 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -8,7 +8,7 @@ * * * 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 */ int mtype; + readmessage: mtype = pq_getbyte(); if (mtype == EOF) ereport(ERROR, @@ -409,6 +410,15 @@ CopyGetData(void *databuf, int datasize) 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), diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c index 0e2cd6a8a76..3e652766813 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.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; } + /* 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 @@ -901,6 +904,9 @@ PQsendQueryGuts(PGconn *conn, 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 @@ -1187,29 +1193,28 @@ PQexecStart(PGconn *conn) */ 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) { @@ -1224,13 +1229,11 @@ PQexecStart(PGconn *conn) 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 */ @@ -1409,6 +1412,16 @@ PQputCopyEnd(PGconn *conn, const char *errormsg) 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 { @@ -2055,12 +2068,15 @@ PQgetisnull(const PGresult *res, int tup_num, int field_num) 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); /* @@ -2074,7 +2090,7 @@ PQsetnonblocking(PGconn *conn, int arg) if (pqFlush(conn)) return (-1); - conn->nonblocking = arg; + conn->nonblocking = barg; return (0); } diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c index cc2546128c5..0591d63da98 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.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 || 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; + } } /* diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index 1da73ad30b8..49cd29cb6c8 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -12,7 +12,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -262,8 +262,10 @@ struct pg_conn 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 */