]> granicus.if.org Git - postgresql/commitdiff
Second round of FE/BE protocol changes. Frontend->backend messages now
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 19 Apr 2003 00:02:30 +0000 (00:02 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 19 Apr 2003 00:02:30 +0000 (00:02 +0000)
have length counts, and COPY IN data is packetized into messages.

24 files changed:
doc/src/sgml/libpq.sgml
doc/src/sgml/protocol.sgml
src/backend/commands/copy.c
src/backend/lib/stringinfo.c
src/backend/libpq/auth.c
src/backend/libpq/be-secure.c
src/backend/libpq/pqcomm.c
src/backend/libpq/pqformat.c
src/backend/postmaster/postmaster.c
src/backend/tcop/dest.c
src/backend/tcop/fastpath.c
src/backend/tcop/postgres.c
src/include/lib/stringinfo.h
src/include/libpq/libpq.h
src/include/libpq/pqcomm.h
src/include/libpq/pqformat.h
src/include/tcop/dest.h
src/include/tcop/fastpath.h
src/interfaces/libpq/fe-auth.c
src/interfaces/libpq/fe-connect.c
src/interfaces/libpq/fe-exec.c
src/interfaces/libpq/fe-misc.c
src/interfaces/libpq/libpq-fe.h
src/interfaces/libpq/libpq-int.h

index a1f8d6b5f6b768cde94c3251dcb433eaaf1f680e..7a7bb48ff390d26a8ffa25b6ad5e4c76807b55e1 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/libpq.sgml,v 1.118 2003/04/17 22:26:00 tgl Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/libpq.sgml,v 1.119 2003/04/19 00:02:29 tgl Exp $
 -->
 
  <chapter id="libpq">
@@ -1749,9 +1749,10 @@ state will never end.
 <term><function>PQflush</function></term>
 <listitem>
 <para>
-Attempts to flush any data queued to the server,
-returns 0 if successful (or if the send queue is empty) or <symbol>EOF</symbol> if it failed for
-some reason.
+Attempts to flush any data queued to the server.
+Returns 0 if successful (or if the send queue is empty), -1 if it failed for
+some reason, or 1 if it was unable to send all the data in the send queue yet
+(this case can only occur if the connection is nonblocking).
 <synopsis>
 int PQflush(PGconn *conn);
 </synopsis>
index bb25eb74b144210d6a9542665bce4a2d2e310940..529baa1f31be37784e1b17dad8d805e27785b57c 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $Header: /cvsroot/pgsql/doc/src/sgml/protocol.sgml,v 1.27 2003/04/16 20:53:38 tgl Exp $ -->
+<!-- $Header: /cvsroot/pgsql/doc/src/sgml/protocol.sgml,v 1.28 2003/04/19 00:02:29 tgl Exp $ -->
 
 <chapter id="protocol">
  <title>Frontend/Backend Protocol</title>
     Copy-in mode (data transfer to the server) is initiated when the
     backend executes a <command>COPY FROM STDIN</> SQL statement.  The backend
     sends a CopyInResponse message to the frontend.  The frontend should
-    then send zero or more CopyDataRow messages, one per row to be loaded.
-    (For <command>COPY BINARY</>, send CopyBinaryRow messages instead.)
+    then send zero or more CopyData messages, forming a stream of input
+    data.  (The message boundaries are not required to have anything to do
+    with row boundaries, although that is often a reasonable choice.)
     The frontend can terminate the copy-in mode by sending either a CopyDone
     message (allowing successful termination) or a CopyFail message (which
     will cause the <command>COPY</> SQL statement to fail with an
    <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
-    CopyDataRow, CopyBinaryRow, or CopyDone), 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, the backend will now discard frontend messages until a Sync
     message is received, then it will issue ReadyForQuery and return to normal
     processing.  If the <command>COPY</> command was issued in a simple
     Query message, the rest of that message is discarded and ReadyForQuery
-    is issued.  In either case, any subsequent CopyDataRow, CopyBinaryRow,
-    CopyDone, or CopyFail messages issued by the frontend will simply be
-    dropped.
+    is issued.  In either case, any subsequent CopyData, CopyDone, or CopyFail
+    messages issued by the frontend will simply be dropped.
    </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
     sends a CopyOutResponse message to the frontend, followed by
-    zero or more CopyDataRow messages, one per row, followed by CopyDone.
-    (For <command>COPY BINARY</>, CopyBinaryRow messages are sent instead.)
+    zero or more CopyData messages (always one per row), followed by CopyDone.
     The backend then reverts to the command-processing mode it was
     in before the <command>COPY</> started, and sends CommandComplete.
-    The frontend cannot abort
-    the transfer (short of closing the connection), but it can discard
-    unwanted CopyDataRow, CopyBinaryRow, and CopyDone messages.
+    The frontend cannot abort the transfer (short of closing the connection),
+    but it can discard unwanted CopyData and CopyDone messages.
    </para>
 
    <para>
     In the event of a backend-detected error during copy-out mode,
     the backend will issue an ErrorResponse message and revert to normal
     processing.  The frontend should treat receipt of ErrorResponse (or
-    indeed any message type other than CopyDataRow, CopyBinaryRow, or
-    CopyDone) as terminating the copy-out mode.
+    indeed any message type other than CopyData or CopyDone) as terminating
+    the copy-out mode.
    </para>
   </sect2>
 
@@ -1157,7 +1154,9 @@ indicate that it may be sent by a frontend (F), a backend (B), or both
 (F &amp; B).
 Notice that although each message includes a byte count at the beginning,
 the message format is defined so that the message end can be found without
-reference to the byte count.  This aids validity checking.
+reference to the byte count.  This aids validity checking.  (The CopyData
+message is an exception, because it forms part of a data stream; the contents
+may not be interpretable on their own.)
 </para>
 
 <VariableList>
@@ -2002,83 +2001,7 @@ CommandComplete (B)
 
 <VarListEntry>
 <Term>
-CopyBinaryRow (F &amp; B)
-</Term>
-<ListItem>
-<Para>
-<VariableList>
-<VarListEntry>
-<Term>
-        Byte1('b')
-</Term>
-<ListItem>
-<Para>
-                Identifies the message as binary COPY data.
-               Note that the message body format is identical to the
-               <command>COPY BINARY</> file-format representation for
-               a single row of data.
-</Para>
-</ListItem>
-</VarListEntry>
-<VarListEntry>
-<Term>
-        Int32
-</Term>
-<ListItem>
-<Para>
-                Length of message contents in bytes, including self.
-</Para>
-</ListItem>
-</VarListEntry>
-<VarListEntry>
-<Term>
-        Int16
-</Term>
-<ListItem>
-<Para>
-                Specifies the number of fields in the row (can be zero).
-</Para>
-</ListItem>
-</VarListEntry>
-</VariableList>
-        Then, for each field, there is the following:
-<VariableList>
-<VarListEntry>
-<Term>
-        Int16
-</Term>
-<ListItem>
-<Para>
-                Zero if the field is null, otherwise the <varname>typlen</>
-               for the field datatype.
-</Para>
-</ListItem>
-</VarListEntry>
-<VarListEntry>
-<Term>
-        Byte<Replaceable>n</Replaceable>
-</Term>
-<ListItem>
-<Para>
-                The value of the field itself in binary format.
-               Omitted if the field is null.
-               <Replaceable>n</Replaceable> is the <varname>typlen</>
-               value if <varname>typlen</> is positive.  If
-               <varname>typlen</> is -1 then the field value begins with
-               its own length as an Int32 (the length includes itself).
-</Para>
-</ListItem>
-</VarListEntry>
-</VariableList>
-
-</Para>
-</ListItem>
-</VarListEntry>
-
-
-<VarListEntry>
-<Term>
-CopyDataRow (F &amp; B)
+CopyData (F &amp; B)
 </Term>
 <ListItem>
 <Para>
@@ -2089,7 +2012,7 @@ CopyDataRow (F &amp; B)
 </Term>
 <ListItem>
 <Para>
-                Identifies the message as textual COPY data.
+                Identifies the message as COPY data.
 </Para>
 </ListItem>
 </VarListEntry>
@@ -2105,12 +2028,14 @@ CopyDataRow (F &amp; B)
 </VarListEntry>
 <VarListEntry>
 <Term>
-        String
+        Byte<Replaceable>n</Replaceable>
 </Term>
 <ListItem>
 <Para>
-                The textual representation of a single row of table data.
-               It should end with a newline.
+                Data that forms part of a COPY datastream.  Messages sent
+               from the backend will always correspond to single data rows,
+               but messages sent by frontends may divide the datastream
+               arbitrarily.
 </Para>
 </ListItem>
 </VarListEntry>
@@ -2236,8 +2161,7 @@ CopyInResponse (B)
 </Term>
 <ListItem>
 <Para>
-                0 for textual copy (CopyDataRow is expected), 1 for
-               binary copy (CopyBinaryRow is expected).
+                0 for textual copy, 1 for binary copy.
 </Para>
 </ListItem>
 </VarListEntry>
@@ -2283,8 +2207,7 @@ CopyOutResponse (B)
 </Term>
 <ListItem>
 <Para>
-                0 for textual copy (CopyDataRow will follow), 1 for
-               binary copy (CopyBinaryRow will follow).
+                0 for textual copy, 1 for binary copy.
 </Para>
 </ListItem>
 </VarListEntry>
@@ -3606,8 +3529,9 @@ StartupMessage (F)
 <ListItem>
 <Para>
                 The protocol version number.  The most significant 16 bits are
-                the major version number (3 for the format described here).
-               The least 16 significant bits are the minor version number.
+                the major version number (3 or more for the format described
+               here).
+               The least significant 16 bits are the minor version number.
 </Para>
 </ListItem>
 </VarListEntry>
@@ -3654,17 +3578,18 @@ StartupMessage (F)
 <ListItem>
 <Para>
                         Command-line arguments for the backend.  (This is
-                       deprecated in favor of setting individual GUC
+                       deprecated in favor of setting individual run-time
                        parameters.)
 </Para>
 </ListItem>
 </VarListEntry>
 </VariableList>
 
-                In addition to the above, any GUC parameter that can be
+                In addition to the above, any run-time parameter that can be
                set at backend start time may be listed.  Such settings
                will be applied during backend start (after parsing the
-               command-line options if any).
+               command-line options if any).  The values will act as
+               session defaults.
 </Para>
 </ListItem>
 </VarListEntry>
@@ -3913,4 +3838,41 @@ not line breaks.
 </sect1>
 
 
+<Sect1 id="protocol-changes">
+<Title>Summary of Changes since Protocol 2.0</Title>
+
+<para>
+This section provides a quick checklist of changes, for the benefit of
+developers trying to update existing client libraries to protocol 3.0.
+</para>
+
+<para>
+The initial startup packet uses a flexible list-of-strings format
+instead of a fixed format.  Notice that session default values for run-time
+parameters can now be specified directly in the startup packet.  (Actually,
+you could do that before using the <literal>options</> field, but given the
+limited width of <literal>options</> and the lack of any way to quote
+whitespace in the values, it wasn't a very safe technique.)
+</para>
+
+<para>
+All messages now have a length count immediately following the message type
+byte (except for startup packets, which have no type byte).  Also note that
+PasswordMessage now has a type byte.
+</para>
+
+<para>
+COPY data is now encapsulated into CopyData and CopyDone messages.  There
+is a well-defined way to recover from errors during COPY.
+</para>
+
+<note>
+<para>
+Additional changes will be documented as they are implemented.
+</para>
+</note>
+
+</sect1>
+
+
 </Chapter>
index 0f20bfb2aeadbbdba6dc9aced3c7c7c2766622f1..fd85f48b592877ce2318e50e02c9b9c32a478adf 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.191 2003/04/04 20:42:11 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.192 2003/04/19 00:02:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -31,6 +31,7 @@
 #include "commands/trigger.h"
 #include "executor/executor.h"
 #include "libpq/libpq.h"
+#include "libpq/pqformat.h"
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
 #define ISOCTAL(c) (((c) >= '0') && ((c) <= '7'))
 #define OCTVALUE(c) ((c) - '0')
 
+/*
+ * Represents the different source/dest cases we need to worry about at
+ * the bottom level
+ */
+typedef enum CopyDest
+{
+       COPY_FILE,                                      /* to/from file */
+       COPY_OLD_FE,                            /* to/from frontend (old protocol) */
+       COPY_NEW_FE                                     /* to/from frontend (new protocol) */
+} CopyDest;
+
 /*
  * Represents the type of data returned by CopyReadAttribute()
  */
@@ -61,13 +73,13 @@ typedef enum CopyReadResult
 
 /* non-export function prototypes */
 static void CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
-          FILE *fp, char *delim, char *null_print);
+                                  char *delim, char *null_print);
 static void CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
-                FILE *fp, char *delim, char *null_print);
+                                        char *delim, char *null_print);
 static Oid     GetInputFunction(Oid type);
 static Oid     GetTypeElement(Oid type);
-static char *CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result);
-static void CopyAttributeOut(FILE *fp, char *string, char *delim);
+static char *CopyReadAttribute(const char *delim, CopyReadResult *result);
+static void CopyAttributeOut(char *string, char *delim);
 static List *CopyGetAttnums(Relation rel, List *attnamelist);
 
 static const char BinarySignature[12] = "PGBCOPY\n\377\r\n\0";
@@ -77,7 +89,11 @@ static const char BinarySignature[12] = "PGBCOPY\n\377\r\n\0";
  * never been reentrant...
  */
 int                    copy_lineno = 0;        /* exported for use by elog() -- dz */
-static bool fe_eof;
+
+static CopyDest copy_dest;
+static FILE *copy_file;                        /* if copy_dest == COPY_FILE */
+static StringInfo copy_msgbuf; /* if copy_dest == COPY_NEW_FE */
+static bool fe_eof;                            /* true if detected end of copy data */
 
 /*
  * These static variables are used to avoid incurring overhead for each
@@ -96,98 +112,229 @@ static int        server_encoding;
 /*
  * Internal communications functions
  */
-static void CopySendData(void *databuf, int datasize, FILE *fp);
-static void CopySendString(const char *str, FILE *fp);
-static void CopySendChar(char c, FILE *fp);
-static void CopyGetData(void *databuf, int datasize, FILE *fp);
-static int     CopyGetChar(FILE *fp);
-static int     CopyGetEof(FILE *fp);
-static int     CopyPeekChar(FILE *fp);
-static void CopyDonePeek(FILE *fp, int c, bool pickup);
+static void SendCopyBegin(bool binary);
+static void ReceiveCopyBegin(bool binary);
+static void SendCopyEnd(bool binary);
+static void CopySendData(void *databuf, int datasize);
+static void CopySendString(const char *str);
+static void CopySendChar(char c);
+static void CopyGetData(void *databuf, int datasize);
+static int     CopyGetChar(void);
+#define CopyGetEof()  (fe_eof)
+static int     CopyPeekChar(void);
+static void CopyDonePeek(int c, bool pickup);
 
 /*
- * CopySendData sends output data either to the file
- *     specified by fp or, if fp is NULL, using the standard
- *     backend->frontend functions
- *
- * CopySendString does the same for null-terminated strings
- * CopySendChar does the same for single characters
- *
- * NB: no data conversion is applied by these functions
+ * Send copy start/stop messages for frontend copies.  These have changed
+ * in past protocol redesigns.
  */
 static void
-CopySendData(void *databuf, int datasize, FILE *fp)
+SendCopyBegin(bool binary)
 {
-       if (!fp)
+       if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
+       {
+               pq_putbytes("H", 1);    /* new way */
+               /* XXX grottiness needed for old protocol */
+               pq_startcopyout();
+               copy_dest = COPY_NEW_FE;
+       }
+       else if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
        {
-               if (pq_putbytes((char *) databuf, datasize))
-                       fe_eof = true;
+               pq_putbytes("H", 1);    /* old way */
+               /* grottiness needed for old protocol */
+               pq_startcopyout();
+               copy_dest = COPY_OLD_FE;
        }
        else
        {
-               fwrite(databuf, datasize, 1, fp);
-               if (ferror(fp))
-                       elog(ERROR, "CopySendData: %m");
+               pq_putbytes("B", 1);    /* very old way */
+               /* grottiness needed for old protocol */
+               pq_startcopyout();
+               copy_dest = COPY_OLD_FE;
        }
 }
 
 static void
-CopySendString(const char *str, FILE *fp)
+ReceiveCopyBegin(bool binary)
 {
-       CopySendData((void *) str, strlen(str), fp);
+       if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
+       {
+               pq_putbytes("G", 1);    /* new way */
+               copy_dest = COPY_NEW_FE;
+               copy_msgbuf = makeStringInfo();
+       }
+       else if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
+       {
+               pq_putbytes("G", 1);    /* old way */
+               copy_dest = COPY_OLD_FE;
+       }
+       else
+       {
+               pq_putbytes("D", 1);    /* very old way */
+               copy_dest = COPY_OLD_FE;
+       }
+       /* We *must* flush here to ensure FE knows it can send. */
+       pq_flush();
 }
 
 static void
