diff --git a/src/backend/access/common/printtup.c b/src/backend/access/common/printtup.c
index 35355b8b9b9c132aa1056fd06c69e01b4717dfed..d5a29ef16c6d08fc746ceefd0281ca907a397035 100644
--- a/src/backend/access/common/printtup.c
+++ b/src/backend/access/common/printtup.c
@@ -9,7 +9,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.70 2003/05/06 20:26:26 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.71 2003/05/08 18:16:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,12 +20,17 @@
 #include "libpq/libpq.h"
 #include "libpq/pqformat.h"
 #include "utils/lsyscache.h"
+#include "utils/portal.h"
 
 
 static void printtup_startup(DestReceiver *self, int operation,
-			   const char *portalName, TupleDesc typeinfo, List *targetlist);
-static void printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self);
-static void printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self);
+							 TupleDesc typeinfo);
+static void printtup(HeapTuple tuple, TupleDesc typeinfo,
+					 DestReceiver *self);
+static void printtup_20(HeapTuple tuple, TupleDesc typeinfo,
+						DestReceiver *self);
+static void printtup_internal_20(HeapTuple tuple, TupleDesc typeinfo,
+								 DestReceiver *self);
 static void printtup_shutdown(DestReceiver *self);
 static void printtup_destroy(DestReceiver *self);
 
@@ -50,6 +55,7 @@ typedef struct
 typedef struct
 {
 	DestReceiver pub;			/* publicly-known function pointers */
+	Portal		portal;			/* the Portal we are printing from */
 	bool		sendDescrip;	/* send RowDescription at startup? */
 	TupleDesc	attrinfo;		/* The attr info we are set up for */
 	int			nattrs;
@@ -61,43 +67,33 @@ typedef struct
  * ----------------
  */
 DestReceiver *
-printtup_create_DR(CommandDest dest)
+printtup_create_DR(CommandDest dest, Portal portal)
 {
 	DR_printtup *self = (DR_printtup *) palloc(sizeof(DR_printtup));
-	bool	isBinary;
-	bool	sendDescrip;
 
-	switch (dest)
+	if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
+		self->pub.receiveTuple = printtup;
+	else
 	{
-		case Remote:
-			isBinary = false;
-			sendDescrip = true;
-			break;
-		case RemoteInternal:
-			isBinary = true;
-			sendDescrip = true;
-			break;
-		case RemoteExecute:
-			isBinary = false;
-			sendDescrip = false; /* no T message for Execute */
-			break;
-		case RemoteExecuteInternal:
-			isBinary = true;
-			sendDescrip = false; /* no T message for Execute */
-			break;
-
-		default:
-			elog(ERROR, "printtup_create_DR: unsupported dest");
-			return NULL;
+		/*
+		 * In protocol 2.0 the Bind message does not exist, so there is
+		 * no way for the columns to have different print formats; it's
+		 * sufficient to look at the first one.
+		 */
+		if (portal->formats && portal->formats[0] != 0)
+			self->pub.receiveTuple = printtup_internal_20;
+		else
+			self->pub.receiveTuple = printtup_20;
 	}
-
-	self->pub.receiveTuple = isBinary ? printtup_internal : printtup;
 	self->pub.startup = printtup_startup;
 	self->pub.shutdown = printtup_shutdown;
 	self->pub.destroy = printtup_destroy;
 	self->pub.mydest = dest;
 
-	self->sendDescrip = sendDescrip;
+	self->portal = portal;
+
+	/* Send T message automatically if Remote, but not if RemoteExecute */
+	self->sendDescrip = (dest == Remote);
 
 	self->attrinfo = NULL;
 	self->nattrs = 0;
@@ -107,10 +103,10 @@ printtup_create_DR(CommandDest dest)
 }
 
 static void
-printtup_startup(DestReceiver *self, int operation,
-				 const char *portalName, TupleDesc typeinfo, List *targetlist)
+printtup_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
 {
 	DR_printtup *myState = (DR_printtup *) self;
+	Portal	portal = myState->portal;
 
 	if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
 	{
@@ -119,7 +115,9 @@ printtup_startup(DestReceiver *self, int operation,
 		 *
 		 * If portal name not specified, use "blank" portal.
 		 */
-		if (portalName == NULL)
+		const char *portalName = portal->name;
+
+		if (portalName == NULL || portalName[0] == '\0')
 			portalName = "blank";
 
 		pq_puttextmessage('P', portalName);
@@ -130,7 +128,16 @@ printtup_startup(DestReceiver *self, int operation,
 	 * then we send back the tuple descriptor of the tuples.  
 	 */
 	if (operation == CMD_SELECT && myState->sendDescrip)
-		SendRowDescriptionMessage(typeinfo, targetlist);
+	{
+		List	   *targetlist;
+
+		if (portal->strategy == PORTAL_ONE_SELECT)
+			targetlist = ((Query *) lfirst(portal->parseTrees))->targetList;
+		else
+			targetlist = NIL;
+
+		SendRowDescriptionMessage(typeinfo, targetlist, portal->formats);
+	}
 
 	/* ----------------
 	 * We could set up the derived attr info at this time, but we postpone it
@@ -150,11 +157,13 @@ printtup_startup(DestReceiver *self, int operation,
  * Notes: the TupleDesc has typically been manufactured by ExecTypeFromTL()
  * or some similar function; it does not contain a full set of fields.
  * The targetlist will be NIL when executing a utility function that does
- * not have a plan.  If the targetlist isn't NIL then it is a Plan node's
- * targetlist; it is up to us to ignore resjunk columns in it.
+ * not have a plan.  If the targetlist isn't NIL then it is a Query node's
+ * targetlist; it is up to us to ignore resjunk columns in it.  The formats[]
+ * array pointer might be NULL (if we are doing Describe on a prepared stmt);
+ * send zeroes for the format codes in that case.
  */
 void
-SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist)
+SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist, int16 *formats)
 {
 	Form_pg_attribute *attrs = typeinfo->attrs;
 	int			natts = typeinfo->natts;
@@ -198,6 +207,14 @@ SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist)
 		if (proto >= 2)
 			pq_sendint(&buf, attrs[i]->atttypmod,
 					   sizeof(attrs[i]->atttypmod));
+		/* format info appears in protocol 3.0 and up */
+		if (proto >= 3)
+		{
+			if (formats)
+				pq_sendint(&buf, formats[i], 2);
+			else
+				pq_sendint(&buf, 0, 2);
+		}
 	}
 	pq_endmessage(&buf);
 }
@@ -228,11 +245,98 @@ printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
 }
 
 /* ----------------
- *		printtup
+ *		printtup --- print a tuple in protocol 3.0
  * ----------------
  */
 static void
 printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
+{
+	DR_printtup *myState = (DR_printtup *) self;
+	int16	   *formats = myState->portal->formats;
+	StringInfoData buf;
+	int			natts = tuple->t_data->t_natts;
+	int			i;
+
+	/* Set or update my derived attribute info, if needed */
+	if (myState->attrinfo != typeinfo || myState->nattrs != natts)
+		printtup_prepare_info(myState, typeinfo, natts);
+
+	/*
+	 * Prepare a DataRow message
+	 */
+	pq_beginmessage(&buf, 'D');
+
+	pq_sendint(&buf, natts, 2);
+
+	/*
+	 * send the attributes of this tuple
+	 */
+	for (i = 0; i < natts; ++i)
+	{
+		PrinttupAttrInfo *thisState = myState->myinfo + i;
+		int16		format = (formats ? formats[i] : 0);
+		Datum		origattr,
+					attr;
+		bool		isnull;
+		char	   *outputstr;
+
+		origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
+		if (isnull)
+		{
+			pq_sendint(&buf, -1, 4);
+			continue;
+		}
+		if (format == 0)
+		{
+			if (OidIsValid(thisState->typoutput))
+			{
+				/*
+				 * If we have a toasted datum, forcibly detoast it here to
+				 * avoid memory leakage inside the type's output routine.
+				 */
+				if (thisState->typisvarlena)
+					attr = PointerGetDatum(PG_DETOAST_DATUM(origattr));
+				else
+					attr = origattr;
+
+				outputstr = DatumGetCString(FunctionCall3(&thisState->finfo,
+														  attr,
+									ObjectIdGetDatum(thisState->typelem),
+						  Int32GetDatum(typeinfo->attrs[i]->atttypmod)));
+
+				pq_sendcountedtext(&buf, outputstr, strlen(outputstr), false);
+
+				/* Clean up detoasted copy, if any */
+				if (attr != origattr)
+					pfree(DatumGetPointer(attr));
+				pfree(outputstr);
+			}
+			else
+			{
+				outputstr = "<unprintable>";
+				pq_sendcountedtext(&buf, outputstr, strlen(outputstr), false);
+			}
+		}
+		else if (format == 1)
+		{
+			/* XXX something similar to above */
+			elog(ERROR, "Binary transmission not implemented yet");
+		}
+		else
+		{
+			elog(ERROR, "Invalid format code %d", format);
+		}
+	}
+
+	pq_endmessage(&buf);
+}
+
+/* ----------------
+ *		printtup_20 --- print a tuple in protocol 2.0
+ * ----------------
+ */
+static void
+printtup_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
 {
 	DR_printtup *myState = (DR_printtup *) self;
 	StringInfoData buf;
@@ -300,7 +404,7 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
 									ObjectIdGetDatum(thisState->typelem),
 						  Int32GetDatum(typeinfo->attrs[i]->atttypmod)));
 
-			pq_sendcountedtext(&buf, outputstr, strlen(outputstr));
+			pq_sendcountedtext(&buf, outputstr, strlen(outputstr), true);
 
 			/* Clean up detoasted copy, if any */
 			if (attr != origattr)
@@ -310,7 +414,7 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
 		else
 		{
 			outputstr = "<unprintable>";
-			pq_sendcountedtext(&buf, outputstr, strlen(outputstr));
+			pq_sendcountedtext(&buf, outputstr, strlen(outputstr), true);
 		}
 	}
 
@@ -363,38 +467,23 @@ printatt(unsigned attributeId,
 		   attributeP->attbyval ? 't' : 'f');
 }
 
-/* ----------------
- *		showatts
- * ----------------
- */
-static void
-showatts(const char *name, TupleDesc tupleDesc)
-{
-	int			natts = tupleDesc->natts;
-	Form_pg_attribute *attinfo = tupleDesc->attrs;
-	int			i;
-
-	puts(name);
-	for (i = 0; i < natts; ++i)
-		printatt((unsigned) i + 1, attinfo[i], (char *) NULL);
-	printf("\t----\n");
-}
-
 /* ----------------
  *		debugStartup - prepare to print tuples for an interactive backend
  * ----------------
  */
 void
-debugStartup(DestReceiver *self, int operation,
-			 const char *portalName, TupleDesc typeinfo, List *targetlist)
+debugStartup(DestReceiver *self, int operation, TupleDesc typeinfo)
 {
+	int			natts = typeinfo->natts;
+	Form_pg_attribute *attinfo = typeinfo->attrs;
+	int			i;
+
 	/*
 	 * show the return type of the tuples
 	 */
-	if (portalName == NULL)
-		portalName = "blank";
-
-	showatts(portalName, typeinfo);
+	for (i = 0; i < natts; ++i)
+		printatt((unsigned) i + 1, attinfo[i], (char *) NULL);
+	printf("\t----\n");
 }
 
 /* ----------------
@@ -448,15 +537,16 @@ debugtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
 }
 
 /* ----------------
- *		printtup_internal
- *		We use a different data prefix, e.g. 'B' instead of 'D' to
- *		indicate a tuple in internal (binary) form.
+ *		printtup_internal_20 --- print a binary tuple in protocol 2.0
+ *
+ * We use a different message type, i.e. 'B' instead of 'D' to
+ * indicate a tuple in internal (binary) form.
  *
- *		This is largely same as printtup, except we don't use the typout func.
+ * This is largely same as printtup_20, except we don't use the typout func.
  * ----------------
  */
 static void
-printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
+printtup_internal_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
 {
 	DR_printtup *myState = (DR_printtup *) self;
 	StringInfoData buf;
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index ec6e771aedcaba3f81655d96c545115d1806c40b..93d4b8406e87a59bea18ec5254c16a84747e1667 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.197 2003/04/25 02:28:22 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.198 2003/05/08 18:16:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -125,8 +125,8 @@ static int	server_encoding;
 /*
  * Internal communications functions
  */
-static void SendCopyBegin(bool binary);
-static void ReceiveCopyBegin(bool binary);
+static void SendCopyBegin(bool binary, int natts);
+static void ReceiveCopyBegin(bool binary, int natts);
 static void SendCopyEnd(bool binary);
 static void CopySendData(void *databuf, int datasize);
 static void CopySendString(const char *str);
@@ -143,15 +143,20 @@ static void CopyDonePeek(int c, bool pickup);
  * in past protocol redesigns.
  */
 static void
-SendCopyBegin(bool binary)
+SendCopyBegin(bool binary, int natts)
 {
 	if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
 	{
 		/* new way */
 		StringInfoData buf;
+		int16	format = (binary ? 1 : 0);
+		int		i;
 
 		pq_beginmessage(&buf, 'H');
-		pq_sendbyte(&buf, binary ? 1 : 0);
+		pq_sendbyte(&buf, format);			/* overall format */
+		pq_sendint(&buf, natts, 2);
+		for (i = 0; i < natts; i++)
+			pq_sendint(&buf, format, 2);	/* per-column formats */
 		pq_endmessage(&buf);
 		copy_dest = COPY_NEW_FE;
 		copy_msgbuf = makeStringInfo();
@@ -179,15 +184,20 @@ SendCopyBegin(bool binary)
 }
 
 static void
-ReceiveCopyBegin(bool binary)
+ReceiveCopyBegin(bool binary, int natts)
 {
 	if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
 	{
 		/* new way */
 		StringInfoData buf;
+		int16	format = (binary ? 1 : 0);
+		int		i;
 
 		pq_beginmessage(&buf, 'G');
-		pq_sendbyte(&buf, binary ? 1 : 0);
+		pq_sendbyte(&buf, format);			/* overall format */
+		pq_sendint(&buf, natts, 2);
+		for (i = 0; i < natts; i++)
+			pq_sendint(&buf, format, 2);	/* per-column formats */
 		pq_endmessage(&buf);
 		copy_dest = COPY_NEW_FE;
 		copy_msgbuf = makeStringInfo();
@@ -682,7 +692,7 @@ DoCopy(const CopyStmt *stmt)
 		if (pipe)
 		{
 			if (IsUnderPostmaster)
-				ReceiveCopyBegin(binary);
+				ReceiveCopyBegin(binary, length(attnumlist));
 			else
 				copy_file = stdin;
 		}
@@ -724,7 +734,7 @@ DoCopy(const CopyStmt *stmt)
 		if (pipe)
 		{
 			if (IsUnderPostmaster)
-				SendCopyBegin(binary);
+				SendCopyBegin(binary, length(attnumlist));
 			else
 				copy_file = stdout;
 		}
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 28a6e0378c8c27088440b6fd404b661e468359bf..879a7e6d6825f5cc40857248a2414bfd81b2ad59 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994-5, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.108 2003/05/06 20:26:26 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.109 2003/05/08 18:16:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -179,7 +179,7 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, TupOutputState *tstate)
 	plan = planner(query, isCursor, cursorOptions);
 
 	/* Create a QueryDesc requesting no output */
-	queryDesc = CreateQueryDesc(query, plan, None_Receiver, NULL, NULL,
+	queryDesc = CreateQueryDesc(query, plan, None_Receiver, NULL,
 								stmt->analyze);
 
 	ExplainOnePlan(queryDesc, stmt, tstate);
diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c
index 1c51a1bb89fece70ddc95ba9bdf79273935dccf5..f9e31c3aaa1cdba27089e36cf94328ae5710ee07 100644
--- a/src/backend/commands/portalcmds.c
+++ b/src/backend/commands/portalcmds.c
@@ -14,7 +14,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.15 2003/05/06 20:26:26 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.16 2003/05/08 18:16:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -25,7 +25,6 @@
 
 #include "commands/portalcmds.h"
 #include "executor/executor.h"
-#include "executor/tstoreReceiver.h"
 #include "optimizer/planner.h"
 #include "rewrite/rewriteHandler.h"
 #include "tcop/pquery.h"
@@ -145,7 +144,6 @@ PerformPortalFetch(FetchStmt *stmt,
 				   DestReceiver *dest,
 				   char *completionTag)
 {
-	DestReceiver *mydest = dest;
 	Portal		portal;
 	long		nprocessed;
 
@@ -168,35 +166,21 @@ PerformPortalFetch(FetchStmt *stmt,
 		return;
 	}
 
-	/*
-	 * Adjust dest if needed.  MOVE wants destination None.
-	 *
-	 * If fetching from a binary cursor and the requested destination is
-	 * Remote, change it to RemoteInternal.  Note we do NOT change if the
-	 * destination is RemoteExecute --- so the Execute message's format
-	 * specification wins out over the cursor's type.
-	 */
+	/* Adjust dest if needed.  MOVE wants destination None */
 	if (stmt->ismove)
-		mydest = CreateDestReceiver(None);
-	else if (dest->mydest == Remote &&
-			 (portal->cursorOptions & CURSOR_OPT_BINARY))
-		mydest = CreateDestReceiver(RemoteInternal);
+		dest = None_Receiver;
 
 	/* Do it */
 	nprocessed = PortalRunFetch(portal,
 								stmt->direction,
 								stmt->howMany,
-								mydest);
+								dest);
 
 	/* Return command status if wanted */
 	if (completionTag)
 		snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s %ld",
 				 stmt->ismove ? "MOVE" : "FETCH",
 				 nprocessed);
-
-	/* Clean up if we created a local destination */
-	if (mydest != dest)
-		(mydest->destroy) (mydest);
 }
 
 /*
@@ -329,8 +313,7 @@ PersistHoldablePortal(Portal portal)
 	ExecutorRewind(queryDesc);
 
 	/* Change the destination to output to the tuplestore */
-	queryDesc->dest = CreateTuplestoreDestReceiver(portal->holdStore,
-												   portal->holdContext);
+	queryDesc->dest = CreateDestReceiver(Tuplestore, portal);
 
 	/* Fetch the result set into the tuplestore */
 	ExecutorRun(queryDesc, ForwardScanDirection, 0L);
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index 6e16853fd78a11dba6c9d94694bc520825986671..433fd8e049e86a7a4c8419ac43a2385c6c40f981 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -10,7 +10,7 @@
  * Copyright (c) 2002-2003, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/prepare.c,v 1.17 2003/05/06 21:51:41 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/prepare.c,v 1.18 2003/05/08 18:16:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -528,7 +528,7 @@ ExplainExecuteQuery(ExplainStmt *stmt, TupOutputState *tstate)
 			}
 
 			/* Create a QueryDesc requesting no output */
-			qdesc = CreateQueryDesc(query, plan, None_Receiver, NULL,
+			qdesc = CreateQueryDesc(query, plan, None_Receiver,
 									paramLI, stmt->analyze);
 
 			ExplainOnePlan(qdesc, stmt, tstate);
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 3d1b950e210021f2d17a726b4a3fd2430066fc50..8b720c110fd80272b8ec8bfa95b9721f6088eb0e 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -26,7 +26,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.208 2003/05/06 20:26:26 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.209 2003/05/08 18:16:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -217,10 +217,7 @@ ExecutorRun(QueryDesc *queryDesc,
 	estate->es_processed = 0;
 	estate->es_lastoid = InvalidOid;
 
-	(*dest->startup) (dest, operation,
-					  queryDesc->portalName,
-					  queryDesc->tupDesc,
-					  queryDesc->planstate->plan->targetlist);
+	(*dest->startup) (dest, operation, queryDesc->tupDesc);
 
 	/*
 	 * run plan
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 2e040f7890351c63afcb18038aa3dec99a391db0..cb85be630f9ade7f3f3b84ccd7dd0ef8057fcc54 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.65 2003/05/06 20:26:27 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.66 2003/05/08 18:16:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -765,8 +765,7 @@ begin_tup_output_tupdesc(DestReceiver *dest, TupleDesc tupdesc)
 	tstate->metadata = TupleDescGetAttInMetadata(tupdesc);
 	tstate->dest = dest;
 
-	(*tstate->dest->startup) (tstate->dest, (int) CMD_SELECT,
-							  NULL, tupdesc, NIL);
+	(*tstate->dest->startup) (tstate->dest, (int) CMD_SELECT, tupdesc);
 
 	return tstate;
 }
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index e89e5f32d645447bca98df271cc5e963806fd5bc..46d1e51c4b9b9eac600d4ac36cbfa2ea14680fdd 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.64 2003/05/06 20:26:27 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.65 2003/05/08 18:16:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -245,7 +245,7 @@ postquel_start(execution_state *es, SQLFunctionCachePtr fcache)
 {
 	Assert(es->qd == NULL);
 	es->qd = CreateQueryDesc(es->query, es->plan,
-							 None_Receiver, NULL,
+							 None_Receiver,
 							 fcache->paramLI, false);
 
 	/* Utility commands don't need Executor. */
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index c3b3ec3530a6462e5a5ffbd2776a49f3347b77af..fe420ce978e7ce6d5a4b324aac6e57f4799c3210 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.96 2003/05/06 20:26:27 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.97 2003/05/08 18:16:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -841,7 +841,8 @@ SPI_cursor_find(const char *name)
 void
 SPI_cursor_fetch(Portal portal, bool forward, int count)
 {
-	_SPI_cursor_operation(portal, forward, count, CreateDestReceiver(SPI));
+	_SPI_cursor_operation(portal, forward, count,
+						  CreateDestReceiver(SPI, NULL));
 	/* we know that the SPI receiver doesn't need a destroy call */
 }
 
@@ -880,8 +881,7 @@ SPI_cursor_close(Portal portal)
  *		of current SPI procedure
  */
 void
-spi_dest_startup(DestReceiver *self, int operation,
-				 const char *portalName, TupleDesc typeinfo, List *targetlist)
+spi_dest_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
 {
 	SPITupleTable *tuptable;
 	MemoryContext oldcxt;
@@ -1035,7 +1035,7 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
 			planTree = pg_plan_query(queryTree);
 			plan_list = lappend(plan_list, planTree);
 
-			dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None);
+			dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None, NULL);
 			if (queryTree->commandType == CMD_UTILITY)
 			{
 				if (IsA(queryTree->utilityStmt, CopyStmt))
@@ -1061,7 +1061,7 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
 			else if (plan == NULL)
 			{
 				qdesc = CreateQueryDesc(queryTree, planTree, dest,
-										NULL, NULL, false);
+										NULL, false);
 				res = _SPI_pquery(qdesc, true,
 								  queryTree->canSetTag ? tcount : 0);
 				if (res < 0)
@@ -1071,7 +1071,7 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
 			else
 			{
 				qdesc = CreateQueryDesc(queryTree, planTree, dest,
-										NULL, NULL, false);
+										NULL, false);
 				res = _SPI_pquery(qdesc, false, 0);
 				if (res < 0)
 					return res;
@@ -1150,7 +1150,7 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
 			planTree = lfirst(plan_list);
 			plan_list = lnext(plan_list);
 
-			dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None);
+			dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None, NULL);
 			if (queryTree->commandType == CMD_UTILITY)
 			{
 				ProcessUtility(queryTree->utilityStmt, dest, NULL);
@@ -1160,7 +1160,7 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
 			else
 			{
 				qdesc = CreateQueryDesc(queryTree, planTree, dest,
-										NULL, paramLI, false);
+										paramLI, false);
 				res = _SPI_pquery(qdesc, true,
 								  queryTree->canSetTag ? tcount : 0);
 				if (res < 0)
diff --git a/src/backend/executor/tstoreReceiver.c b/src/backend/executor/tstoreReceiver.c
index bcab8154a323bc355c4cf39f7a67ab76328f9b1d..3d8479faee24fa352487e160a76e47e689e7b742 100644
--- a/src/backend/executor/tstoreReceiver.c
+++ b/src/backend/executor/tstoreReceiver.c
@@ -9,7 +9,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/tstoreReceiver.c,v 1.5 2003/05/06 20:26:27 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/tstoreReceiver.c,v 1.6 2003/05/08 18:16:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -31,9 +31,7 @@ typedef struct
  * Prepare to receive tuples from executor.
  */
 static void
-tstoreStartupReceiver(DestReceiver *self, int operation,
-					  const char *portalname,
-					  TupleDesc typeinfo, List *targetlist)
+tstoreStartupReceiver(DestReceiver *self, int operation, TupleDesc typeinfo)
 {
 	/* do nothing */
 }
diff --git a/src/backend/libpq/pqformat.c b/src/backend/libpq/pqformat.c
index dacfa93ecc7c0f2bef6a8d18e929df7ef122c6d1..6f574e383b72c38ded820f3d663b426b6a836d14 100644
--- a/src/backend/libpq/pqformat.c
+++ b/src/backend/libpq/pqformat.c
@@ -18,7 +18,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *	$Header: /cvsroot/pgsql/src/backend/libpq/pqformat.c,v 1.28 2003/04/22 00:08:06 tgl Exp $
+ *	$Header: /cvsroot/pgsql/src/backend/libpq/pqformat.c,v 1.29 2003/05/08 18:16:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -104,27 +104,32 @@ pq_sendbytes(StringInfo buf, const char *data, int datalen)
  *		pq_sendcountedtext - append a text string (with character set conversion)
  *
  * The data sent to the frontend by this routine is a 4-byte count field
- * (the count includes itself, by convention) followed by the string.
+ * followed by the string.  The count includes itself or not, as per the
+ * countincludesself flag (pre-3.0 protocol requires it to include itself).
  * The passed text string need not be null-terminated, and the data sent
  * to the frontend isn't either.
  * --------------------------------
  */
 void
-pq_sendcountedtext(StringInfo buf, const char *str, int slen)
+pq_sendcountedtext(StringInfo buf, const char *str, int slen,
+				   bool countincludesself)
 {
+	int			extra = countincludesself ? 4 : 0;
 	char	   *p;
 
 	p = (char *) pg_server_to_client((unsigned char *) str, slen);
 	if (p != str)				/* actual conversion has been done? */
 	{
 		slen = strlen(p);
-		pq_sendint(buf, slen + 4, 4);
+		pq_sendint(buf, slen + extra, 4);
 		appendBinaryStringInfo(buf, p, slen);
 		pfree(p);
-		return;
 	}
-	pq_sendint(buf, slen + 4, 4);
-	appendBinaryStringInfo(buf, str, slen);
+	else
+	{
+		pq_sendint(buf, slen + extra, 4);
+		appendBinaryStringInfo(buf, str, slen);
+	}
 }
 
 /* --------------------------------
@@ -296,7 +301,7 @@ pq_getmsgbytes(StringInfo msg, int datalen)
 {
 	const char *result;
 
-	if (datalen > (msg->len - msg->cursor))
+	if (datalen < 0 || datalen > (msg->len - msg->cursor))
 		elog(ERROR, "pq_getmsgbytes: insufficient data left in message");
 	result = &msg->data[msg->cursor];
 	msg->cursor += datalen;
@@ -312,7 +317,7 @@ pq_getmsgbytes(StringInfo msg, int datalen)
 void
 pq_copymsgbytes(StringInfo msg, char *buf, int datalen)
 {
-	if (datalen > (msg->len - msg->cursor))
+	if (datalen < 0 || datalen > (msg->len - msg->cursor))
 		elog(ERROR, "pq_copymsgbytes: insufficient data left in message");
 	memcpy(buf, &msg->data[msg->cursor], datalen);
 	msg->cursor += datalen;
diff --git a/src/backend/tcop/dest.c b/src/backend/tcop/dest.c
index bce77603f5b9b0ed60de3675e59cf9b813be2056..a590cffd35a273e8a10e93e88f98ae4d1aea53ea 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.57 2003/05/06 20:26:27 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.58 2003/05/08 18:16:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -30,8 +30,10 @@
 
 #include "access/printtup.h"
 #include "access/xact.h"
+#include "executor/tstoreReceiver.h"
 #include "libpq/libpq.h"
 #include "libpq/pqformat.h"
+#include "utils/portal.h"
 
 
 /* ----------------
@@ -44,8 +46,7 @@ donothingReceive(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
 }
 
 static void
-donothingStartup(DestReceiver *self, int operation,
-				 const char *portalName, TupleDesc typeinfo, List *targetlist)
+donothingStartup(DestReceiver *self, int operation, TupleDesc typeinfo)
 {
 }
 
@@ -90,18 +91,21 @@ BeginCommand(const char *commandTag, CommandDest dest)
 
 /* ----------------
  *		CreateDestReceiver - return appropriate receiver function set for dest
+ *
+ * Note: a Portal must be specified for destinations Remote, RemoteExecute,
+ * and Tuplestore.  It can be NULL for the others.
  * ----------------
  */
 DestReceiver *
-CreateDestReceiver(CommandDest dest)
+CreateDestReceiver(CommandDest dest, Portal portal)
 {
 	switch (dest)
 	{
 		case Remote:
-		case RemoteInternal:
 		case RemoteExecute:
-		case RemoteExecuteInternal:
-			return printtup_create_DR(dest);
+			if (portal == NULL)
+				elog(ERROR, "CreateDestReceiver: no portal specified");
+			return printtup_create_DR(dest, portal);
 
 		case None:
 			return &donothingDR;
@@ -113,12 +117,13 @@ CreateDestReceiver(CommandDest dest)
 			return &spi_printtupDR;
 
 		case Tuplestore:
-			/*
-			 * This is disallowed, you must use tstoreReceiver.c's
-			 * specialized function to create a Tuplestore DestReceiver
-			 */
-			elog(ERROR, "CreateDestReceiver: cannot handle Tuplestore");
-			break;
+			if (portal == NULL)
+				elog(ERROR, "CreateDestReceiver: no portal specified");
+			if (portal->holdStore == NULL ||
+				portal->holdContext == NULL)
+				elog(ERROR, "CreateDestReceiver: portal has no holdStore");
+			return CreateTuplestoreDestReceiver(portal->holdStore,
+												portal->holdContext);
 	}
 
 	/* should never get here */
@@ -135,9 +140,7 @@ EndCommand(const char *commandTag, CommandDest dest)
 	switch (dest)
 	{
 		case Remote:
-		case RemoteInternal:
 		case RemoteExecute:
-		case RemoteExecuteInternal:
 			pq_puttextmessage('C', commandTag);
 			break;
 
@@ -167,9 +170,7 @@ NullCommand(CommandDest dest)
 	switch (dest)
 	{
 		case Remote:
-		case RemoteInternal:
 		case RemoteExecute:
-		case RemoteExecuteInternal:
 
 			/*
 			 * tell the fe that we saw an empty query string.  In protocols
@@ -206,9 +207,7 @@ ReadyForQuery(CommandDest dest)
 	switch (dest)
 	{
 		case Remote:
-		case RemoteInternal:
 		case RemoteExecute:
-		case RemoteExecuteInternal:
 			if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
 			{
 				StringInfoData buf;
diff --git a/src/backend/tcop/fastpath.c b/src/backend/tcop/fastpath.c
index 65161c54ff3cb6d2ad63da391862ad0da9223917..0720f4e971df0ca59050a1a2ea6ad52ff03b1d13 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.61 2003/05/05 00:44:56 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/tcop/fastpath.c,v 1.62 2003/05/08 18:16:36 tgl Exp $
  *
  * NOTES
  *	  This cruft is the server side of PQfn.
@@ -47,6 +47,34 @@
 #include "utils/tqual.h"
 
 
+/*
+ * Formerly, this code attempted to cache the function and type info
+ * looked up by fetch_fp_info, but only for the duration of a single
+ * transaction command (since in theory the info could change between
+ * commands).  This was utterly useless, because postgres.c executes
+ * each fastpath call as a separate transaction command, and so the
+ * cached data could never actually have been reused.  If it had worked
+ * as intended, it would have had problems anyway with dangling references
+ * in the FmgrInfo struct.	So, forget about caching and just repeat the
+ * syscache fetches on each usage.	They're not *that* expensive.
+ */
+struct fp_info
+{
+	Oid			funcid;
+	FmgrInfo	flinfo;			/* function lookup info for funcid */
+	int16		arglen[FUNC_MAX_ARGS];
+	bool		argbyval[FUNC_MAX_ARGS];
+	int16		retlen;
+	bool		retbyval;
+};
+
+
+static void parse_fcall_arguments(StringInfo msgBuf, struct fp_info *fip,
+								  FunctionCallInfo fcinfo);
+static void parse_fcall_arguments_20(StringInfo msgBuf, struct fp_info *fip,
+									 FunctionCallInfo fcinfo);
+
+
 /* ----------------
  *		GetOldFunctionMessage
  *
@@ -121,56 +149,72 @@ SendFunctionResult(Datum retval, bool retbyval, int retlen)
 
 	pq_beginmessage(&buf, 'V');
 
-	if (retlen != 0)
+	if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
 	{
-		pq_sendbyte(&buf, 'G');
-		if (retbyval)
-		{						/* by-value */
-			pq_sendint(&buf, retlen, 4);
-			pq_sendint(&buf, DatumGetInt32(retval), retlen);
+		/* New-style message */
+		/* XXX replace this with standard binary (or text!) output */
+		if (retlen != 0)
+		{
+			if (retbyval)
+			{						/* by-value */
+				pq_sendint(&buf, retlen, 4);
+				pq_sendint(&buf, DatumGetInt32(retval), retlen);
+			}
+			else
+			{						/* by-reference ... */
+				if (retlen == -1)
+				{					/* ... varlena */
+					struct varlena *v = PG_DETOAST_DATUM(retval);
+
+					pq_sendint(&buf, VARSIZE(v) - VARHDRSZ, VARHDRSZ);
+					pq_sendbytes(&buf, VARDATA(v), VARSIZE(v) - VARHDRSZ);
+				}
+				else
+				{					/* ... fixed */
+					pq_sendint(&buf, retlen, 4);
+					pq_sendbytes(&buf, DatumGetPointer(retval), retlen);
+				}
+			}
 		}
 		else
-		{						/* by-reference ... */
-			if (retlen == -1)
-			{					/* ... varlena */
-				struct varlena *v = PG_DETOAST_DATUM(retval);
-
-				pq_sendint(&buf, VARSIZE(v) - VARHDRSZ, VARHDRSZ);
-				pq_sendbytes(&buf, VARDATA(v), VARSIZE(v) - VARHDRSZ);
+		{
+			/* NULL marker */
+			pq_sendint(&buf, -1, 4);
+		}
+	}
+	else
+	{
+		/* Old-style message */
+		if (retlen != 0)
+		{
+			pq_sendbyte(&buf, 'G');
+			if (retbyval)
+			{						/* by-value */
+				pq_sendint(&buf, retlen, 4);
+				pq_sendint(&buf, DatumGetInt32(retval), retlen);
 			}
 			else
-			{					/* ... fixed */
-				pq_sendint(&buf, retlen, 4);
-				pq_sendbytes(&buf, DatumGetPointer(retval), retlen);
+			{						/* by-reference ... */
+				if (retlen == -1)
+				{					/* ... varlena */
+					struct varlena *v = PG_DETOAST_DATUM(retval);
+
+					pq_sendint(&buf, VARSIZE(v) - VARHDRSZ, VARHDRSZ);
+					pq_sendbytes(&buf, VARDATA(v), VARSIZE(v) - VARHDRSZ);
+				}
+				else
+				{					/* ... fixed */
+					pq_sendint(&buf, retlen, 4);
+					pq_sendbytes(&buf, DatumGetPointer(retval), retlen);
+				}
 			}
 		}
+		pq_sendbyte(&buf, '0');
 	}
 
-	pq_sendbyte(&buf, '0');
 	pq_endmessage(&buf);
 }
 
-/*
- * Formerly, this code attempted to cache the function and type info
- * looked up by fetch_fp_info, but only for the duration of a single
- * transaction command (since in theory the info could change between
- * commands).  This was utterly useless, because postgres.c executes
- * each fastpath call as a separate transaction command, and so the
- * cached data could never actually have been reused.  If it had worked
- * as intended, it would have had problems anyway with dangling references
- * in the FmgrInfo struct.	So, forget about caching and just repeat the
- * syscache fetches on each usage.	They're not *that* expensive.
- */
-struct fp_info
-{
-	Oid			funcid;
-	FmgrInfo	flinfo;			/* function lookup info for funcid */
-	int16		arglen[FUNC_MAX_ARGS];
-	bool		argbyval[FUNC_MAX_ARGS];
-	int16		retlen;
-	bool		retbyval;
-};
-
 /*
  * fetch_fp_info
  *
@@ -262,11 +306,9 @@ int
 HandleFunctionRequest(StringInfo msgBuf)
 {
 	Oid			fid;
-	int			nargs;
 	AclResult	aclresult;
 	FunctionCallInfoData fcinfo;
 	Datum		retval;
-	int			i;
 	struct fp_info my_fp;
 	struct fp_info *fip;
 
@@ -294,9 +336,10 @@ HandleFunctionRequest(StringInfo msgBuf)
 	/*
 	 * Parse the buffer contents.
 	 */
-	(void) pq_getmsgstring(msgBuf);	/* dummy string */
+	if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
+		(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
@@ -316,19 +359,61 @@ HandleFunctionRequest(StringInfo msgBuf)
 	SetQuerySnapshot();
 
 	/*
-	 * Prepare function call info block.
+	 * Prepare function call info block and insert arguments.
 	 */
+	MemSet(&fcinfo, 0, sizeof(fcinfo));
+	fcinfo.flinfo = &fip->flinfo;
+
+	if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
+		parse_fcall_arguments(msgBuf, fip, &fcinfo);
+	else
+		parse_fcall_arguments_20(msgBuf, fip, &fcinfo);
+
+	/* Verify we reached the end of the message where expected. */
+	pq_getmsgend(msgBuf);
+
+	/* Okay, do it ... */
+	retval = FunctionCallInvoke(&fcinfo);
+
+	if (fcinfo.isnull)
+		SendFunctionResult(retval, fip->retbyval, 0);
+	else
+		SendFunctionResult(retval, fip->retbyval, fip->retlen);
+
+	return 0;
+}
+
+/*
+ * Parse function arguments in a 3.0 protocol message
+ */
+static void
+parse_fcall_arguments(StringInfo msgBuf, struct fp_info *fip,
+					  FunctionCallInfo fcinfo)
+{
+	int			nargs;
+	int			i;
+	int			numAFormats;
+	int16	   *aformats = NULL;
+
+	/* Get the argument format codes */
+	numAFormats = pq_getmsgint(msgBuf, 2);
+	if (numAFormats > 0)
+	{
+		aformats = (int16 *) palloc(numAFormats * sizeof(int16));
+		for (i = 0; i < numAFormats; i++)
+			aformats[i] = pq_getmsgint(msgBuf, 2);
+	}
+
+	nargs = pq_getmsgint(msgBuf, 2);	/* # of arguments */
+
 	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;
-	fcinfo.nargs = nargs;
+	fcinfo->nargs = nargs;
 
 	/*
-	 * Copy supplied arguments into arg vector.  Note there is no way for
-	 * frontend to specify a NULL argument --- this protocol is misdesigned.
+	 * Copy supplied arguments into arg vector.
 	 */
 	for (i = 0; i < nargs; ++i)
 	{
@@ -342,7 +427,7 @@ HandleFunctionRequest(StringInfo msgBuf)
 				elog(ERROR, "HandleFunctionRequest: bogus argsize %d",
 					 argsize);
 			/* XXX should we demand argsize == fip->arglen[i] ? */
-			fcinfo.arg[i] = (Datum) pq_getmsgint(msgBuf, argsize);
+			fcinfo->arg[i] = (Datum) pq_getmsgint(msgBuf, argsize);
 		}
 		else
 		{						/* by-reference ... */
@@ -363,25 +448,70 @@ HandleFunctionRequest(StringInfo msgBuf)
 				p = palloc(argsize + 1);		/* +1 in case argsize is 0 */
 				pq_copymsgbytes(msgBuf, p, argsize);
 			}
-			fcinfo.arg[i] = PointerGetDatum(p);
+			fcinfo->arg[i] = PointerGetDatum(p);
 		}
 	}
 
-	/* Verify we reached the end of the message where expected. */
-	pq_getmsgend(msgBuf);
+	/* XXX for the moment, ignore result format code */
+	(void) pq_getmsgint(msgBuf, 2);
+}
 
-#ifdef NO_FASTPATH
-	/* force a NULL return */
-	retval = (Datum) 0;
-	fcinfo.isnull = true;
-#else
-	retval = FunctionCallInvoke(&fcinfo);
-#endif   /* NO_FASTPATH */
+/*
+ * Parse function arguments in a 2.0 protocol message
+ */
+static void
+parse_fcall_arguments_20(StringInfo msgBuf, struct fp_info *fip,
+						 FunctionCallInfo fcinfo)
+{
+	int			nargs;
+	int			i;
 
-	if (fcinfo.isnull)
-		SendFunctionResult(retval, fip->retbyval, 0);
-	else
-		SendFunctionResult(retval, fip->retbyval, fip->retlen);
+	nargs = pq_getmsgint(msgBuf, 4);	/* # of arguments */
 
-	return 0;
+	if (fip->flinfo.fn_nargs != nargs || nargs > FUNC_MAX_ARGS)
+		elog(ERROR, "HandleFunctionRequest: actual arguments (%d) != registered arguments (%d)",
+			 nargs, fip->flinfo.fn_nargs);
+
+	fcinfo->nargs = nargs;
+
+	/*
+	 * Copy supplied arguments into arg vector.  Note there is no way for
+	 * frontend to specify a NULL argument --- this protocol is misdesigned.
+	 */
+	for (i = 0; i < nargs; ++i)
+	{
+		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] ? */
+			fcinfo->arg[i] = (Datum) pq_getmsgint(msgBuf, argsize);
+		}
+		else
+		{						/* by-reference ... */
+			if (fip->arglen[i] == -1)
+			{					/* ... varlena */
+				if (argsize < 0)
+					elog(ERROR, "HandleFunctionRequest: bogus argsize %d",
+						 argsize);
+				p = palloc(argsize + VARHDRSZ);
+				VARATT_SIZEP(p) = argsize + VARHDRSZ;
+				pq_copymsgbytes(msgBuf, VARDATA(p), argsize);
+			}
+			else
+			{					/* ... fixed */
+				if (argsize != fip->arglen[i])
+					elog(ERROR, "HandleFunctionRequest: bogus argsize %d, should be %d",
+						 argsize, fip->arglen[i]);
+				p = palloc(argsize + 1);		/* +1 in case argsize is 0 */
+				pq_copymsgbytes(msgBuf, p, argsize);
+			}
+			fcinfo->arg[i] = PointerGetDatum(p);
+		}
+	}
 }
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 895645587085cfb2d31c45bc5bc1f4df44911395..63b08dc969e18149b015c9d654c22eddc0b349f7 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.339 2003/05/08 14:49:04 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.340 2003/05/08 18:16:36 tgl Exp $
  *
  * NOTES
  *	  this is the "main" module of the postgres backend and
@@ -658,7 +658,6 @@ static void
 exec_simple_query(const char *query_string)
 {
 	CommandDest	dest = whereToSendOutput;
-	DestReceiver *receiver;
 	MemoryContext oldcontext;
 	List	   *parsetree_list,
 			   *parsetree_item;
@@ -685,12 +684,6 @@ exec_simple_query(const char *query_string)
 	if (save_log_statement_stats)
 		ResetUsage();
 
-	/*
-	 * Create destination receiver object --- we can reuse it for all
-	 * queries in the string.  Note it is created in MessageContext.
-	 */
-	receiver = CreateDestReceiver(dest);
-
 	/*
 	 * Start up a transaction command.	All queries generated by the
 	 * query_string will be in this same command block, *unless* we find a
@@ -743,6 +736,8 @@ exec_simple_query(const char *query_string)
 		List	   *querytree_list,
 				   *plantree_list;
 		Portal		portal;
+		DestReceiver *receiver;
+		int16		format;
 
 		/*
 		 * Get the command name for use in status display (it also becomes the
@@ -803,11 +798,6 @@ exec_simple_query(const char *query_string)
 		/* If we got a cancel signal in analysis or planning, quit */
 		CHECK_FOR_INTERRUPTS();
 
-		/*
-		 * Switch back to transaction context for execution.
-		 */
-		MemoryContextSwitchTo(oldcontext);
-
 		/*
 		 * Create unnamed portal to run the query or queries in.
 		 * If there already is one, silently drop it.
@@ -822,16 +812,53 @@ exec_simple_query(const char *query_string)
 						  MessageContext);
 
 		/*
-		 * Run the portal to completion, and then drop it.
+		 * Start the portal.  No parameters here.
 		 */
 		PortalStart(portal, NULL);
 
+		/*
+		 * Select the appropriate output format: text unless we are doing
+		 * a FETCH from a binary cursor.  (Pretty grotty to have to do this
+		 * here --- but it avoids grottiness in other places.  Ah, the joys
+		 * of backward compatibility...)
+		 */
+		format = 0;				/* TEXT is default */
+		if (IsA(parsetree, FetchStmt))
+		{
+			FetchStmt  *stmt = (FetchStmt *) parsetree;
+
+			if (!stmt->ismove)
+			{
+				Portal		fportal = GetPortalByName(stmt->portalname);
+
+				if (PortalIsValid(fportal) &&
+					(fportal->cursorOptions & CURSOR_OPT_BINARY))
+					format = 1;		/* BINARY */
+			}
+		}
+		PortalSetResultFormat(portal, 1, &format);
+
+		/*
+		 * Now we can create the destination receiver object.
+		 */
+		receiver = CreateDestReceiver(dest, portal);
+
+		/*
+		 * Switch back to transaction context for execution.
+		 */
+		MemoryContextSwitchTo(oldcontext);
+
+		/*
+		 * Run the portal to completion, and then drop it (and the receiver).
+		 */
 		(void) PortalRun(portal,
 						 FETCH_ALL,
 						 receiver,
 						 receiver,
 						 completionTag);
 
+		(*receiver->destroy) (receiver);
+
 		PortalDrop(portal, false);
 
 
@@ -886,8 +913,6 @@ exec_simple_query(const char *query_string)
 	if (!parsetree_list)
 		NullCommand(dest);
 
-	(*receiver->destroy) (receiver);
-
 	QueryContext = NULL;
 
 	/*
@@ -1156,8 +1181,12 @@ exec_bind_message(StringInfo input_message)
 {
 	const char *portal_name;
 	const char *stmt_name;
-	int			is_binary;
+	int			numPFormats;
+	int16	   *pformats = NULL;
 	int			numParams;
+	int			numRFormats;
+	int16	   *rformats = NULL;
+	int			i;
 	PreparedStatement *pstmt;
 	Portal		portal;
 	ParamListInfo params;
@@ -1173,14 +1202,28 @@ exec_bind_message(StringInfo input_message)
 	 */
 	start_xact_command();
 
+	/* Switch back to message context */
+	MemoryContextSwitchTo(MessageContext);
+
 	/* Get the fixed part of the message */
 	portal_name = pq_getmsgstring(input_message);
 	stmt_name = pq_getmsgstring(input_message);
-	is_binary = pq_getmsgbyte(input_message);
-	numParams = pq_getmsgint(input_message, 4);
 
-	if (is_binary)
-		elog(ERROR, "Binary BIND not implemented yet");
+	/* Get the parameter format codes */
+	numPFormats = pq_getmsgint(input_message, 2);
+	if (numPFormats > 0)
+	{
+		pformats = (int16 *) palloc(numPFormats * sizeof(int16));
+		for (i = 0; i < numPFormats; i++)
+			pformats[i] = pq_getmsgint(input_message, 2);
+	}
+
+	/* Get the parameter value count */
+	numParams = pq_getmsgint(input_message, 2);
+
+	if (numPFormats > 1 && numPFormats != numParams)
+		elog(ERROR, "BIND message has %d parameter formats but %d parameters",
+			 numPFormats, numParams);
 
 	/* Find prepared statement */
 	if (stmt_name[0] != '\0')
@@ -1217,45 +1260,98 @@ exec_bind_message(StringInfo input_message)
 	 * Fetch parameters, if any, and store in the portal's memory context.
 	 *
 	 * In an aborted transaction, we can't risk calling user-defined functions,
-	 * so bind all parameters to null values.
+	 * but we can't fail to Bind either, so bind all parameters to null values.
 	 */
 	if (numParams > 0)
 	{
 		bool	isaborted = IsAbortedTransactionBlockState();
-		int		i = 0;
+		StringInfoData pbuf;
 		List   *l;
 		MemoryContext oldContext;
 
+		/* Note that the string buffer lives in MessageContext */
+		initStringInfo(&pbuf);
+
 		oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
 
 		params = (ParamListInfo)
 			palloc0((numParams + 1) * sizeof(ParamListInfoData));
 
+		i = 0;
 		foreach(l, pstmt->argtype_list)
 		{
 			Oid			ptype = lfirsto(l);
+			int32		plength;
 			bool		isNull;
 
-			isNull = (pq_getmsgbyte(input_message) != 0) ? false : true;
+			plength = pq_getmsgint(input_message, 4);
+			isNull = (plength == -1);
+
 			if (!isNull)
 			{
-				const char *ptext = pq_getmsgstring(input_message);
+				/* Reset pbuf to empty, and insert raw data into it */
+				pbuf.len = 0;
+				pbuf.data[0] = '\0';
+				pbuf.cursor = 0;
+
+				appendBinaryStringInfo(&pbuf,
+									   pq_getmsgbytes(input_message, plength),
+									   plength);
 
 				if (isaborted)
+				{
+					/* We don't bother to check the format in this case */
 					isNull = true;
+				}
 				else
 				{
-					Oid			typInput;
-					Oid			typElem;
-
-					getTypeInputInfo(ptype, &typInput, &typElem);
-					params[i].value =
-						OidFunctionCall3(typInput,
-										 CStringGetDatum(ptext),
-										 ObjectIdGetDatum(typElem),
-										 Int32GetDatum(-1));
+					int16	pformat;
+
+					if (numPFormats > 1)
+						pformat = pformats[i];
+					else if (numPFormats > 0)
+						pformat = pformats[0];
+					else
+						pformat = 0;		/* default = text */
+
+					if (pformat == 0)
+					{
+						Oid			typInput;
+						Oid			typElem;
+						char	   *pstring;
+
+						getTypeInputInfo(ptype, &typInput, &typElem);
+						/*
+						 * Since stringinfo.c keeps a trailing null in
+						 * place even for binary data, the contents of
+						 * pbuf are a valid C string.  We have to do
+						 * encoding conversion before calling the typinput
+						 * routine, though.
+						 */
+						pstring = (char *)
+							pg_client_to_server((unsigned char *) pbuf.data,
+												plength);
+						params[i].value =
+							OidFunctionCall3(typInput,
+											 CStringGetDatum(pstring),
+											 ObjectIdGetDatum(typElem),
+											 Int32GetDatum(-1));
+						/* Free result of encoding conversion, if any */
+						if (pstring != pbuf.data)
+							pfree(pstring);
+					}
+					else if (pformat == 1)
+					{
+						/* XXX something similar to above */
+						elog(ERROR, "Binary BIND not implemented yet");
+					}
+					else
+					{
+						elog(ERROR, "Invalid format code %d", pformat);
+					}
 				}
 			}
+
 			params[i].kind = PARAM_NUM;
 			params[i].id = i + 1;
 			params[i].isnull = isNull;
@@ -1270,6 +1366,15 @@ exec_bind_message(StringInfo input_message)
 	else
 		params = NULL;
 
+	/* Get the result format codes */
+	numRFormats = pq_getmsgint(input_message, 2);
+	if (numRFormats > 0)
+	{
+		rformats = (int16 *) palloc(numRFormats * sizeof(int16));
+		for (i = 0; i < numRFormats; i++)
+			rformats[i] = pq_getmsgint(input_message, 2);
+	}
+
 	pq_getmsgend(input_message);
 
 	/*
@@ -1277,6 +1382,11 @@ exec_bind_message(StringInfo input_message)
 	 */
 	PortalStart(portal, params);
 
+	/*
+	 * Apply the result format requests to the portal.
+	 */
+	PortalSetResultFormat(portal, numRFormats, rformats);
+
 	/*
 	 * Send BindComplete.
 	 */
@@ -1290,7 +1400,7 @@ exec_bind_message(StringInfo input_message)
  * Process an "Execute" message for a portal
  */
 static void
-exec_execute_message(const char *portal_name, int is_binary, long max_rows)
+exec_execute_message(const char *portal_name, long max_rows)
 {
 	CommandDest	dest;
 	DestReceiver *receiver;
@@ -1303,7 +1413,7 @@ exec_execute_message(const char *portal_name, int is_binary, long max_rows)
 	/* Adjust destination to tell printtup.c what to do */
 	dest = whereToSendOutput;
 	if (dest == Remote)
-		dest = is_binary ? RemoteExecuteInternal : RemoteExecute;
+		dest = RemoteExecute;
 
 	portal = GetPortalByName(portal_name);
 	if (!PortalIsValid(portal))
@@ -1352,6 +1462,12 @@ exec_execute_message(const char *portal_name, int is_binary, long max_rows)
 		}
 	}
 
+	/*
+	 * Create dest receiver in MessageContext (we don't want it in transaction
+	 * context, because that may get deleted if portal contains VACUUM).
+	 */
+	receiver = CreateDestReceiver(dest, portal);
+
 	/*
 	 * Ensure we are in a transaction command (this should normally be
 	 * the case already due to prior BIND).
@@ -1375,8 +1491,6 @@ exec_execute_message(const char *portal_name, int is_binary, long max_rows)
 	/*
 	 * Okay to run the portal.
 	 */
-	receiver = CreateDestReceiver(dest);
-
 	if (max_rows <= 0)
 		max_rows = FETCH_ALL;
 
@@ -1451,7 +1565,7 @@ exec_describe_statement_message(const char *stmt_name)
 	 * First describe the parameters...
 	 */
 	pq_beginmessage(&buf, 't');		/* parameter description message type */
-	pq_sendint(&buf, length(pstmt->argtype_list), 4);
+	pq_sendint(&buf, length(pstmt->argtype_list), 2);
 
 	foreach(l, pstmt->argtype_list)
 	{
@@ -1473,7 +1587,7 @@ exec_describe_statement_message(const char *stmt_name)
 			targetlist = ((Query *) lfirst(pstmt->query_list))->targetList;
 		else
 			targetlist = NIL;
-		SendRowDescriptionMessage(tupdesc, targetlist);
+		SendRowDescriptionMessage(tupdesc, targetlist, NULL);
 	}
 	else
 		pq_putemptymessage('n');	/* NoData */
@@ -1502,10 +1616,11 @@ exec_describe_portal_message(const char *portal_name)
 		List   *targetlist;
 
 		if (portal->strategy == PORTAL_ONE_SELECT)
-			targetlist = ((Plan *) lfirst(portal->planTrees))->targetlist;
+			targetlist = ((Query *) lfirst(portal->parseTrees))->targetList;
 		else
 			targetlist = NIL;
-		SendRowDescriptionMessage(portal->tupDesc, targetlist);
+		SendRowDescriptionMessage(portal->tupDesc, targetlist,
+								  portal->formats);
 	}
 	else
 		pq_putemptymessage('n');	/* NoData */
@@ -2397,7 +2512,7 @@ PostgresMain(int argc, char *argv[], const char *username)
 	if (!IsUnderPostmaster)
 	{
 		puts("\nPOSTGRES backend interactive interface ");
-		puts("$Revision: 1.339 $ $Date: 2003/05/08 14:49:04 $\n");
+		puts("$Revision: 1.340 $ $Date: 2003/05/08 18:16:36 $\n");
 	}
 
 	/*
@@ -2613,7 +2728,7 @@ PostgresMain(int argc, char *argv[], const char *username)
 
 					stmt_name = pq_getmsgstring(input_message);
 					query_string = pq_getmsgstring(input_message);
-					numParams = pq_getmsgint(input_message, 4);
+					numParams = pq_getmsgint(input_message, 2);
 					if (numParams > 0)
 					{
 						int		i;
@@ -2640,15 +2755,13 @@ PostgresMain(int argc, char *argv[], const char *username)
 			case 'E':			/* execute */
 				{
 					const char *portal_name;
-					int		is_binary;
 					int		max_rows;
 
 					portal_name = pq_getmsgstring(input_message);
-					is_binary = pq_getmsgbyte(input_message);
 					max_rows = pq_getmsgint(input_message, 4);
 					pq_getmsgend(input_message);
 
-					exec_execute_message(portal_name, is_binary, max_rows);
+					exec_execute_message(portal_name, max_rows);
 				}
 				break;
 
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index e3a37b7310e2f532f088f48929a309ad46052924..bf63dcbc29c2c832ac66fc14476d6366c3f59d19 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -8,14 +8,13 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.63 2003/05/06 21:01:04 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.64 2003/05/08 18:16:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
 #include "executor/executor.h"
-#include "executor/tstoreReceiver.h"
 #include "miscadmin.h"
 #include "tcop/tcopprot.h"
 #include "tcop/pquery.h"
@@ -47,7 +46,6 @@ QueryDesc *
 CreateQueryDesc(Query *parsetree,
 				Plan *plantree,
 				DestReceiver *dest,
-				const char *portalName,
 				ParamListInfo params,
 				bool doInstrument)
 {
@@ -57,7 +55,6 @@ CreateQueryDesc(Query *parsetree,
 	qd->parsetree = parsetree;	/* parse tree */
 	qd->plantree = plantree;	/* plan */
 	qd->dest = dest;			/* output dest */
-	qd->portalName = portalName;	/* name, if dest is a portal */
 	qd->params = params;		/* parameter values passed into query */
 	qd->doInstrument = doInstrument; /* instrumentation wanted? */
 
@@ -89,7 +86,6 @@ FreeQueryDesc(QueryDesc *qdesc)
  *	parsetree: the query tree
  *	plan: the plan tree for the query
  *	params: any parameters needed
- *	portalName: name of portal being used
  *	dest: where to send results
  *	completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
  *		in which to store a command completion status string.
@@ -103,7 +99,6 @@ void
 ProcessQuery(Query *parsetree,
 			 Plan *plan,
 			 ParamListInfo params,
-			 const char *portalName,
 			 DestReceiver *dest,
 			 char *completionTag)
 {
@@ -131,8 +126,7 @@ ProcessQuery(Query *parsetree,
 	/*
 	 * Create the QueryDesc object
 	 */
-	queryDesc = CreateQueryDesc(parsetree, plan, dest, portalName, params,
-								false);
+	queryDesc = CreateQueryDesc(parsetree, plan, dest, params, false);
 
 	/*
 	 * Call ExecStart to prepare the plan for execution
@@ -269,7 +263,6 @@ PortalStart(Portal portal, ParamListInfo params)
 			queryDesc = CreateQueryDesc((Query *) lfirst(portal->parseTrees),
 										(Plan *) lfirst(portal->planTrees),
 										None_Receiver,
-										portal->name,
 										params,
 										false);
 			/*
@@ -281,7 +274,7 @@ PortalStart(Portal portal, ParamListInfo params)
 			 */
 			portal->queryDesc = queryDesc;
 			/*
-			 * Remember tuple descriptor
+			 * Remember tuple descriptor (computed by ExecutorStart)
 			 */
 			portal->tupDesc = queryDesc->tupDesc;
 			/*
@@ -320,6 +313,53 @@ PortalStart(Portal portal, ParamListInfo params)
 	portal->portalReady = true;
 }
 
+/*
+ * PortalSetResultFormat
+ *		Select the format codes for a portal's output.
+ *
+ * This must be run after PortalStart for a portal that will be read by
+ * a Remote or RemoteExecute destination.  It is not presently needed for
+ * other destination types.
+ *
+ * formats[] is the client format request, as per Bind message conventions.
+ */
+void
+PortalSetResultFormat(Portal portal, int nFormats, int16 *formats)
+{
+	int			natts;
+	int			i;
+
+	/* Do nothing if portal won't return tuples */
+	if (portal->tupDesc == NULL)
+		return;
+	natts = portal->tupDesc->natts;
+	/* +1 avoids palloc(0) if no columns */
+	portal->formats = (int16 *)
+		MemoryContextAlloc(PortalGetHeapMemory(portal),
+						   (natts + 1) * sizeof(int16));
+	if (nFormats > 1)
+	{
+		/* format specified for each column */
+		if (nFormats != natts)
+			elog(ERROR, "BIND message has %d result formats but query has %d columns",
+				 nFormats, natts);
+		memcpy(portal->formats, formats, natts * sizeof(int16));
+	} else if (nFormats > 0)
+	{
+		/* single format specified, use for all columns */
+		int16		format1 = formats[0];
+
+		for (i = 0; i < natts; i++)
+			portal->formats[i] = format1;
+	}
+	else
+	{
+		/* use default format for all columns */
+		for (i = 0; i < natts; i++)
+			portal->formats[i] = 0;
+	}
+}
+
 /*
  * PortalRun
  *		Run a portal's query or queries.
@@ -399,8 +439,7 @@ PortalRun(Portal portal, long count,
 				DestReceiver *treceiver;
 
 				PortalCreateHoldStore(portal);
-				treceiver = CreateTuplestoreDestReceiver(portal->holdStore,
-														 portal->holdContext);
+				treceiver = CreateDestReceiver(Tuplestore, portal);
 				PortalRunUtility(portal, lfirst(portal->parseTrees),
 								 treceiver, NULL);
 				(*treceiver->destroy) (treceiver);
@@ -604,16 +643,9 @@ static uint32
 RunFromStore(Portal portal, ScanDirection direction, long count,
 			 DestReceiver *dest)
 {
-	List	   *targetlist;
 	long		current_tuple_count = 0;
 
-	if (portal->strategy == PORTAL_ONE_SELECT)
-		targetlist = ((Plan *) lfirst(portal->planTrees))->targetlist;
-	else
-		targetlist = NIL;
-
-	(*dest->startup) (dest, CMD_SELECT, portal->name, portal->tupDesc,
-					  targetlist);
+	(*dest->startup) (dest, CMD_SELECT, portal->tupDesc);
 
 	if (direction == NoMovementScanDirection)
 	{
@@ -737,11 +769,9 @@ PortalRunMulti(Portal portal,
 	 * but the results will be discarded unless you use "simple Query"
 	 * protocol.
 	 */
-	if (dest->mydest == RemoteExecute ||
-		dest->mydest == RemoteExecuteInternal)
+	if (dest->mydest == RemoteExecute)
 		dest = None_Receiver;
-	if (altdest->mydest == RemoteExecute ||
-		altdest->mydest == RemoteExecuteInternal)
+	if (altdest->mydest == RemoteExecute)
 		altdest = None_Receiver;
 
 	/*
@@ -791,14 +821,14 @@ PortalRunMulti(Portal portal,
 			{
 				/* statement can set tag string */
 				ProcessQuery(query, plan,
-							 portal->portalParams, portal->name,
+							 portal->portalParams,
 							 dest, completionTag);
 			}
 			else
 			{
 				/* stmt added by rewrite cannot set tag */
 				ProcessQuery(query, plan,
-							 portal->portalParams, portal->name,
+							 portal->portalParams,
 							 altdest, NULL);
 			}
 
diff --git a/src/include/access/printtup.h b/src/include/access/printtup.h
index 9d10fe4607bb87b945bf277a2504534cf58f11ac..981b0f26483471058c0f6f0e01d440549ed1df59 100644
--- a/src/include/access/printtup.h
+++ b/src/include/access/printtup.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: printtup.h,v 1.26 2003/05/06 20:26:27 tgl Exp $
+ * $Id: printtup.h,v 1.27 2003/05/08 18:16:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -16,18 +16,19 @@
 
 #include "tcop/dest.h"
 
-extern DestReceiver *printtup_create_DR(CommandDest dest);
+extern DestReceiver *printtup_create_DR(CommandDest dest, Portal portal);
 
-extern void SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist);
+extern void SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist,
+									  int16 *formats);
 
 extern void debugStartup(DestReceiver *self, int operation,
-		   const char *portalName, TupleDesc typeinfo, List *targetlist);
+						 TupleDesc typeinfo);
 extern void debugtup(HeapTuple tuple, TupleDesc typeinfo,
 		 DestReceiver *self);
 
 /* XXX these are really in executor/spi.c */
 extern void spi_dest_startup(DestReceiver *self, int operation,
-		   const char *portalName, TupleDesc typeinfo, List *targetlist);
+							 TupleDesc typeinfo);
 extern void spi_printtup(HeapTuple tuple, TupleDesc typeinfo,
 			 DestReceiver *self);
 
diff --git a/src/include/executor/execdesc.h b/src/include/executor/execdesc.h
index 9c54ed1d6439984a7510751d91a0908aa6cee318..5eecb53b8d2aaea741cb7c5ace746f8c4172ea70 100644
--- a/src/include/executor/execdesc.h
+++ b/src/include/executor/execdesc.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: execdesc.h,v 1.23 2003/05/06 20:26:28 tgl Exp $
+ * $Id: execdesc.h,v 1.24 2003/05/08 18:16:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -34,7 +34,6 @@ typedef struct QueryDesc
 	Query	   *parsetree;		/* rewritten parsetree */
 	Plan	   *plantree;		/* planner's output */
 	DestReceiver *dest;			/* the destination for tuple output */
-	const char *portalName;		/* name of portal, or NULL */
 	ParamListInfo params;		/* param values being passed in */
 	bool		doInstrument;	/* TRUE requests runtime instrumentation */
 
@@ -46,7 +45,7 @@ typedef struct QueryDesc
 
 /* in pquery.c */
 extern QueryDesc *CreateQueryDesc(Query *parsetree, Plan *plantree,
-								  DestReceiver *dest, const char *portalName,
+								  DestReceiver *dest,
 								  ParamListInfo params,
 								  bool doInstrument);
 
diff --git a/src/include/libpq/pqcomm.h b/src/include/libpq/pqcomm.h
index 998b3a391f535d43a619bcde5c430ff50e5ae138..e23b4563edba90e25f9b6ff0ca500e43e2efa892 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.83 2003/05/06 21:51:42 tgl Exp $
+ * $Id: pqcomm.h,v 1.84 2003/05/08 18:16:37 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,107) /* XXX temporary value */
+#define PG_PROTOCOL_LATEST		PG_PROTOCOL(3,108) /* 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 229de38c9b36d3e6213235bea6ee178d37f8cd15..7167c6d63add484301233e87ccbb2f23d9509d7d 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.15 2003/04/22 00:08:07 tgl Exp $
+ * $Id: pqformat.h,v 1.16 2003/05/08 18:16:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,7 +18,8 @@
 extern void pq_beginmessage(StringInfo buf, char msgtype);
 extern void pq_sendbyte(StringInfo buf, int byt);
 extern void pq_sendbytes(StringInfo buf, const char *data, int datalen);
-extern void pq_sendcountedtext(StringInfo buf, const char *str, int slen);
+extern void pq_sendcountedtext(StringInfo buf, const char *str, int slen,
+							   bool countincludesself);
 extern void pq_sendstring(StringInfo buf, const char *str);
 extern void pq_sendint(StringInfo buf, int i, int b);
 extern void pq_endmessage(StringInfo buf);
diff --git a/src/include/tcop/dest.h b/src/include/tcop/dest.h
index d2162e99760243a044809a8289d33483e4a8cfd5..b5721f4593f44c6767a5a483cde8fc155666d679 100644
--- a/src/include/tcop/dest.h
+++ b/src/include/tcop/dest.h
@@ -54,7 +54,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.37 2003/05/06 20:26:28 tgl Exp $
+ * $Id: dest.h,v 1.38 2003/05/08 18:16:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -82,12 +82,9 @@ typedef enum
 	None,						/* results are discarded */
 	Debug,						/* results go to debugging output */
 	Remote,						/* results sent to frontend process */
-	RemoteInternal,				/* results sent to frontend process in
-								 * internal (binary) form */
-	SPI,						/* results sent to SPI manager */
-	Tuplestore,					/* results sent to Tuplestore */
 	RemoteExecute,				/* sent to frontend, in Execute command */
-	RemoteExecuteInternal		/* same, but binary format */
+	SPI,						/* results sent to SPI manager */
+	Tuplestore					/* results sent to Tuplestore */
 } CommandDest;
 
 /* ----------------
@@ -106,13 +103,13 @@ typedef struct _DestReceiver DestReceiver;
 struct _DestReceiver
 {
 	/* Called for each tuple to be output: */
