From bd8d4417757b1f3edd9ef36897cf47fe96b6e37a Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Sat, 19 Apr 2003 00:02:30 +0000
Subject: [PATCH] Second round of FE/BE protocol changes.  Frontend->backend
 messages now have length counts, and COPY IN data is packetized into
 messages.

---
 doc/src/sgml/libpq.sgml             |   9 +-
 doc/src/sgml/protocol.sgml          | 174 ++++-----
 src/backend/commands/copy.c         | 560 ++++++++++++++++++----------
 src/backend/lib/stringinfo.c        |  86 +++--
 src/backend/libpq/auth.c            | 113 ++++--
 src/backend/libpq/be-secure.c       |   9 +-
 src/backend/libpq/pqcomm.c          | 140 ++++---
 src/backend/libpq/pqformat.c        | 150 +++++---
 src/backend/postmaster/postmaster.c |  26 +-
 src/backend/tcop/dest.c             |  33 +-
 src/backend/tcop/fastpath.c         | 206 +++++-----
 src/backend/tcop/postgres.c         | 166 +++++----
 src/include/lib/stringinfo.h        |  12 +-
 src/include/libpq/libpq.h           |  16 +-
 src/include/libpq/pqcomm.h          |   4 +-
 src/include/libpq/pqformat.h        |  12 +-
 src/include/tcop/dest.h             |   4 +-
 src/include/tcop/fastpath.h         |   6 +-
 src/interfaces/libpq/fe-auth.c      |   4 +-
 src/interfaces/libpq/fe-connect.c   |  40 +-
 src/interfaces/libpq/fe-exec.c      | 129 +++----
 src/interfaces/libpq/fe-misc.c      | 374 +++++++++++--------
 src/interfaces/libpq/libpq-fe.h     |   3 +-
 src/interfaces/libpq/libpq-int.h    |  11 +-
 24 files changed, 1321 insertions(+), 966 deletions(-)

diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index a1f8d6b5f6b..7a7bb48ff39 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -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>
diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml
index bb25eb74b14..529baa1f31b 100644
--- a/doc/src/sgml/protocol.sgml
+++ b/doc/src/sgml/protocol.sgml
@@ -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>
@@ -819,8 +819,9 @@
     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
@@ -833,37 +834,33 @@
    <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>
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 0f20bfb2aea..fd85f48b592 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.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"
@@ -49,6 +50,17 @@
 #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;
 		}
diff --git a/src/backend/lib/stringinfo.c b/src/backend/lib/stringinfo.c
index 9b9fc3d1800..0f758b1bd2d 100644
--- a/src/backend/lib/stringinfo.c
+++ b/src/backend/lib/stringinfo.c
@@ -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;
+}
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 5396cc47c18..a5dc8eff2da 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -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;
 }
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index 923f38a98c9..e7674c7807c 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -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
@@ -110,13 +110,6 @@
 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);
diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
index cc06347e45d..9a4f51b7786 100644
--- a/src/backend/libpq/pqcomm.c
+++ b/src/backend/libpq/pqcomm.c
@@ -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 $
  *
  *-------------------------------------------------------------------------
  */
@@ -48,12 +48,13 @@
  * 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
diff --git a/src/backend/libpq/pqformat.c b/src/backend/libpq/pqformat.c
index 50f17977d3e..80ca3190999 100644
--- a/src/backend/libpq/pqformat.c
+++ b/src/backend/libpq/pqformat.c
@@ -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 $
  *
  *-------------------------------------------------------------------------
  */
@@ -37,12 +39,13 @@
  * 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");
 }
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 499c4f25ca2..d6beb0fc1a6 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -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
diff --git a/src/backend/tcop/dest.c b/src/backend/tcop/dest.c
index ad9d2327717..07e4614e799 100644
--- a/src/backend/tcop/dest.c
+++ b/src/backend/tcop/dest.c
@@ -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
  *
diff --git a/src/backend/tcop/fastpath.c b/src/backend/tcop/fastpath.c
index 53b3a09ba27..eeddea6f6eb 100644
--- a/src/backend/tcop/fastpath.c
+++ b/src/backend/tcop/fastpath.c
@@ -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.
@@ -28,38 +28,13 @@
  *	  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"
@@ -72,6 +47,67 @@
 #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
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 1048d2fa1c6..fcc6591f7c0 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -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
diff --git a/src/include/lib/stringinfo.h b/src/include/lib/stringinfo.h
index 051ce239a7a..c49a73c0eb1 100644
--- a/src/include/lib/stringinfo.h
+++ b/src/include/lib/stringinfo.h
@@ -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 */
diff --git a/src/include/libpq/libpq.h b/src/include/libpq/libpq.h
index 04248b5c95c..cbe0b646e74 100644
--- a/src/include/libpq/libpq.h
+++ b/src/include/libpq/libpq.h
@@ -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 */
diff --git a/src/include/libpq/pqcomm.h b/src/include/libpq/pqcomm.h
index fabfb0cb253..61aa695e272 100644
--- a/src/include/libpq/pqcomm.h
+++ b/src/include/libpq/pqcomm.h
@@ -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 */
 
diff --git a/src/include/libpq/pqformat.h b/src/include/libpq/pqformat.h
index 829727c38f0..cb80ec2c201 100644
--- a/src/include/libpq/pqformat.h
+++ b/src/include/libpq/pqformat.h
@@ -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 */
diff --git a/src/include/tcop/dest.h b/src/include/tcop/dest.h
index bbf86ef4ca6..39063af6e16 100644
--- a/src/include/tcop/dest.h
+++ b/src/include/tcop/dest.h
@@ -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);
 
diff --git a/src/include/tcop/fastpath.h b/src/include/tcop/fastpath.h
index e9b961d8f05..9e0f5e6bbb9 100644
--- a/src/include/tcop/fastpath.h
+++ b/src/include/tcop/fastpath.h
@@ -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 */
diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c
index fca2d2e3035..10e2ee15f13 100644
--- a/src/interfaces/libpq/fe-auth.c
+++ b/src/interfaces/libpq/fe-auth.c
@@ -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;
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 9f5c8714a68..a322d8a73d1 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -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");
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index 9e86b3aa672..487acff83df 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.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);
 }
-
-
diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c
index 0f971343ccc..dfc46fdf598 100644
--- a/src/interfaces/libpq/fe-misc.c
+++ b/src/interfaces/libpq/fe-misc.c
@@ -16,16 +16,14 @@
  * 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 $
  *
  *-------------------------------------------------------------------------
  */
@@ -63,15 +61,15 @@
 #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))
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index d32b6fdeea3..a86b63eadae 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -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
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 43c3bd11c56..8671922547d 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -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);
-- 
GitLab