-CopySendChar(char c, FILE *fp)
+SendCopyEnd(bool binary)
 {
-       CopySendData(&c, 1, fp);
+       if (!binary)
+               CopySendData("\\.\n", 3);
+       pq_endcopyout(false);
 }
 
 /*
- * CopyGetData reads output data either from the file
- *     specified by fp or, if fp is NULL, using the standard
- *     backend->frontend functions
- *
- * CopyGetChar does the same for single characters
- * CopyGetEof checks if it's EOF on the input (or, check for EOF result
- *             from CopyGetChar)
+ * CopySendData sends output data to the destination (file or frontend)
+ * CopySendString does the same for null-terminated strings
+ * CopySendChar does the same for single characters
  *
  * NB: no data conversion is applied by these functions
  */
 static void
-CopyGetData(void *databuf, int datasize, FILE *fp)
+CopySendData(void *databuf, int datasize)
 {
-       if (!fp)
+       switch (copy_dest)
        {
-               if (pq_getbytes((char *) databuf, datasize))
-                       fe_eof = true;
+               case COPY_FILE:
+                       fwrite(databuf, datasize, 1, copy_file);
+                       if (ferror(copy_file))
+                               elog(ERROR, "CopySendData: %m");
+                       break;
+               case COPY_OLD_FE:
+                       if (pq_putbytes((char *) databuf, datasize))
+                               fe_eof = true;
+                       break;
+               case COPY_NEW_FE:
+                       /* XXX fix later */
+                       if (pq_putbytes((char *) databuf, datasize))
+                               fe_eof = true;
+                       break;
        }
-       else
-               fread(databuf, datasize, 1, fp);
 }
 
-static int
-CopyGetChar(FILE *fp)
+static void
+CopySendString(const char *str)
+{
+       CopySendData((void *) str, strlen(str));
+}
+
+static void
+CopySendChar(char c)
+{
+       CopySendData(&c, 1);
+}
+
+/*
+ * CopyGetData reads data from the source (file or frontend)
+ * CopyGetChar does the same for single characters
+ *
+ * CopyGetEof checks if EOF was detected by previous Get operation.
+ *
+ * Note: when copying from the frontend, we expect a proper EOF mark per
+ * protocol; if the frontend simply drops the connection, we raise error.
+ * It seems unwise to allow the COPY IN to complete normally in that case.
+ *
+ * NB: no data conversion is applied by these functions
+ */
+static void
+CopyGetData(void *databuf, int datasize)
 {
-       if (!fp)
+       switch (copy_dest)
        {
-               int                     ch = pq_getbyte();
+               case COPY_FILE:
+                       fread(databuf, datasize, 1, copy_file);
+                       if (feof(copy_file))
+                               fe_eof = true;
+                       break;
+               case COPY_OLD_FE:
+                       if (pq_getbytes((char *) databuf, datasize))
+                       {
+                               /* Only a \. terminator is legal EOF in old protocol */
+                               elog(ERROR, "unexpected EOF on client connection");
+                       }
+                       break;
+               case COPY_NEW_FE:
+                       while (datasize > 0 && !fe_eof)
+                       {
+                               int             avail;
 
-               if (ch == EOF)
-                       fe_eof = true;
-               return ch;
+                               while (copy_msgbuf->cursor >= copy_msgbuf->len)
+                               {
+                                       /* Try to receive another message */
+                                       int                     mtype;
+
+                                       mtype = pq_getbyte();
+                                       if (mtype == EOF)
+                                               elog(ERROR, "unexpected EOF on client connection");
+                                       if (pq_getmessage(copy_msgbuf, 0))
+                                               elog(ERROR, "unexpected EOF on client connection");
+                                       switch (mtype)
+                                       {
+                                               case 'd': /* CopyData */
+                                                       break;
+                                               case 'c': /* CopyDone */
+                                                       /* COPY IN correctly terminated by frontend */
+                                                       fe_eof = true;
+                                                       return;
+                                               case 'f': /* CopyFail */
+                                                       elog(ERROR, "COPY IN failed: %s",
+                                                                pq_getmsgstring(copy_msgbuf));
+                                                       break;
+                                               default:
+                                                       elog(ERROR, "unexpected message type %c during COPY IN",
+                                                                mtype);
+                                                       break;
+                                       }
+                               }
+                               avail = copy_msgbuf->len - copy_msgbuf->cursor;
+                               if (avail > datasize)
+                                       avail = datasize;
+                               pq_copymsgbytes(copy_msgbuf, databuf, avail);
+                               databuf = (void *) ((char *) databuf + avail);
+                               datasize =- avail;
+                       }
+                       break;
        }
-       else
-               return getc(fp);
 }
 
 static int
-CopyGetEof(FILE *fp)
+CopyGetChar(void)
 {
-       if (!fp)
-               return fe_eof;
-       else
-               return feof(fp);
+       int             ch;
+
+       switch (copy_dest)
+       {
+               case COPY_FILE:
+                       ch = getc(copy_file);
+                       break;
+               case COPY_OLD_FE:
+                       ch = pq_getbyte();
+                       if (ch == EOF)
+                       {
+                               /* Only a \. terminator is legal EOF in old protocol */
+                               elog(ERROR, "unexpected EOF on client connection");
+                       }
+                       break;
+               case COPY_NEW_FE:
+               {
+                       unsigned char   cc;
+
+                       CopyGetData(&cc, 1);
+                       if (fe_eof)
+                               ch = EOF;
+                       else
+                               ch = cc;
+                       break;
+               }
+               default:
+                       ch = EOF;
+                       break;
+       }
+       if (ch == EOF)
+               fe_eof = true;
+       return ch;
 }
 
 /*
@@ -200,40 +347,74 @@ CopyGetEof(FILE *fp)
  * (if pickup is true) or leave it on the stream (if pickup is false).
  */
 static int
-CopyPeekChar(FILE *fp)
+CopyPeekChar(void)
 {
-       if (!fp)
+       int             ch;
+
+       switch (copy_dest)
        {
-               int                     ch = pq_peekbyte();
+               case COPY_FILE:
+                       ch = getc(copy_file);
+                       break;
+               case COPY_OLD_FE:
+                       ch = pq_peekbyte();
+                       if (ch == EOF)
+                       {
+                               /* Only a \. terminator is legal EOF in old protocol */
+                               elog(ERROR, "unexpected EOF on client connection");
+                       }
+                       break;
+               case COPY_NEW_FE:
+               {
+                       unsigned char   cc;
 
-               if (ch == EOF)
-                       fe_eof = true;
-               return ch;
+                       CopyGetData(&cc, 1);
+                       if (fe_eof)
+                               ch = EOF;
+                       else
+                               ch = cc;
+                       break;
+               }
+               default:
+                       ch = EOF;
+                       break;
        }
-       else
-               return getc(fp);
+       if (ch == EOF)
+               fe_eof = true;
+       return ch;
 }
 
 static void
-CopyDonePeek(FILE *fp, int c, bool pickup)
+CopyDonePeek(int c, bool pickup)
 {
-       if (!fp)
-       {
-               if (pickup)
-               {
-                       /* We want to pick it up */
-                       (void) pq_getbyte();
-               }
-               /* If we didn't want to pick it up, just leave it where it sits */
-       }
-       else
+       if (fe_eof)
+               return;                                 /* can't unget an EOF */
+       switch (copy_dest)
        {
-               if (!pickup)
-               {
-                       /* We don't want to pick it up - so put it back in there */
-                       ungetc(c, fp);
-               }
-               /* If we wanted to pick it up, it's already done */
+               case COPY_FILE:
+                       if (!pickup) 
+                       {
+                               /* We don't want to pick it up - so put it back in there */
+                               ungetc(c, copy_file);
+                       }
+                       /* If we wanted to pick it up, it's already done */
+                       break;
+               case COPY_OLD_FE:
+                       if (pickup)
+                       {
+                               /* We want to pick it up */
+                               (void) pq_getbyte();
+                       }
+                       /* If we didn't want to pick it up, just leave it where it sits */
+                       break;
+               case COPY_NEW_FE:
+                       if (!pickup)
+                       {
+                               /* We don't want to pick it up - so put it back in there */
+                               copy_msgbuf->cursor--;
+                       }
+                       /* If we wanted to pick it up, it's already done */
+                       break;
        }
 }
 
@@ -287,7 +468,6 @@ DoCopy(const CopyStmt *stmt)
        bool            oids = false;
        char       *delim = NULL;
        char       *null_print = NULL;
-       FILE       *fp;
        Relation        rel;
        AclMode         required_access = (is_from ? ACL_INSERT : ACL_SELECT);
        AclResult       aclresult;
@@ -397,6 +577,11 @@ DoCopy(const CopyStmt *stmt)
        client_encoding = pg_get_client_encoding();
        server_encoding = GetDatabaseEncoding();
 
+       copy_dest = COPY_FILE;          /* default */
+       copy_file = NULL;
+       copy_msgbuf = NULL;
+       fe_eof = false;
+
        if (is_from)
        {                                                       /* copy from file to database */
                if (rel->rd_rel->relkind != RELKIND_RELATION)
@@ -414,33 +599,30 @@ DoCopy(const CopyStmt *stmt)
                if (pipe)
                {
                        if (IsUnderPostmaster)
-                       {
-                               ReceiveCopyBegin();
-                               fp = NULL;
-                       }
+                               ReceiveCopyBegin(binary);
                        else
-                               fp = stdin;
+                               copy_file = stdin;
                }
                else
                {
                        struct stat st;
 
-                       fp = AllocateFile(filename, PG_BINARY_R);
+                       copy_file = AllocateFile(filename, PG_BINARY_R);
 
-                       if (fp == NULL)
+                       if (copy_file == NULL)
                                elog(ERROR, "COPY command, running in backend with "
                                         "effective uid %d, could not open file '%s' for "
                                         "reading.  Errno = %s (%d).",
                                         (int) geteuid(), filename, strerror(errno), errno);
 
-                       fstat(fileno(fp), &st);
+                       fstat(fileno(copy_file), &st);
                        if (S_ISDIR(st.st_mode))
                        {
-                               FreeFile(fp);
+                               FreeFile(copy_file);
                                elog(ERROR, "COPY: %s is a directory", filename);
                        }
                }
-               CopyFrom(rel, attnumlist, binary, oids, fp, delim, null_print);
+               CopyFrom(rel, attnumlist, binary, oids, delim, null_print);
        }
        else
        {                                                       /* copy from database to file */
@@ -459,13 +641,9 @@ DoCopy(const CopyStmt *stmt)
                if (pipe)
                {
                        if (IsUnderPostmaster)
-                       {
-                               SendCopyBegin();
-                               pq_startcopyout();
-                               fp = NULL;
-                       }
+                               SendCopyBegin(binary);
                        else
-                               fp = stdout;
+                               copy_file = stdout;
                }
                else
                {
@@ -481,33 +659,28 @@ DoCopy(const CopyStmt *stmt)
                                         " COPY command");
 
                        oumask = umask((mode_t) 022);
-                       fp = AllocateFile(filename, PG_BINARY_W);
+                       copy_file = AllocateFile(filename, PG_BINARY_W);
                        umask(oumask);
 
-                       if (fp == NULL)
+                       if (copy_file == NULL)
                                elog(ERROR, "COPY command, running in backend with "
                                         "effective uid %d, could not open file '%s' for "
                                         "writing.  Errno = %s (%d).",
                                         (int) geteuid(), filename, strerror(errno), errno);
-                       fstat(fileno(fp), &st);
+                       fstat(fileno(copy_file), &st);
                        if (S_ISDIR(st.st_mode))
                        {
-                               FreeFile(fp);
+                               FreeFile(copy_file);
                                elog(ERROR, "COPY: %s is a directory", filename);
                        }
                }
-               CopyTo(rel, attnumlist, binary, oids, fp, delim, null_print);
+               CopyTo(rel, attnumlist, binary, oids, delim, null_print);
        }
 
        if (!pipe)
-               FreeFile(fp);
-       else if (!is_from)
-       {
-               if (!binary)
-                       CopySendData("\\.\n", 3, fp);
-               if (IsUnderPostmaster)
-                       pq_endcopyout(false);
-       }
+               FreeFile(copy_file);
+       else if (IsUnderPostmaster && !is_from)
+               SendCopyEnd(binary);
        pfree(attribute_buf.data);
 
        /*
@@ -525,7 +698,7 @@ DoCopy(const CopyStmt *stmt)
  */
 static void
 CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
-          FILE *fp, char *delim, char *null_print)
+          char *delim, char *null_print)
 {
        HeapTuple       tuple;
        TupleDesc       tupDesc;
@@ -589,18 +762,18 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
                int32           tmp;
 
                /* Signature */
-               CopySendData((char *) BinarySignature, 12, fp);
+               CopySendData((char *) BinarySignature, 12);
                /* Integer layout field */
                tmp = 0x01020304;
-               CopySendData(&tmp, sizeof(int32), fp);
+               CopySendData(&tmp, sizeof(int32));
                /* Flags field */
                tmp = 0;
                if (oids)
                        tmp |= (1 << 16);
-               CopySendData(&tmp, sizeof(int32), fp);
+               CopySendData(&tmp, sizeof(int32));
                /* No header extension */
                tmp = 0;
-               CopySendData(&tmp, sizeof(int32), fp);
+               CopySendData(&tmp, sizeof(int32));
        }
 
        mySnapshot = CopyQuerySnapshot();
@@ -621,15 +794,15 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
                        /* Binary per-tuple header */
                        int16           fld_count = attr_count;
 
-                       CopySendData(&fld_count, sizeof(int16), fp);
+                       CopySendData(&fld_count, sizeof(int16));
                        /* Send OID if wanted --- note fld_count doesn't include it */
                        if (oids)
                        {
                                Oid                     oid = HeapTupleGetOid(tuple);
 
                                fld_size = sizeof(Oid);
-                               CopySendData(&fld_size, sizeof(int16), fp);
-                               CopySendData(&oid, sizeof(Oid), fp);
+                               CopySendData(&fld_size, sizeof(int16));
+                               CopySendData(&oid, sizeof(Oid));
                        }
                }
                else
@@ -639,7 +812,7 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
                        {
                                string = DatumGetCString(DirectFunctionCall1(oidout,
                                                          ObjectIdGetDatum(HeapTupleGetOid(tuple))));
-                               CopySendString(string, fp);
+                               CopySendString(string);
                                need_delim = true;
                        }
                }
@@ -655,7 +828,7 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
                        if (!binary)
                        {
                                if (need_delim)
-                                       CopySendChar(delim[0], fp);
+                                       CopySendChar(delim[0]);
                                need_delim = true;
                        }
 
@@ -663,12 +836,12 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
                        {
                                if (!binary)
                                {
-                                       CopySendString(null_print, fp);         /* null indicator */
+                                       CopySendString(null_print);             /* null indicator */
                                }
                                else
                                {
                                        fld_size = 0;           /* null marker */
-                                       CopySendData(&fld_size, sizeof(int16), fp);
+                                       CopySendData(&fld_size, sizeof(int16));
                                }
                        }
                        else
@@ -679,12 +852,12 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
                                                                                                                   value,
                                                                  ObjectIdGetDatum(elements[attnum - 1]),
                                                        Int32GetDatum(attr[attnum - 1]->atttypmod)));
-                                       CopyAttributeOut(fp, string, delim);
+                                       CopyAttributeOut(string, delim);
                                }
                                else
                                {
                                        fld_size = attr[attnum - 1]->attlen;
-                                       CopySendData(&fld_size, sizeof(int16), fp);
+                                       CopySendData(&fld_size, sizeof(int16));
                                        if (isvarlena[attnum - 1])
                                        {
                                                /* varlena */
@@ -694,16 +867,14 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
                                                value = PointerGetDatum(PG_DETOAST_DATUM(value));
 
                                                CopySendData(DatumGetPointer(value),
-                                                                        VARSIZE(value),
-                                                                        fp);
+                                                                        VARSIZE(value));
                                        }
                                        else if (!attr[attnum - 1]->attbyval)
                                        {
                                                /* fixed-length pass-by-reference */
                                                Assert(fld_size > 0);
                                                CopySendData(DatumGetPointer(value),
-                                                                        fld_size,
-                                                                        fp);
+                                                                        fld_size);
                                        }
                                        else
                                        {
@@ -717,15 +888,14 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
                                                 */
                                                store_att_byval(&datumBuf, value, fld_size);
                                                CopySendData(&datumBuf,
-                                                                        fld_size,
-                                                                        fp);
+                                                                        fld_size);
                                        }
                                }
                        }
                }
 
                if (!binary)
-                       CopySendChar('\n', fp);
+                       CopySendChar('\n');
 
                MemoryContextSwitchTo(oldcontext);
        }
@@ -737,7 +907,7 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
                /* Generate trailer for a binary copy */
                int16           fld_count = -1;
 
-               CopySendData(&fld_count, sizeof(int16), fp);
+               CopySendData(&fld_count, sizeof(int16));
        }
 
        MemoryContextDelete(mycontext);
@@ -753,7 +923,7 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
  */
 static void
 CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