-	void		(*receiveTuple) (HeapTuple tuple, TupleDesc typeinfo,
+	void		(*receiveTuple) (HeapTuple tuple,
+								 TupleDesc typeinfo,
 								 DestReceiver *self);
 	/* Per-executor-run initialization and shutdown: */
-	void		(*startup) (DestReceiver *self, int operation,
-							const char *portalName,
-							TupleDesc typeinfo,
-							List *targetlist);
+	void		(*startup) (DestReceiver *self,
+							int operation,
+							TupleDesc typeinfo);
 	void		(*shutdown) (DestReceiver *self);
 	/* Destroy the receiver object itself (if dynamically allocated) */
 	void		(*destroy) (DestReceiver *self);
@@ -123,10 +120,14 @@ struct _DestReceiver
 
 extern DestReceiver *None_Receiver;	/* permanent receiver for None */
 
+/* This is a forward reference to utils/portal.h */
+
+typedef struct PortalData *Portal;
+
 /* The primary destination management functions */
 
 extern void BeginCommand(const char *commandTag, CommandDest dest);
-extern DestReceiver *CreateDestReceiver(CommandDest dest);
+extern DestReceiver *CreateDestReceiver(CommandDest dest, Portal portal);
 extern void EndCommand(const char *commandTag, CommandDest dest);
 
 /* Additional functions that go with destination management, more or less. */
diff --git a/src/include/tcop/pquery.h b/src/include/tcop/pquery.h
index d9d6a6221d5af75c7ce8e750e0bc486fd9a4cea6..ff9cc9d76acd78e2d31e921c3cb36188f1cc9d84 100644
--- a/src/include/tcop/pquery.h
+++ b/src/include/tcop/pquery.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: pquery.h,v 1.26 2003/05/06 20:26:28 tgl Exp $
+ * $Id: pquery.h,v 1.27 2003/05/08 18:16:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,7 +20,6 @@
 extern void ProcessQuery(Query *parsetree,
 						 Plan *plan,
 						 ParamListInfo params,
-						 const char *portalName,
 						 DestReceiver *dest,
 						 char *completionTag);
 
@@ -28,6 +27,9 @@ extern PortalStrategy ChoosePortalStrategy(List *parseTrees);
 
 extern void PortalStart(Portal portal, ParamListInfo params);
 
+extern void PortalSetResultFormat(Portal portal, int nFormats,
+								  int16 *formats);
+
 extern bool PortalRun(Portal portal, long count,
 					  DestReceiver *dest, DestReceiver *altdest,
 					  char *completionTag);
diff --git a/src/include/utils/portal.h b/src/include/utils/portal.h
index e2617f321ec048568e245421f8a5fa64a9b72766..7da359cbe73b40e42551a010dd8d341038b69e9e 100644
--- a/src/include/utils/portal.h
+++ b/src/include/utils/portal.h
@@ -39,7 +39,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: portal.h,v 1.43 2003/05/06 20:26:28 tgl Exp $
+ * $Id: portal.h,v 1.44 2003/05/08 18:16:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -79,7 +79,10 @@ typedef enum PortalStrategy
 	PORTAL_MULTI_QUERY
 } PortalStrategy;
 
