diff --git a/src/backend/access/common/printtup.c b/src/backend/access/common/printtup.c
index 9833a8d4d0a1d36ca0be7c3663a44e530b82bca0..5ce4830ebd4a7caa5043d67ff7b99845c31f21f2 100644
--- a/src/backend/access/common/printtup.c
+++ b/src/backend/access/common/printtup.c
@@ -8,23 +8,25 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.42 1999/02/13 23:14:12 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.43 1999/04/25 03:19:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include <string.h>
-#include <postgres.h>
 
-#include <fmgr.h>
-#include <access/heapam.h>
-#include <access/printtup.h>
-#include <catalog/pg_type.h>
-#include <libpq/libpq.h>
-#include <utils/syscache.h>
+#include "postgres.h"
+
+#include "fmgr.h"
+#include "access/heapam.h"
+#include "access/printtup.h"
+#include "catalog/pg_type.h"
+#include "libpq/libpq.h"
+#include "libpq/pqformat.h"
+#include "utils/syscache.h"
 
 #ifdef MULTIBYTE
-#include <mb/pg_wchar.h>
+#include "mb/pg_wchar.h"
 #endif
 
 static void printtup_setup(DestReceiver* self, TupleDesc typeinfo);
@@ -152,6 +154,7 @@ static void
 printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self)
 {
 	DR_printtup *myState = (DR_printtup*) self;
+	StringInfoData buf;
 	int			i,
 				j,
 				k,
@@ -172,7 +175,8 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self)
 	 *	tell the frontend to expect new tuple data (in ASCII style)
 	 * ----------------
 	 */
-	pq_putnchar("D", 1);
+	pq_beginmessage(&buf);
+	pq_sendbyte(&buf, 'D');
 
 	/* ----------------
 	 *	send a bitmap of which attributes are not null
@@ -187,13 +191,13 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self)
 		k >>= 1;
 		if (k == 0)				/* end of byte? */
 		{
-			pq_putint(j, 1);
+			pq_sendint(&buf, j, 1);
 			j = 0;
 			k = 1 << 7;
 		}
 	}
 	if (k != (1 << 7))			/* flush last partial byte */
-		pq_putint(j, 1);
+		pq_sendint(&buf, j, 1);
 
 	/* ----------------
 	 *	send the attributes of this tuple
@@ -212,12 +216,12 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self)
 #ifdef MULTIBYTE
 			p = pg_server_to_client(outputstr, strlen(outputstr));
 			outputlen = strlen(p);
-			pq_putint(outputlen + VARHDRSZ, VARHDRSZ);
-			pq_putnchar(p, outputlen);
+			pq_sendint(&buf, outputlen + VARHDRSZ, VARHDRSZ);
+			pq_sendbytes(&buf, p, outputlen);
 #else
 			outputlen = strlen(outputstr);
-			pq_putint(outputlen + VARHDRSZ, VARHDRSZ);
-			pq_putnchar(outputstr, outputlen);
+			pq_sendint(&buf, outputlen + VARHDRSZ, VARHDRSZ);
+			pq_sendbytes(&buf, outputstr, outputlen);
 #endif
 			pfree(outputstr);
 		}
@@ -225,10 +229,12 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self)
 		{
 			outputstr = "<unprintable>";
 			outputlen = strlen(outputstr);
-			pq_putint(outputlen + VARHDRSZ, VARHDRSZ);
-			pq_putnchar(outputstr, outputlen);
+			pq_sendint(&buf, outputlen + VARHDRSZ, VARHDRSZ);
+			pq_sendbytes(&buf, outputstr, outputlen);
 		}
 	}
+
+	pq_endmessage(&buf);
 }
 
 /* ----------------
@@ -325,6 +331,7 @@ debugtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self)
 void
 printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self)
 {
+	StringInfoData buf;
 	int			i,
 				j,
 				k;
@@ -335,7 +342,8 @@ printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self)
 	 *	tell the frontend to expect new tuple data (in binary style)
 	 * ----------------
 	 */
-	pq_putnchar("B", 1);
+	pq_beginmessage(&buf);
+	pq_sendbyte(&buf, 'B');
 
 	/* ----------------
 	 *	send a bitmap of which attributes are not null
@@ -350,13 +358,13 @@ printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self)
 		k >>= 1;
 		if (k == 0)				/* end of byte? */
 		{
-			pq_putint(j, 1);
+			pq_sendint(&buf, j, 1);
 			j = 0;
 			k = 1 << 7;
 		}
 	}
 	if (k != (1 << 7))			/* flush last partial byte */
-		pq_putint(j, 1);
+		pq_sendint(&buf, j, 1);
 
 	/* ----------------
 	 *	send the attributes of this tuple
@@ -378,8 +386,8 @@ printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self)
 				/* variable length, assume a varlena structure */
 				len = VARSIZE(attr) - VARHDRSZ;
 
-				pq_putint(len, VARHDRSZ);
-				pq_putnchar(VARDATA(attr), len);
+				pq_sendint(&buf, len, VARHDRSZ);
+				pq_sendbytes(&buf, VARDATA(attr), len);
 
 #ifdef IPORTAL_DEBUG
 				{
@@ -399,20 +407,20 @@ printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self)
 					int16		i16;
 					int32		i32;
 
-					pq_putint(len, sizeof(int32));
+					pq_sendint(&buf, len, sizeof(int32));
 					switch (len)
 					{
 						case sizeof(int8):
 							i8 = DatumGetChar(attr);
-							pq_putnchar((char *) &i8, len);
+							pq_sendbytes(&buf, (char *) &i8, len);
 							break;
 						case sizeof(int16):
 							i16 = DatumGetInt16(attr);
-							pq_putnchar((char *) &i16, len);
+							pq_sendbytes(&buf, (char *) &i16, len);
 							break;
 						case sizeof(int32):
 							i32 = DatumGetInt32(attr);
-							pq_putnchar((char *) &i32, len);
+							pq_sendbytes(&buf, (char *) &i32, len);
 							break;
 					}
 #ifdef IPORTAL_DEBUG
@@ -421,8 +429,8 @@ printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self)
 				}
 				else
 				{
-					pq_putint(len, sizeof(int32));
-					pq_putnchar(DatumGetPointer(attr), len);
+					pq_sendint(&buf, len, sizeof(int32));
+					pq_sendbytes(&buf, DatumGetPointer(attr), len);
 #ifdef IPORTAL_DEBUG
 					fprintf(stderr, "byref length %d data %x\n", len,
 							DatumGetPointer(attr));
@@ -431,4 +439,6 @@ printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self)
 			}
 		}
 	}
+
+	pq_endmessage(&buf);
 }
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
index 453019871e3a8394d540bb6fb4a6667e058a02db..3d5cf92f7d1299d61e78e698eeba8dd48e7cd732 100644
--- a/src/backend/commands/async.c
+++ b/src/backend/commands/async.c
@@ -6,7 +6,7 @@
  * Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/async.c,v 1.44 1999/02/13 23:15:00 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/async.c,v 1.45 1999/04/25 03:19:08 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -94,6 +94,7 @@
 #include "fmgr.h"
 #include "lib/dllist.h"
 #include "libpq/libpq.h"
+#include "libpq/pqformat.h"
 #include "miscadmin.h"
 #include "storage/bufmgr.h"
 #include "storage/lmgr.h"