-                FILE *fp, char *delim, char *null_print)
+                char *delim, char *null_print)
 {
        HeapTuple       tuple;
        TupleDesc       tupDesc;
@@ -905,30 +1075,30 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
                int32           tmp;
 
                /* Signature */
-               CopyGetData(readSig, 12, fp);
-               if (CopyGetEof(fp) || memcmp(readSig, BinarySignature, 12) != 0)
+               CopyGetData(readSig, 12);
+               if (CopyGetEof() || memcmp(readSig, BinarySignature, 12) != 0)
                        elog(ERROR, "COPY BINARY: file signature not recognized");
                /* Integer layout field */
-               CopyGetData(&tmp, sizeof(int32), fp);
-               if (CopyGetEof(fp) || tmp != 0x01020304)
+               CopyGetData(&tmp, sizeof(int32));
+               if (CopyGetEof() || tmp != 0x01020304)
                        elog(ERROR, "COPY BINARY: incompatible integer layout");
                /* Flags field */
-               CopyGetData(&tmp, sizeof(int32), fp);
-               if (CopyGetEof(fp))
+               CopyGetData(&tmp, sizeof(int32));
+               if (CopyGetEof())
                        elog(ERROR, "COPY BINARY: bogus file header (missing flags)");
                file_has_oids = (tmp & (1 << 16)) != 0;
                tmp &= ~(1 << 16);
                if ((tmp >> 16) != 0)
                        elog(ERROR, "COPY BINARY: unrecognized critical flags in header");
                /* Header extension length */
-               CopyGetData(&tmp, sizeof(int32), fp);
-               if (CopyGetEof(fp) || tmp < 0)
+               CopyGetData(&tmp, sizeof(int32));
+               if (CopyGetEof() || tmp < 0)
                        elog(ERROR, "COPY BINARY: bogus file header (missing length)");
                /* Skip extension header, if present */
                while (tmp-- > 0)
                {
-                       CopyGetData(readSig, 1, fp);
-                       if (CopyGetEof(fp))
+                       CopyGetData(readSig, 1);
+                       if (CopyGetEof())
                                elog(ERROR, "COPY BINARY: bogus file header (wrong length)");
                }
        }
@@ -936,6 +1106,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
        values = (Datum *) palloc(num_phys_attrs * sizeof(Datum));
        nulls = (char *) palloc(num_phys_attrs * sizeof(char));
 
+       /* Initialize static variables */
        copy_lineno = 0;
        fe_eof = false;
 
@@ -970,7 +1141,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
 
                        if (file_has_oids)
                        {
-                               string = CopyReadAttribute(fp, delim, &result);
+                               string = CopyReadAttribute(delim, &result);
 
                                if (result == END_OF_FILE && *string == '\0')
                                {
@@ -1006,7 +1177,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
                                        elog(ERROR, "Missing data for column \"%s\"",
                                                 NameStr(attr[m]->attname));
 
-                               string = CopyReadAttribute(fp, delim, &result);
+                               string = CopyReadAttribute(delim, &result);
 
                                if (result == END_OF_FILE && *string == '\0' &&
                                        cur == attnumlist && !file_has_oids)
@@ -1051,8 +1222,8 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
                        int16           fld_count,
                                                fld_size;
 
-                       CopyGetData(&fld_count, sizeof(int16), fp);
-                       if (CopyGetEof(fp) || fld_count == -1)
+                       CopyGetData(&fld_count, sizeof(int16));
+                       if (CopyGetEof() || fld_count == -1)
                        {
                                done = true;
                                break;
@@ -1064,14 +1235,14 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
 
                        if (file_has_oids)
                        {
-                               CopyGetData(&fld_size, sizeof(int16), fp);
-                               if (CopyGetEof(fp))
+                               CopyGetData(&fld_size, sizeof(int16));
+                               if (CopyGetEof())
                                        elog(ERROR, "COPY BINARY: unexpected EOF");
                                if (fld_size != (int16) sizeof(Oid))
                                        elog(ERROR, "COPY BINARY: sizeof(Oid) is %d, expected %d",
                                                 (int) fld_size, (int) sizeof(Oid));
-                               CopyGetData(&loaded_oid, sizeof(Oid), fp);
-                               if (CopyGetEof(fp))
+                               CopyGetData(&loaded_oid, sizeof(Oid));
+                               if (CopyGetEof())
                                        elog(ERROR, "COPY BINARY: unexpected EOF");
                                if (loaded_oid == InvalidOid)
                                        elog(ERROR, "COPY BINARY: Invalid Oid");
@@ -1085,8 +1256,8 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
 
                                i++;
 
-                               CopyGetData(&fld_size, sizeof(int16), fp);
-                               if (CopyGetEof(fp))
+                               CopyGetData(&fld_size, sizeof(int16));
+                               if (CopyGetEof())
                                        elog(ERROR, "COPY BINARY: unexpected EOF");
                                if (fld_size == 0)
                                        continue;       /* it's NULL; nulls[attnum-1] already set */
@@ -1099,17 +1270,16 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
                                        int32           varlena_size;
                                        Pointer         varlena_ptr;
 
-                                       CopyGetData(&varlena_size, sizeof(int32), fp);
-                                       if (CopyGetEof(fp))
+                                       CopyGetData(&varlena_size, sizeof(int32));
+                                       if (CopyGetEof())
                                                elog(ERROR, "COPY BINARY: unexpected EOF");
                                        if (varlena_size < (int32) sizeof(int32))
                                                elog(ERROR, "COPY BINARY: bogus varlena length");
                                        varlena_ptr = (Pointer) palloc(varlena_size);
                                        VARATT_SIZEP(varlena_ptr) = varlena_size;
                                        CopyGetData(VARDATA(varlena_ptr),
-                                                               varlena_size - sizeof(int32),
-                                                               fp);
-                                       if (CopyGetEof(fp))
+                                                               varlena_size - sizeof(int32));
+                                       if (CopyGetEof())
                                                elog(ERROR, "COPY BINARY: unexpected EOF");
                                        values[m] = PointerGetDatum(varlena_ptr);
                                }
@@ -1120,8 +1290,8 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
 
                                        Assert(fld_size > 0);
                                        refval_ptr = (Pointer) palloc(fld_size);
-                                       CopyGetData(refval_ptr, fld_size, fp);
-                                       if (CopyGetEof(fp))
+                                       CopyGetData(refval_ptr, fld_size);
+                                       if (CopyGetEof())
                                                elog(ERROR, "COPY BINARY: unexpected EOF");
                                        values[m] = PointerGetDatum(refval_ptr);
                                }
@@ -1135,8 +1305,8 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
                                         * how shorter data values are aligned within a Datum.
                                         */
                                        Assert(fld_size > 0 && fld_size <= sizeof(Datum));
-                                       CopyGetData(&datumBuf, fld_size, fp);
-                                       if (CopyGetEof(fp))
+                                       CopyGetData(&datumBuf, fld_size);
+                                       if (CopyGetEof())
                                                elog(ERROR, "COPY BINARY: unexpected EOF");
                                        values[m] = fetch_att(&datumBuf, true, fld_size);
                                }
@@ -1324,7 +1494,7 @@ GetTypeElement(Oid type)
  */
 
 static char *
-CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result)
+CopyReadAttribute(const char *delim, CopyReadResult *result)
 {
        int                     c;
        int                     delimc = (unsigned char) delim[0];
@@ -1344,7 +1514,7 @@ CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result)
 
        for (;;)
        {
-               c = CopyGetChar(fp);
+               c = CopyGetChar();
                if (c == EOF)
                {
                        *result = END_OF_FILE;
@@ -1359,7 +1529,7 @@ CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result)
                        break;
                if (c == '\\')
                {
-                       c = CopyGetChar(fp);
+                       c = CopyGetChar();
                        if (c == EOF)
                        {
                                *result = END_OF_FILE;
@@ -1379,16 +1549,16 @@ CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result)
                                                int                     val;
 
                                                val = OCTVALUE(c);
-                                               c = CopyPeekChar(fp);
+                                               c = CopyPeekChar();
                                                if (ISOCTAL(c))
                                                {
                                                        val = (val << 3) + OCTVALUE(c);
-                                                       CopyDonePeek(fp, c, true /* pick up */ );
-                                                       c = CopyPeekChar(fp);
+                                                       CopyDonePeek(c, true /* pick up */ );
+                                                       c = CopyPeekChar();
                                                        if (ISOCTAL(c))
                                                        {
                                                                val = (val << 3) + OCTVALUE(c);
-                                                               CopyDonePeek(fp, c, true /* pick up */ );
+                                                               CopyDonePeek(c, true /* pick up */ );
                                                        }
                                                        else
                                                        {
@@ -1397,7 +1567,7 @@ CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result)
                                                                        *result = END_OF_FILE;
                                                                        goto copy_eof;
                                                                }
-                                                               CopyDonePeek(fp, c, false /* put back */ );
+                                                               CopyDonePeek(c, false /* put back */ );
                                                        }
                                                }
                                                else
@@ -1407,7 +1577,7 @@ CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result)
                                                                *result = END_OF_FILE;
                                                                goto copy_eof;
                                                        }
-                                                       CopyDonePeek(fp, c, false /* put back */ );
+                                                       CopyDonePeek(c, false /* put back */ );
                                                }
                                                c = val & 0377;
                                        }
@@ -1441,9 +1611,21 @@ CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result)
                                        c = '\v';
                                        break;
                                case '.':
-                                       c = CopyGetChar(fp);
+                                       c = CopyGetChar();
                                        if (c != '\n')
                                                elog(ERROR, "CopyReadAttribute: end of record marker corrupted");
+                                       /*
+                                        * In protocol version 3, we should ignore anything after
+                                        * \. up to the protocol end of copy data.  (XXX maybe
+                                        * better not to treat \. as special?)
+                                        */
+                                       if (copy_dest == COPY_NEW_FE)
+                                       {
+                                               while (c != EOF)
+                                               {
+                                                       c = CopyGetChar();
+                                               }
+                                       }
                                        *result = END_OF_FILE;
                                        goto copy_eof;
                        }
@@ -1458,7 +1640,7 @@ CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result)
                        mblen = pg_encoding_mblen(client_encoding, s);
                        for (j = 1; j < mblen; j++)
                        {
-                               c = CopyGetChar(fp);
+                               c = CopyGetChar();
                                if (c == EOF)
                                {
                                        *result = END_OF_FILE;
@@ -1488,7 +1670,7 @@ copy_eof:
 }
 
 static void
-CopyAttributeOut(FILE *fp, char *server_string, char *delim)
+CopyAttributeOut(char *server_string, char *delim)
 {
        char       *string;
        char            c;
@@ -1511,30 +1693,30 @@ CopyAttributeOut(FILE *fp, char *server_string, char *delim)
                switch (c)
                {
                        case '\b':
-                               CopySendString("\\b", fp);
+                               CopySendString("\\b");
                                break;
                        case '\f':
-                               CopySendString("\\f", fp);
+                               CopySendString("\\f");
                                break;
                        case '\n':
-                               CopySendString("\\n", fp);
+                               CopySendString("\\n");
                                break;
                        case '\r':
-                               CopySendString("\\r", fp);
+                               CopySendString("\\r");
                                break;
                        case '\t':
-                               CopySendString("\\t", fp);
+                               CopySendString("\\t");
                                break;
                        case '\v':
-                               CopySendString("\\v", fp);
+                               CopySendString("\\v");
                                break;
                        case '\\':
-                               CopySendString("\\\\", fp);
+                               CopySendString("\\\\");
                                break;
                        default:
                                if (c == delimc)
-                                       CopySendChar('\\', fp);
-                               CopySendChar(c, fp);
+                                       CopySendChar('\\');
+                               CopySendChar(c);
 
                                /*
                                 * We can skip pg_encoding_mblen() overhead when encoding
@@ -1546,7 +1728,7 @@ CopyAttributeOut(FILE *fp, char *server_string, char *delim)
                                        /* send additional bytes of the char, if any */
                                        mblen = pg_encoding_mblen(client_encoding, string);
                                        for (i = 1; i < mblen; i++)
-                                               CopySendChar(string[i], fp);
+                                               CopySendChar(string[i]);
                                }
                                break;
                }
index 9b9fc3d180078aa951695113432ec771a78bc55d..0f758b1bd2dda4c8a168e35853f2365719be1ac2 100644 (file)
@@ -9,15 +9,15 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *       $Id: stringinfo.c,v 1.32 2002/09/04 20:31:18 momjian Exp $
+ *       $Id: stringinfo.c,v 1.33 2003/04/19 00:02:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
-
-
 #include "postgres.h"
+
 #include "lib/stringinfo.h"
 
+
 /*
  * makeStringInfo
  *
@@ -50,41 +50,7 @@ initStringInfo(StringInfo str)
        str->maxlen = size;
        str->len = 0;
        str->data[0] = '\0';
-}
-
-/*
- * enlargeStringInfo
- *
- * Internal routine: make sure there is enough space for 'needed' more bytes
- * ('needed' does not include the terminating null).
- *
- * NB: because we use repalloc() to enlarge the buffer, the string buffer
- * will remain allocated in the same memory context that was current when
- * initStringInfo was called, even if another context is now current.
- * This is the desired and indeed critical behavior!
- */
-static void
-enlargeStringInfo(StringInfo str, int needed)
-{
-       int                     newlen;
-
-       needed += str->len + 1;         /* total space required now */
-       if (needed <= str->maxlen)
-               return;                                 /* got enough space already */
-
-       /*
-        * We don't want to allocate just a little more space with each
-        * append; for efficiency, double the buffer size each time it
-        * overflows. Actually, we might need to more than double it if
-        * 'needed' is big...
-        */
-       newlen = 2 * str->maxlen;
-       while (needed > newlen)
-               newlen = 2 * newlen;
-
-       str->data = (char *) repalloc(str->data, newlen);
-
-       str->maxlen = newlen;
+       str->cursor = 0;
 }
 
 /*
@@ -147,8 +113,9 @@ appendStringInfo(StringInfo str, const char *fmt,...)
        }
 }
 
-/*------------------------
+/*
  * appendStringInfoChar
+ *
  * Append a single byte to str.
  * Like appendStringInfo(str, "%c", ch) but much faster.
  */
@@ -189,3 +156,44 @@ appendBinaryStringInfo(StringInfo str, const char *data, int datalen)
         */
        str->data[str->len] = '\0';
 }
+
+/*
+ * enlargeStringInfo
+ *
+ * Make sure there is enough space for 'needed' more bytes
+ * ('needed' does not include the terminating null).
+ *
+ * External callers need not concern themselves with this, since all
+ * stringinfo.c routines do it automatically.  However, if a caller
+ * knows that a StringInfo will eventually become X bytes large, it
+ * can save some palloc overhead by enlarging the buffer before starting
+ * to store data in it.
+ *
+ * NB: because we use repalloc() to enlarge the buffer, the string buffer
+ * will remain allocated in the same memory context that was current when
+ * initStringInfo was called, even if another context is now current.
+ * This is the desired and indeed critical behavior!
+ */
+void
+enlargeStringInfo(StringInfo str, int needed)
+{
+       int                     newlen;
+
+       needed += str->len + 1;         /* total space required now */
+       if (needed <= str->maxlen)
+               return;                                 /* got enough space already */
+
+       /*
+        * We don't want to allocate just a little more space with each
+        * append; for efficiency, double the buffer size each time it
+        * overflows. Actually, we might need to more than double it if
+        * 'needed' is big...
+        */
+       newlen = 2 * str->maxlen;
+       while (needed > newlen)
+               newlen = 2 * newlen;
+
+       str->data = (char *) repalloc(str->data, newlen);
+
+       str->maxlen = newlen;
+}
index 5396cc47c18435b27eb275a9c7567daa0f238701..a5dc8eff2da02191773fc3439a6f60fa7c6cd83e 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.98 2003/04/17 22:26:01 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.99 2003/04/19 00:02:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -37,6 +37,7 @@
 
 static void sendAuthRequest(Port *port, AuthRequest areq);
 static void auth_failed(Port *port, int status);
+static char *recv_password_packet(Port *port);
 static int     recv_and_check_password_packet(Port *port);
 
 char      *pg_krb_server_keyfile;
@@ -539,11 +540,9 @@ sendAuthRequest(Port *port, AuthRequest areq)
  */
 
 static int