-typedef struct PortalData *Portal;
+/*
+ * Note: typedef Portal is declared in tcop/dest.h as
+ *		typedef struct PortalData *Portal;
+ */
 
 typedef struct PortalData
 {
@@ -119,6 +122,8 @@ typedef struct PortalData
 
 	/* If portal returns tuples, this is their tupdesc: */
 	TupleDesc	tupDesc;		/* descriptor for result tuples */
+	/* and these are the format codes to use for the columns: */
+	int16	   *formats;		/* a format code for each column */
 
 	/*
 	 * Where we store tuples for a held cursor or a PORTAL_UTIL_SELECT query.
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index db513d64ef5df47c325c4be69d326a04fbc67b7c..7d0eb39f96cbe22cb638b0a9a24053ea91cf5379 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.134 2003/04/26 20:22:59 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.135 2003/05/08 18:16:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -55,250 +55,10 @@ static void parseInput(PGconn *conn);
 static void handleSendFailure(PGconn *conn);
 static void handleSyncLoss(PGconn *conn, char id, int msgLength);
 static int	getRowDescriptions(PGconn *conn);
-static int	getAnotherTuple(PGconn *conn, int binary);
+static int	getAnotherTuple(PGconn *conn);
 static int	getParameterStatus(PGconn *conn);
 static int	getNotify(PGconn *conn);
 
-/* ---------------
- * Escaping arbitrary strings to get valid SQL strings/identifiers.
- *
- * Replaces "\\" with "\\\\" and "'" with "''".
- * length is the length of the buffer pointed to by
- * from.  The buffer at to must be at least 2*length + 1 characters
- * long.  A terminating NUL character is written.
- * ---------------
- */
-
-size_t
-PQescapeString(char *to, const char *from, size_t length)
-{
-	const char *source = from;
-	char	   *target = to;
-	unsigned int remaining = length;
-
-	while (remaining > 0)
-	{
-		switch (*source)
-		{
-			case '\\':
-				*target = '\\';
-				target++;
-				*target = '\\';
-				/* target and remaining are updated below. */
-				break;
-
-			case '\'':
-				*target = '\'';
-				target++;
-				*target = '\'';
-				/* target and remaining are updated below. */
-				break;
-
-			default:
-				*target = *source;
-				/* target and remaining are updated below. */
-		}
-		source++;
-		target++;
-		remaining--;
-	}
-
-	/* Write the terminating NUL character. */
-	*target = '\0';
-
-	return target - to;
-}
-
-/*
- *		PQescapeBytea	- converts from binary string to the
- *		minimal encoding necessary to include the string in an SQL
- *		INSERT statement with a bytea type column as the target.
- *
- *		The following transformations are applied
- *		'\0' == ASCII  0 == \\000
- *		'\'' == ASCII 39 == \'
- *		'\\' == ASCII 92 == \\\\
- *		anything >= 0x80 ---> \\ooo (where ooo is an octal expression)
- */
-unsigned char *
-PQescapeBytea(const unsigned char *bintext, size_t binlen, size_t *bytealen)
-{
-	const unsigned char *vp;
-	unsigned char *rp;
-	unsigned char *result;
-	size_t		i;
-	size_t		len;
-
-	/*
-	 * empty string has 1 char ('\0')
-	 */
-	len = 1;
-
-	vp = bintext;
-	for (i = binlen; i > 0; i--, vp++)
-	{
-		if (*vp == 0 || *vp >= 0x80)
-			len += 5;			/* '5' is for '\\ooo' */
-		else if (*vp == '\'')
-			len += 2;
-		else if (*vp == '\\')
-			len += 4;
-		else
-			len++;
-	}
-
-	rp = result = (unsigned char *) malloc(len);
-	if (rp == NULL)
-		return NULL;
-
-	vp = bintext;
-	*bytealen = len;
-
-	for (i = binlen; i > 0; i--, vp++)
-	{
-		if (*vp == 0 || *vp >= 0x80)
-		{
-			(void) sprintf(rp, "\\\\%03o", *vp);
-			rp += 5;
-		}
-		else if (*vp == '\'')
-		{
-			rp[0] = '\\';
-			rp[1] = '\'';
-			rp += 2;
-		}
-		else if (*vp == '\\')
-		{
-			rp[0] = '\\';
-			rp[1] = '\\';
-			rp[2] = '\\';
-			rp[3] = '\\';
-			rp += 4;
-		}
-		else
-			*rp++ = *vp;
-	}
-	*rp = '\0';
-
-	return result;
-}
-
-/*
- *		PQunescapeBytea - converts the null terminated string representation
- *		of a bytea, strtext, into binary, filling a buffer. It returns a
- *		pointer to the buffer which is NULL on error, and the size of the
- *		buffer in retbuflen. The pointer may subsequently be used as an
- *		argument to the function free(3). It is the reverse of PQescapeBytea.
- *
- *		The following transformations are reversed:
- *		'\0' == ASCII  0 == \000
- *		'\'' == ASCII 39 == \'
- *		'\\' == ASCII 92 == \\
- *
- *		States:
- *		0	normal		0->1->2->3->4
- *		1	\			   1->5
- *		2	\0			   1->6
- *		3	\00
- *		4	\000
- *		5	\'
- *		6	\\
- */
-unsigned char *
-PQunescapeBytea(const unsigned char *strtext, size_t *retbuflen)
-{
-	size_t		buflen;
-	unsigned char *buffer,
-			   *bp;
-	const unsigned char *sp;
-	unsigned int state = 0;
-
-	if (strtext == NULL)
-		return NULL;
-	buflen = strlen(strtext);	/* will shrink, also we discover if
-								 * strtext */
-	buffer = (unsigned char *) malloc(buflen);	/* isn't NULL terminated */
-	if (buffer == NULL)
-		return NULL;
-	for (bp = buffer, sp = strtext; *sp != '\0'; bp++, sp++)
-	{
-		switch (state)
-		{
-			case 0:
-				if (*sp == '\\')
-					state = 1;
-				*bp = *sp;
-				break;
-			case 1:
-				if (*sp == '\'')	/* state=5 */
-				{				/* replace \' with 39 */
-					bp--;
-					*bp = '\'';
-					buflen--;
-					state = 0;
-				}
-				else if (*sp == '\\')	/* state=6 */
-				{				/* replace \\ with 92 */
-					bp--;
-					*bp = '\\';
-					buflen--;
-					state = 0;
-				}
-				else
-				{
-					if (isdigit(*sp))
-						state = 2;
-					else
-						state = 0;
-					*bp = *sp;
-				}
-				break;
-			case 2:
-				if (isdigit(*sp))
-					state = 3;
-				else
-					state = 0;
-				*bp = *sp;
-				break;
-			case 3:
-				if (isdigit(*sp))		/* state=4 */
-				{
-					int			v;
-
-					bp -= 3;
-					sscanf(sp - 2, "%03o", &v);
-					*bp = v;
-					buflen -= 3;
-					state = 0;
-				}
-				else
-				{
-					*bp = *sp;
-					state = 0;
-				}
-				break;
-		}
-	}
-	buffer = realloc(buffer, buflen);
-	if (buffer == NULL)
-		return NULL;
-
-	*retbuflen = buflen;
-	return buffer;
-}
-
-
-/*
- *		PQfreemem - safely frees memory allocated
- *
- * Needed mostly by Win32, unless multithreaded DLL (/MD in VC6)
- * Used for freeing memory from PQescapeByte()a/PQunescapeBytea()
- */
-void PQfreemem(void *ptr)
-{
-	free(ptr);
-}
-
 
 /* ----------------
  * Space management for PGresult.
@@ -909,7 +669,7 @@ parseInput(PGconn *conn)
 			return;
 		}
 		if (msgLength > 30000 &&
-			!(id == 'T' || id == 'D' || id == 'B' || id == 'd'))
+			!(id == 'T' || id == 'D' || id == 'd'))
 		{
 			handleSyncLoss(conn, id, msgLength);
 			return;
@@ -1074,11 +834,11 @@ parseInput(PGconn *conn)
 						return;
 					}
 					break;
-				case 'D':		/* ASCII data tuple */
+				case 'D':		/* Data Row */
 					if (conn->result != NULL)
 					{
 						/* Read another tuple of a normal query response */
-						if (getAnotherTuple(conn, FALSE))
+						if (getAnotherTuple(conn))
 							return;
 					}
 					else
@@ -1090,30 +850,18 @@ parseInput(PGconn *conn)
 						conn->inCursor += msgLength;
 					}
 					break;
