libpq failed to cope with COPY FROM STDIN if the command was issued
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 13 Aug 2003 18:56:21 +0000 (18:56 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 13 Aug 2003 18:56:21 +0000 (18:56 +0000)
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.

doc/src/sgml/protocol.sgml
src/backend/commands/copy.c
src/interfaces/libpq/fe-exec.c
src/interfaces/libpq/fe-protocol3.c
src/interfaces/libpq/libpq-int.h

index 75fe6b3982eb894ba2084d04aed08eeddec0d0ee..c4605a13efc8b62368ad42cc55e139ca380197e7 100644 (file)
@@ -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">
  <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
index eb8a796f229cb9ea45382e4d61e92620c80b673c..bf798288f1b21368a3ca60276fb6d361c4687f81 100644 (file)
@@ -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),
index 0e2cd6a8a7638af7a908700a00ff86868c5484df..3e6527668137d47d4352b898066450ccf802154c 100644 (file)
@@ -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);
 }
index cc2546128c58793182957f2d4a3b2c9f5c8d50a2..0591d63da98fed4c1f268b3b833fb1d30e13a7c9 100644 (file)
@@ -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;
+               }
        }
 
        /*
index 1da73ad30b826c07b9c8cd4a7256e9326fbb10c4..49cd29cb6c83eed400304d3f3c85a40003965520 100644 (file)
@@ -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 */