-pam_passwd_conv_proc(int num_msg, const struct pam_message ** msg, struct pam_response ** resp, void *appdata_ptr)
+pam_passwd_conv_proc(int num_msg, const struct pam_message ** msg,
+                                        struct pam_response ** resp, void *appdata_ptr)
 {
-       StringInfoData buf;
-       int32           len;
-
        if (num_msg != 1 || msg[0]->msg_style != PAM_PROMPT_ECHO_OFF)
        {
                switch (msg[0]->msg_style)
@@ -574,23 +573,20 @@ pam_passwd_conv_proc(int num_msg, const struct pam_message ** msg, struct pam_re
         */
        if (strlen(appdata_ptr) == 0)
        {
-               sendAuthRequest(pam_port_cludge, AUTH_REQ_PASSWORD);
-               if (pq_eof() == EOF || pq_getint(&len, 4) == EOF)
-                       return PAM_CONV_ERR;    /* client didn't want to send password */
+               char       *passwd;
 
-               initStringInfo(&buf);
-               if (pq_getstr_bounded(&buf, 1000) == EOF)
-                       return PAM_CONV_ERR;    /* EOF while reading password */
+               sendAuthRequest(pam_port_cludge, AUTH_REQ_PASSWORD);
+               passwd = recv_password_packet(pam_port_cludge);
 
-               /* Do not echo failed password to logs, for security. */
-               elog(DEBUG5, "received PAM packet");
+               if (passwd == NULL)
+                       return PAM_CONV_ERR;    /* client didn't want to send password */
 
-               if (strlen(buf.data) == 0)
+               if (strlen(passwd) == 0)
                {
                        elog(LOG, "pam_passwd_conv_proc: no password");
                        return PAM_CONV_ERR;
                }
-               appdata_ptr = buf.data;
+               appdata_ptr = passwd;
        }
 
        /*
@@ -601,8 +597,6 @@ pam_passwd_conv_proc(int num_msg, const struct pam_message ** msg, struct pam_re
        if (!*resp)
        {
                elog(LOG, "pam_passwd_conv_proc: Out of memory!");
-               if (buf.data)
-                       pfree(buf.data);
                return PAM_CONV_ERR;
        }
 
@@ -708,42 +702,87 @@ CheckPAMAuth(Port *port, char *user, char *password)
 
 
 /*
- * Called when we have received the password packet.
+ * Collect password response packet from frontend.
+ *
+ * Returns NULL if couldn't get password, else palloc'd string.
  */
-static int
-recv_and_check_password_packet(Port *port)
+static char *
+recv_password_packet(Port *port)
 {
        StringInfoData buf;
-       int32           len;
-       int                     result;
 
-       if (pq_eof() == EOF || pq_getint(&len, 4) == EOF)
-               return STATUS_EOF;              /* client didn't want to send password */
+       if (PG_PROTOCOL_MAJOR(port->proto) >= 3)
+       {
+               /* Expect 'p' message type */
+               int             mtype;
+
+               mtype = pq_getbyte();
+               if (mtype != 'p')
+               {
+                       /*
+                        * If the client just disconnects without offering a password,
+                        * don't make a log entry.  This is legal per protocol spec and
+                        * in fact commonly done by psql, so complaining just clutters
+                        * the log.
+                        */
+                       if (mtype != EOF)
+                               elog(COMMERROR, "Expected password response, got %c", mtype);
+                       return NULL;            /* EOF or bad message type */
+               }
+       }
+       else
+       {
+               /* For pre-3.0 clients, avoid log entry if they just disconnect */
+               if (pq_peekbyte() == EOF)
+                       return NULL;            /* EOF */
+       }
 
        initStringInfo(&buf);
-       if (pq_getstr_bounded(&buf, 1000) == EOF) /* receive password */
+       if (pq_getmessage(&buf, 1000)) /* receive password */
        {
+               /* EOF - pq_getmessage already logged a suitable message */
                pfree(buf.data);
-               return STATUS_EOF;
+               return NULL;
        }
 
        /*
-        * We don't actually use the password packet length the frontend sent
-        * us; however, it's a reasonable sanity check to ensure that we
-        * actually read as much data as we expected to.
-        *
-        * The password packet size is the length of the buffer, plus the size
-        * field itself (4 bytes), plus a 1-byte terminator.
+        * Apply sanity check: password packet length should agree with length
+        * of contained string.  Note it is safe to use strlen here because
+        * StringInfo is guaranteed to have an appended '\0'.
         */
-       if (len != (buf.len + 4 + 1))
-               elog(LOG, "unexpected password packet size: read %d, expected %d",
-                        buf.len + 4 + 1, len);
+       if (strlen(buf.data) + 1 != buf.len)
+               elog(COMMERROR, "bogus password packet size");
 
        /* Do not echo password to logs, for security. */
        elog(DEBUG5, "received password packet");
 
-       result = md5_crypt_verify(port, port->user_name, buf.data);
+       /*
+        * Return the received string.  Note we do not attempt to do any
+        * character-set conversion on it; since we don't yet know the
+        * client's encoding, there wouldn't be much point.
+        */
+       return buf.data;
+}
+
+
+/*
+ * Called when we have sent an authorization request for a password.
+ * Get the response and check it.
+ */
+static int
+recv_and_check_password_packet(Port *port)
+{
+       char       *passwd;
+       int                     result;
+
+       passwd = recv_password_packet(port);
+
+       if (passwd == NULL)
+               return STATUS_EOF;              /* client wouldn't send password */
+
+       result = md5_crypt_verify(port, port->user_name, passwd);
+
+       pfree(passwd);
 
-       pfree(buf.data);
        return result;
 }
index 923f38a98c9b505140107d01af2e045401e2e9e4..e7674c7807cc07afc4d66398bda1aecd91d35dfd 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/libpq/be-secure.c,v 1.29 2003/04/10 23:03:08 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/libpq/be-secure.c,v 1.30 2003/04/19 00:02:29 tgl Exp $
  *
  *       Since the server static private key ($DataDir/server.key)
  *       will normally be stored unencrypted so that the database
 extern void ExitPostmaster(int);
 extern void postmaster_error(const char *fmt,...);
 
-int                    secure_initialize(void);
-void           secure_destroy(void);
-int                    secure_open_server(Port *);
-void           secure_close(Port *);
-ssize_t                secure_read(Port *, void *ptr, size_t len);
-ssize_t                secure_write(Port *, void *ptr, size_t len);
-
 #ifdef USE_SSL
 static DH  *load_dh_file(int keylength);
 static DH  *load_dh_buffer(const char *, size_t);
index cc06347e45d7599fa27b0475566d97971ec3d805..9a4f51b7786939355d45e123ecbe1df133df4a3c 100644 (file)
@@ -6,8 +6,8 @@
  * These routines handle the low-level details of communication between
  * frontend and backend.  They just shove data across the communication
  * channel, and are ignorant of the semantics of the data --- or would be,
- * except for major brain damage in the design of the COPY OUT protocol.
- * Unfortunately, COPY OUT is designed to commandeer the communication
+ * except for major brain damage in the design of the old COPY OUT protocol.
+ * Unfortunately, COPY OUT was designed to commandeer the communication
  * channel (it just transfers data without wrapping it into messages).
  * No other messages can be sent while COPY OUT is in progress; and if the
  * copy is aborted by an elog(ERROR), we need to close out the copy so that
@@ -29,7 +29,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *     $Id: pqcomm.c,v 1.149 2003/04/02 00:49:28 tgl Exp $
+ *     $Header: /cvsroot/pgsql/src/backend/libpq/pqcomm.c,v 1.150 2003/04/19 00:02:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
  * low-level I/O:
  *             pq_getbytes             - get a known number of bytes from connection
  *             pq_getstring    - get a null terminated string from connection
+ *             pq_getmessage   - get a message with length word from connection
  *             pq_getbyte              - get next byte from connection
  *             pq_peekbyte             - peek at next byte from connection
  *             pq_putbytes             - send bytes to connection (not flushed until pq_flush)
  *             pq_flush                - flush pending output
  *
- * message-level I/O (and COPY OUT cruft):
+ * message-level I/O (and old-style-COPY-OUT cruft):
  *             pq_putmessage   - send a normal message (suppressed in COPY OUT mode)
  *             pq_startcopyout - inform libpq that a COPY OUT transfer is beginning
  *             pq_endcopyout   - end a COPY OUT transfer
@@ -85,9 +86,6 @@
 #include "miscadmin.h"
 #include "storage/ipc.h"
 
-extern void secure_close(Port *);
-extern ssize_t secure_read(Port *, void *, size_t);
-extern ssize_t secure_write(Port *, const void *, size_t);
 
 static void pq_close(void);
 
@@ -562,8 +560,10 @@ pq_recvbuf(void)
                }
                if (r == 0)
                {
-                       /* as above, only write to postmaster log */
-                       elog(COMMERROR, "pq_recvbuf: unexpected EOF on client connection");
+                       /*
+                        * EOF detected.  We used to write a log message here, but it's
+                        * better to expect the ultimate caller to do that.
+                        */
                        return EOF;
                }
                /* r contains number of bytes read, so just incr length */
@@ -636,35 +636,29 @@ pq_getbytes(char *s, size_t len)
 /* --------------------------------
  *             pq_getstring    - get a null terminated string from connection
  *
- *             The return value is placed in an expansible StringInfo.
- *             Note that space allocation comes from the current memory context!
+ *             The return value is placed in an expansible StringInfo, which has
+ *             already been initialized by the caller.
  *
- *             If maxlen is not zero, it is an upper limit on the length of the
- *             string we are willing to accept.  We abort the connection (by
- *             returning EOF) if client tries to send more than that.  Note that
- *             since we test maxlen in the outer per-bufferload loop, the limit
- *             is fuzzy: we might accept up to PQ_BUFFER_SIZE more bytes than
- *             specified.      This is fine for the intended purpose, which is just
- *             to prevent DoS attacks from not-yet-authenticated clients.
- *
- *             NOTE: this routine does not do any character set conversion,
- *             even though it is presumably useful only for text, because
- *             no code in this module should depend on the encoding.
- *             See pq_getstr_bounded in pqformat.c for that.
+ *             This is used only for dealing with old-protocol clients.  The idea
+ *             is to produce a StringInfo that looks the same as we would get from
+ *             pq_getmessage() with a newer client; we will then process it with
+ *             pq_getmsgstring.  Therefore, no character set conversion is done here,
+ *             even though this is presumably useful only for text.
  *
  *             returns 0 if OK, EOF if trouble
  * --------------------------------
  */
 int
-pq_getstring(StringInfo s, int maxlen)
+pq_getstring(StringInfo s)
 {
        int                     i;
 
        /* Reset string to empty */
        s->len = 0;
        s->data[0] = '\0';
+       s->cursor = 0;
 
-       /* Read until we get the terminating '\0' or overrun maxlen */
+       /* Read until we get the terminating '\0' */
        for (;;)
        {
                while (PqRecvPointer >= PqRecvLength)
@@ -677,9 +671,9 @@ pq_getstring(StringInfo s, int maxlen)
                {
                        if (PqRecvBuffer[i] == '\0')
                        {
-                               /* does not copy the \0 */
+                               /* include the '\0' in the copy */
                                appendBinaryStringInfo(s, PqRecvBuffer + PqRecvPointer,
-                                                                          i - PqRecvPointer);
+                                                                          i - PqRecvPointer + 1);
                                PqRecvPointer = i + 1;  /* advance past \0 */
                                return 0;
                        }
@@ -689,11 +683,70 @@ pq_getstring(StringInfo s, int maxlen)
                appendBinaryStringInfo(s, PqRecvBuffer + PqRecvPointer,
                                                           PqRecvLength - PqRecvPointer);
                PqRecvPointer = PqRecvLength;
+       }
+}
+
+
+/* --------------------------------
+ *             pq_getmessage   - get a message with length word from connection
+ *
+ *             The return value is placed in an expansible StringInfo, which has
+ *             already been initialized by the caller.
+ *             Only the message body is placed in the StringInfo; the length word
+ *             is removed.  Also, s->cursor is initialized to zero for convenience
+ *             in scanning the message contents.
+ *
+ *             If maxlen is not zero, it is an upper limit on the length of the
+ *             message we are willing to accept.  We abort the connection (by
+ *             returning EOF) if client tries to send more than that.
+ *
+ *             returns 0 if OK, EOF if trouble
+ * --------------------------------
+ */
+int
+pq_getmessage(StringInfo s, int maxlen)
+{
+       int32           len;
+
+       /* Reset message buffer to empty */
+       s->len = 0;
+       s->data[0] = '\0';
+       s->cursor = 0;
 
-               /* If maxlen is specified, check for overlength input. */
-               if (maxlen > 0 && s->len > maxlen)
+       /* Read message length word */
+       if (pq_getbytes((char *) &len, 4) == EOF)
+       {
+               elog(COMMERROR, "unexpected EOF within message length word");
+               return EOF;
+       }
+
+       len = ntohl(len);
+       len -= 4;                                       /* discount length itself */
+
+       if (len < 0 ||
+               (maxlen > 0 && len > maxlen))
+       {
+               elog(COMMERROR, "invalid message length");
+               return EOF;
+       }
+
+       if (len > 0)
+       {
+               /* Allocate space for message */
+               enlargeStringInfo(s, len);
+
+               /* And grab the message */
+               if (pq_getbytes(s->data, len) == EOF)
+               {
+                       elog(COMMERROR, "incomplete client message");
                        return EOF;
+               }
+               s->len = len;
+               /* Place a trailing null per StringInfo convention */
+               s->data[len] = '\0';
        }
+
+       return 0;
 }
 
 
@@ -781,34 +834,10 @@ pq_flush(void)
 }
 
 
-/*
- * Return EOF if the connection has been broken, else 0.
- */
-int
-pq_eof(void)
-{
-       char            x;
-       int                     res;
-
-       res = recv(MyProcPort->sock, &x, 1, MSG_PEEK);
-
-       if (res < 0)
-       {
-               /* can log to postmaster log only */
-               elog(COMMERROR, "pq_eof: recv() failed: %m");
-               return EOF;
-       }
-       if (res == 0)
-               return EOF;
-       else
-               return 0;
-}
-
-
 /* --------------------------------
  * Message-level I/O routines begin here.
  *
- * These routines understand about COPY OUT protocol.
+ * These routines understand about the old-style COPY OUT protocol.
  * --------------------------------
  */
 
@@ -840,7 +869,8 @@ pq_putmessage(char msgtype, const char *s, size_t len)
 }
 
 /* --------------------------------
- *             pq_startcopyout - inform libpq that a COPY OUT transfer is beginning
+ *             pq_startcopyout - inform libpq that an old-style COPY OUT transfer
+ *                     is beginning
  * --------------------------------
  */
 void
index 50f17977d3eb9196c9bae2b754bc17385d4bbb93..80ca3190999c5f5eb30f1b4f810ae4dbb2dce9ec 100644 (file)
@@ -8,15 +8,17 @@
  * formatting/conversion routines that are needed to produce valid messages.
  * Note in particular the distinction between "raw data" and "text"; raw data
  * is message protocol characters and binary values that are not subject to
- * character set conversion, while text is converted by character encoding rules.
+ * character set conversion, while text is converted by character encoding
+ * rules.
  *
- * Incoming messages are read directly off the wire, as it were, but there
- * are still data-conversion tasks to be performed.
+ * Incoming messages are similarly read into a StringInfo buffer, via
+ * pq_getmessage, and then parsed and converted from that using the routines
+ * in this module.
  *
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *     $Id: pqformat.c,v 1.26 2003/04/02 00:49:28 tgl Exp $
+ *     $Header: /cvsroot/pgsql/src/backend/libpq/pqformat.c,v 1.27 2003/04/19 00:02:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
  * Special-case message output:
  *             pq_puttextmessage - generate a character set-converted message in one step
  *
- * Message input:
- *             pq_getint                       - get an integer from connection
- *             pq_getstr_bounded       - get a null terminated string from connection
- * pq_getstr_bounded performs character set conversion on the collected
- * string.  Use the raw pqcomm.c routines pq_getstring or pq_getbytes
- * to fetch data without conversion.
+ * Message parsing after input:
+ *             pq_getmsgbyte   - get a raw byte from a message buffer
+ *             pq_getmsgint    - get a binary integer from a message buffer
+ *             pq_getmsgbytes  - get raw data from a message buffer
+ *             pq_copymsgbytes - copy raw data from a message buffer
+ *             pq_getmsgstring - get a null-terminated text string (with conversion)
+ *             pq_getmsgend    - verify message fully consumed
  */
 
 #include "postgres.h"
@@ -206,16 +209,29 @@ pq_puttextmessage(char msgtype, const char *str)
        return pq_putmessage(msgtype, str, slen + 1);
 }
 
+
 /* --------------------------------
- *             pq_getint - get an integer from connection
- *
- *             returns 0 if OK, EOF if trouble
+ *             pq_getmsgbyte   - get a raw byte from a message buffer
  * --------------------------------
  */
 int