-				case 'B':		/* Binary data tuple */
-					if (conn->result != NULL)
-					{
-						/* Read another tuple of a normal query response */
-						if (getAnotherTuple(conn, TRUE))
-							return;
-					}
-					else
-					{
-						snprintf(noticeWorkspace, sizeof(noticeWorkspace),
-								 libpq_gettext("server sent binary data (\"B\" message) without prior row description (\"T\" message)\n"));
-						DONOTICE(conn, noticeWorkspace);
-						/* Discard the unexpected message */
-						conn->inCursor += msgLength;
-					}
-					break;
 				case 'G':		/* Start Copy In */
 					if (pqGetc(&conn->copy_is_binary, conn))
 						return;
+					/* XXX we currently ignore the rest of the message */
+					conn->inCursor = conn->inStart + 5 + msgLength;
 					conn->asyncStatus = PGASYNC_COPY_IN;
 					break;
 				case 'H':		/* Start Copy Out */
 					if (pqGetc(&conn->copy_is_binary, conn))
 						return;
+					/* XXX we currently ignore the rest of the message */
+					conn->inCursor = conn->inStart + 5 + msgLength;
 					conn->asyncStatus = PGASYNC_COPY_OUT;
 					conn->copy_already_done = 0;
 					break;
@@ -1229,13 +977,15 @@ getRowDescriptions(PGconn *conn)
 		int			typid;
 		int			typlen;
 		int			atttypmod;