@@ -798,9 +799,12 @@ NotifyMyFrontEnd(char *relname, int32 listenerPID)
 {
 	if (whereToSendOutput == Remote)
 	{
-		pq_putnchar("A", 1);
-		pq_putint(listenerPID, sizeof(int32));
-		pq_putstr(relname);
+		StringInfoData buf;
+		pq_beginmessage(&buf);
+		pq_sendbyte(&buf, 'A');
+		pq_sendint(&buf, listenerPID, sizeof(int32));
+		pq_sendstring(&buf, relname, strlen(relname));
+		pq_endmessage(&buf);
 		/* NOTE: we do not do pq_flush() here.  For a self-notify, it will
 		 * happen at the end of the transaction, and for incoming notifies
 		 * ProcessIncomingNotify will do it after finding all the notifies.
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 8b75f5b3cd9cebc758c5838bf92ce11ed3fd2fa7..4efa13635aa54c866898e74eb2fbd917887daa7e 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -6,7 +6,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.73 1999/02/13 23:15:04 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.74 1999/04/25 03:19:09 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -89,12 +89,14 @@ inline void CopyDonePeek(FILE *fp, int c, int pickup);
  *
  * CopySendString does the same for null-terminated strings
  * CopySendChar does the same for single characters
+ *
+ * NB: no data conversion is applied by these functions
  */
 inline void CopySendData(void *databuf, int datasize, FILE *fp) {
   if (!fp)
-    pq_putnchar(databuf, datasize);
+	  pq_putbytes((char*) databuf, datasize);
   else
-    fwrite(databuf, datasize, 1, fp);
+	  fwrite(databuf, datasize, 1, fp);
 }
     
 inline void CopySendString(char *str, FILE *fp) {
@@ -112,17 +114,24 @@ inline void CopySendChar(char c, FILE *fp) {
  *
  * CopyGetChar does the same for single characters
  * CopyGetEof checks if it's EOF on the input
+ *
+ * NB: no data conversion is applied by these functions
  */
 inline void CopyGetData(void *databuf, int datasize, FILE *fp) {
   if (!fp)
-    pq_getnchar(databuf, 0, datasize); 
+    pq_getbytes((char*) databuf, datasize); 
   else 
     fread(databuf, datasize, 1, fp);
 }
 
 inline int CopyGetChar(FILE *fp) {
   if (!fp) 
-    return pq_getchar();
+  {
+	  unsigned char ch;
+	  if (pq_getbytes((char*) &ch, 1))
+		  return EOF;
+	  return ch;
+  }
   else
     return getc(fp);
 }
@@ -143,7 +152,7 @@ inline int CopyGetEof(FILE *fp) {
  */
 inline int CopyPeekChar(FILE *fp) {
   if (!fp) 
-    return pq_peekchar();
+    return pq_peekbyte();
   else
     return getc(fp);
 }
@@ -153,7 +162,7 @@ inline void CopyDonePeek(FILE *fp, int c, int pickup) {
     if (pickup) {
       /* We want to pick it up - just receive again into dummy buffer */
       char c;
-      pq_getnchar(&c, 0, 1);
+      pq_getbytes(&c, 1);
     }
     /* If we didn't want to pick it up, just leave it where it sits */
   }
@@ -216,7 +225,10 @@ DoCopy(char *relname, bool binary, bool oids, bool from, bool pipe,
 	 * descriptor leak.  bjm 1998/08/29
 	 */
 	if (file_opened)
+	{
 		FreeFile(fp);
+		file_opened = false;
+	}
 
 	rel = heap_openr(relname);
 	if (rel == NULL)
@@ -271,6 +283,7 @@ DoCopy(char *relname, bool binary, bool oids, bool from, bool pipe,
 				if (IsUnderPostmaster)
 				{
 					SendCopyBegin();
+					pq_startcopyout();
 					fp = NULL;
 				}
 				else
@@ -301,9 +314,12 @@ DoCopy(char *relname, bool binary, bool oids, bool from, bool pipe,
 			FreeFile(fp);
 			file_opened = false;
 		}
-		else if (!from && !binary)
+		else if (!from)
 		{
-			CopySendData("\\.\n",3,fp);
+			if (!binary)
+				CopySendData("\\.\n",3,fp);
+			if (IsUnderPostmaster)
+				pq_endcopyout(false);
 		}
 	}
 }
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 6745b4f1f38af12a26e6c5b7176fb9f238b79730..a26579270c96b5ac5d3896b98e562257677b8725 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -4,7 +4,7 @@
  *
  * Copyright (c) 1994-5, Regents of the University of California
  *
- *	  $Id: explain.c,v 1.34 1999/04/23 21:23:48 momjian Exp $
+ *	  $Id: explain.c,v 1.35 1999/04/25 03:19:09 tgl Exp $
  *
  */
 #include <stdio.h>
@@ -350,18 +350,13 @@ explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es)
 static char *
 Explain_PlanToString(Plan *plan, ExplainState *es)
 {
-	StringInfo	str;
-	char	   *s;
+	StringInfoData	str;
 
-	if (plan == NULL)
-		return "";
-	Assert(plan != NULL);
-	str = makeStringInfo();
-	explain_outNode(str, plan, 0, es);
-	s = str->data;
-	pfree(str);
-
-	return s;
+	/* see stringinfo.h for an explanation of this maneuver */
+	initStringInfo(&str);
+	if (plan != NULL)
+		explain_outNode(&str, plan, 0, es);
+	return str.data;
 }
 
 /*
diff --git a/src/backend/lib/stringinfo.c b/src/backend/lib/stringinfo.c
index 2d129d12b2eb2eff0718b8a4f23eced7f9013bac..3ded7799749e5dbea416f6bcb38cf14eaac2e18f 100644
--- a/src/backend/lib/stringinfo.c
+++ b/src/backend/lib/stringinfo.c
@@ -1,125 +1,171 @@
-/*
+/*-------------------------------------------------------------------------
+ *
  * stringinfo.c
- *	  These are routines that can be used to write informations to a string,
- *	  without having to worry about string lengths, space allocation etc.
- *	  Ideally the interface should look like the file i/o interface,
+ *
+ * StringInfo provides an indefinitely-extensible string data type.
+ * It can be used to buffer either ordinary C strings (null-terminated text)
+ * or arbitrary binary data.  All storage is allocated with palloc().
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- *	  $Id: stringinfo.c,v 1.14 1999/02/13 23:15:36 momjian Exp $
+ *	  $Id: stringinfo.c,v 1.15 1999/04/25 03:19:25 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
  */
 
 #include <stdio.h>
 #include <string.h>
 
-#include <stdarg.h>
-
-#include <postgres.h>
-
-#include <nodes/pg_list.h>
-#include <lib/stringinfo.h>
+#include "postgres.h"
+#include "lib/stringinfo.h"
 
 /*
  * makeStringInfo
  *
- * Create a StringInfoData & return a pointer to it.
- *
+ * Create an empty 'StringInfoData' & return a pointer to it.
  */
 StringInfo
-makeStringInfo()
+makeStringInfo(void)
 {
 	StringInfo	res;
-	int			size;
 
 	res = (StringInfo) palloc(sizeof(StringInfoData));
 	if (res == NULL)
-		elog(ERROR, "makeStringInfo: Out of memory!");
+		elog(ERROR, "makeStringInfo: Out of memory");
 
-	size = 256;					/* initial default size */
-	res->data = palloc(size);
-	if (res->data == NULL)
-	{
-		elog(ERROR,
-		   "makeStringInfo: Out of memory! (%d bytes requested)", size);
-	}
-	res->maxlen = size;
-	res->len = 0;
-	/* Make sure the string is empty initially. */
-	res->data[0] = '\0';
+	initStringInfo(res);
 
 	return res;
 }
 
 /*
- * appendStringInfo
+ * initStringInfo
  *
- * append to the current 'StringInfo' a new string.
- * If there is not enough space in the current 'data', then reallocate
- * some more...
+ * Initialize a StringInfoData struct (with previously undefined contents)
+ * to describe an empty string.
+ */
+void
+initStringInfo(StringInfo str)
+{
+	int			size = 256;		/* initial default buffer size */
+
+	str->data = palloc(size);
+	if (str->data == NULL)
+		elog(ERROR,
+			 "initStringInfo: Out of memory (%d bytes requested)", size);
+	str->maxlen = size;
+	str->len = 0;
+	str->data[0] = '\0';
+}
+
+/*
+ * enlargeStringInfo
  *
- * NOTE: if we reallocate space, we pfree the old one!
+ * Internal routine: make sure there is enough space for 'needed' more bytes
+ * ('needed' does not include the terminating null).
+ */
+static void
+enlargeStringInfo(StringInfo str, int needed)
+{
+	int		newlen;
+	char   *newdata;
+
+	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;
+
+	newdata = palloc(newlen);
+	if (newdata == NULL)
+		elog(ERROR,
+			 "enlargeStringInfo: Out of memory (%d bytes requested)", newlen);
+
+	/* OK, transfer data into new buffer, and release old buffer */
+	memcpy(newdata, str->data, str->len + 1);
+	pfree(str->data);
+	str->data = newdata;
+	str->maxlen = newlen;
+}
+
+/*
+ * appendStringInfo
+ *
+ * Format text data under the control of fmt (an sprintf-like format string)
+ * and append it to whatever is already in str.  More space is allocated
+ * to str if necessary.  This is sort of like a combination of sprintf and
+ * strcat.
  *
+ * CAUTION: the current implementation has a 1K limit on the amount of text
+ * generated in a single call (not on the total string length).
  */
 void
-appendStringInfo(StringInfo str, const char *fmt,...)
+appendStringInfo(StringInfo str, const char *fmt, ...)
 {
-	int		buflen,
-				newlen,
-				needed;
-	char	*s,
-				buffer[512];
+	va_list	args;
+	char	buffer[1024];
+	int		buflen;
+
+	Assert(str != NULL);
 
-  va_list args;
 	va_start(args, fmt);
-  buflen = vsnprintf(buffer, 512, fmt, args);
-  va_end(args);
+	buflen = vsnprintf(buffer, sizeof(buffer), fmt, args);
+	va_end(args);
+
+	/* Make more room if needed */
+	enlargeStringInfo(str, buflen);
+
+	/* OK, append the data, including the trailing null */
+	memcpy(str->data + str->len, buffer, buflen + 1);
+	str->len += buflen;
+}
 
+/*------------------------
+ * appendStringInfoChar
+ * Append a single byte to str.
+ * Like appendStringInfo(str, "%c", ch) but much faster.
+ */
+void
+appendStringInfoChar(StringInfo str, char ch)
+{
 	Assert(str != NULL);
-	if (buflen == 0)
-		strcpy(buffer, "<>");
 
-	/*
-	 * do we have enough space to append the new string? (don't forget to
-	 * count the null string terminating char!) If no, then reallocate
-	 * some more.
-	 */
-	needed = str->len + buflen + 1;
-	if (needed > str->maxlen)
-	{
-
-		/*
-		 * how much more space to allocate ? Let's say double the current
-		 * space... However we must check if this is enough!
-		 */
-		newlen = 2 * str->maxlen;
-		while (needed > newlen)
-			newlen = 2 * newlen;
-
-		/*
-		 * allocate enough space.
-		 */
-		s = palloc(newlen);
-		if (s == NULL)
-		{
-			elog(ERROR,
-				 "appendStringInfo: Out of memory (%d bytes requested)", newlen);
-		}
-		/*
-		 * transfer the data.  strcpy() would work, but is probably a tad
-		 * slower than memcpy(), and since we know the string length...
-		 */
-		memcpy(s, str->data, str->len + 1);
-		pfree(str->data);
-		str->maxlen = newlen;
-		str->data = s;
-	}
+	/* Make more room if needed */
+	enlargeStringInfo(str, 1);
 
-	/*
-	 * OK, we have enough space now, append 'buffer' at the end of the
-	 * string & update the string length. NOTE: strcat() would work,
-	 * but is certainly slower than just memcpy'ing the data to the right
-	 * place.
+	/* OK, append the character */
+	str->data[str->len] = ch;
+	str->len++;
+	str->data[str->len] = '\0';
+}
+
+/*
+ * appendBinaryStringInfo
+ *
+ * Append arbitrary binary data to a StringInfo, allocating more space
+ * if necessary.
+ */
+void
+appendBinaryStringInfo(StringInfo str, const char *data, int datalen)
+{
+	Assert(str != NULL);
+
+	/* Make more room if needed */
+	enlargeStringInfo(str, datalen);
+
+	/* OK, append the data */
+	memcpy(str->data + str->len, data, datalen);
+	str->len += datalen;
+
+	/* Keep a trailing null in place, even though it's probably useless
+	 * for binary data...
 	 */
-	memcpy(str->data + str->len, buffer, buflen + 1);
-	str->len += buflen;
+	str->data[str->len] = '\0';
 }
diff --git a/src/backend/libpq/portal.c b/src/backend/libpq/portal.c
index a297387c6862184a928eda6ec62366204968fd48..05970a5cc38d4b065bda922221f262e24e94b5da 100644
--- a/src/backend/libpq/portal.c
+++ b/src/backend/libpq/portal.c
@@ -5,7 +5,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- *  $Id: portal.c,v 1.20 1999/02/13 23:15:45 momjian Exp $
+ *  $Id: portal.c,v 1.21 1999/04/25 03:19:20 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -37,9 +37,6 @@
  *		PQgetvalue		- Return an attribute (field) value
  *		PQgetlength		- Return an attribute (field) length
  *		PQclear			- free storage claimed by named portal
- *		PQnotifies		- Return a list of relations on which notification
- *						  has occurred.
- *		PQremoveNotify	- Remove this notification from the list.
  *
  *	 NOTES
  *		These functions may be used by both frontend routines which
@@ -647,85 +644,3 @@ PQclear(char *pname)
 		return;
 	pbuf_close(pname);
 }
-
-/*
- * async notification.
- * This is going away with pending rewrite of comm. code...
- */
-/* static SLList pqNotifyList;*/
-static Dllist *pqNotifyList = NULL;
-
-/* remove invalid notifies before returning */
-void
-PQcleanNotify()
-{
-	Dlelem	   *e,
-			   *next;
-	PQNotifyList *p;
-
-	e = DLGetHead(pqNotifyList);
-
-	while (e)
-	{
-		next = DLGetSucc(e);
-		p = (PQNotifyList *) DLE_VAL(e);
-		if (p->valid == 0)
-		{
-			DLRemove(e);
-			DLFreeElem(e);
-			pfree(p);
-		}
-		e = next;
-	}
-}
-
-void
-PQnotifies_init()
-{
-	Dlelem	   *e;
-	PQNotifyList *p;
-
-	if (pqNotifyList == NULL)
-		pqNotifyList = DLNewList();
-	else
-	{
-		/* clean all notifies */
-		for (e = DLGetHead(pqNotifyList); e != NULL; e = DLGetSucc(e))
-		{
-			p = (PQNotifyList *) DLE_VAL(e);
-			p->valid = 0;
-		}
-		PQcleanNotify();
-	}
-}
-
-PQNotifyList *
-PQnotifies()
-{
-	Dlelem	   *e;
-
-	PQcleanNotify();
-	e = DLGetHead(pqNotifyList);
-	return e ? (PQNotifyList *) DLE_VAL(e) : NULL;
-}
-
-void
-PQremoveNotify(PQNotifyList *nPtr)
-{
-	nPtr->valid = 0;			/* remove later */
-}
-
-void
-PQappendNotify(char *relname, int pid)
-{
-	PQNotifyList *p;
-
-	if (pqNotifyList == NULL)
-		pqNotifyList = DLNewList();
-
-	p = (PQNotifyList *) pbuf_alloc(sizeof(PQNotifyList));
-	StrNCpy(p->relname, relname, NAMEDATALEN);
-	p->be_pid = pid;
-	p->valid = 1;
-	DLAddTail(pqNotifyList, DLNewElem(p));
-}
diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
index a4843fb3f6139654072c24252734282c20d6d48f..7f890569d3b4f56231ed77a51cbdac4e5d5dc99e 100644
--- a/src/backend/libpq/pqcomm.c
+++ b/src/backend/libpq/pqcomm.c
@@ -3,43 +3,60 @@
  * pqcomm.c
  *	  Communication functions between the Frontend and the Backend
  *
+ * 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
+ * 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
+ * the frontend gets back into sync.  Therefore, these routines have to be
+ * aware of COPY OUT state.
+ *
+ * NOTE: generally, it's a bad idea to emit outgoing messages directly with
+ * pq_putbytes(), especially if the message would require multiple calls
+ * to send.  Instead, use the routines in pqformat.c to construct the message
+ * in a buffer and then emit it in one call to pq_putmessage.  This helps
+ * ensure that the channel will not be clogged by an incomplete message
+ * if execution is aborted by elog(ERROR) partway through the message.
+ * The only non-libpq code that should call pq_putbytes directly is COPY OUT.
+ *
+ * At one time, libpq was shared between frontend and backend, but now
+ * the backend's "backend/libpq" is quite separate from "interfaces/libpq".
+ * All that remains is similarities of names to trap the unwary...
+ *
  * Copyright (c) 1994, Regents of the University of California
  *
- *  $Id: pqcomm.c,v 1.67 1999/02/18 01:13:26 tgl Exp $
+ *  $Id: pqcomm.c,v 1.68 1999/04/25 03:19:21 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
-/*
+
+/*------------------------
  * INTERFACE ROUTINES
- *		pq_init			- initialize libpq
- *		pq_getport		- return the PGPORT setting
- *		pq_close		- close input / output connections
- *		pq_flush		- flush pending output
- *		pq_recvbuf		- load some bytes into the input buffer
- *		pq_getstr		- get a null terminated string from connection
- *		pq_getchar		- get 1 character from connection
- *		pq_peekchar		- peek at next character from connection
- *		pq_getnchar		- get n characters from connection, and null-terminate
- *		pq_getint		- get an integer from connection
- *		pq_putchar		- send 1 character to connection
- *		pq_putstr		- send a null terminated string to connection
- *		pq_putnchar		- send n characters to connection
- *		pq_putint		- send an integer to connection
- *		pq_putncharlen	- send n characters to connection
- *					  (also send an int header indicating
- *					   the length)
- *		pq_getinaddr	- initialize address from host and port number
- *		pq_getinserv	- initialize address from host and service name
  *
- *		StreamDoUnlink		- Shutdown UNIX socket connection
- *		StreamServerPort	- Open socket stream
+ * setup/teardown:
+ *		StreamServerPort	- Open postmaster's server port
  *		StreamConnection	- Create new connection with client
  *		StreamClose			- Close a client/backend connection
- * 
- * NOTES
- *              Frontend is now completely in interfaces/libpq, and no 
- *              functions from this file are used there.
+ *		pq_getport		- return the PGPORT setting
+ *		pq_init			- initialize libpq at backend startup
+ *		pq_close		- shutdown libpq at backend exit
  *
+ * low-level I/O:
+ *		pq_getbytes		- get a known number of bytes from connection
+ *		pq_getstring	- get a null terminated string 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):
+ *		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
+ *
+ *------------------------
  */
 #include "postgres.h"
 
@@ -62,89 +79,57 @@
 #include <arpa/inet.h>
 #include <sys/file.h>
 
-#if defined(linux)
-#ifndef SOMAXCONN
-#define SOMAXCONN 5				/* from Linux listen(2) man page */
-#endif	 /* SOMAXCONN */
-#endif	 /* linux */
-
+#include "libpq/libpq.h"		/* where my declarations go */
 #include "miscadmin.h"
 #include "libpq/pqsignal.h"
 #include "libpq/auth.h"
-#include "libpq/libpq.h"		/* where the declarations go */
 #include "storage/ipc.h"
-#ifdef MULTIBYTE
-#include "mb/pg_wchar.h"
-#endif
 #include "utils/trace.h"
 
+#ifndef SOMAXCONN
+#define SOMAXCONN 5				/* from Linux listen(2) man page */
+#endif	 /* SOMAXCONN */
+
 extern FILE * debug_port; /* in util.c */
 
 /*
- * Buffers 
+ * Buffers for low-level I/O
+ */
+
+#define PQ_BUFFER_SIZE 8192
+
+static unsigned char PqSendBuffer[PQ_BUFFER_SIZE];
+static int PqSendPointer;	/* Next index to store a byte in PqSendBuffer */
+
+static unsigned char PqRecvBuffer[PQ_BUFFER_SIZE];
+static int PqRecvPointer;	/* Next index to read a byte from PqRecvBuffer */
+static int PqRecvLength;	/* End of data available in PqRecvBuffer */
+
+/*
+ * Message status
  */
-unsigned char PqSendBuffer[PQ_BUFFER_SIZE];
-unsigned char PqRecvBuffer[PQ_BUFFER_SIZE];
-int PqSendPointer,PqRecvPointer,PqRecvLength;
+static bool DoingCopyOut;
 
 
 /* --------------------------------
- *		pq_init - open portal file descriptors
+ *		pq_init - initialize libpq at backend startup
  * --------------------------------
  */
 void
-pq_init(int fd)
+pq_init(void)
 {
 	PqSendPointer = PqRecvPointer = PqRecvLength = 0;
-	PQnotifies_init();
+	DoingCopyOut = false;
 	if (getenv("LIBPQ_DEBUG"))
 	  debug_port = stderr;
 }
 
-/* -------------------------
- *	 pq_getchar()
- *
- *	 get a character from the input file, or EOF if trouble
- * --------------------------------
- */
-
-int
-pq_getchar(void)
-{
-	while (PqRecvPointer >= PqRecvLength)
-	{
-		if (pq_recvbuf())		/* If nothing in buffer, then recv some */
-			return EOF;			/* Failed to recv data */
-	}
-	return PqRecvBuffer[PqRecvPointer++];
-}
-
-/* -------------------------
- *	 pq_peekchar()
- *
- *	 get a character from the connection, but leave it in the buffer
- *	 to be read again
- * --------------------------------
- */
-
-int
-pq_peekchar(void)
-{
-	while (PqRecvPointer >= PqRecvLength)
-	{
-		if (pq_recvbuf())		/* If nothing in buffer, then recv some */
-			return EOF;			/* Failed to recv data */
-	}
-	/* Note we don't bump the pointer... */
-	return PqRecvBuffer[PqRecvPointer];
-}
-
 /* --------------------------------
  *		pq_getport - return the PGPORT setting
  * --------------------------------
  */
 int
-pq_getport()
+pq_getport(void)
 {
 	char	   *envport = getenv("PGPORT");
 
@@ -154,327 +139,16 @@ pq_getport()
 }
 
 /* --------------------------------
- *		pq_close - close input / output connections
+ *		pq_close - shutdown libpq at backend exit
  * --------------------------------
  */
 void
-pq_close()
+pq_close(void)
 {
 	close(MyProcPort->sock);
-	PQnotifies_init();
-}
-
-/* --------------------------------
- *		pq_flush - flush pending output
- *
- *		returns 0 if OK, EOF if trouble
- * --------------------------------
- */
-int
-pq_flush()
-{
-	unsigned char *bufptr = PqSendBuffer;
-	unsigned char *bufend = PqSendBuffer + PqSendPointer;
-
-	while (bufptr < bufend)
-	{
-		int r = send(MyProcPort->sock, bufptr, bufend - bufptr, 0);
-		if (r <= 0)
-		{
-			if (errno == EINTR)
-				continue;		/* Ok if we were interrupted */
-			/* We would like to use elog() here, but cannot because elog
-			 * tries to write to the client, which would cause a recursive
-			 * flush attempt!  So just write it out to the postmaster log.
-			 */
-			fprintf(stderr, "pq_flush: send() failed, errno %d\n", errno);
-			/* We drop the buffered data anyway so that processing
-			 * can continue, even though we'll probably quit soon.
-			 */
-			PqSendPointer = 0;
-			return EOF;
-		}
-		bufptr += r;
-	}
-	PqSendPointer = 0;
-	return 0;
-}
-
-/* --------------------------------
- *		pq_recvbuf - load some bytes into the input buffer
- *
- *		returns 0 if OK, EOF if trouble
- * --------------------------------
- */
-
-int
-pq_recvbuf()
-{
-	if (PqRecvPointer > 0)
-	{
-		if (PqRecvLength > PqRecvPointer)
-		{
-			/* still some unread data, left-justify it in the buffer */
-			memmove(PqRecvBuffer, PqRecvBuffer+PqRecvPointer,
-					PqRecvLength-PqRecvPointer);
-			PqRecvLength -= PqRecvPointer;
-			PqRecvPointer = 0;
-		}
-		else
-			PqRecvLength = PqRecvPointer = 0;
-	}
-
-	/* Can fill buffer from PqRecvLength and upwards */
-	for (;;)
-	{
-		int r = recv(MyProcPort->sock, PqRecvBuffer + PqRecvLength,
-					 PQ_BUFFER_SIZE - PqRecvLength, 0);
-		if (r < 0)
-		{
-			if (errno == EINTR)
-				continue;		/* Ok if interrupted */
-			/* We would like to use elog() here, but dare not because elog
-			 * tries to write to the client, which will cause problems
-			 * if we have a hard communications failure ...
-			 * So just write the message to the postmaster log.
-			 */
-			fprintf(stderr, "pq_recvbuf: recv() failed, errno=%d\n", errno);
-			return EOF;
-		}
-		if (r == 0)
-		{
-			/* as above, elog not safe */
-			fprintf(stderr, "pq_recvbuf: unexpected EOF on client connection\n");
-			return EOF;
-		}
-		/* r contains number of bytes read, so just incr length */
-		PqRecvLength += r;
-		return 0;
-	}
-}
-
-/* --------------------------------
- *		pq_getstr - get a null terminated string from connection
- * --------------------------------
- */
-int
-pq_getstr(char *s, int maxlen)
-{
-	int			c;
-#ifdef MULTIBYTE
-	char	   *p;
-#endif
-
-	c = pqGetString(s, maxlen);
-
-#ifdef MULTIBYTE
-	p = (char*) pg_client_to_server((unsigned char *) s, maxlen);
-	if (s != p)					/* actual conversion has been done? */
-		strcpy(s, p);
-#endif
-
-	return c;
-}
-
-/* --------------------------------
- *		pq_getnchar - get n characters from connection, and null terminate
- * --------------------------------
- */
-int
-pq_getnchar(char *s, int off, int maxlen)
-{
-	int r = pqGetNBytes(s + off, maxlen);
-	s[off+maxlen] = '\0';
-	return r;
-}
-
-/* --------------------------------
- *		pq_getint - get an integer from connection
- *	 we receive an integer a byte at a type and reconstruct it so that
- *	 machines with different ENDIAN representations can talk to each
- *	 other
- * --------------------------------
- */
-int
-pq_getint(int b)
-{
-	int			n,
-				status = 1;
-
-	/*
-	 * mjl: Seems inconsisten w/ return value of pq_putint (void). Also,
-	 * EOF is a valid return value for an int! XXX
-	 */
-
-	switch (b)
-	{
-		case 1:
-			status = ((n = pq_getchar()) == EOF);
-			break;
-		case 2:
-			status = pqGetShort(&n);
-			break;
-		case 4:
-			status = pqGetLong(&n);
-			break;
-		default:
-			fprintf(stderr, "** Unsupported size %d\n", b);
-	}
-
-	if (status)
-	{
-		snprintf(PQerrormsg, ERROR_MSG_LENGTH,
-				"FATAL: pq_getint failed: errno=%d\n", errno);
-		fputs(PQerrormsg, stderr);
-		pqdebug("%s", PQerrormsg);
-		n = 0;
-	}
-
-	return n;
 }
 
-/* --------------------------------
- *		pq_putstr - send a null terminated string to connection
- * --------------------------------
- */
-void
-pq_putstr(char *s)
-{
-#ifdef MULTIBYTE
-	unsigned char *p;
 
-	p = pg_server_to_client(s, strlen(s));
-	if (pqPutString(p))
-#else
-	if (pqPutString(s))
-#endif
-	{
-		snprintf(PQerrormsg, ERROR_MSG_LENGTH,
-				"FATAL: pq_putstr: fputs() failed: errno=%d\n", errno);
-		fputs(PQerrormsg, stderr);
-		pqdebug("%s", PQerrormsg);
-	}
-}
-
-/* --------------------------------
- *		pq_putnchar - send n characters to connection
- * --------------------------------
- */
-void
-pq_putnchar(char *s, int n)
-{
-	if (pqPutNBytes(s, n))
-	{
-		snprintf(PQerrormsg, ERROR_MSG_LENGTH,
-				"FATAL: pq_putnchar: pqPutNBytes() failed: errno=%d\n",
-				errno);
-		fputs(PQerrormsg, stderr);
-		pqdebug("%s", PQerrormsg);
-	}
-}
-
-/* --------------------------------
- *		pq_putint - send an integer to connection
- *	 we chop an integer into bytes and send individual bytes
- *	 machines with different ENDIAN representations can still talk to each
- *	 other
- * --------------------------------
- */
-void
-pq_putint(int i, int b)
-{
-	int			status;
-
-	status = 1;
-	switch (b)
-	{
-		case 1:
-			status = (pq_putchar(i) == EOF);
-			break;
-		case 2:
-			status = pqPutShort(i);
-			break;
-		case 4:
-			status = pqPutLong(i);
-			break;
-		default:
-			fprintf(stderr, "** Unsupported size %d\n", b);
-	}
-
-	if (status)
-	{
-		snprintf(PQerrormsg, ERROR_MSG_LENGTH,
-				"FATAL: pq_putint failed: errno=%d\n", errno);
-		fputs(PQerrormsg, stderr);
-		pqdebug("%s", PQerrormsg);
-	}
-}
-
-/* --------------------------------
- *		pq_getinaddr - initialize address from host and port number
- * --------------------------------
- */
-int
-pq_getinaddr(struct sockaddr_in * sin,
-			 char *host,
-			 int port)
-{
-	struct hostent *hs;
-
-	MemSet((char *) sin, 0, sizeof(*sin));
-
-	if (host)
-	{
-		if (*host >= '0' && *host <= '9')
-			sin->sin_addr.s_addr = inet_addr(host);
-		else
-		{
-			if (!(hs = gethostbyname(host)))
-			{
-				perror(host);
-				return 1;
-			}
-			if (hs->h_addrtype != AF_INET)
-			{
-				snprintf(PQerrormsg, ERROR_MSG_LENGTH,
-						"FATAL: pq_getinaddr: %s not on Internet\n",
-						host);
-				fputs(PQerrormsg, stderr);
-				pqdebug("%s", PQerrormsg);
-				return 1;
-			}
-			memmove((char *) &sin->sin_addr,
-					hs->h_addr,
-					hs->h_length);
-		}
-	}
-	sin->sin_family = AF_INET;
-	sin->sin_port = htons(port);
-	return 0;
-}
-
-/* --------------------------------
- *		pq_getinserv - initialize address from host and servive name
- * --------------------------------
- */
-int
-pq_getinserv(struct sockaddr_in * sin, char *host, char *serv)
-{
-	struct servent *ss;
-
-	if (*serv >= '0' && *serv <= '9')
-		return pq_getinaddr(sin, host, atoi(serv));
-	if (!(ss = getservbyname(serv, NULL)))
-	{
-		snprintf(PQerrormsg, ERROR_MSG_LENGTH,
-				"FATAL: pq_getinserv: unknown service: %s\n",
-				serv);
-		fputs(PQerrormsg, stderr);
-		pqdebug("%s", PQerrormsg);
-		return 1;
-	}
-	return pq_getinaddr(sin, host, ntohs(ss->s_port));
-}
 
 /*
  * Streams -- wrapper around Unix socket system calls
@@ -489,7 +163,7 @@ static char sock_path[MAXPGPATH + 1] = "";
  * Shutdown routine for backend connection
  * If a Unix socket is used for communication, explicitly close it.
  */
-void
+static void
 StreamDoUnlink()
 {
 	Assert(sock_path[0]);
@@ -499,12 +173,7 @@ StreamDoUnlink()
 /*
  * StreamServerPort -- open a sock stream "listening" port.
  *
- * This initializes the Postmaster's connection
- *		accepting port.
- *
- * ASSUME: that this doesn't need to be non-blocking because
- *		the Postmaster uses select() to tell when the socket
- *		is ready.
+ * This initializes the Postmaster's connection-accepting port.
  *
  * RETURNS: STATUS_OK or STATUS_ERROR
  */
@@ -648,7 +317,9 @@ StreamServerPort(char *hostName, short portName, int *fdP)
  * StreamConnection -- create a new connection with client using
  *		server port.
  *
- * This one should be non-blocking.
+ * ASSUME: that this doesn't need to be non-blocking because
+ *		the Postmaster uses select() to tell when the server master
+ *		socket is ready for accept().
  *
  * RETURNS: STATUS_OK or STATUS_ERROR
  */
@@ -711,31 +382,295 @@ StreamClose(int sock)
 	close(sock);
 }
 
-#ifdef MULTIBYTE
-void
-pq_putncharlen(char *s, int n)
+
+/* --------------------------------
+ * Low-level I/O routines begin here.
+ *
+ * These routines communicate with a frontend client across a connection
+ * already established by the preceding routines.
+ * --------------------------------
+ */
+
+
+/* --------------------------------
+ *		pq_recvbuf - load some bytes into the input buffer
+ *
+ *		returns 0 if OK, EOF if trouble
+ * --------------------------------
+ */
+static int
+pq_recvbuf(void)
 {
-	unsigned char *p;
-	int			len;
+	if (PqRecvPointer > 0)
+	{
+		if (PqRecvLength > PqRecvPointer)
+		{
+			/* still some unread data, left-justify it in the buffer */
+			memmove(PqRecvBuffer, PqRecvBuffer+PqRecvPointer,
+					PqRecvLength-PqRecvPointer);
+			PqRecvLength -= PqRecvPointer;
+			PqRecvPointer = 0;
+		}
+		else
+			PqRecvLength = PqRecvPointer = 0;
+	}
 
-	p = pg_server_to_client(s, n);
-	len = strlen(p);
-	pq_putint(len, sizeof(int));
-	pq_putnchar(p, len);
+	/* Can fill buffer from PqRecvLength and upwards */
+	for (;;)
+	{
+		int r = recv(MyProcPort->sock, PqRecvBuffer + PqRecvLength,
+					 PQ_BUFFER_SIZE - PqRecvLength, 0);
+		if (r < 0)
+		{
+			if (errno == EINTR)
+				continue;		/* Ok if interrupted */
+			/* We would like to use elog() here, but dare not because elog
+			 * tries to write to the client, which will cause problems
+			 * if we have a hard communications failure ...
+			 * So just write the message to the postmaster log.
+			 */
+			fprintf(stderr, "pq_recvbuf: recv() failed, errno=%d\n", errno);
+			return EOF;
+		}
+		if (r == 0)
+		{
+			/* as above, elog not safe */
+			fprintf(stderr, "pq_recvbuf: unexpected EOF on client connection\n");
+			return EOF;
+		}
+		/* r contains number of bytes read, so just incr length */
+		PqRecvLength += r;
+		return 0;
+	}
 }
 
-#endif
+/* --------------------------------
+ *		pq_getbyte	- get a single byte from connection, or return EOF
+ * --------------------------------
+ */
+static int
+pq_getbyte(void)
+{
+	while (PqRecvPointer >= PqRecvLength)
+	{
+		if (pq_recvbuf())		/* If nothing in buffer, then recv some */
+			return EOF;			/* Failed to recv data */
+	}
+	return PqRecvBuffer[PqRecvPointer++];
+}
 
+/* --------------------------------
+ *		pq_peekbyte		- peek at next byte from connection
+ *
+ *	 Same as pq_getbyte() except we don't advance the pointer.
+ * --------------------------------
+ */
+int
+pq_peekbyte(void)
+{
+	while (PqRecvPointer >= PqRecvLength)
+	{
+		if (pq_recvbuf())		/* If nothing in buffer, then recv some */
+			return EOF;			/* Failed to recv data */
+	}
+	return PqRecvBuffer[PqRecvPointer];
+}
 
-/* 
- * Act like the stdio putc() function. Write one character
- * to the stream. Return this character, or EOF on error.
+/* --------------------------------
+ *		pq_getbytes		- get a known number of bytes from connection
+ *
+ *		returns 0 if OK, EOF if trouble
+ * --------------------------------
  */
-int pq_putchar(unsigned char c) 
+int
+pq_getbytes(char *s, size_t len)
 {
-	if (PqSendPointer >= PQ_BUFFER_SIZE)
-		if (pq_flush())			/* If buffer is full, then flush it out */
+	size_t amount;
+
+	while (len > 0)
+	{
+		while (PqRecvPointer >= PqRecvLength)
+		{
+			if (pq_recvbuf())	/* If nothing in buffer, then recv some */
+				return EOF;		/* Failed to recv data */
+		}
+		amount = PqRecvLength - PqRecvPointer;
+		if (amount > len)
+			amount = len;
+		memcpy(s, PqRecvBuffer + PqRecvPointer, amount);
+		PqRecvPointer += amount;
+		s += amount;
+		len -= amount;
+	}
+	return 0;
+}
+
+/* --------------------------------
+ *		pq_getstring	- get a null terminated string from connection
+ *
+ *		NOTE: this routine does not do any MULTIBYTE conversion,
+ *		even though it is presumably useful only for text, because
+ *		no code in this module should depend on MULTIBYTE mode.
+ *		See pq_getstr in pqformat.c for that.
+ *
+ *		FIXME: we ought to use an expansible StringInfo buffer,
+ *		rather than dropping data if the message is too long.
+ *
+ *		returns 0 if OK, EOF if trouble
+ * --------------------------------
+ */
+int
+pq_getstring(char *s, size_t len)
+{
+	int			c;
+
+	/*
+	 * Keep on reading until we get the terminating '\0',
+	 * discarding any bytes we don't have room for.
+	 */
+
+	while ((c = pq_getbyte()) != EOF && c != '\0')
+	{
+		if (len > 1)
+		{
+			*s++ = c;
+			len--;
+		}
+	}
+
+	*s = '\0';
+
+	if (c == EOF)
+		return EOF;
+
+	return 0;
+}
+
+
+/* --------------------------------
+ *		pq_putbytes		- send bytes to connection (not flushed until pq_flush)
+ *
+ *		returns 0 if OK, EOF if trouble
+ * --------------------------------
+ */
+int
+pq_putbytes(const char *s, size_t len)
+{
+	size_t amount;
+
+	while (len > 0)
+	{
+		if (PqSendPointer >= PQ_BUFFER_SIZE)
+			if (pq_flush())		/* If buffer is full, then flush it out */
+				return EOF;
+		amount = PQ_BUFFER_SIZE - PqSendPointer;
+		if (amount > len)
+			amount = len;
+		memcpy(PqSendBuffer + PqSendPointer, s, amount);
+		PqSendPointer += amount;
+		s += amount;
+		len -= amount;
+	}
+	return 0;
+}
+
+/* --------------------------------
+ *		pq_flush		- flush pending output
+ *
+ *		returns 0 if OK, EOF if trouble
+ * --------------------------------
+ */
+int
+pq_flush(void)
+{
+	unsigned char *bufptr = PqSendBuffer;
+	unsigned char *bufend = PqSendBuffer + PqSendPointer;
+
+	while (bufptr < bufend)
+	{
+		int r = send(MyProcPort->sock, bufptr, bufend - bufptr, 0);
+		if (r <= 0)
+		{
+			if (errno == EINTR)
+				continue;		/* Ok if we were interrupted */
+			/* We would like to use elog() here, but cannot because elog
+			 * tries to write to the client, which would cause a recursive
+			 * flush attempt!  So just write it out to the postmaster log.
+			 */
+			fprintf(stderr, "pq_flush: send() failed, errno %d\n", errno);
+			/* We drop the buffered data anyway so that processing
+			 * can continue, even though we'll probably quit soon.
+			 */
+			PqSendPointer = 0;
+			return EOF;
+		}
+		bufptr += r;
+	}
+	PqSendPointer = 0;
+	return 0;
+}
+
+
+/* --------------------------------
+ * Message-level I/O routines begin here.
+ *
+ * These routines understand about COPY OUT protocol.
+ * --------------------------------
+ */
+
+
+/* --------------------------------
+ *		pq_putmessage	- send a normal message (suppressed in COPY OUT mode)
+ *
+ *		If msgtype is not '\0', it is a message type code to place before
+ *		the message body (len counts only the body size!).
+ *		If msgtype is '\0', then the buffer already includes the type code.
+ *
+ *		All normal messages are suppressed while COPY OUT is in progress.
+ *		(In practice only NOTICE messages might get emitted then; dropping
+ *		them is annoying, but at least they will still appear in the
+ *		postmaster log.)
+ *
+ *		returns 0 if OK, EOF if trouble
+ * --------------------------------
+ */
+int
+pq_putmessage(char msgtype, const char *s, size_t len)
+{
+	if (DoingCopyOut)
+		return 0;
+	if (msgtype)
+		if (pq_putbytes(&msgtype, 1))
 			return EOF;
-	PqSendBuffer[PqSendPointer++] = c; /* Put in buffer */
-	return c;
+	return pq_putbytes(s, len);
+}
+
+/* --------------------------------
+ *		pq_startcopyout	- inform libpq that a COPY OUT transfer is beginning
+ * --------------------------------
+ */
+void
+pq_startcopyout(void)
+{
+	DoingCopyOut = true;
+}
+
+/* --------------------------------
+ *		pq_endcopyout	- end a COPY OUT transfer
+ *
+ *		If errorAbort is indicated, we are aborting a COPY OUT due to an error,
+ *		and must send a terminator line.  Since a partial data line might have
+ *		been emitted, send a couple of newlines first (the first one could
+ *		get absorbed by a backslash...)
+ * --------------------------------
+ */
+void
+pq_endcopyout(bool errorAbort)
+{
+	if (! DoingCopyOut)
+		return;
+	if (errorAbort)
+		pq_putbytes("\n\n\\.\n", 5);
+	/* in non-error case, copy.c will have emitted the terminator line */
+	DoingCopyOut = false;
 }
diff --git a/src/backend/libpq/pqcomprim.c b/src/backend/libpq/pqcomprim.c
deleted file mode 100644
index e48a1c16888600ee82c27fbb3143abf3cb5bb56b..0000000000000000000000000000000000000000
--- a/src/backend/libpq/pqcomprim.c
+++ /dev/null
@@ -1,220 +0,0 @@
-#include <stdlib.h>
-#include <stdio.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-
-#include "postgres.h"
-#include "miscadmin.h"
-#include "libpq/pqcomm.h"
-#include "libpq/libpq.h"
-
-
-/*
- * The backend supports the old little endian byte order and the current
- * network byte order.
- */
-
-#ifndef FRONTEND
-
-#include "libpq/libpq-be.h"
-
-#ifdef HAVE_ENDIAN_H
-#include <endian.h>
-#endif
-
-#ifndef BYTE_ORDER
-#error BYTE_ORDER must be defined as LITTLE_ENDIAN, BIG_ENDIAN or PDP_ENDIAN
-#endif
-
-#if BYTE_ORDER == LITTLE_ENDIAN
-
-#define ntoh_s(n)	n
-#define ntoh_l(n)	n
-#define hton_s(n)	n
-#define hton_l(n)	n
-
-#else
-#if BYTE_ORDER == BIG_ENDIAN
-
-/*
-#define ntoh_s(n)	(uint16)(((u_char *)&n)[1] << 8 \
-			  | ((u_char *)&n)[0])
-#define ntoh_l(n)	(uint32)(((u_char *)&n)[3] << 24 \
-			  | ((u_char *)&n)[2] << 16 \
-			  | ((u_char *)&n)[1] <<  8 \
-			  | ((u_char *)&n)[0])
-*/
-#define ntoh_s(n)	(uint16)((((uint16)n & 0x00ff) <<  8) | \
-				 (((uint16)n & 0xff00) >>  8))
-#define ntoh_l(n)	(uint32)((((uint32)n & 0x000000ff) << 24) | \
-				 (((uint32)n & 0x0000ff00) <<  8) | \
-				 (((uint32)n & 0x00ff0000) >>  8) | \
-				 (((uint32)n & 0xff000000) >> 24))
-#define hton_s(n)	(ntoh_s(n))
-#define hton_l(n)	(ntoh_l(n))
-
-#else
-#if BYTE_ORDER == PDP_ENDIAN
-
-#error PDP_ENDIAN macros not written yet
-
-#else
-
-#error BYTE_ORDER not defined as anything understood
-
-#endif
-#endif
-#endif
-
-#endif
-
-
-/* --------------------------------------------------------------------- */
-int
-pqPutShort(int integer)
-{
-	uint16		n;
-
-#ifdef FRONTEND
-	n = htons((uint16) integer);
-#else
-	n = ((PG_PROTOCOL_MAJOR(FrontendProtocol) == 0) ? hton_s(integer) : htons((uint16) integer));
-#endif
-
-	return pqPutNBytes((char *)&n, 2); /* 0 on success, EOF otherwise */
-}
-
-/* --------------------------------------------------------------------- */
-int
-pqPutLong(int integer)
-{
-	uint32		n;
-
-#ifdef FRONTEND
-	n = htonl((uint32) integer);
-#else
-	n = ((PG_PROTOCOL_MAJOR(FrontendProtocol) == 0) ? hton_l(integer) : htonl((uint32) integer));
-#endif
-
-	return pqPutNBytes((char *)&n, 4);
-}
-
-/* --------------------------------------------------------------------- */
-int
-pqGetShort(int *result)
-{
-	uint16		n;
-
-	if (pqGetNBytes((char *)&n, 2) != 0)
-	  return EOF;
-
-#ifdef FRONTEND
-	*result = (int) ntohs(n);
-#else
-	*result = (int) ((PG_PROTOCOL_MAJOR(FrontendProtocol) == 0) ? ntoh_s(n) : ntohs(n));
-#endif
-
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-int
-pqGetLong(int *result)
-{
-	uint32		n;
-
-	if (pqGetNBytes((char *)&n, 4) != 0)
-	  return EOF;
-
-#ifdef FRONTEND
-	*result = (int) ntohl(n);
-#else
-	*result = (int) ((PG_PROTOCOL_MAJOR(FrontendProtocol) == 0) ? ntoh_l(n) : ntohl(n));
-#endif
-
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-/* pqGetNBytes: Read a chunk of exactly len bytes into buffer s.
- *	Return 0 if ok, EOF if trouble.
- */
-int
-pqGetNBytes(char *s, size_t len)
-{
-	size_t amount;
-
-	while (len > 0)
-	{
-		while (PqRecvPointer >= PqRecvLength)
-		{
-			if (pq_recvbuf())	/* If nothing in buffer, then recv some */
-				return EOF;		/* Failed to recv data */
-		}
-		amount = PqRecvLength - PqRecvPointer;
-		if (amount > len)
-			amount = len;
-		memcpy(s, PqRecvBuffer + PqRecvPointer, amount);
-		PqRecvPointer += amount;
-		s += amount;
-		len -= amount;
-	}
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-int
-pqPutNBytes(const char *s, size_t len)
-{
-	size_t amount;
-
-	while (len > 0)
-	{
-		if (PqSendPointer >= PQ_BUFFER_SIZE)
-			if (pq_flush())		/* If buffer is full, then flush it out */
-				return EOF;
-		amount = PQ_BUFFER_SIZE - PqSendPointer;
-		if (amount > len)
-			amount = len;
-		memcpy(PqSendBuffer + PqSendPointer, s, amount);
-		PqSendPointer += amount;
-		s += amount;
-		len -= amount;
-	}
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-int
-pqGetString(char *s, size_t len)
-{
-	int			c;
-
-	/*
-	 * Keep on reading until we get the terminating '\0',
-	 * discarding any bytes we don't have room for.
-	 */
-
-	while ((c = pq_getchar()) != EOF && c != '\0')
-		if (len > 1)
-		{
-			*s++ = c;
-			len--;
-		}
-
-	*s = '\0';
-
-	if (c == EOF)
-		return EOF;
-
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-int
-pqPutString(const char *s)
-{
-  return pqPutNBytes(s,strlen(s)+1);
-}
diff --git a/src/backend/libpq/pqformat.c b/src/backend/libpq/pqformat.c
new file mode 100644
index 0000000000000000000000000000000000000000..06f55d648bc9896016e0ecfb4ddc532f43b40ad6
--- /dev/null
+++ b/src/backend/libpq/pqformat.c
@@ -0,0 +1,293 @@
+/*-------------------------------------------------------------------------
+ *
+ * pqformat.c
+ *		Routines for formatting and parsing frontend/backend messages
+ *
+ * Outgoing messages are built up in a StringInfo buffer (which is expansible)
+ * and then sent in a single call to pq_putmessage.  This module provides data
+ * 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
+ * MULTIBYTE conversion, while text is converted by MULTIBYTE rules.
+ *
+ * Incoming messages are read directly off the wire, as it were, but there
+ * are still data-conversion tasks to be performed.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *  $Id: pqformat.c,v 1.1 1999/04/25 03:19:22 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * INTERFACE ROUTINES
+ * Message output:
+ *		pq_beginmessage	- initialize StringInfo buffer
+ *		pq_sendbyte		- append a raw byte to a StringInfo buffer
+ *		pq_sendint		- append a binary integer to a StringInfo buffer
+ *		pq_sendbytes	- append raw data to a StringInfo buffer
+ *		pq_sendtext		- append a text string (with MULTIBYTE conversion)
+ *		pq_sendstring	- append a null-terminated text string (with MULTIBYTE)
+ *		pq_endmessage	- send the completed message to the frontend
+ * Note: it is also possible to append data to the StringInfo buffer using
+ * the regular StringInfo routines, but this is discouraged since required
+ * MULTIBYTE conversion may not occur.
+ *
+ * Message input:
+ *		pq_getint		- get an integer from connection
+ *		pq_getstr		- get a null terminated string from connection
+ *		pq_getnchar		- get n characters from connection, and null-terminate
+ * pq_getstr and pq_getnchar perform MULTIBYTE conversion on the collected
+ * string.  Use the raw pqcomm.c routines pq_getstring and pq_getbytes
+ * to fetch data without conversion.
+ */
+#include "postgres.h"
+
+#include "libpq/pqformat.h"
+#include "libpq/libpq.h"
+#ifdef MULTIBYTE
+#include "mb/pg_wchar.h"
+#endif
+#ifdef HAVE_ENDIAN_H
+#include <endian.h>
+#endif
+
+#ifndef BYTE_ORDER
+#error BYTE_ORDER must be defined as LITTLE_ENDIAN, BIG_ENDIAN or PDP_ENDIAN
+#endif
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+
+#define ntoh_s(n)	n
+#define ntoh_l(n)	n
+#define hton_s(n)	n
+#define hton_l(n)	n
+
+#else
+#if BYTE_ORDER == BIG_ENDIAN
+
+#define ntoh_s(n)	(uint16)((((uint16)n & 0x00ff) <<  8) | \
+				 (((uint16)n & 0xff00) >>  8))
+#define ntoh_l(n)	(uint32)((((uint32)n & 0x000000ff) << 24) | \
+				 (((uint32)n & 0x0000ff00) <<  8) | \
+				 (((uint32)n & 0x00ff0000) >>  8) | \
+				 (((uint32)n & 0xff000000) >> 24))
+#define hton_s(n)	(ntoh_s(n))
+#define hton_l(n)	(ntoh_l(n))
+
+#else
+#if BYTE_ORDER == PDP_ENDIAN
+
+#error PDP_ENDIAN macros not written yet
+
+#else
+
+#error BYTE_ORDER not defined as anything understood
+
+#endif
+#endif
+#endif
+
+
+/* --------------------------------
+ *		pq_sendbyte		- append a raw byte to a StringInfo buffer
+ * --------------------------------
+ */
+void
+pq_sendbyte(StringInfo buf, int byt)
+{
+	appendStringInfoChar(buf, byt);
+}
+
+/* --------------------------------
+ *		pq_sendbytes	- append raw data to a StringInfo buffer
+ * --------------------------------
+ */
+void
+pq_sendbytes(StringInfo buf, const char *data, int datalen)
+{
+	appendBinaryStringInfo(buf, data, datalen);
+}
+
+/* --------------------------------
+ *		pq_sendtext		- append a text string (with MULTIBYTE conversion)
+ *
+ * NB: passed text string must be null-terminated, even though we expect
+ * the caller to hand us the length (this is just because the caller
+ * usually knows the length anyway).  In this routine, the data sent to
+ * the frontend is NOT null-terminated.
+ * --------------------------------
+ */
+void
+pq_sendtext(StringInfo buf, const char *str, int slen)
+{
+#ifdef MULTIBYTE
+	str = (const char *) pg_server_to_client(str, slen);
+#endif
+	appendBinaryStringInfo(buf, str, slen);
+}
+
+/* --------------------------------
+ *		pq_sendstring	- append a null-terminated text string (with MULTIBYTE)
+ *
+ * NB: passed text string must be null-terminated, even though we expect
+ * the caller to hand us the length (this is just because the caller
+ * usually knows the length anyway).
+ * --------------------------------
+ */
+void
+pq_sendstring(StringInfo buf, const char *str, int slen)
+{
+#ifdef MULTIBYTE
+	str = (const char *) pg_server_to_client(str, slen);
+#endif
+	appendBinaryStringInfo(buf, str, slen+1);
+}
+
+/* --------------------------------
+ *		pq_sendint		- append a binary integer to a StringInfo buffer
+ * --------------------------------
+ */
+void
+pq_sendint(StringInfo buf, int i, int b)
+{
+	unsigned char	n8;
+	uint16			n16;
+	uint32			n32;
+
+	switch (b)
+	{
+		case 1:
+			n8 = (unsigned char) i;
+			appendBinaryStringInfo(buf, (char *) &n8, 1);
+			break;
+		case 2:
+			n16 = ((PG_PROTOCOL_MAJOR(FrontendProtocol) == 0) ? hton_s(i) : htons((uint16) i));
+			appendBinaryStringInfo(buf, (char *) &n16, 2);
+			break;
+		case 4:
+			n32 = ((PG_PROTOCOL_MAJOR(FrontendProtocol) == 0) ? hton_l(i) : htonl((uint32) i));
+			appendBinaryStringInfo(buf, (char *) &n32, 4);
+			break;
+		default:
+			elog(ERROR, "pq_sendint: unsupported size %d", b);
+			break;
+	}
+}
+
+/* --------------------------------
+ *		pq_endmessage	- send the completed message to the frontend
+ *
+ * The data buffer is pfree()d, but if the StringInfo was allocated with
+ * makeStringInfo then the caller must still pfree it.
+ * --------------------------------
+ */
+void
+pq_endmessage(StringInfo buf)
+{
+	if (pq_putmessage('\0', buf->data, buf->len))
+	{
+		snprintf(PQerrormsg, ERROR_MSG_LENGTH,
+				 "FATAL: pq_endmessage failed: errno=%d\n", errno);
+		fputs(PQerrormsg, stderr);
+		pqdebug("%s", PQerrormsg);
+	}
+	pfree(buf->data);
+	buf->data = NULL;
+}
+
+/* --------------------------------
+ *		pq_getint - get an integer from connection
+ *
+ *		returns 0 if OK, EOF if trouble
+ * --------------------------------
+ */
+int
+pq_getint(int *result, int b)
+{
+	int				status;
+	unsigned char	n8;
+	uint16			n16;
+	uint32			n32;
+
+	switch (b)
+	{
+		case 1:
+			status = pq_getbytes((char *) &n8, 1);
+			*result = (int) n8;
+			break;
+		case 2:
+			status = pq_getbytes((char *) &n16, 2);
+			*result = (int) ((PG_PROTOCOL_MAJOR(FrontendProtocol) == 0) ?
+							 ntoh_s(n16) : ntohs(n16));
+			break;
+		case 4:
+			status = pq_getbytes((char *) &n32, 4);
+			*result = (int) ((PG_PROTOCOL_MAJOR(FrontendProtocol) == 0) ?
+							 ntoh_l(n32) : ntohl(n32));
+			break;
+		default:
+			/* if we elog(ERROR) here, we will lose sync with the frontend,
+			 * so just complain to postmaster log instead...
+			 */
+			fprintf(stderr, "pq_getint: unsupported size %d\n", b);
+			status = EOF;
+			*result = 0;
+			break;
+	}
+	return status;
+}
+
+/* --------------------------------
+ *		pq_getstr - get a null terminated string from connection
+ *
+ *		FIXME: we ought to use an expansible StringInfo buffer,
+ *		rather than dropping data if the message is too long.
+ *
+ *		returns 0 if OK, EOF if trouble
+ * --------------------------------
+ */
+int
+pq_getstr(char *s, int maxlen)
+{
+	int			c;
+#ifdef MULTIBYTE
+	char	   *p;
+#endif
+
+	c = pq_getstring(s, maxlen);
+
+#ifdef MULTIBYTE
+	p = (char*) pg_client_to_server((unsigned char *) s, maxlen);
+	if (s != p)					/* actual conversion has been done? */
+		strcpy(s, p);
+#endif
+
+	return c;
+}
+
+/* --------------------------------
+ *		pq_getnchar - get n characters from connection, and null-terminate
+ *
+ *		returns 0 if OK, EOF if trouble
+ * --------------------------------
+ */
+int
+pq_getnchar(char *s, int len)
+{
+	int			c;
+#ifdef MULTIBYTE
+	char	   *p;
+#endif
+
+	c = pq_getbytes(s, len);
+	s[len] = '\0';
+
+#ifdef MULTIBYTE
+	p = (char*) pg_client_to_server((unsigned char *) s, len+1);
+	if (s != p)					/* actual conversion has been done? */
+		strcpy(s, p);
+#endif
+
+	return c;
+}
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index e4816e2e2569e4f80e2e61cc0031bd80bbf283a1..3de9b7bda1a5efc12d0fbc4b8d24e120306db079 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -5,7 +5,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- *  $Id: outfuncs.c,v 1.78 1999/03/23 16:50:53 momjian Exp $
+ *  $Id: outfuncs.c,v 1.79 1999/04/25 03:19:15 tgl Exp $
  *
  * NOTES
  *	  Every (plan) node in POSTGRES has an associated "out" routine which
@@ -1658,21 +1658,15 @@ _outNode(StringInfo str, void *obj)
 
 /*
  * nodeToString -
- *	   returns the ascii representation of the Node
+ *	   returns the ascii representation of the Node as a palloc'd string
  */
 char *
 nodeToString(void *obj)
 {
-	StringInfo	str;
-	char	   *s;
+	StringInfoData	str;
 
-	if (obj == NULL)
-		return "";
-	Assert(obj != NULL);
-	str = makeStringInfo();
-	_outNode(str, obj);
-	s = str->data;
-	pfree(str);
-
-	return s;
+	/* see stringinfo.h for an explanation of this maneuver */
+	initStringInfo(&str);
+	_outNode(&str, obj);
+	return str.data;
 }
diff --git a/src/backend/tcop/dest.c b/src/backend/tcop/dest.c
index 9d8232e3af163cfd1129ae62e1e0333def56ead3..d6f29aa8afcd4195b942c8a3fa522e32ece28b5c 100644
--- a/src/backend/tcop/dest.c
+++ b/src/backend/tcop/dest.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.25 1999/02/13 23:18:42 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.26 1999/04/25 03:19:09 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -39,6 +39,7 @@
 
 #include "access/htup.h"
 #include "libpq/libpq.h"
+#include "libpq/pqformat.h"
 #include "access/printtup.h"
 #include "utils/portal.h"
 #include "utils/palloc.h"
@@ -137,8 +138,7 @@ BeginCommand(char *pname,
 			 *		send fe info on tuples we're about to send
 			 * ----------------
 			 */
-			pq_putnchar("P", 1);/* new portal.. */
-			pq_putstr(pname);	/* portal name */
+			pq_putmessage('P', pname, strlen(pname)+1);
 
 			/* ----------------
 			 *		if this is a retrieve, then we send back the tuple
@@ -148,17 +148,24 @@ BeginCommand(char *pname,
 			 */
 			if (operation == CMD_SELECT && !isIntoRel)
 			{
-				pq_putnchar("T", 1);	/* type info to follow.. */
-				pq_putint(natts, 2);	/* number of attributes in tuples */
+				StringInfoData buf;
+				pq_beginmessage(&buf);
+				pq_sendbyte(&buf, 'T');	/* tuple descriptor message type */
+				pq_sendint(&buf, natts, 2);	/* # of attributes in tuples */
 
 				for (i = 0; i < natts; ++i)
 				{
-					pq_putstr(attrs[i]->attname.data);
-					pq_putint((int) attrs[i]->atttypid, sizeof(attrs[i]->atttypid));
-					pq_putint(attrs[i]->attlen, sizeof(attrs[i]->attlen));
+					pq_sendstring(&buf, attrs[i]->attname.data,
+								  strlen(attrs[i]->attname.data));
+					pq_sendint(&buf, (int) attrs[i]->atttypid,
+							   sizeof(attrs[i]->atttypid));
+					pq_sendint(&buf, attrs[i]->attlen,
+							   sizeof(attrs[i]->attlen));
 					if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
-						pq_putint(attrs[i]->atttypmod, sizeof(attrs[i]->atttypmod));
+						pq_sendint(&buf, attrs[i]->atttypmod,
+								   sizeof(attrs[i]->atttypmod));
 				}
+				pq_endmessage(&buf);
 			}
 			break;
 
@@ -265,8 +272,8 @@ EndCommand(char *commandTag, CommandDest dest)
 			 *		tell the fe that the query is over
 			 * ----------------
 			 */
-			sprintf(buf, "C%s%s", commandTag, CommandInfo);
-			pq_putstr(buf);
+			sprintf(buf, "%s%s", commandTag, CommandInfo);
+			pq_putmessage('C', buf, strlen(buf)+1);
 			CommandInfo[0] = '\0';
 			break;
 
@@ -293,18 +300,18 @@ void
 SendCopyBegin(void)
 {
 	if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
-		pq_putnchar("H", 1);	/* new way */
+		pq_putbytes("H", 1);	/* new way */
 	else
-		pq_putnchar("B", 1);	/* old way */
+		pq_putbytes("B", 1);	/* old way */
 }
 
 void
 ReceiveCopyBegin(void)
 {
 	if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
-		pq_putnchar("G", 1);	/* new way */
+		pq_putbytes("G", 1);	/* new way */
 	else
-		pq_putnchar("D", 1);	/* old way */
+		pq_putbytes("D", 1);	/* old way */
 	/* We *must* flush here to ensure FE knows it can send. */
 	pq_flush();
 }
@@ -330,7 +337,7 @@ NullCommand(CommandDest dest)
 			 *		tell the fe that we saw an empty query string
 			 * ----------------
 			 */
-			pq_putstr("I");
+			pq_putbytes("I", 1);
 			break;
 
 		case Local:
@@ -359,7 +366,7 @@ ReadyForQuery(CommandDest dest)
 		case RemoteInternal:
 		case Remote:
 			if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
-				pq_putnchar("Z", 1);
+				pq_putbytes("Z", 1);
 			/* Flush output at end of cycle in any case. */
 			pq_flush();
 			break;
diff --git a/src/backend/tcop/fastpath.c b/src/backend/tcop/fastpath.c
index f9ea00df123f2654eeba94e24cc433a06a04f10a..5dd71b2b9e2b7552699b620a8aff0cf9bad80ac8 100644
--- a/src/backend/tcop/fastpath.c
+++ b/src/backend/tcop/fastpath.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/tcop/fastpath.c,v 1.22 1999/02/13 23:18:44 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/tcop/fastpath.c,v 1.23 1999/04/25 03:19:10 tgl Exp $
  *
  * NOTES
  *	  This cruft is the server side of PQfn.
@@ -68,6 +68,7 @@
 #include "utils/builtins.h"		/* for oideq */
 #include "tcop/fastpath.h"
 #include "libpq/libpq.h"
+#include "libpq/pqformat.h"
 
 #include "access/xact.h"		/* for TransactionId/CommandId protos */
 
@@ -87,32 +88,36 @@ SendFunctionResult(Oid fid,		/* function id */
 				   int retlen	/* the length according to the catalogs */
 )
 {
-	pq_putnchar("V", 1);
+	StringInfoData buf;
+
+	pq_beginmessage(&buf);
+	pq_sendbyte(&buf, 'V');
 
 	if (retlen != 0)
 	{
-		pq_putnchar("G", 1);
+		pq_sendbyte(&buf, 'G');
 		if (retbyval)
 		{						/* by-value */
-			pq_putint(retlen, 4);
-			pq_putint((int) (Datum) retval, retlen);
+			pq_sendint(&buf, retlen, 4);
+			pq_sendint(&buf, (int) (Datum) retval, retlen);
 		}
 		else
 		{						/* by-reference ... */
 			if (retlen < 0)
 			{					/* ... varlena */
-				pq_putint(VARSIZE(retval) - VARHDRSZ, VARHDRSZ);
-				pq_putnchar(VARDATA(retval), VARSIZE(retval) - VARHDRSZ);
+				pq_sendint(&buf, VARSIZE(retval) - VARHDRSZ, VARHDRSZ);
+				pq_sendbytes(&buf, VARDATA(retval), VARSIZE(retval) - VARHDRSZ);
 			}
 			else
 			{					/* ... fixed */
-				pq_putint(retlen, 4);
-				pq_putnchar(retval, retlen);
+				pq_sendint(&buf, retlen, 4);
+				pq_sendbytes(&buf, retval, retlen);
 			}
 		}
 	}
 
-	pq_putnchar("0", 1);
+	pq_sendbyte(&buf, '0');
+	pq_endmessage(&buf);
 }
 
 /*
@@ -276,6 +281,7 @@ HandleFunctionRequest()
 	Oid			fid;
 	int			argsize;
 	int			nargs;
+	int			tmp;
 	char	   *arg[8];
 	char	   *retval;
 	int			i;
@@ -283,8 +289,9 @@ HandleFunctionRequest()
 	char	   *p;
 	struct fp_info *fip;
 
-	fid = (Oid) pq_getint(4);	/* function oid */
-	nargs = pq_getint(4);		/* # of arguments */
+	pq_getint(&tmp, 4);			/* function oid */
+	fid = (Oid) tmp;
+	pq_getint(&nargs, 4);		/* # of arguments */
 
 	/*
 	 * This is where the one-back caching is done. If you want to save
@@ -311,13 +318,14 @@ HandleFunctionRequest()
 			arg[i] = (char *) NULL;
 		else
 		{
-			argsize = pq_getint(4);
+			pq_getint(&argsize, 4);
 
 			Assert(argsize > 0);
 			if (fip->argbyval[i])
 			{					/* by-value */
 				Assert(argsize <= 4);
-				arg[i] = (char *) pq_getint(argsize);
+				pq_getint(&tmp, argsize);
+				arg[i] = (char *) tmp;
 			}
 			else
 			{					/* by-reference ... */
@@ -328,14 +336,14 @@ HandleFunctionRequest()
 																 * 98 Jan 6 */
 						elog(ERROR, "HandleFunctionRequest: palloc failed");
 					VARSIZE(p) = argsize + VARHDRSZ;
-					pq_getnchar(VARDATA(p), 0, argsize);
+					pq_getbytes(VARDATA(p), argsize);
 				}
 				else
 				{				/* ... fixed */
 					/* XXX cross our fingers and trust "argsize" */
 					if (!(p = palloc(argsize + 1)))
 						elog(ERROR, "HandleFunctionRequest: palloc failed");
-					pq_getnchar(p, 0, argsize);
+					pq_getbytes(p, argsize);
 				}
 				palloced |= (1 << i);
 				arg[i] = p;
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 231b88d620a0761392e41746bebe524616aea19d..5e82de3edaa4aabcc2ac45a659ca1cf2f467835c 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.107 1999/04/20 02:19:53 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.108 1999/04/25 03:19:10 tgl Exp $
  *
  * NOTES
  *	  this is the "main" module of the postgres backend and
@@ -52,6 +52,7 @@
 #include "executor/execdebug.h"
 #include "executor/executor.h"
 #include "libpq/libpq.h"
+#include "libpq/pqformat.h"
 #include "libpq/libpq-be.h"
 #include "libpq/pqsignal.h"
 #include "nodes/pg_list.h"
@@ -304,15 +305,15 @@ InteractiveBackend(char *inBuf)
 static char
 SocketBackend(char *inBuf)
 {
-	char		qtype[2];
+	char		qtype;
 	char		result = '\0';
 
 	/* ----------------
 	 *	get input from the frontend
 	 * ----------------
 	 */
-	strcpy(qtype, "?");
-	if (pq_getnchar(qtype, 0, 1) == EOF)
+	qtype = '?';
+	if (pq_getbytes(&qtype, 1) == EOF)
 	{
 		/* ------------
 		 *	when front-end applications quits/dies
@@ -321,7 +322,7 @@ SocketBackend(char *inBuf)
 		proc_exit(0);
 	}
 
-	switch (*qtype)
+	switch (qtype)
 	{
 			/* ----------------
 			 *	'Q': user entered a query
@@ -358,7 +359,7 @@ SocketBackend(char *inBuf)
 			 * ----------------
 			 */
 		default:
-			elog(FATAL, "Socket command type %c unknown\n", *qtype);
+			elog(FATAL, "Socket command type %c unknown", qtype);
 			break;
 	}
 	return result;
@@ -1461,7 +1462,7 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[])
 			Portfd = open(NULL_DEV, O_RDWR | O_BINARY, 0666);
 #endif
 		}
-		pq_init(Portfd);
+		pq_init();	/* reset libpq */
 		whereToSendOutput = Remote;
 	}
 	else
@@ -1521,16 +1522,19 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[])
 	if (whereToSendOutput == Remote &&
 		PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
 	{
-		pq_putnchar("K", 1);
-		pq_putint((int32) MyProcPid, sizeof(int32));
-		pq_putint((int32) MyCancelKey, sizeof(int32));
+		StringInfoData buf;
+		pq_beginmessage(&buf);
+		pq_sendbyte(&buf, 'K');
+		pq_sendint(&buf, (int32) MyProcPid, sizeof(int32));
+		pq_sendint(&buf, (int32) MyCancelKey, sizeof(int32));
+		pq_endmessage(&buf);
 		/* Need not flush since ReadyForQuery will do it. */
 	}
 
 	if (!IsUnderPostmaster)
 	{
 		puts("\nPOSTGRES backend interactive interface ");
-		puts("$Revision: 1.107 $ $Date: 1999/04/20 02:19:53 $\n");
+		puts("$Revision: 1.108 $ $Date: 1999/04/25 03:19:10 $\n");
 	}
 
 	/* ----------------
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index e1fa74634af3f07b46650610ec81686365e4b0c7..096cd09344345ecdac8cd14464fbc131e53249e0 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/error/elog.c,v 1.41 1999/04/20 02:19:54 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/error/elog.c,v 1.42 1999/04/25 03:19:11 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -188,12 +188,21 @@ elog(int lev, const char *fmt,...)
 	/* Send IPC message to the front-end program */
 	if (IsUnderPostmaster && lev > DEBUG)
 	{
-		/* notices are not exactly errors, handle it differently */
+		/* notices are not errors, handle 'em differently */
+		char msgtype;
 		if (lev == NOTICE)
-			pq_putnchar("N", 1);
+			msgtype = 'N';
 		else
-			pq_putnchar("E", 1);
-		pq_putstr(line + TIMESTAMP_SIZE);		/* don't show timestamps */
+		{
+			/* Abort any COPY OUT in progress when an error is detected.
+			 * This hack is necessary because of poor design of copy protocol.
+			 */
+			pq_endcopyout(true);
+			msgtype = 'E';
+		}
+		/* exclude the timestamp from msg sent to frontend */
+		pq_putmessage(msgtype, line + TIMESTAMP_SIZE,
+					  strlen(line + TIMESTAMP_SIZE) + 1);
 		/*
 		 * This flush is normally not necessary, since postgres.c will
 		 * flush out waiting data when control returns to the main loop.
diff --git a/src/include/lib/stringinfo.h b/src/include/lib/stringinfo.h
index cf766521ecb2822a551a035582d8109294101aa3..6b86fd6552e787299b0b43c77abcffecabfb9e2f 100644
--- a/src/include/lib/stringinfo.h
+++ b/src/include/lib/stringinfo.h
@@ -1,52 +1,108 @@
 /*-------------------------------------------------------------------------
  *
  * stringinfo.h
- *	  Declarations/definitons for "string" functions.
+ *	  Declarations/definitions for "StringInfo" functions.
  *
+ * StringInfo provides an indefinitely-extensible string data type.
+ * It can be used to buffer either ordinary C strings (null-terminated text)
+ * or arbitrary binary data.  All storage is allocated with palloc().
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: stringinfo.h,v 1.10 1999/02/13 23:21:32 momjian Exp $
+ * $Id: stringinfo.h,v 1.11 1999/04/25 03:19:27 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef STRINGINFO_H
 #define STRINGINFO_H
 
-
 /*-------------------------
- * StringInfoData holds information about a string.
- *		'data' is the string.
- *		'len' is the current string length (as returned by 'strlen')
- *		'maxlen' is the size in bytes of 'data', i.e. the maximum string
- *				size (including the terminating '\0' char) that we can
+ * StringInfoData holds information about an extensible string.
+ *		data	is the current buffer for the string (allocated with palloc).
+ *		len		is the current string length.  There is guaranteed to be
+ *				a terminating '\0' at data[len], although this is not very
+ *				useful when the string holds binary data rather than text.
+ *		maxlen	is the allocated size in bytes of 'data', i.e. the maximum
+ *				string size (including the terminating '\0' char) that we can
  *				currently store in 'data' without having to reallocate
- *				more space.
+ *				more space.  We must always have maxlen > len.
+ *-------------------------
  */
 typedef struct StringInfoData
 {
 	char	   *data;
-	int			maxlen;
 	int			len;
+	int			maxlen;
 } StringInfoData;
 
 typedef StringInfoData *StringInfo;
 
+
+/*------------------------
+ * There are two ways to create a StringInfo object initially:
+ *
+ * StringInfo stringptr = makeStringInfo();
+ *		Both the StringInfoData and the data buffer are palloc'd.
+ *
+ * StringInfoData string;
+ * initStringInfo(&string);
+ *		The data buffer is palloc'd but the StringInfoData is just local.
+ *		This is the easiest approach for a StringInfo object that will
+ *		only live as long as the current routine.
+ *
+ * To destroy a StringInfo, pfree() the data buffer, and then pfree() the
+ * StringInfoData if it was palloc'd.  There's no special support for this.
+ *
+ * NOTE: some routines build up a string using StringInfo, and then
+ * release the StringInfoData but return the data string itself to their
+ * caller.  At that point the data string looks like a plain palloc'd
+ * string.
+ *-------------------------
+ */
+
 /*------------------------
  * makeStringInfo
- * create a 'StringInfoData' & return a pointer to it.
+ * Create an empty 'StringInfoData' & return a pointer to it.
  */
 extern StringInfo makeStringInfo(void);
 
+/*------------------------
+ * initStringInfo
+ * Initialize a StringInfoData struct (with previously undefined contents)
+ * to describe an empty string.
+ */
+extern void initStringInfo(StringInfo str);
+
 /*------------------------
  * appendStringInfo
- * similar to 'strcat' but reallocates more space if necessary...
+ * Format text data under the control of fmt (an sprintf-like format string)
+ * and append it to whatever is already in str.  More space is allocated
+ * to str if necessary.  This is sort of like a combination of sprintf and
+ * strcat.
+ * CAUTION: the current implementation has a 1K limit on the amount of text
+ * generated in a single call (not on the total string length).
+ */
+extern void appendStringInfo(StringInfo str, const char *fmt, ...);
+
+/*------------------------
+ * appendStringInfoChar
+ * Append a single byte to str.
+ * Like appendStringInfo(str, "%c", ch) but much faster.
+ */
+extern void appendStringInfoChar(StringInfo str, char ch);
+
+/*------------------------
+ * appendBinaryStringInfo
+ * Append arbitrary binary data to a StringInfo, allocating more space
+ * if necessary.
  */
-extern void appendStringInfo(StringInfo str, const char *fmt,...);
+extern void appendBinaryStringInfo(StringInfo str,
+								   const char *data, int datalen);
 
 /*------------------------
- * nullStringInfo
- * return the string itself or "<>" if it is NULL
+ * stringStringInfo
+ * Return the string itself or "<>" if it is NULL.
+ * This is just a convenience macro used by many callers of appendStringInfo.
  */
 #define stringStringInfo(s) (((s) == NULL) ? "<>" : (s))
 
diff --git a/src/include/libpq/libpq.h b/src/include/libpq/libpq.h
index 99d408d9c154a3b6e06af18e0e41a35143f81c51..873992739ee45da074c8e9385257eb7645b23d61 100644
--- a/src/include/libpq/libpq.h
+++ b/src/include/libpq/libpq.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: libpq.h,v 1.27 1999/02/13 23:21:35 momjian Exp $
+ * $Id: libpq.h,v 1.28 1999/04/25 03:19:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -120,17 +120,6 @@ typedef struct PortalEntry
 extern PortalEntry **portals;
 extern size_t portals_array_size;
 
-/*
- *	Asynchronous notification
- */
-typedef struct PQNotifyList
-{
-	char		relname[NAMEDATALEN];	/* listen/notify name */
-	int			be_pid;			/* process id of backend */
-	int			valid;			/* has this already been handled by user. */
-/*	  SLNode Node; */
-} PQNotifyList;
-
 /*
  * Exceptions.
  */
@@ -194,11 +183,6 @@ extern char *PQgetvalue(PortalBuffer *portal, int tuple_index, int field_number)
 extern char *PQgetAttr(PortalBuffer *portal, int tuple_index, int field_number);
 extern int	PQgetlength(PortalBuffer *portal, int tuple_index, int field_number);
 extern void PQclear(char *pname);
-extern void PQcleanNotify(void);
-extern void PQnotifies_init(void);
-extern PQNotifyList *PQnotifies(void);
-extern void PQremoveNotify(PQNotifyList *nPtr);
-extern void PQappendNotify(char *relname, int pid);
 
 /*
  * prototypes for functions in portalbuf.c
@@ -250,51 +234,19 @@ extern int32 pqtest(struct varlena * vlena);
 /*
  * prototypes for functions in pqcomm.c
  */
-extern void pq_init(int fd);
-extern void pq_gettty(char *tp);
-extern int	pq_getport(void);
-extern void pq_close(void);
-extern int	pq_flush(void);
-extern int	pq_recvbuf(void);
-extern int	pq_getstr(char *s, int maxlen);
-extern int	PQgetline(char *s, int maxlen);
-extern int	PQputline(char *s);
-extern int	pq_getchar(void);
-extern int	pq_peekchar(void);
-extern int	pq_getnchar(char *s, int off, int maxlen);
-extern int	pq_getint(int b);
-extern int  pq_putchar(unsigned char c);
-extern void pq_putstr(char *s);
-extern void pq_putnchar(char *s, int n);
-extern void pq_putint(int i, int b);
-extern int	pq_getinaddr(struct sockaddr_in * sin, char *host, int port);
-extern int	pq_getinserv(struct sockaddr_in * sin, char *host, char *serv);
-
-#ifdef MULTIBYTE
-extern void pq_putncharlen(char *s, int n);
-
-#endif
-
-extern int pq_connect(char *dbname, char *user, char *args, char *hostName,
-		   char *debugTty, char *execFile, short portName);
-extern int	StreamOpen(char *hostName, short portName, Port *port);
-extern void StreamDoUnlink(void);
 extern int	StreamServerPort(char *hostName, short portName, int *fdP);
 extern int	StreamConnection(int server_fd, Port *port);
 extern void StreamClose(int sock);
-
-/*
- * Internal send/receive buffers in libpq.
- * These probably shouldn't be global at all, but unless we merge
- * pqcomm.c and pqcomprim.c they have to be...
- */
-
-#define PQ_BUFFER_SIZE 8192
-
-extern unsigned char PqSendBuffer[PQ_BUFFER_SIZE];
-extern int PqSendPointer;	/* Next index to store a byte in PqSendBuffer */
-extern unsigned char PqRecvBuffer[PQ_BUFFER_SIZE];
-extern int PqRecvPointer;	/* Next index to read a byte from PqRecvBuffer */
-extern int PqRecvLength;	/* End of data available in PqRecvBuffer */
+extern void pq_init(void);
+extern int	pq_getport(void);
+extern void pq_close(void);
+extern int	pq_getbytes(char *s, size_t len);
+extern int	pq_getstring(char *s, size_t len);
+extern int	pq_peekbyte(void);
+extern int	pq_putbytes(const char *s, size_t len);
+extern int	pq_flush(void);
+extern int	pq_putmessage(char msgtype, const char *s, size_t len);
+extern void	pq_startcopyout(void);
+extern void	pq_endcopyout(bool errorAbort);
 
 #endif	 /* LIBPQ_H */
diff --git a/src/include/libpq/pqcomm.h b/src/include/libpq/pqcomm.h
index 2245b83d6258fa31a89a415baa0a5feed412d472..53542993d870461bfe55736c44c90e255fb325a7 100644
--- a/src/include/libpq/pqcomm.h
+++ b/src/include/libpq/pqcomm.h
@@ -3,16 +3,20 @@
  * pqcomm.h
  *		Definitions common to frontends and backends.
  *
+ * NOTE: for historical reasons, this does not correspond to pqcomm.c.
+ * pqcomm.c's routines are declared in libpq.h.
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pqcomm.h,v 1.33 1999/02/13 23:21:36 momjian Exp $
+ * $Id: pqcomm.h,v 1.34 1999/04/25 03:19:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef PQCOMM_H
 #define PQCOMM_H
 
+#include "postgres.h"
+
 #include <stdio.h>
 #include <sys/types.h>
 #ifdef WIN32
@@ -23,8 +27,6 @@
 #include <netinet/in.h>
 #endif
 
-#include "postgres.h"
-
 /* Define a generic socket address type. */
 
 typedef union SockAddr
@@ -151,18 +153,4 @@ typedef struct CancelRequestPacket
 	uint32		cancelAuthCode; /* secret key to authorize cancel */
 }			CancelRequestPacket;
 
-
-/* in pqcomprim.c */
-int			pqGetShort(int *);
-int			pqGetLong(int *);
-int			pqGetNBytes(char *, size_t);
-int			pqGetString(char *, size_t);
-int			pqGetByte(void);
-
-int			pqPutShort(int);
-int			pqPutLong(int);
-int			pqPutNBytes(const char *, size_t);
-int			pqPutString(const char *);
-int			pqPutByte(int);
-
 #endif	 /* PQCOMM_H */
diff --git a/src/include/libpq/pqformat.h b/src/include/libpq/pqformat.h
new file mode 100644
index 0000000000000000000000000000000000000000..2fe48539a2d2ed7a050c57aa1abd59535cd73db6
--- /dev/null
+++ b/src/include/libpq/pqformat.h
@@ -0,0 +1,31 @@
+/*-------------------------------------------------------------------------
+ *
+ * pqformat.h
+ *		Definitions for formatting and parsing frontend/backend messages
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: pqformat.h,v 1.1 1999/04/25 03:19:14 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PQFORMAT_H
+#define PQFORMAT_H
+
+#include "postgres.h"
+#include "lib/stringinfo.h"
+
+#define pq_beginmessage(buf)  initStringInfo(buf)
+
+extern void	pq_sendbyte(StringInfo buf, int byt);
+extern void	pq_sendbytes(StringInfo buf, const char *data, int datalen);
+extern void	pq_sendtext(StringInfo buf, const char *str, int slen);
+extern void	pq_sendstring(StringInfo buf, const char *str, int slen);
+extern void	pq_sendint(StringInfo buf, int i, int b);
+extern void	pq_endmessage(StringInfo buf);
+
+extern int	pq_getint(int *result, int b);
+extern int	pq_getstr(char *s, int maxlen);
+extern int	pq_getnchar(char *s, int len);
+
+#endif	 /* PQFORMAT_H */