-pq_getint(int *result, int b)
+pq_getmsgbyte(StringInfo msg)
+{
+       if (msg->cursor >= msg->len)
+               elog(ERROR, "pq_getmsgbyte: no data left in message");
+       return (unsigned char) msg->data[msg->cursor++];
+}
+
+/* --------------------------------
+ *             pq_getmsgint    - get a binary integer from a message buffer
+ *
+ *             Values are treated as unsigned.
+ * --------------------------------
+ */
+unsigned int
+pq_getmsgint(StringInfo msg, int b)
 {
-       int                     status;
+       unsigned int result;
        unsigned char n8;
        uint16          n16;
        uint32          n32;
@@ -223,59 +239,93 @@ pq_getint(int *result, int b)
        switch (b)
        {
                case 1:
-                       status = pq_getbytes((char *) &n8, 1);
-                       *result = (int) n8;
+                       pq_copymsgbytes(msg, (char *) &n8, 1);
+                       result = n8;
                        break;
                case 2:
-                       status = pq_getbytes((char *) &n16, 2);
-                       *result = (int) (ntohs(n16));
+                       pq_copymsgbytes(msg, (char *) &n16, 2);
+                       result = ntohs(n16);
                        break;
                case 4:
-                       status = pq_getbytes((char *) &n32, 4);
-                       *result = (int) (ntohl(n32));
+                       pq_copymsgbytes(msg, (char *) &n32, 4);
+                       result = ntohl(n32);
                        break;
                default:
-
-                       /*
-                        * if we elog(ERROR) here, we will lose sync with the
-                        * frontend, so just complain to postmaster log instead...
-                        */
-                       elog(COMMERROR, "pq_getint: unsupported size %d", b);
-                       status = EOF;
-                       *result = 0;
+                       elog(ERROR, "pq_getmsgint: unsupported size %d", b);
+                       result = 0;                     /* keep compiler quiet */
                        break;
        }
-       return status;
+       return result;
 }
 
 /* --------------------------------
- *             pq_getstr_bounded - get a null terminated string from connection
+ *             pq_getmsgbytes  - get raw data from a message buffer
  *
- *             The return value is placed in an expansible StringInfo.
- *             Note that space allocation comes from the current memory context!
+ *             Returns a pointer directly into the message buffer; note this
+ *             may not have any particular alignment.
+ * --------------------------------
+ */
+const char *
+pq_getmsgbytes(StringInfo msg, int datalen)
+{
+       const char *result;
+
+       if (datalen > (msg->len - msg->cursor))
+               elog(ERROR, "pq_getmsgbytes: insufficient data left in message");
+       result = &msg->data[msg->cursor];
+       msg->cursor += datalen;
+       return result;
+}
+
+/* --------------------------------
+ *             pq_copymsgbytes - copy raw data from a message buffer
  *
- *             The maxlen parameter is interpreted as per pq_getstring.
+ *             Same as above, except data is copied to caller's buffer.
+ * --------------------------------
+ */
+void
+pq_copymsgbytes(StringInfo msg, char *buf, int datalen)
+{
+       if (datalen > (msg->len - msg->cursor))
+               elog(ERROR, "pq_copymsgbytes: insufficient data left in message");
+       memcpy(buf, &msg->data[msg->cursor], datalen);
+       msg->cursor += datalen;
+}
+
+/* --------------------------------
+ *             pq_getmsgstring - get a null-terminated text string (with conversion)
  *
- *             returns 0 if OK, EOF if trouble
+ *             May return a pointer directly into the message buffer, or a pointer
+ *             to a palloc'd conversion result.
  * --------------------------------
  */
-int
-pq_getstr_bounded(StringInfo s, int maxlen)
+const char *
+pq_getmsgstring(StringInfo msg)
 {
-       int                     result;
-       char       *p;
+       char   *str;
+       int             slen;
 
-       result = pq_getstring(s, maxlen);
+       str = &msg->data[msg->cursor];
+       /*
+        * It's safe to use strlen() here because a StringInfo is guaranteed
+        * to have a trailing null byte.  But check we found a null inside
+        * the message.
+        */
+       slen = strlen(str);
+       if (msg->cursor + slen >= msg->len)
+               elog(ERROR, "pq_getmsgstring: invalid string in message");
+       msg->cursor += slen + 1;
 
-       p = (char *) pg_client_to_server((unsigned char *) s->data, s->len);
-       if (p != s->data)                       /* actual conversion has been done? */
-       {
-               /* reset s to empty, and append the new string p */
-               s->len = 0;
-               s->data[0] = '\0';
-               appendBinaryStringInfo(s, p, strlen(p));
-               pfree(p);
-       }
+       return (const char *) pg_client_to_server((unsigned char *) str, slen);
+}
 
-       return result;
+/* --------------------------------
+ *             pq_getmsgend    - verify message fully consumed
+ * --------------------------------
+ */
+void
+pq_getmsgend(StringInfo msg)
+{
+       if (msg->cursor != msg->len)
+               elog(ERROR, "pq_getmsgend: invalid message format");
 }
index 499c4f25ca25250c71e48d53a363ecdf190de20d..d6beb0fc1a6262a133518fd009bc2ef407656bf2 100644 (file)
@@ -37,7 +37,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.312 2003/04/18 01:03:42 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.313 2003/04/19 00:02:29 tgl Exp $
  *
  * NOTES
  *
@@ -1118,7 +1118,7 @@ ProcessStartupPacket(Port *port, bool SSLdone)
 
        if (pq_getbytes((char *) &len, 4) == EOF)
        {
-               elog(LOG, "incomplete startup packet");
+               elog(COMMERROR, "incomplete startup packet");
                return STATUS_ERROR;
        }
 
@@ -1142,7 +1142,7 @@ ProcessStartupPacket(Port *port, bool SSLdone)
 
        if (pq_getbytes(buf, len) == EOF)
        {
-               elog(LOG, "incomplete startup packet");
+               elog(COMMERROR, "incomplete startup packet");
                return STATUS_ERROR;
        }
 
@@ -1189,6 +1189,16 @@ ProcessStartupPacket(Port *port, bool SSLdone)
        /* Could add additional special packet types here */
 
 
+       /*
+        * XXX temporary for 3.0 protocol development: we are using the minor
+        * number as a test-version number.  Insist it match exactly so people
+        * don't get burnt by using yesterday's libpq with today's server.
+        * XXX this must go away before release!!!
+        */
+       if (PG_PROTOCOL_MAJOR(proto) == 3 &&
+               PG_PROTOCOL_MINOR(proto) != PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST))
+               elog(FATAL, "Your development libpq is out of sync with the server");
+
        /* Check we can handle the protocol the frontend is using. */
 
        if (PG_PROTOCOL_MAJOR(proto) < PG_PROTOCOL_MAJOR(PG_PROTOCOL_EARLIEST) ||
@@ -1201,16 +1211,6 @@ ProcessStartupPacket(Port *port, bool SSLdone)
                         PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST),
                         PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST));
 
-       /*
-        * XXX temporary for 3.0 protocol development: we are using the minor
-        * number as a test-version number.  Insist it match exactly so people
-        * don't get burnt by using yesterday's libpq with today's server.
-        * XXX this must go away before release!!!
-        */
-       if (PG_PROTOCOL_MAJOR(proto) == 3 &&
-               PG_PROTOCOL_MINOR(proto) != PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST))
-               elog(FATAL, "Your development libpq is out of sync with the server");
-
        /*
         * Now fetch parameters out of startup packet and save them into the
         * Port structure.  All data structures attached to the Port struct
index ad9d2327717ed849f4ee1bdaa00c6602f0c513bb..07e4614e799be2c030a32320d49c07d2e19cc496 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.51 2003/03/27 16:51:29 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.52 2003/04/19 00:02:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -134,37 +134,6 @@ EndCommand(const char *commandTag, CommandDest dest)
        }
 }
 
-/*
- * These are necessary to sync communications between fe/be processes doing
- * COPY rel TO stdout
- *
- * or
- *
- * COPY rel FROM stdin
- *
- * NOTE: the message code letters are changed at protocol version 2.0
- * to eliminate possible confusion with data tuple messages.
- */
-void
-SendCopyBegin(void)
-{
-       if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
-               pq_putbytes("H", 1);    /* new way */
-       else
-               pq_putbytes("B", 1);    /* old way */
-}
-
-void
-ReceiveCopyBegin(void)
-{
-       if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
-               pq_putbytes("G", 1);    /* new way */
-       else
-               pq_putbytes("D", 1);    /* old way */
-       /* We *must* flush here to ensure FE knows it can send. */
-       pq_flush();
-}
-
 /* ----------------
  *             NullCommand - tell dest that an empty query string was recognized
  *
index 53b3a09ba2731efdfbe03e195a520434df67c277..eeddea6f6eb8cb82c6dc63f6e3754bbe20f4b983 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/tcop/fastpath.c,v 1.57 2003/01/09 18:00:23 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/tcop/fastpath.c,v 1.58 2003/04/19 00:02:29 tgl Exp $
  *
  * NOTES
  *       This cruft is the server side of PQfn.
  *       back to the frontend.  If the return returns by reference,
  *       send down only the data portion and set the return size appropriately.
  *
- *      OLD COMMENTS FOLLOW
- *
- *       The VAR_LENGTH_{ARGS,RESULT} stuff is limited to MAX_STRING_LENGTH
- *       (see src/backend/tmp/fastpath.h) for no obvious reason.  Since its
- *       primary use (for us) is for Inversion path names, it should probably
- *       be increased to 256 (MAXPATHLEN for Inversion, hidden in pg_type
- *       as well as utils/adt/filename.c).
- *
- *       Quoth PMA on 08/15/93:
- *
- *       This code has been almost completely rewritten with an eye to
- *       keeping it as compatible as possible with the previous (broken)
- *       implementation.
- *
- *       The previous implementation would assume (1) that any value of
- *       length <= 4 bytes was passed-by-value, and that any other value
- *       was a struct varlena (by-reference).  There was NO way to pass a
- *       fixed-length by-reference argument (like name) or a struct
- *       varlena of size <= 4 bytes.
- *
- *       The new implementation checks the catalogs to determine whether
- *       a value is by-value (type "0" is null-delimited character string,
- *       as it is for, e.g., the parser).      The only other item obtained
- *       from the catalogs is whether or not the value should be placed in
- *       a struct varlena or not.      Otherwise, the size given by the
- *       frontend is assumed to be correct (probably a bad decision, but
- *       we do strange things in the name of compatibility).
- *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
 #include "access/xact.h"
 #include "catalog/pg_proc.h"
 #include "libpq/libpq.h"
 #include "utils/tqual.h"
 
 
+/* ----------------
+ *             GetOldFunctionMessage
+ *
+ * In pre-3.0 protocol, there is no length word on the message, so we have
+ * to have code that understands the message layout to absorb the message
+ * into a buffer.  We want to do this before we start execution, so that
+ * we do not lose sync with the frontend if there's an error.
+ *
+ * The caller should already have initialized buf to empty.
+ * ----------------
+ */
+static int
+GetOldFunctionMessage(StringInfo buf)
+{
+       int32           ibuf;
+       int                     nargs;
+
+       /* Dummy string argument */
+       if (pq_getstring(buf))
+               return EOF;
+       /* Function OID */
+       if (pq_getbytes((char *) &ibuf, 4))
+               return EOF;
+       appendBinaryStringInfo(buf, (char *) &ibuf, 4);
+       /* Number of arguments */
+       if (pq_getbytes((char *) &ibuf, 4))
+               return EOF;
+       appendBinaryStringInfo(buf, (char *) &ibuf, 4);
+       nargs = ntohl(ibuf);
+       /* For each argument ... */
+       while (nargs-- > 0)
+       {
+               int                     argsize;
+
+               /* argsize */
+               if (pq_getbytes((char *) &ibuf, 4))
+                       return EOF;
+               appendBinaryStringInfo(buf, (char *) &ibuf, 4);
+               argsize = ntohl(ibuf);
+               if (argsize < 0)
+               {
+                       /* FATAL here since no hope of regaining message sync */
+                       elog(FATAL, "HandleFunctionRequest: bogus argsize %d",
+                                argsize);
+               }
+               /* and arg contents */
+               if (argsize > 0)
+               {
+                       /* Allocate space for arg */
+                       enlargeStringInfo(buf, argsize);
+                       /* And grab it */
+                       if (pq_getbytes(buf->data + buf->len, argsize))
+                               return EOF;
+                       buf->len += argsize;
+                       /* Place a trailing null per StringInfo convention */
+                       buf->data[buf->len] = '\0';
+               }
+       }
+       return 0;
+}
+
 /* ----------------
  *             SendFunctionResult
  *
@@ -205,6 +241,12 @@ fetch_fp_info(Oid func_id, struct fp_info * fip)
  * Server side of PQfn (fastpath function calls from the frontend).
  * This corresponds to the libpq protocol symbol "F".
  *
+ * INPUT:
+ *             In protocol version 3, postgres.c has already read the message body
+ *             and will pass it in msgBuf.
+ *             In old protocol, the passed msgBuf is empty and we must read the
+ *             message here.
+ * 
  * RETURNS:
  *             0 if successful completion, EOF if frontend connection lost.
  *
@@ -218,54 +260,44 @@ fetch_fp_info(Oid func_id, struct fp_info * fip)
  * control returns to PostgresMain.
  */
 int
-HandleFunctionRequest(void)
+HandleFunctionRequest(StringInfo msgBuf)
 {
        Oid                     fid;
-       int                     argsize;
        int                     nargs;
-       int                     tmp;
        AclResult       aclresult;
        FunctionCallInfoData fcinfo;
        Datum           retval;
        int                     i;
-       char       *p;
        struct fp_info my_fp;
        struct fp_info *fip;
 
        /*
-        * XXX FIXME: This protocol is misdesigned.
-        *
-        * We really do not want to elog() before having swallowed all of the
-        * frontend's fastpath message; otherwise we will lose sync with the
-        * input datastream.  What should happen is we absorb all of the input
-        * message per protocol syntax, and *then* do error checking
-        * (including lookup of the given function ID) and elog if
-        * appropriate.  Unfortunately, because we cannot even read the
-        * message properly without knowing whether the data types are
-        * pass-by-ref or pass-by-value, it's not all that easy to do :-(. The
-        * protocol should require the client to supply what it thinks is the
-        * typbyval and typlen value for each arg, so that we can read the
-        * data without having to do any lookups.  Then after we've read the
-        * message, we should do the lookups, verify agreement of the actual
-        * function arg types with what we received, and finally call the
-        * function.
-        *
-        * As things stand, not only will we lose sync for an invalid message
-        * (such as requested function OID doesn't exist), but we may lose
-        * sync for a perfectly valid message if we are in transaction-aborted
-        * state! This can happen because our database lookup attempts may
-        * fail entirely in abort state.
-        *
-        * Unfortunately I see no way to fix this without breaking a lot of
-        * existing clients.  Maybe do it as part of next protocol version
-        * change.
+        * Read message contents if not already done.
         */
+       if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
+       {
+               if (GetOldFunctionMessage(msgBuf))
+               {
+                       elog(COMMERROR, "unexpected EOF on client connection");
+                       return EOF;
+               }
+       }
 
-       if (pq_getint(&tmp, 4))         /* function oid */
-               return EOF;
-       fid = (Oid) tmp;
-       if (pq_getint(&nargs, 4))       /* # of arguments */
-               return EOF;
+       /*
+        * Now that we've eaten the input message, check to see if we actually
+        * want to do the function call or not.  It's now safe to elog(); we won't
+        * lose sync with the frontend.
+        */
+       if (IsAbortedTransactionBlockState())
+               elog(ERROR, "current transaction is aborted, "
+                        "queries ignored until end of transaction block");
+
+       /*
+        * Parse the buffer contents.
+        */
+       (void) pq_getmsgstring(msgBuf); /* dummy string */
+       fid = (Oid) pq_getmsgint(msgBuf, 4); /* function oid */
+       nargs = pq_getmsgint(msgBuf, 4);        /* # of arguments */
 
        /*
         * There used to be a lame attempt at caching lookup info here. Now we
@@ -274,11 +306,14 @@ HandleFunctionRequest(void)
        fip = &my_fp;
        fetch_fp_info(fid, fip);
 
+       /* Check permission to call function */
+       aclresult = pg_proc_aclcheck(fid, GetUserId(), ACL_EXECUTE);
+       if (aclresult != ACLCHECK_OK)
+               aclcheck_error(aclresult, get_func_name(fid));
+
        if (fip->flinfo.fn_nargs != nargs || nargs > FUNC_MAX_ARGS)
-       {
                elog(ERROR, "HandleFunctionRequest: actual arguments (%d) != registered arguments (%d)",
                         nargs, fip->flinfo.fn_nargs);
-       }
 
        MemSet(&fcinfo, 0, sizeof(fcinfo));
        fcinfo.flinfo = &fip->flinfo;
@@ -286,21 +321,21 @@ HandleFunctionRequest(void)
 
        /*
         * Copy supplied arguments into arg vector.  Note there is no way for
-        * frontend to specify a NULL argument --- more misdesign.
+        * frontend to specify a NULL argument --- this protocol is misdesigned.
         */
        for (i = 0; i < nargs; ++i)
        {
-               if (pq_getint(&argsize, 4))
-                       return EOF;
+               int                     argsize;
+               char       *p;
+
+               argsize = pq_getmsgint(msgBuf, 4);
                if (fip->argbyval[i])
                {                                               /* by-value */
                        if (argsize < 1 || argsize > 4)
                                elog(ERROR, "HandleFunctionRequest: bogus argsize %d",
                                         argsize);
                        /* XXX should we demand argsize == fip->arglen[i] ? */
-                       if (pq_getint(&tmp, argsize))
-                               return EOF;
-                       fcinfo.arg[i] = (Datum) tmp;
+                       fcinfo.arg[i] = (Datum) pq_getmsgint(msgBuf, argsize);
                }
                else
                {                                               /* by-reference ... */
@@ -309,13 +344,9 @@ HandleFunctionRequest(void)
                                if (argsize < 0)
                                        elog(ERROR, "HandleFunctionRequest: bogus argsize %d",
                                                 argsize);
-                               /* I suspect this +1 isn't really needed - tgl 5/2000 */
-                               p = palloc(argsize + VARHDRSZ + 1);             /* Added +1 to solve
-                                                                                                                * memory leak - Peter
-                                                                                                                * 98 Jan 6 */
+                               p = palloc(argsize + VARHDRSZ);
                                VARATT_SIZEP(p) = argsize + VARHDRSZ;
-                               if (pq_getbytes(VARDATA(p), argsize))
-                                       return EOF;
+                               pq_copymsgbytes(msgBuf, VARDATA(p), argsize);
                        }
                        else
                        {                                       /* ... fixed */
@@ -323,29 +354,12 @@ HandleFunctionRequest(void)
                                        elog(ERROR, "HandleFunctionRequest: bogus argsize %d, should be %d",
                                                 argsize, fip->arglen[i]);
                                p = palloc(argsize + 1);                /* +1 in case argsize is 0 */
-                               if (pq_getbytes(p, argsize))
-                                       return EOF;
+                               pq_copymsgbytes(msgBuf, p, argsize);
                        }
                        fcinfo.arg[i] = PointerGetDatum(p);
                }
        }
 