+		int			format;
 
 		if (pqGets(&conn->workBuffer, conn) ||
 			pqGetInt(&tableid, 4, conn) ||
 			pqGetInt(&columnid, 2, conn) ||
 			pqGetInt(&typid, 4, conn) ||
 			pqGetInt(&typlen, 2, conn) ||
-			pqGetInt(&atttypmod, 4, conn))
+			pqGetInt(&atttypmod, 4, conn) ||
+			pqGetInt(&format, 2, conn))
 		{
 			PQclear(result);
 			return EOF;
@@ -1247,13 +997,14 @@ getRowDescriptions(PGconn *conn)
 		 */
 		columnid = (int) ((int16) columnid);
 		typlen = (int) ((int16) typlen);
+		format = (int) ((int16) format);
 
 		result->attDescs[i].name = pqResultStrdup(result,
 												  conn->workBuffer.data);
 		result->attDescs[i].typid = typid;
 		result->attDescs[i].typlen = typlen;
 		result->attDescs[i].atttypmod = atttypmod;
-		/* XXX todo: save tableid/columnid too */
+		/* XXX todo: save tableid/columnid, format too */
 	}
 
 	/* Success! */
@@ -1262,7 +1013,7 @@ getRowDescriptions(PGconn *conn)
 }
 
 /*
- * parseInput subroutine to read a 'B' or 'D' (row data) message.
+ * parseInput subroutine to read a 'D' (row data) message.
  * We add another tuple to the existing PGresult structure.
  * Returns: 0 if completed message, EOF if error or not enough data yet.
  *
@@ -1272,23 +1023,14 @@ getRowDescriptions(PGconn *conn)
  */
 
 static int
-getAnotherTuple(PGconn *conn, int binary)
+getAnotherTuple(PGconn *conn)
 {
 	PGresult   *result = conn->result;
 	int			nfields = result->numAttributes;
 	PGresAttValue *tup;
-
-	/* the backend sends us a bitmap of which attributes are null */
-	char		std_bitmap[64]; /* used unless it doesn't fit */
-	char	   *bitmap = std_bitmap;
-	int			i;
-	size_t		nbytes;			/* the number of bytes in bitmap  */
-	char		bmap;			/* One byte of the bitmap */
-	int			bitmap_index;	/* Its index */
-	int			bitcnt;			/* number of bits examined in current byte */
+	int			tupnfields;		/* # fields from tuple */
 	int			vlen;			/* length of the current field value */
-
-	result->binary = binary;
+	int			i;
 
 	/* Allocate tuple space if first time for this data message */
 	if (conn->curTuple == NULL)
@@ -1301,61 +1043,50 @@ getAnotherTuple(PGconn *conn, int binary)
 	}
 	tup = conn->curTuple;
 
-	/* Get the null-value bitmap */
-	nbytes = (nfields + BYTELEN - 1) / BYTELEN;
-	/* malloc() only for unusually large field counts... */
-	if (nbytes > sizeof(std_bitmap))
-		bitmap = (char *) malloc(nbytes);
+	/* Get the field count and make sure it's what we expect */
+	if (pqGetInt(&tupnfields, 2, conn))
+		return EOF;
 
-	if (pqGetnchar(bitmap, nbytes, conn))
-		goto EOFexit;
+	if (tupnfields != nfields)
+	{
+		/* Replace partially constructed result with an error result */
+		pqClearAsyncResult(conn);
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("unexpected field count in D message\n"));
+		saveErrorResult(conn);
+		conn->asyncStatus = PGASYNC_READY;
+		/* Discard the failed message by pretending we read it */
+		return 0;
+	}
 
 	/* Scan the fields */