-       /*
-        * Now that we've eaten the input message, check to see if we actually
-        * want to do the function call or not.
-        *
-        * Currently, we report an error if in ABORT state, or return a dummy
-        * NULL response if fastpath support has been compiled out.
-        */
-       if (IsAbortedTransactionBlockState())
-               elog(ERROR, "current transaction is aborted, "
-                        "queries ignored until end of transaction block");
-
-       /* Check permission to call function */
-       aclresult = pg_proc_aclcheck(fid, GetUserId(), ACL_EXECUTE);
-       if (aclresult != ACLCHECK_OK)
-               aclcheck_error(aclresult, get_func_name(fid));
-
        /*
         * Set up a query snapshot in case function needs one.  (It is not safe
         * to do this if we are in transaction-abort state, so we have to postpone
index 1048d2fa1c6816386299b49df1ddb69ba56593d6..fcc6591f7c017fa12aa4f9b394280618b6c26a5b 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.321 2003/04/17 22:26:01 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.322 2003/04/19 00:02:29 tgl Exp $
  *
  * NOTES
  *       this is the "main" module of the postgres backend and
@@ -133,7 +133,9 @@ static const char *CreateCommandTag(Node *parsetree);
 
 /* ----------------
  *     InteractiveBackend() is called for user interactive connections
- *     the string entered by the user is placed in its parameter inBuf.
+ *
+ *     the string entered by the user is placed in its parameter inBuf,
+ *     and we act like a Q message was received.
  *
  *     EOF is returned if end-of-file input is seen; time to shut down.
  * ----------------
@@ -155,6 +157,7 @@ InteractiveBackend(StringInfo inBuf)
        /* Reset inBuf to empty */
        inBuf->len = 0;
        inBuf->data[0] = '\0';
+       inBuf->cursor = 0;
 
        for (;;)
        {
@@ -214,6 +217,9 @@ InteractiveBackend(StringInfo inBuf)
                break;
        }
 
+       /* Add '\0' to make it look the same as message case. */
+       appendStringInfoChar(inBuf, (char) '\0');
+
        /*
         * if the query echo flag was given, print the query..
         */
@@ -227,66 +233,79 @@ InteractiveBackend(StringInfo inBuf)
 /* ----------------
  *     SocketBackend()         Is called for frontend-backend connections
  *
- *     If the input is a query (case 'Q') then the string entered by
- *     the user is placed in its parameter inBuf.
- *
- *     If the input is a fastpath function call (case 'F') then
- *     the function call is processed in HandleFunctionRequest()
- *     (now called from PostgresMain()).
+ *     Returns the message type code, and loads message body data into inBuf.
  *
  *     EOF is returned if the connection is lost.
  * ----------------
  */
-
 static int
 SocketBackend(StringInfo inBuf)
 {
        int                     qtype;
 
        /*
-        * get input from the frontend
+        * Get message type code from the frontend.
         */
        qtype = pq_getbyte();
 
+       if (qtype == EOF)                       /* frontend disconnected */
+       {
+               elog(COMMERROR, "unexpected EOF on client connection");
+               return qtype;
+       }
+
+       /*
+        * Validate message type code before trying to read body; if we have
+        * lost sync, better to say "command unknown" than to run out of memory
+        * because we used garbage as a length word.
+        */
        switch (qtype)
        {
-               case EOF:
-                       /* frontend disconnected */
+               case 'Q':                               /* simple query */
+                       if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
+                       {
+                               /* old style without length word; convert */
+                               if (pq_getstring(inBuf))
+                               {
+                                       elog(COMMERROR, "unexpected EOF on client connection");
+                                       return EOF;
+                               }
+                       }
                        break;
 
-                       /*
-                        * 'Q': user entered a query
-                        */
-               case 'Q':
-                       if (pq_getstr(inBuf))
-                               return EOF;
+               case 'F':                               /* fastpath function call */
                        break;
 
-                       /*
-                        * 'F':  calling user/system functions
-                        */
-               case 'F':
-                       if (pq_getstr(inBuf))
-                               return EOF;             /* ignore "string" at start of F message */
+               case 'X':                               /* terminate */
                        break;
 
-                       /*
-                        * 'X':  frontend is exiting
-                        */
-               case 'X':
+               case 'd':                               /* copy data */
+               case 'c':                               /* copy done */
+               case 'f':                               /* copy fail */
+                       /* Accept but ignore these messages, per protocol spec */
                        break;
 
+               default:
                        /*
-                        * otherwise we got garbage from the frontend.
-                        *
-                        * XXX are we certain that we want to do an elog(FATAL) here?
-                        * -cim 1/24/90
+                        * Otherwise we got garbage from the frontend.  We treat this
+                        * as fatal because we have probably lost message boundary sync,
+                        * and there's no good way to recover.
                         */
-               default:
                        elog(FATAL, "Socket command type %c unknown", qtype);
                        break;
        }
 
+       /*
+        * In protocol version 3, all frontend messages have a length word
+        * next after the type code; we can read the message contents
+        * independently of the type.
+        */
+       if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
+       {
+               if (pq_getmessage(inBuf, 0))
+                       return EOF;                     /* suitable message already logged */
+       }
+
        return qtype;
 }
 
@@ -1220,19 +1239,17 @@ int
 PostgresMain(int argc, char *argv[], const char *username)
 {
        int                     flag;
-
        const char *DBName = NULL;
+       char       *potential_DataDir = NULL;
        bool            secure;
        int                     errs = 0;
        int                     debug_flag = 0;
        GucContext      ctx;
        GucSource       gucsource;
        char       *tmp;
-
        int                     firstchar;
        StringInfo      parser_input;
-
-       char       *potential_DataDir = NULL;
+       bool            send_rfq;
 
        /*
         * Catch standard options before doing much else.  This even works on
@@ -1815,7 +1832,7 @@ PostgresMain(int argc, char *argv[], const char *username)
        if (!IsUnderPostmaster)
        {
                puts("\nPOSTGRES backend interactive interface ");
-               puts("$Revision: 1.321 $ $Date: 2003/04/17 22:26:01 $\n");
+               puts("$Revision: 1.322 $ $Date: 2003/04/19 00:02:29 $\n");
        }
 
        /*
@@ -1902,6 +1919,8 @@ PostgresMain(int argc, char *argv[], const char *username)
 
        PG_SETMASK(&UnBlockSig);
 
+       send_rfq = true;                        /* initially, or after error */
+
        /*
         * Non-error queries loop here.
         */
@@ -1922,7 +1941,11 @@ PostgresMain(int argc, char *argv[], const char *username)
                 *
                 * Note: this includes fflush()'ing the last of the prior output.
                 */
-               ReadyForQuery(whereToSendOutput);
+               if (send_rfq)
+               {
+                       ReadyForQuery(whereToSendOutput);
+                       send_rfq = false;
+               }
 
                /* ----------
                 * Tell the statistics collector what we've collected
@@ -1986,20 +2009,36 @@ PostgresMain(int argc, char *argv[], const char *username)
                 */
                switch (firstchar)
                {
+                       case 'Q':                       /* simple query */
                                /*
-                                * 'F' indicates a fastpath call.
-                                */
-                       case 'F':
-                               /* ----------
-                                * Tell the collector what we're doing
-                                * ----------
+                                * Process the query string.
+                                *
+                                * Note: transaction command start/end is now done within
+                                * pg_exec_query_string(), not here.
                                 */
+                               if (log_statement_stats)
+                                       ResetUsage();
+
+                               pgstat_report_activity(parser_input->data);
+
+                               pg_exec_query_string(parser_input,
+                                                                        whereToSendOutput,
+                                                                        QueryContext);
+
+                               if (log_statement_stats)
+                                       ShowUsage("QUERY STATISTICS");
+
+                               send_rfq = true;
+                               break;
+
+                       case 'F':                       /* fastpath function call */
+                               /* Tell the collector what we're doing */
                                pgstat_report_activity("<FASTPATH> function call");
 
                                /* start an xact for this function invocation */
                                start_xact_command();
 
-                               if (HandleFunctionRequest() == EOF)
+                               if (HandleFunctionRequest(parser_input) == EOF)
                                {
                                        /* lost frontend connection during F message input */
 
@@ -2015,29 +2054,8 @@ PostgresMain(int argc, char *argv[], const char *username)
 
                                /* commit the function-invocation transaction */
                                finish_xact_command(false);
-                               break;
-
-                               /*
-                                * 'Q' indicates a user query
-                                */
-                       case 'Q':
-                               /*
-                                * otherwise, process the input string.
-                                *
-                                * Note: transaction command start/end is now done within
-                                * pg_exec_query_string(), not here.
-                                */
-                               if (log_statement_stats)
-                                       ResetUsage();
 
-                               pgstat_report_activity(parser_input->data);
-
-                               pg_exec_query_string(parser_input,
-                                                                        whereToSendOutput,
-                                                                        QueryContext);
-
-                               if (log_statement_stats)
-                                       ShowUsage("QUERY STATISTICS");
+                               send_rfq = true;
                                break;
 
                                /*
@@ -2064,8 +2082,18 @@ PostgresMain(int argc, char *argv[], const char *username)
                                 */
                                proc_exit(0);
 
+                       case 'd':                               /* copy data */
+                       case 'c':                               /* copy done */
+                       case 'f':                               /* copy fail */
+                               /*
+                                * Accept but ignore these messages, per protocol spec;
+                                * we probably got here because a COPY failed, and the
+                                * frontend is still sending data.
+                                */
+                               break;
+
                        default:
-                               elog(ERROR, "unknown frontend message was received");
+                               elog(FATAL, "Socket command type %c unknown", firstchar);
                }
 
 #ifdef MEMORY_CONTEXT_CHECKING
index 051ce239a7a76ab1e24fff9cb674842940ed4463..c49a73c0eb18cb7a19683cd7b764ee080a136baf 100644 (file)
@@ -10,7 +10,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: stringinfo.h,v 1.24 2002/06/20 20:29:49 momjian Exp $
+ * $Id: stringinfo.h,v 1.25 2003/04/19 00:02:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -27,6 +27,9 @@
  *                             string size (including the terminating '\0' char) that we can
  *                             currently store in 'data' without having to reallocate
  *                             more space.  We must always have maxlen > len.
+ *             cursor  is initialized to zero by makeStringInfo or initStringInfo,
+ *                             but is not otherwise touched by the stringinfo.c routines.
+ *                             Some routines use it to scan through a StringInfo.
  *-------------------------
  */
 typedef struct StringInfoData
@@ -34,6 +37,7 @@ typedef struct StringInfoData
        char       *data;
        int                     len;
        int                     maxlen;
+       int                     cursor;
 } StringInfoData;
 
 typedef StringInfoData *StringInfo;
@@ -111,4 +115,10 @@ extern void appendStringInfoChar(StringInfo str, char ch);
 extern void appendBinaryStringInfo(StringInfo str,
                                           const char *data, int datalen);
 
+/*------------------------
+ * enlargeStringInfo
+ * Make sure a StringInfo's buffer can hold at least 'needed' more bytes.
+ */
+extern void enlargeStringInfo(StringInfo str, int needed);
+
 #endif   /* STRINGINFO_H */
index 04248b5c95c95b4b61c9c6292191d7dad9708a7d..cbe0b646e74e3092fcfa6836c9e27f76d9ae262d 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: libpq.h,v 1.56 2003/01/25 05:19:47 tgl Exp $
+ * $Id: libpq.h,v 1.57 2003/04/19 00:02:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -52,14 +52,24 @@ extern void StreamClose(int sock);
 extern void TouchSocketFile(void);
 extern void pq_init(void);
 extern int     pq_getbytes(char *s, size_t len);
-extern int     pq_getstring(StringInfo s, int maxlen);
+extern int     pq_getstring(StringInfo s);
+extern int     pq_getmessage(StringInfo s, int maxlen);
 extern int     pq_getbyte(void);
 extern int     pq_peekbyte(void);
 extern int     pq_putbytes(const char *s, size_t len);
 extern int     pq_flush(void);
-extern int     pq_eof(void);
 extern int     pq_putmessage(char msgtype, const char *s, size_t len);
 extern void pq_startcopyout(void);
 extern void pq_endcopyout(bool errorAbort);
 
+/*
+ * prototypes for functions in be-secure.c
+ */
+extern int             secure_initialize(void);
+extern void            secure_destroy(void);
+extern int             secure_open_server(Port *port);
+extern void            secure_close(Port *port);
+extern ssize_t secure_read(Port *port, void *ptr, size_t len);
+extern ssize_t secure_write(Port *port, void *ptr, size_t len);
+
 #endif   /* LIBPQ_H */
index fabfb0cb2534d311788df194dab7a0f998d72dd9..61aa695e27217356e9fdc99a1647c1b3f216cda5 100644 (file)
@@ -9,7 +9,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pqcomm.h,v 1.76 2003/04/17 22:26:01 tgl Exp $
+ * $Id: pqcomm.h,v 1.77 2003/04/19 00:02:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -106,7 +106,7 @@ typedef union SockAddr
 /* The earliest and latest frontend/backend protocol version supported. */
 
 #define PG_PROTOCOL_EARLIEST   PG_PROTOCOL(1,0)
-#define PG_PROTOCOL_LATEST             PG_PROTOCOL(3,100) /* XXX temporary value */
+#define PG_PROTOCOL_LATEST             PG_PROTOCOL(3,101) /* XXX temporary value */
 
 typedef uint32 ProtocolVersion; /* FE/BE protocol version number */
 
index 829727c38f0dd756b966ceaad131881923845fdc..cb80ec2c2014dbee579aa3c4e223e0831eea8077 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pqformat.h,v 1.13 2002/09/04 23:31:35 tgl Exp $
+ * $Id: pqformat.h,v 1.14 2003/04/19 00:02:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -26,9 +26,11 @@ extern void pq_endmessage(StringInfo buf);
 
 extern int     pq_puttextmessage(char msgtype, const char *str);
 
-extern int     pq_getint(int *result, int b);
-extern int     pq_getstr_bounded(StringInfo s, int maxlen);
-
-#define pq_getstr(s)   pq_getstr_bounded(s, 0)
+extern int     pq_getmsgbyte(StringInfo msg);
+extern unsigned int pq_getmsgint(StringInfo msg, int b);
+extern const char *pq_getmsgbytes(StringInfo msg, int datalen);
+extern void pq_copymsgbytes(StringInfo msg, char *buf, int datalen);
+extern const char *pq_getmsgstring(StringInfo msg);
+extern void pq_getmsgend(StringInfo msg);
 
 #endif   /* PQFORMAT_H */
index bbf86ef4ca60b03a39af8fd1f0761da1e0309c9f..39063af6e16e1d251b76e41e7643f89e17d3e03b 100644 (file)
@@ -44,7 +44,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: dest.h,v 1.33 2003/03/27 16:51:29 momjian Exp $
+ * $Id: dest.h,v 1.34 2003/04/19 00:02:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -102,8 +102,6 @@ extern void EndCommand(const char *commandTag, CommandDest dest);
 
 /* Additional functions that go with destination management, more or less. */
 
-extern void SendCopyBegin(void);
-extern void ReceiveCopyBegin(void);
 extern void NullCommand(CommandDest dest);
 extern void ReadyForQuery(CommandDest dest);
 
index e9b961d8f0533f9eda938bbf47dd664ab69dda66..9e0f5e6bbb9a02c1044f6a9d938bd7ad925af465 100644 (file)
@@ -6,13 +6,15 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: fastpath.h,v 1.13 2002/06/20 20:29:52 momjian Exp $
+ * $Id: fastpath.h,v 1.14 2003/04/19 00:02:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef FASTPATH_H
 #define FASTPATH_H
 
-extern int     HandleFunctionRequest(void);
+#include "lib/stringinfo.h"
+
+extern int     HandleFunctionRequest(StringInfo msgBuf);
 
 #endif   /* FASTPATH_H */
index fca2d2e3035d8d665348cce360d37e0f8f9eb8de..10e2ee15f137b5fbdaf69983cb55e39dd6ec56f0 100644 (file)
@@ -10,7 +10,7 @@
  * exceed INITIAL_EXPBUFFER_SIZE (currently 256 bytes).
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-auth.c,v 1.75 2003/04/17 22:26:01 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-auth.c,v 1.76 2003/04/19 00:02:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -559,7 +559,7 @@ pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
                default:
                        return STATUS_ERROR;
        }