-	bitmap_index = 0;
-	bmap = bitmap[bitmap_index];
-	bitcnt = 0;
-
 	for (i = 0; i < nfields; i++)
 	{
-		if (!(bmap & 0200))
+		/* get the value length */
+		if (pqGetInt(&vlen, 4, conn))
+			return EOF;
+		if (vlen == -1)
 		{
-			/* if the field value is absent, make it a null string */
+			/* null field */
 			tup[i].value = result->null_field;
 			tup[i].len = NULL_LEN;
+			continue;
 		}
-		else
+		if (vlen < 0)
+			vlen = 0;
+		if (tup[i].value == NULL)
 		{
-			/* get the value length (the first four bytes are for length) */
-			if (pqGetInt(&vlen, 4, conn))
-				goto EOFexit;
-			if (binary == 0)
-				vlen = vlen - 4;
-			if (vlen < 0)
-				vlen = 0;
+			tup[i].value = (char *) pqResultAlloc(result, vlen + 1, false);
 			if (tup[i].value == NULL)
-			{
-				tup[i].value = (char *) pqResultAlloc(result, vlen + 1, (bool) binary);
-				if (tup[i].value == NULL)
-					goto outOfMemory;
-			}
-			tup[i].len = vlen;
-			/* read in the value */
-			if (vlen > 0)
-				if (pqGetnchar((char *) (tup[i].value), vlen, conn))
-					goto EOFexit;
-			/* we have to terminate this ourselves */
-			tup[i].value[vlen] = '\0';
-		}
-		/* advance the bitmap stuff */
-		bitcnt++;
-		if (bitcnt == BYTELEN)
-		{
-			bitmap_index++;
-			bmap = bitmap[bitmap_index];
-			bitcnt = 0;
+				goto outOfMemory;
 		}
-		else
-			bmap <<= 1;
+		tup[i].len = vlen;
+		/* read in the value */
+		if (vlen > 0)
+			if (pqGetnchar((char *) (tup[i].value), vlen, conn))
+				return EOF;
+		/* we have to terminate this ourselves */
+		tup[i].value[vlen] = '\0';
 	}
 
 	/* Success!  Store the completed tuple in the result */
@@ -1364,8 +1095,6 @@ getAnotherTuple(PGconn *conn, int binary)
 	/* and reset for a new message */
 	conn->curTuple = NULL;
 
-	if (bitmap != std_bitmap)
-		free(bitmap);
 	return 0;
 
 outOfMemory:
@@ -1380,13 +1109,8 @@ outOfMemory:
 					  libpq_gettext("out of memory\n"));
 	conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
 	conn->asyncStatus = PGASYNC_READY;
-	/* Discard the failed message --- good idea? */
-	conn->inStart = conn->inEnd;
-
-EOFexit:
-	if (bitmap != std_bitmap)
-		free(bitmap);
-	return EOF;
+	/* Discard the failed message by pretending we read it */
+	return 0;
 }
 
 
@@ -2129,10 +1853,11 @@ PQfn(PGconn *conn,
 		return NULL;
 	}
 
-	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 */
+	if (pqPutMsgStart('F', conn) < 0 ||		/* function call msg */
+		pqPutInt(fnid, 4, conn) < 0 ||		/* function id */
+		pqPutInt(1, 2, conn) < 0 ||			/* # of format codes */
+		pqPutInt(1, 2, conn) < 0 ||			/* format code: BINARY */
+		pqPutInt(nargs, 2, conn) < 0)		/* # of args */
 	{
 		handleSendFailure(conn);
 		return NULL;
@@ -2145,10 +1870,12 @@ PQfn(PGconn *conn,
 			handleSendFailure(conn);
 			return NULL;
 		}
+		if (args[i].len == -1)
+			continue;			/* it's NULL */
 
 		if (args[i].isint)
 		{
-			if (pqPutInt(args[i].u.integer, 4, conn))
+			if (pqPutInt(args[i].u.integer, args[i].len, conn))
 			{
 				handleSendFailure(conn);
 				return NULL;
@@ -2164,6 +1891,12 @@ PQfn(PGconn *conn,
 		}
 	}
 
+	if (pqPutInt(1, 2, conn) < 0)		/* result format code: BINARY */
+	{
+		handleSendFailure(conn);
+		return NULL;
+	}
+
 	if (pqPutMsgEnd(conn) < 0 ||
 		pqFlush(conn))
 	{
@@ -2204,7 +1937,7 @@ PQfn(PGconn *conn,
 			break;
 		}
 		if (msgLength > 30000 &&
-			!(id == 'T' || id == 'D' || id == 'B' || id == 'd' || id == 'V'))
+			!(id == 'T' || id == 'D' || id == 'd' || id == 'V'))
 		{
 			handleSyncLoss(conn, id, msgLength);
 			break;
@@ -2243,16 +1976,13 @@ PQfn(PGconn *conn,
 		switch (id)
 		{
 			case 'V':			/* function result */
-				if (pqGetc(&id, conn))
+				if (pqGetInt(actual_result_len, 4, conn))
 					continue;
-				if (id == 'G')
+				if (*actual_result_len != -1)
 				{
-					/* function returned nonempty value */
-					if (pqGetInt(actual_result_len, 4, conn))
-						continue;
 					if (result_is_int)
 					{
-						if (pqGetInt(result_buf, 4, conn))
+						if (pqGetInt(result_buf, *actual_result_len, conn))
 							continue;
 					}
 					else
@@ -2262,24 +1992,9 @@ PQfn(PGconn *conn,
 									   conn))
 							continue;
 					}
-					if (pqGetc(&id, conn))		/* get the last '0' */
-						continue;
-				}
-				if (id == '0')
-				{
-					/* correctly finished function result message */
-					status = PGRES_COMMAND_OK;
-				}
-				else
-				{
-					/* The backend violates the protocol. */
-					printfPQExpBuffer(&conn->errorMessage,
-							  libpq_gettext("protocol error: id=0x%x\n"),
-									  id);
-					saveErrorResult(conn);
-					conn->inStart += 5 + msgLength;
-					return prepareAsyncResult(conn);
 				}
+				/* correctly finished function result message */
+				status = PGRES_COMMAND_OK;
 				break;
 			case 'E':			/* error return */
 				if (pqGetErrorNotice(conn, true))
@@ -2744,6 +2459,18 @@ PQflush(PGconn *conn)
 	return (pqFlush(conn));
 }
 
+
+/*
+ *		PQfreemem - safely frees memory allocated
+ *
+ * Needed mostly by Win32, unless multithreaded DLL (/MD in VC6)
+ * Used for freeing memory from PQescapeByte()a/PQunescapeBytea()
+ */
+void PQfreemem(void *ptr)
+{
+	free(ptr);
+}
+
 /*
  * PQfreeNotify - free's the memory associated with a PGnotify
  *
@@ -2760,3 +2487,232 @@ PQfreeNotify(PGnotify *notify)
 {
 	PQfreemem(notify);
 }
+
+
+/* ---------------
+ * Escaping arbitrary strings to get valid SQL strings/identifiers.
+ *
+ * Replaces "\\" with "\\\\" and "'" with "''".
+ * length is the length of the buffer pointed to by
+ * from.  The buffer at to must be at least 2*length + 1 characters
+ * long.  A terminating NUL character is written.
+ * ---------------
+ */
+
+size_t
+PQescapeString(char *to, const char *from, size_t length)
+{
+	const char *source = from;
+	char	   *target = to;
+	unsigned int remaining = length;
+
+	while (remaining > 0)
+	{
+		switch (*source)
+		{
+			case '\\':
+				*target = '\\';
+				target++;
+				*target = '\\';
+				/* target and remaining are updated below. */
+				break;
+
+			case '\'':
+				*target = '\'';
+				target++;
+				*target = '\'';
+				/* target and remaining are updated below. */
+				break;
+
+			default:
+				*target = *source;
+				/* target and remaining are updated below. */
+		}
+		source++;
+		target++;
+		remaining--;
+	}
+
+	/* Write the terminating NUL character. */
+	*target = '\0';
+
+	return target - to;
+}
+
+/*
+ *		PQescapeBytea	- converts from binary string to the
+ *		minimal encoding necessary to include the string in an SQL
+ *		INSERT statement with a bytea type column as the target.
+ *
+ *		The following transformations are applied
+ *		'\0' == ASCII  0 == \\000
+ *		'\'' == ASCII 39 == \'
+ *		'\\' == ASCII 92 == \\\\
+ *		anything >= 0x80 ---> \\ooo (where ooo is an octal expression)
+ */
+unsigned char *
+PQescapeBytea(const unsigned char *bintext, size_t binlen, size_t *bytealen)
+{
+	const unsigned char *vp;
+	unsigned char *rp;
+	unsigned char *result;
+	size_t		i;
+	size_t		len;
+
+	/*
+	 * empty string has 1 char ('\0')
+	 */
+	len = 1;
+
+	vp = bintext;
+	for (i = binlen; i > 0; i--, vp++)
+	{
+		if (*vp == 0 || *vp >= 0x80)
+			len += 5;			/* '5' is for '\\ooo' */
+		else if (*vp == '\'')
+			len += 2;
+		else if (*vp == '\\')
+			len += 4;
+		else
+			len++;
+	}
+
+	rp = result = (unsigned char *) malloc(len);
+	if (rp == NULL)
+		return NULL;
+
+	vp = bintext;
+	*bytealen = len;
+
+	for (i = binlen; i > 0; i--, vp++)
+	{
+		if (*vp == 0 || *vp >= 0x80)
+		{
+			(void) sprintf(rp, "\\\\%03o", *vp);
+			rp += 5;
+		}
+		else if (*vp == '\'')
+		{
+			rp[0] = '\\';
+			rp[1] = '\'';
+			rp += 2;
+		}
+		else if (*vp == '\\')
+		{
+			rp[0] = '\\';
+			rp[1] = '\\';
+			rp[2] = '\\';
+			rp[3] = '\\';
+			rp += 4;
+		}
+		else
+			*rp++ = *vp;
+	}
+	*rp = '\0';
+
+	return result;
+}
+
+/*
+ *		PQunescapeBytea - converts the null terminated string representation
+ *		of a bytea, strtext, into binary, filling a buffer. It returns a
+ *		pointer to the buffer which is NULL on error, and the size of the
+ *		buffer in retbuflen. The pointer may subsequently be used as an
+ *		argument to the function free(3). It is the reverse of PQescapeBytea.
+ *
+ *		The following transformations are reversed:
+ *		'\0' == ASCII  0 == \000
+ *		'\'' == ASCII 39 == \'
+ *		'\\' == ASCII 92 == \\
+ *
+ *		States:
+ *		0	normal		0->1->2->3->4
+ *		1	\			   1->5
+ *		2	\0			   1->6
+ *		3	\00
+ *		4	\000
+ *		5	\'
+ *		6	\\
+ */
+unsigned char *
+PQunescapeBytea(const unsigned char *strtext, size_t *retbuflen)
+{
+	size_t		buflen;
+	unsigned char *buffer,
+			   *bp;
+	const unsigned char *sp;
+	unsigned int state = 0;
+
+	if (strtext == NULL)
+		return NULL;
+	buflen = strlen(strtext);	/* will shrink, also we discover if
+								 * strtext */
+	buffer = (unsigned char *) malloc(buflen);	/* isn't NULL terminated */
+	if (buffer == NULL)
+		return NULL;
+	for (bp = buffer, sp = strtext; *sp != '\0'; bp++, sp++)
+	{
+		switch (state)
+		{
+			case 0:
+				if (*sp == '\\')
+					state = 1;
+				*bp = *sp;
+				break;
+			case 1:
+				if (*sp == '\'')	/* state=5 */
+				{				/* replace \' with 39 */
+					bp--;
+					*bp = '\'';
+					buflen--;
+					state = 0;
+				}
+				else if (*sp == '\\')	/* state=6 */
+				{				/* replace \\ with 92 */
+					bp--;
+					*bp = '\\';
+					buflen--;
+					state = 0;
+				}
+				else
+				{
+					if (isdigit(*sp))
+						state = 2;
+					else
+						state = 0;
+					*bp = *sp;
+				}
+				break;
+			case 2:
+				if (isdigit(*sp))
+					state = 3;
+				else
+					state = 0;
+				*bp = *sp;
+				break;
+			case 3:
+				if (isdigit(*sp))		/* state=4 */
+				{
+					int			v;
+
+					bp -= 3;
+					sscanf(sp - 2, "%03o", &v);
+					*bp = v;
+					buflen -= 3;
+					state = 0;
+				}
+				else
+				{
+					*bp = *sp;
+					state = 0;
+				}
+				break;
+		}
+	}
+	buffer = realloc(buffer, buflen);
+	if (buffer == NULL)
+		return NULL;
+
+	*retbuflen = buflen;
+	return buffer;
+}
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 1a25105ead0523d2e89988e0dbf03ce58899b874..b65f47efbf7d8e9f7efe5ceecaac7b96814a78a3 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.68 2003/05/06 21:51:42 tgl Exp $
+ * $Id: libpq-int.h,v 1.69 2003/05/08 18:16:37 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,107) /* XXX temporary value */
+#define PG_PROTOCOL_LIBPQ	PG_PROTOCOL(3,108) /* XXX temporary value */
 
 /*
  * POSTGRES backend dependent Constants.