-       ret = pqPacketSend(conn, 0, crypt_pwd, strlen(crypt_pwd) + 1);
+       ret = pqPacketSend(conn, 'p', crypt_pwd, strlen(crypt_pwd) + 1);
        if (areq == AUTH_REQ_MD5)
                free(crypt_pwd);
        return ret;
index 9f5c8714a68caa9b90bb9f2181b31f1377da5a12..a322d8a73d132da84a37f51b043861ade6f7a9d5 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.232 2003/04/17 22:26:02 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.233 2003/04/19 00:02:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1820,11 +1820,11 @@ makeEmptyPGconn(void)
 #endif
 
        /*
-        * The output buffer size is set to 8K, which is the usual size of
-        * pipe buffers on Unix systems.  That way, when we are sending a
+        * We try to send at least 8K at a time, which is the usual size
+        * of pipe buffers on Unix systems.  That way, when we are sending a
         * large amount of data, we avoid incurring extra kernel context swaps
-        * for partial bufferloads.  Note that we currently don't ever enlarge
-        * the output buffer.
+        * for partial bufferloads.  The output buffer is initially made 16K
+        * in size, and we try to dump it after accumulating 8K.
         *
         * With the same goal of minimizing context swaps, the input buffer will
         * be enlarged anytime it has less than 8K free, so we initially
@@ -1832,7 +1832,7 @@ makeEmptyPGconn(void)
         */
        conn->inBufSize = 16 * 1024;
        conn->inBuffer = (char *) malloc(conn->inBufSize);
-       conn->outBufSize = 8 * 1024;
+       conn->outBufSize = 16 * 1024;
        conn->outBuffer = (char *) malloc(conn->outBufSize);
        conn->nonblocking = FALSE;
        initPQExpBuffer(&conn->errorMessage);
@@ -1918,11 +1918,10 @@ closePGconn(PGconn *conn)
        {
                /*
                 * Try to send "close connection" message to backend. Ignore any
-                * error. Note: this routine used to go to substantial lengths to
-                * avoid getting SIGPIPE'd if the connection were already closed.
-                * Now we rely on pqFlush to avoid the signal.
+                * error.
                 */
-               pqPutc('X', conn);
+               pqPutMsgStart('X', conn);
+               pqPutMsgEnd(conn);
                pqFlush(conn);
        }
 
@@ -2152,7 +2151,7 @@ cancel_errReturn:
 
 
 /*
- * pqPacketSend() -- send a single-packet message.
+ * pqPacketSend() -- convenience routine to send a message to server.
  *
  * pack_type: the single-byte message type code.  (Pass zero for startup
  * packets, which have no message type code.)
@@ -2167,19 +2166,18 @@ int
 pqPacketSend(PGconn *conn, char pack_type,
                         const void *buf, size_t buf_len)
 {
-       /* Send the message type. */
-       if (pack_type != 0)
-               if (pqPutc(pack_type, conn))
-                       return STATUS_ERROR;
-                       
-       /* Send the (self-inclusive) message length word. */
-       if (pqPutInt(buf_len + 4, 4, conn))
+       /* Start the message. */
+       if (pqPutMsgStart(pack_type, conn))
                return STATUS_ERROR;
 
        /* Send the message body. */
        if (pqPutnchar(buf, buf_len, conn))
                return STATUS_ERROR;
 
+       /* Finish the message. */
+       if (pqPutMsgEnd(conn))
+               return STATUS_ERROR;
+
        /* Flush to ensure backend gets it. */
        if (pqFlush(conn))
                return STATUS_ERROR;
@@ -2624,7 +2622,7 @@ build_startup_packet(const PGconn *conn, char *packet)
        packet_len += sizeof(ProtocolVersion);
 
        /* Add user name, database name, options */
-       if (conn->pguser)
+       if (conn->pguser && conn->pguser[0])
        {
                if (packet)
                        strcpy(packet + packet_len, "user");
@@ -2633,7 +2631,7 @@ build_startup_packet(const PGconn *conn, char *packet)
                        strcpy(packet + packet_len, conn->pguser);
                packet_len += strlen(conn->pguser) + 1;
        }
-       if (conn->dbName)
+       if (conn->dbName && conn->dbName[0])
        {
                if (packet)
                        strcpy(packet + packet_len, "database");
@@ -2642,7 +2640,7 @@ build_startup_packet(const PGconn *conn, char *packet)
                        strcpy(packet + packet_len, conn->dbName);
                packet_len += strlen(conn->dbName) + 1;
        }
-       if (conn->pgoptions)
+       if (conn->pgoptions && conn->pgoptions[0])
        {
                if (packet)
                        strcpy(packet + packet_len, "options");
index 9e86b3aa6726c0ae1e8bbe2e36bd88d849ae995a..487acff83dfcdca4be8a9a0c4eef8f92ff41f038 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.128 2003/03/25 02:44:36 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.129 2003/04/19 00:02:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -734,7 +734,6 @@ addTuple(PGresult *res, PGresAttValue * tup)
  * Returns: 1 if successfully submitted
  *                     0 if error (conn->errorMessage is set)
  */
-
 int
 PQsendQuery(PGconn *conn, const char *query)
 {
@@ -770,51 +769,24 @@ PQsendQuery(PGconn *conn, const char *query)
        conn->result = NULL;
        conn->curTuple = NULL;
 
-       /* send the query to the backend; */
+       /* construct the outgoing Query message */
+       if (pqPutMsgStart('Q', conn) < 0 ||
+               pqPuts(query, conn) < 0 ||
+               pqPutMsgEnd(conn) < 0)
+       {
+               handleSendFailure(conn);
+               return 0;
+       }
 
        /*
-        * in order to guarantee that we don't send a partial query where we
-        * would become out of sync with the backend and/or block during a
-        * non-blocking connection we must first flush the send buffer before
-        * sending more data
-        *
-        * an alternative is to implement 'queue reservations' where we are able
-        * to roll up a transaction (the 'Q' along with our query) and make
-        * sure we have enough space for it all in the send buffer.
+        * Give the data a push.  In nonblock mode, don't complain if we're
+        * unable to send it all; PQconsumeInput() will do any additional flushing
+        * needed.
         */
-       if (pqIsnonblocking(conn))
+       if (pqFlush(conn) < 0)
        {
-               /*
-                * the buffer must have emptied completely before we allow a new
-                * query to be buffered
-                */
-               if (pqFlush(conn))
-                       return 0;
-               /* 'Q' == queries */
-               /* XXX: if we fail here we really ought to not block */
-               if (pqPutc('Q', conn) != 0 || pqPuts(query, conn) != 0)
-               {
-                       handleSendFailure(conn);
-                       return 0;
-               }
-
-               /*
-                * give the data a push, ignore the return value as ConsumeInput()
-                * will do any additional flushing if needed
-                */
-               pqFlush(conn);
-       }
-       else
-       {
-               /*
-                * the frontend-backend protocol uses 'Q' to designate queries
-                */
-               if (pqPutc('Q', conn) != 0 || pqPuts(query, conn) != 0 ||
-                       pqFlush(conn) != 0)
-               {
-                       handleSendFailure(conn);
-                       return 0;
-               }
+               handleSendFailure(conn);
+               return 0;
        }
 
        /* OK, it's launched! */
@@ -830,7 +802,6 @@ PQsendQuery(PGconn *conn, const char *query)
  *
  * NOTE: this routine should only be called in PGASYNC_IDLE state.
  */
-
 static void
 handleSendFailure(PGconn *conn)
 {
@@ -854,13 +825,23 @@ handleSendFailure(PGconn *conn)
  * 0 return: some kind of trouble
  * 1 return: no problem
  */
-
 int
 PQconsumeInput(PGconn *conn)
 {
        if (!conn)
                return 0;
 
+       /*
+        * for non-blocking connections try to flush the send-queue,
+        * otherwise we may never get a response for something that may
+        * not have already been sent because it's in our write buffer!
+        */
+       if (pqIsnonblocking(conn))
+       {
+               if (pqFlush(conn) < 0)
+                       return 0;
+       }
+
        /*
         * Load more data, if available. We do this no matter what state we
         * are in, since we are probably getting called because the
@@ -868,16 +849,8 @@ PQconsumeInput(PGconn *conn)
         * we will NOT block waiting for more input.
         */
        if (pqReadData(conn) < 0)
-       {
-               /*
-                * for non-blocking connections try to flush the send-queue
-                * otherwise we may never get a responce for something that may
-                * not have already been sent because it's in our write buffer!
-                */
-               if (pqIsnonblocking(conn))
-                       (void) pqFlush(conn);
                return 0;
-       }
+
        /* Parsing of the data waits till later. */
        return 1;
 }
@@ -1733,14 +1706,13 @@ PQgetlineAsync(PGconn *conn, char *buffer, int bufsize)
  * PQputline -- sends a string to the backend.
  * Returns 0 if OK, EOF if not.
  *
- * Chiefly here so that applications can use "COPY <rel> from stdin".
+ * This exists to support "COPY <rel> from stdin".  The backend will ignore
+ * the string if not doing COPY.
  */
 int
 PQputline(PGconn *conn, const char *s)
 {
-       if (!conn || conn->sock < 0)
-               return EOF;
-       return pqPutnchar(s, strlen(s), conn);
+       return PQputnbytes(conn, s, strlen(s));
 }
 
 /*
@@ -1752,7 +1724,14 @@ PQputnbytes(PGconn *conn, const char *buffer, int nbytes)
 {
        if (!conn || conn->sock < 0)
                return EOF;
-       return pqPutnchar(buffer, nbytes, conn);
+       if (nbytes > 0)
+       {
+               if (pqPutMsgStart('d', conn) < 0 ||
+                       pqPutnchar(buffer, nbytes, conn) < 0 ||
+                       pqPutMsgEnd(conn) < 0)
+                       return EOF;
+       }
+       return 0;
 }
 
 /*
@@ -1780,6 +1759,14 @@ PQendcopy(PGconn *conn)
                return 1;
        }
 
+       /* Send the CopyDone message if needed */
+       if (conn->asyncStatus == PGASYNC_COPY_IN)
+       {
+               if (pqPutMsgStart('c', conn) < 0 ||
+                       pqPutMsgEnd(conn) < 0)
+                       return 1;
+       }
+
        /*
         * make sure no data is waiting to be sent, abort if we are
         * non-blocking and the flush fails
@@ -1884,9 +1871,10 @@ PQfn(PGconn *conn,
                return NULL;
        }
 
-       if (pqPuts("F ", conn) != 0 ||          /* function */
-               pqPutInt(fnid, 4, conn) != 0 || /* function id */
-               pqPutInt(nargs, 4, conn) != 0)  /* # of args */
+       if (pqPutMsgStart('F', conn) < 0 ||     /* function call msg */
+               pqPuts("", conn) < 0 || /* useless string */
+               pqPutInt(fnid, 4, conn) < 0 || /* function id */
+               pqPutInt(nargs, 4, conn) < 0)   /* # of args */
        {
                handleSendFailure(conn);
                return NULL;
@@ -1917,7 +1905,9 @@ PQfn(PGconn *conn,
                        }
                }
        }
-       if (pqFlush(conn))
+
+       if (pqPutMsgEnd(conn) < 0 ||
+               pqFlush(conn))
        {
                handleSendFailure(conn);
                return NULL;
@@ -2409,7 +2399,6 @@ PQgetisnull(const PGresult *res, int tup_num, int field_num)
 int
 PQsetnonblocking(PGconn *conn, int arg)
 {
-
        arg = (arg == TRUE) ? 1 : 0;
        /* early out if the socket is already in the state requested */
        if (arg == conn->nonblocking)
@@ -2437,7 +2426,6 @@ PQsetnonblocking(PGconn *conn, int arg)
 int
 PQisnonblocking(const PGconn *conn)
 {
-
        return (pqIsnonblocking(conn));
 }
 
@@ -2445,18 +2433,9 @@ PQisnonblocking(const PGconn *conn)
 int
 PQflush(PGconn *conn)
 {
-
        return (pqFlush(conn));
 }
 
-/* try to force data out, really only useful for non-blocking users.
- * This implementation actually works for non-blocking connections */
-int
-PQsendSome(PGconn *conn)
-{
-       return pqSendSome(conn);
-}
-
 /*
  * PQfreeNotify - free's the memory associated with a PGnotify
  *
@@ -2473,5 +2452,3 @@ PQfreeNotify(PGnotify *notify)
 {
        PQfreemem(notify);
 }
-
-
index 0f971343ccce35dc34f81d4a54e564f255c6e11e..dfc46fdf5987ce9f031cae23bffc7cf62b143b69 100644 (file)
  * will cause repeat printouts.
  *
  * We must speak the same transmitted data representations as the backend
- * routines.  Note that this module supports *only* network byte order
- * for transmitted ints, whereas the backend modules (as of this writing)
- * still handle either network or little-endian byte order.
+ * routines.
+ *
  *
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.88 2003/04/02 00:49:28 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.89 2003/04/19 00:02:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #define DONOTICE(conn,message) \
        ((*(conn)->noticeHook) ((conn)->noticeArg, (message)))
 
+static int     pqPutMsgBytes(const void *buf, size_t len, PGconn *conn);
+static int     pqSendSome(PGconn *conn, int len);
 static int     pqSocketCheck(PGconn *conn, int forRead, int forWrite,
                                                  time_t end_time);
 static int     pqSocketPoll(int sock, int forRead, int forWrite, time_t end_time);
-static int     pqPutBytes(const char *s, size_t nbytes, PGconn *conn);
 
 
 /*
- * pqGetc:
- *     get a character from the connection
+ * pqGetc: get 1 character from the connection
  *
  *     All these routines return 0 on success, EOF on error.
  *     Note that for the Get routines, EOF only means there is not enough
@@ -93,12 +91,12 @@ pqGetc(char *result, PGconn *conn)
 
 
 /*
- * write 1 char to the connection
+ * pqPutc: write 1 char to the current message
  */
 int
 pqPutc(char c, PGconn *conn)
 {
-       if (pqPutBytes(&c, 1, conn) == EOF)
+       if (pqPutMsgBytes(&c, 1, conn))
                return EOF;
 
        if (conn->Pfdebug)
@@ -108,93 +106,6 @@ pqPutc(char c, PGconn *conn)
 }
 
 
-/*
- * pqPutBytes: local routine to write N bytes to the connection,
- * with buffering
- */
-static int
-pqPutBytes(const char *s, size_t nbytes, PGconn *conn)
-{
-       /*
-        * Strategy to handle blocking and non-blocking connections: Fill the
-        * output buffer and flush it repeatedly until either all data has
-        * been sent or is at least queued in the buffer.
-        *
-        * For non-blocking connections, grow the buffer if not all data fits
-        * into it and the buffer can't be sent because the socket would
-        * block.
-        */
-
-       while (nbytes)
-       {
-               size_t          avail,
-                                       remaining;
-
-               /* fill the output buffer */
-               avail = Max(conn->outBufSize - conn->outCount, 0);
-               remaining = Min(avail, nbytes);
-               memcpy(conn->outBuffer + conn->outCount, s, remaining);
-               conn->outCount += remaining;
-               s += remaining;
-               nbytes -= remaining;
-
-               /*
-                * if the data didn't fit completely into the buffer, try to flush
-                * the buffer
-                */
-               if (nbytes)
-               {
-                       int                     send_result = pqSendSome(conn);
-
-                       /* if there were errors, report them */
-                       if (send_result < 0)
-                               return EOF;
-
-                       /*
-                        * if not all data could be sent, increase the output buffer,
-                        * put the rest of s into it and return successfully. This
-                        * case will only happen in a non-blocking connection
-                        */
-                       if (send_result > 0)
-                       {
-                               /*
-                                * try to grow the buffer. FIXME: The new size could be
-                                * chosen more intelligently.
-                                */
-                               size_t          buflen = (size_t) conn->outCount + nbytes;
-
-                               if (buflen > (size_t) conn->outBufSize)
-                               {
-                                       char       *newbuf = realloc(conn->outBuffer, buflen);
-
-                                       if (!newbuf)
-                                       {
-                                               /* realloc failed. Probably out of memory */
-                                               printfPQExpBuffer(&conn->errorMessage,
-                                                  "cannot allocate memory for output buffer\n");
-                                               return EOF;
-                                       }
-                                       conn->outBuffer = newbuf;
-                                       conn->outBufSize = buflen;
-                               }
-                               /* put the data into it */
-                               memcpy(conn->outBuffer + conn->outCount, s, nbytes);
-                               conn->outCount += nbytes;
-
-                               /* report success. */
-                               return 0;
-                       }
-               }
-
-               /*
-                * pqSendSome was able to send all data. Continue with the next
-                * chunk of s.
-                */
-       }                                                       /* while */
-
-       return 0;
-}
-
 /*
  * pqGets:
  * get a null-terminated string from the connection,
@@ -232,14 +143,17 @@ pqGets(PQExpBuffer buf, PGconn *conn)
 }
 
 
+/*
+ * pqPuts: write a null-terminated string to the current message
+ */
 int
 pqPuts(const char *s, PGconn *conn)
 {
-       if (pqPutBytes(s, strlen(s) + 1, conn))
+       if (pqPutMsgBytes(s, strlen(s) + 1, conn))
                return EOF;
 
        if (conn->Pfdebug)
-               fprintf(conn->Pfdebug, "To backend> %s\n", s);
+               fprintf(conn->Pfdebug, "To backend> '%s'\n", s);
 
        return 0;
 }
@@ -267,12 +181,12 @@ pqGetnchar(char *s, size_t len, PGconn *conn)
 
 /*
  * pqPutnchar:
- *     send a string of exactly len bytes, no null termination needed
+ *     write exactly len bytes to the current message
  */
 int
 pqPutnchar(const char *s, size_t len, PGconn *conn)
 {
-       if (pqPutBytes(s, len, conn))
+       if (pqPutMsgBytes(s, len, conn))
                return EOF;
 
        if (conn->Pfdebug)
@@ -325,7 +239,7 @@ pqGetInt(int *result, size_t bytes, PGconn *conn)
 
 /*
  * pgPutInt
- * send an integer of 2 or 4 bytes, converting from host byte order
+ * write an integer of 2 or 4 bytes, converting from host byte order
  * to network byte order.
  */
 int
@@ -339,12 +253,12 @@ pqPutInt(int value, size_t bytes, PGconn *conn)
        {
                case 2:
                        tmp2 = htons((uint16) value);
-                       if (pqPutBytes((const char *) &tmp2, 2, conn))
+                       if (pqPutMsgBytes((const char *) &tmp2, 2, conn))
                                return EOF;
                        break;
                case 4:
                        tmp4 = htonl((uint32) value);
-                       if (pqPutBytes((const char *) &tmp4, 4, conn))
+                       if (pqPutMsgBytes((const char *) &tmp4, 4, conn))
                                return EOF;
                        break;
                default:
@@ -362,24 +276,162 @@ pqPutInt(int value, size_t bytes, PGconn *conn)
 }
 
 /*
- * pqReadReady: is select() saying the file is ready to read?
- * JAB: -or- if SSL is enabled and used, is it buffering bytes?
- * Returns -1 on failure, 0 if not ready, 1 if ready.
+ * Make sure conn's output buffer can hold bytes_needed bytes (caller must
+ * include existing outCount into the value!)
+ *
+ * Returns 0 on success, EOF on error
+ */
+static int
+checkOutBufferSpace(int bytes_needed, PGconn *conn)
+{
+       int                     newsize = conn->outBufSize;
+       char       *newbuf;
+
+       if (bytes_needed <= newsize)
+               return 0;
+       /*
+        * If we need to enlarge the buffer, we first try to double it in size;
+        * if that doesn't work, enlarge in multiples of 8K.  This avoids
+        * thrashing the malloc pool by repeated small enlargements.
+        *
+        * Note: tests for newsize > 0 are to catch integer overflow.
+        */
+       do {
+               newsize *= 2;
+       } while (bytes_needed > newsize && newsize > 0);
+
+       if (bytes_needed <= newsize)
+       {
+               newbuf = realloc(conn->outBuffer, newsize);
+               if (newbuf)
+               {
+                       /* realloc succeeded */
+                       conn->outBuffer = newbuf;
+                       conn->outBufSize = newsize;
+                       return 0;
+               }
+       }
+
+       newsize = conn->outBufSize;
+       do {
+               newsize += 8192;
+       } while (bytes_needed > newsize && newsize > 0);
+
+       if (bytes_needed <= newsize)
+       {
+               newbuf = realloc(conn->outBuffer, newsize);
+               if (newbuf)
+               {
+                       /* realloc succeeded */
+                       conn->outBuffer = newbuf;
+                       conn->outBufSize = newsize;
+                       return 0;
+               }
+       }
+
+       /* realloc failed. Probably out of memory */
+       printfPQExpBuffer(&conn->errorMessage,
+                                         "cannot allocate memory for output buffer\n");
+       return EOF;
+}
+
+/*
+ * pqPutMsgStart: begin construction of a message to the server
+ *
+ * msg_type is the message type byte, or 0 for a message without type byte
+ * (only startup messages have no type byte)
+ *
+ * Returns 0 on success, EOF on error
+ *
+ * The idea here is that we construct the message in conn->outBuffer,
+ * beginning just past any data already in outBuffer (ie, at
+ * outBuffer+outCount).  We enlarge the buffer as needed to hold the message.
+ * When the message is complete, we fill in the length word and then advance
+ * outCount past the message, making it eligible to send.  The state
+ * variable conn->outMsgStart points to the incomplete message's length word
+ * (it is either outCount or outCount+1 depending on whether there is a
+ * type byte).  The state variable conn->outMsgEnd is the end of the data
+ * collected so far.
  */
 int
-pqReadReady(PGconn *conn)
+pqPutMsgStart(char msg_type, PGconn *conn)
 {
-       return pqSocketCheck(conn, 1, 0, (time_t) 0);
+       int                     lenPos;
+
+       /* where the message length word will go */
+       if (msg_type)
+               lenPos = conn->outCount + 1;
+       else
+               lenPos = conn->outCount;
+       /* make sure there is room for it */
+       if (checkOutBufferSpace(lenPos + 4, conn))
+               return EOF;
+       /* okay, save the message type byte if any */
+       if (msg_type)
+               conn->outBuffer[conn->outCount] = msg_type;
+       /* set up the message pointers */
+       conn->outMsgStart = lenPos;
+       conn->outMsgEnd = lenPos + 4;
+       /* length word will be filled in by pqPutMsgEnd */
+
+       if (conn->Pfdebug)
+               fprintf(conn->Pfdebug, "To backend> Msg %c\n",
+                               msg_type ? msg_type : ' ');
+
+       return 0;
 }
 
 /*
- * pqWriteReady: is select() saying the file is ready to write?
- * Returns -1 on failure, 0 if not ready, 1 if ready.
+ * pqPutMsgBytes: add bytes to a partially-constructed message
+ *
+ * Returns 0 on success, EOF on error
+ */
+static int
+pqPutMsgBytes(const void *buf, size_t len, PGconn *conn)
+{
+       /* make sure there is room for it */
+       if (checkOutBufferSpace(conn->outMsgEnd + len, conn))
+               return EOF;
+       /* okay, save the data */
+       memcpy(conn->outBuffer + conn->outMsgEnd, buf, len);
+       conn->outMsgEnd += len;
+       /* no Pfdebug call here, caller should do it */
+       return 0;
+}
+
+/*
+ * pqPutMsgEnd: finish constructing a message and possibly send it
+ *
+ * Returns 0 on success, EOF on error
+ *
+ * We don't actually send anything here unless we've accumulated at least
+ * 8K worth of data (the typical size of a pipe buffer on Unix systems).
+ * This avoids sending small partial packets.  The caller must use pqFlush
+ * when it's important to flush all the data out to the server.
  */
 int
-pqWriteReady(PGconn *conn)
+pqPutMsgEnd(PGconn *conn)
 {
-       return pqSocketCheck(conn, 0, 1, (time_t) 0);
+       uint32          msgLen = conn->outMsgEnd - conn->outMsgStart;
+
+       if (conn->Pfdebug)
+               fprintf(conn->Pfdebug, "To backend> Msg complete, length %u\n",
+                               msgLen);
+
+       msgLen = htonl(msgLen);
+       memcpy(conn->outBuffer + conn->outMsgStart, &msgLen, 4);
+       conn->outCount = conn->outMsgEnd;
+
+       if (conn->outCount >= 8192)
+       {
+               int             toSend = conn->outCount - (conn->outCount % 8192);
+
+               if (pqSendSome(conn, toSend) < 0)
+                       return EOF;
+               /* in nonblock mode, don't complain if unable to send it all */
+       }
+
+       return 0;
 }
 
 /* ----------
@@ -580,16 +632,20 @@ definitelyFailed:
 }
 
 /*
- * pqSendSome: send any data waiting in the output buffer.
+ * pqSendSome: send data waiting in the output buffer.
+ *
+ * len is how much to try to send (typically equal to outCount, but may
+ * be less).
  *
- * Return 0 on sucess, -1 on failure and 1 when data remains because the
- * socket would block and the connection is non-blocking.
+ * Return 0 on success, -1 on failure and 1 when not all data could be sent
+ * because the socket would block and the connection is non-blocking.
  */
-int
-pqSendSome(PGconn *conn)
+static int
+pqSendSome(PGconn *conn, int len)
 {
        char       *ptr = conn->outBuffer;
-       int                     len = conn->outCount;
+       int                     remaining = conn->outCount;
+       int                     result = 0;
 
        if (conn->sock < 0)
        {
@@ -598,13 +654,6 @@ pqSendSome(PGconn *conn)
                return -1;
        }
 
-       /*
-        * don't try to send zero data, allows us to use this function without
-        * too much worry about overhead
-        */
-       if (len == 0)
-               return (0);
-
        /* while there's still data to send */
        while (len > 0)
        {
@@ -648,8 +697,9 @@ pqSendSome(PGconn *conn)
                                         * (typically, a NOTICE message from the backend
                                         * telling us it's committing hara-kiri...).  Leave
                                         * the socket open until pqReadData finds no more data
-                                        * can be read.
+                                        * can be read.  But abandon attempt to send data.
                                         */
+                                       conn->outCount = 0;
                                        return -1;
 
                                default:
@@ -657,6 +707,7 @@ pqSendSome(PGconn *conn)
                                        libpq_gettext("could not send data to server: %s\n"),
                                                                          SOCK_STRERROR(SOCK_ERRNO));
                                        /* We don't assume it's a fatal error... */
+                                       conn->outCount = 0;
                                        return -1;
                        }
                }
@@ -664,6 +715,7 @@ pqSendSome(PGconn *conn)
                {
                        ptr += sent;
                        len -= sent;
+                       remaining -= sent;
                }
 
                if (len > 0)
@@ -681,46 +733,49 @@ pqSendSome(PGconn *conn)
 #endif
                                if (pqIsnonblocking(conn))
                                {
-                                       /* shift the contents of the buffer */
-                                       memmove(conn->outBuffer, ptr, len);
-                                       conn->outCount = len;
-                                       return 1;
+                                       result = 1;
+                                       break;
                                }
 #ifdef USE_SSL
                        }
 #endif
 
                        if (pqWait(FALSE, TRUE, conn))
-                               return -1;
+                       {
+                               result = -1;
+                               break;
+                       }
                }
        }
 
-       conn->outCount = 0;
+       /* shift the remaining contents of the buffer */
+       if (remaining > 0)
+               memmove(conn->outBuffer, ptr, remaining);
+       conn->outCount = remaining;
 
-       if (conn->Pfdebug)
-               fflush(conn->Pfdebug);
-
-       return 0;
+       return result;
 }
 
 
-
 /*
  * pqFlush: send any data waiting in the output buffer
  *
- * Implemented in terms of pqSendSome to recreate the old behavior which
- * returned 0 if all data was sent or EOF. EOF was sent regardless of
- * whether an error occurred or not all data was sent on a non-blocking
- * socket.
+ * Return 0 on success, -1 on failure and 1 when not all data could be sent
+ * because the socket would block and the connection is non-blocking.
  */
 int
 pqFlush(PGconn *conn)
 {
-       if (pqSendSome(conn))
-               return EOF;
+       if (conn->Pfdebug)
+               fflush(conn->Pfdebug);
+
+       if (conn->outCount > 0)
+               return pqSendSome(conn, conn->outCount);
+
        return 0;
 }
 
+
 /*
  * pqWait: wait until we can read or write the connection socket
  *
@@ -766,10 +821,31 @@ pqWaitTimed(int forRead, int forWrite, PGconn *conn, time_t finish_time)
        return 0;
 }
 
+/*
+ * pqReadReady: is select() saying the file is ready to read?
+ * Returns -1 on failure, 0 if not ready, 1 if ready.
+ */
+int
+pqReadReady(PGconn *conn)
+{
+       return pqSocketCheck(conn, 1, 0, (time_t) 0);
+}
+
+/*
+ * pqWriteReady: is select() saying the file is ready to write?
+ * Returns -1 on failure, 0 if not ready, 1 if ready.
+ */
+int
+pqWriteReady(PGconn *conn)
+{
+       return pqSocketCheck(conn, 0, 1, (time_t) 0);
+}
+
 /*
  * Checks a socket, using poll or select, for data to be read, written,
  * or both.  Returns >0 if one or more conditions are met, 0 if it timed
  * out, -1 if an error occurred.
+ *
  * If SSL is in use, the SSL buffer is checked prior to checking the socket
  * for read data directly.
  */
@@ -787,8 +863,8 @@ pqSocketCheck(PGconn *conn, int forRead, int forWrite, time_t end_time)
                return -1;
        }
 
-/* JAB: Check for SSL library buffering read bytes */
 #ifdef USE_SSL
+       /* Check for SSL library buffering read bytes */
        if (forRead && conn->ssl && SSL_pending(conn->ssl) > 0)
        {
                /* short-circuit the select */
@@ -819,6 +895,7 @@ pqSocketCheck(PGconn *conn, int forRead, int forWrite, time_t end_time)
  * If neither forRead nor forWrite are set, immediately return a timeout
  * condition (without waiting).  Return >0 if condition is met, 0
  * if a timeout occurred, -1 if an error or interrupt occurred.
+ *
  * Timeout is infinite if end_time is -1.  Timeout is immediate (no blocking)
  * if end_time is 0 (or indeed, any time before now).
  */
@@ -830,16 +907,17 @@ pqSocketPoll(int sock, int forRead, int forWrite, time_t end_time)
        struct pollfd input_fd;
        int           timeout_ms;
 
+       if (!forRead && !forWrite)
+               return 0;
+
        input_fd.fd      = sock;
-       input_fd.events  = 0;
+       input_fd.events  = POLLERR;
        input_fd.revents = 0;
 
        if (forRead)
                input_fd.events |= POLLIN;
        if (forWrite)
                input_fd.events |= POLLOUT;
-       if (!input_fd.events)
-               return 0;
 
        /* Compute appropriate timeout interval */
        if (end_time == ((time_t) -1))
index d32b6fdeea37f7123ac3f8d1e2b97107bdf86189..a86b63eadae1815c9535c9fdc19a5bd9ddc03984 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: libpq-fe.h,v 1.91 2003/03/25 02:44:36 momjian Exp $
+ * $Id: libpq-fe.h,v 1.92 2003/04/19 00:02:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -284,7 +284,6 @@ extern int  PQisnonblocking(const PGconn *conn);
 
 /* Force the write buffer to be written (or at least try) */
 extern int     PQflush(PGconn *conn);
-extern int     PQsendSome(PGconn *conn);
 
 /*
  * "Fast path" interface --- not really recommended for application
index 43c3bd11c56864e088f237ca3671301fe71da499..8671922547d1cfceb518ccb61ae777831977bdf8 100644 (file)
@@ -12,7 +12,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: libpq-int.h,v 1.61 2003/04/17 22:26:02 tgl Exp $
+ * $Id: libpq-int.h,v 1.62 2003/04/19 00:02:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -56,7 +56,7 @@ typedef int ssize_t;                  /* ssize_t doesn't exist in VC (atleast
  * pqcomm.h describe what the backend knows, not what libpq knows.
  */
 
-#define PG_PROTOCOL_LIBPQ      PG_PROTOCOL(3,100) /* XXX temporary value */
+#define PG_PROTOCOL_LIBPQ      PG_PROTOCOL(3,101) /* XXX temporary value */
 
 /*
  * POSTGRES backend dependent Constants.
@@ -266,6 +266,10 @@ struct pg_conn
        int                     outBufSize;             /* allocated size of buffer */
        int                     outCount;               /* number of chars waiting in buffer */
 
+       /* State for constructing messages in outBuffer */
+       int                     outMsgStart;    /* offset to msg start (length word) */
+       int                     outMsgEnd;              /* offset to msg end (so far) */
+
        /* Status for asynchronous result construction */
        PGresult   *result;                     /* result being constructed */
        PGresAttValue *curTuple;        /* tuple currently being read */
@@ -334,9 +338,10 @@ extern int pqGetnchar(char *s, size_t len, PGconn *conn);
 extern int     pqPutnchar(const char *s, size_t len, PGconn *conn);
 extern int     pqGetInt(int *result, size_t bytes, PGconn *conn);
 extern int     pqPutInt(int value, size_t bytes, PGconn *conn);
+extern int     pqPutMsgStart(char msg_type, PGconn *conn);
+extern int     pqPutMsgEnd(PGconn *conn);
 extern int     pqReadData(PGconn *conn);
 extern int     pqFlush(PGconn *conn);
-extern int     pqSendSome(PGconn *conn);
 extern int     pqWait(int forRead, int forWrite, PGconn *conn);
 extern int     pqWaitTimed(int forRead, int forWrite, PGconn *conn, 
                                                time_t finish_time);