From 0ac6298bb8ac11690cf3d4ad2537b4261ce93ab0 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Fri, 9 May 2003 18:08:48 +0000
Subject: [PATCH] Implement new-protocol binary I/O support in DataRow, Bind,
 and FunctionCall messages.  Binary I/O is now up and working, but only for a
 small set of datatypes (integers, text, bytea).

---
 src/backend/access/common/printtup.c | 273 +++++++++-----------
 src/backend/commands/copy.c          |  10 +-
 src/backend/executor/spi.c           |   8 +-
 src/backend/tcop/fastpath.c          | 367 +++++++++++++++------------
 src/backend/tcop/postgres.c          |  24 +-
 src/backend/utils/cache/lsyscache.c  |  89 ++++++-
 src/include/utils/lsyscache.h        |   7 +-
 7 files changed, 434 insertions(+), 344 deletions(-)

diff --git a/src/backend/access/common/printtup.c b/src/backend/access/common/printtup.c
index d5a29ef16c..6ad53ddb3c 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.71 2003/05/08 18:16:36 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.72 2003/05/09 18:08:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -42,14 +42,19 @@ static void printtup_destroy(DestReceiver *self);
 
 /* ----------------
  *		Private state for a printtup destination object
+ *
+ * NOTE: finfo is the lookup info for either typoutput or typsend, whichever
+ * we are using for this column.
  * ----------------
  */
 typedef struct
 {								/* Per-attribute information */
-	Oid			typoutput;		/* Oid for the attribute's type output fn */
+	Oid			typoutput;		/* Oid for the type's text output fn */
+	Oid			typsend;		/* Oid for the type's binary output fn */
 	Oid			typelem;		/* typelem value to pass to the output fn */
 	bool		typisvarlena;	/* is it varlena (ie possibly toastable)? */
-	FmgrInfo	finfo;			/* Precomputed call info for typoutput */
+	int16		format;			/* format code for this column */
+	FmgrInfo	finfo;			/* Precomputed call info for output fn */
 } PrinttupAttrInfo;
 
 typedef struct
@@ -219,9 +224,13 @@ SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist, int16 *formats)
 	pq_endmessage(&buf);
 }
 
+/*
+ * Get the lookup info that printtup() needs
+ */
 static void
 printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
 {
+	int16	   *formats = myState->portal->formats;
 	int			i;
 
 	if (myState->myinfo)
@@ -232,15 +241,31 @@ printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
 	if (numAttrs <= 0)
 		return;
 	myState->myinfo = (PrinttupAttrInfo *)
-		palloc(numAttrs * sizeof(PrinttupAttrInfo));
+		palloc0(numAttrs * sizeof(PrinttupAttrInfo));
 	for (i = 0; i < numAttrs; i++)
 	{
 		PrinttupAttrInfo *thisState = myState->myinfo + i;
+		int16		format = (formats ? formats[i] : 0);
 
-		if (getTypeOutputInfo(typeinfo->attrs[i]->atttypid,
-							  &thisState->typoutput, &thisState->typelem,
-							  &thisState->typisvarlena))
+		thisState->format = format;
+		if (format == 0)
+		{
+			getTypeOutputInfo(typeinfo->attrs[i]->atttypid,
+							  &thisState->typoutput,
+							  &thisState->typelem,
+							  &thisState->typisvarlena);
 			fmgr_info(thisState->typoutput, &thisState->finfo);
+		}
+		else if (format == 1)
+		{
+			getTypeBinaryOutputInfo(typeinfo->attrs[i]->atttypid,
+									&thisState->typsend,
+									&thisState->typelem,
+									&thisState->typisvarlena);
+			fmgr_info(thisState->typsend, &thisState->finfo);
+		}
+		else
+			elog(ERROR, "Unsupported format code %d", format);
 	}
 }
 
@@ -252,7 +277,6 @@ 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;
@@ -274,11 +298,9 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
 	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)
@@ -286,46 +308,46 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
 			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,
+		/*
+		 * 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;
+
+		if (thisState->format == 0)
+		{
+			/* Text output */
+			char	   *outputstr;
+
+			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");
+			pq_sendcountedtext(&buf, outputstr, strlen(outputstr), false);
+			pfree(outputstr);
 		}
 		else
 		{
-			elog(ERROR, "Invalid format code %d", format);
+			/* Binary output */
+			bytea	   *outputbytes;
+
+			outputbytes = DatumGetByteaP(FunctionCall2(&thisState->finfo,
+													   attr,
+									ObjectIdGetDatum(thisState->typelem)));
+			/* We assume the result will not have been toasted */
+			pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4);
+			pq_sendbytes(&buf, VARDATA(outputbytes),
+						 VARSIZE(outputbytes) - VARHDRSZ);
+			pfree(outputbytes);
 		}
+
+		/* Clean up detoasted copy, if any */
+		if (attr != origattr)
+			pfree(DatumGetPointer(attr));
 	}
 
 	pq_endmessage(&buf);
@@ -388,34 +410,28 @@ printtup_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
 		origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
 		if (isnull)
 			continue;
-		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,
+		Assert(thisState->format == 0);
+
+		/*
+		 * 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), true);
+		pfree(outputstr);
 
-			pq_sendcountedtext(&buf, outputstr, strlen(outputstr), true);
-
-			/* Clean up detoasted copy, if any */
-			if (attr != origattr)
-				pfree(DatumGetPointer(attr));
-			pfree(outputstr);
-		}
-		else
-		{
-			outputstr = "<unprintable>";
-			pq_sendcountedtext(&buf, outputstr, strlen(outputstr), true);
-		}
+		/* Clean up detoasted copy, if any */
+		if (attr != origattr)
+			pfree(DatumGetPointer(attr));
 	}
 
 	pq_endmessage(&buf);
@@ -508,30 +524,29 @@ debugtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
 		origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
 		if (isnull)
 			continue;
-		if (getTypeOutputInfo(typeinfo->attrs[i]->atttypid,
-							  &typoutput, &typelem, &typisvarlena))
-		{
-			/*
-			 * If we have a toasted datum, forcibly detoast it here to
-			 * avoid memory leakage inside the type's output routine.
-			 */
-			if (typisvarlena)
-				attr = PointerGetDatum(PG_DETOAST_DATUM(origattr));
-			else
-				attr = origattr;
+		getTypeOutputInfo(typeinfo->attrs[i]->atttypid,
+						  &typoutput, &typelem, &typisvarlena);
+		/*
+		 * If we have a toasted datum, forcibly detoast it here to
+		 * avoid memory leakage inside the type's output routine.
+		 */
+		if (typisvarlena)
+			attr = PointerGetDatum(PG_DETOAST_DATUM(origattr));
+		else
+			attr = origattr;
 
-			value = DatumGetCString(OidFunctionCall3(typoutput,
-													 attr,
-											   ObjectIdGetDatum(typelem),
+		value = DatumGetCString(OidFunctionCall3(typoutput,
+												 attr,
+												 ObjectIdGetDatum(typelem),
 						  Int32GetDatum(typeinfo->attrs[i]->atttypmod)));
 
-			printatt((unsigned) i + 1, typeinfo->attrs[i], value);
+		printatt((unsigned) i + 1, typeinfo->attrs[i], value);
 
-			/* Clean up detoasted copy, if any */
-			if (attr != origattr)
-				pfree(DatumGetPointer(attr));
-			pfree(value);
-		}
+		pfree(value);
+
+		/* Clean up detoasted copy, if any */
+		if (attr != origattr)
+			pfree(DatumGetPointer(attr));
 	}
 	printf("\t----\n");
 }
@@ -542,7 +557,7 @@ debugtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
  * 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_20, except we don't use the typout func.
+ * This is largely same as printtup_20, except we use binary formatting.
  * ----------------
  */
 static void
@@ -587,83 +602,41 @@ printtup_internal_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
 	/*
 	 * send the attributes of this tuple
 	 */
-#ifdef IPORTAL_DEBUG
-	fprintf(stderr, "sending tuple with %d atts\n", natts);
-#endif
-
 	for (i = 0; i < natts; ++i)
 	{
 		PrinttupAttrInfo *thisState = myState->myinfo + i;
 		Datum		origattr,
 					attr;
 		bool		isnull;
-		int32		len;
+		bytea	   *outputbytes;
 
 		origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
 		if (isnull)
 			continue;
-		/* send # of bytes, and opaque data */
+
+		Assert(thisState->format == 1);
+
+		/*
+		 * If we have a toasted datum, forcibly detoast it here to
+		 * avoid memory leakage inside the type's output routine.
+		 */
 		if (thisState->typisvarlena)
-		{
-			/*
-			 * If we have a toasted datum, must detoast before sending.
-			 */
 			attr = PointerGetDatum(PG_DETOAST_DATUM(origattr));
-
-			len = VARSIZE(attr) - VARHDRSZ;
-
-			pq_sendint(&buf, len, VARHDRSZ);
-			pq_sendbytes(&buf, VARDATA(attr), len);
-
-#ifdef IPORTAL_DEBUG
-			{
-				char	   *d = VARDATA(attr);
-
-				fprintf(stderr, "length %d data %x %x %x %x\n",
-						len, *d, *(d + 1), *(d + 2), *(d + 3));
-			}
-#endif
-
-			/* Clean up detoasted copy, if any */
-			if (attr != origattr)
-				pfree(DatumGetPointer(attr));
-		}
 		else
-		{
-			/* fixed size or cstring */
 			attr = origattr;
-			len = typeinfo->attrs[i]->attlen;
-			if (len <= 0)
-			{
-				/* it's a cstring */
-				Assert(len == -2 && !typeinfo->attrs[i]->attbyval);
-				len = strlen(DatumGetCString(attr)) + 1;
-			}
-			pq_sendint(&buf, len, sizeof(int32));
-			if (typeinfo->attrs[i]->attbyval)
-			{
-				Datum		datumBuf;
 
-				/*
-				 * We need this horsing around because we don't know how
-				 * shorter data values are aligned within a Datum.
-				 */
-				store_att_byval(&datumBuf, attr, len);
-				pq_sendbytes(&buf, (char *) &datumBuf, len);
-#ifdef IPORTAL_DEBUG
-				fprintf(stderr, "byval length %d data %ld\n", len,
-						(long) attr);
-#endif
-			}
-			else
-			{
-				pq_sendbytes(&buf, DatumGetPointer(attr), len);
-#ifdef IPORTAL_DEBUG
-				fprintf(stderr, "byref length %d data %p\n", len,
-						DatumGetPointer(attr));
-#endif
-			}
-		}
+		outputbytes = DatumGetByteaP(FunctionCall2(&thisState->finfo,
+												   attr,
+									ObjectIdGetDatum(thisState->typelem)));
+		/* We assume the result will not have been toasted */
+		pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4);
+		pq_sendbytes(&buf, VARDATA(outputbytes),
+					 VARSIZE(outputbytes) - VARHDRSZ);
+		pfree(outputbytes);
+
+		/* Clean up detoasted copy, if any */
+		if (attr != origattr)
+			pfree(DatumGetPointer(attr));
 	}
 
 	pq_endmessage(&buf);
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 93d4b8406e..06c29c4894 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.198 2003/05/08 18:16:36 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.199 2003/05/09 18:08:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -828,11 +828,9 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
 		int			attnum = lfirsti(cur);
 		Oid			out_func_oid;
 
-		if (!getTypeOutputInfo(attr[attnum - 1]->atttypid,
-							   &out_func_oid, &elements[attnum - 1],
-							   &isvarlena[attnum - 1]))
-			elog(ERROR, "COPY: couldn't lookup info for type %u",
-				 attr[attnum - 1]->atttypid);
+		getTypeOutputInfo(attr[attnum - 1]->atttypid,
+						  &out_func_oid, &elements[attnum - 1],
+						  &isvarlena[attnum - 1]);
 		fmgr_info(out_func_oid, &out_functions[attnum - 1]);
 		if (binary && attr[attnum - 1]->attlen == -2)
 			elog(ERROR, "COPY BINARY: cstring not supported");
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index fe420ce978..785170b0c5 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.97 2003/05/08 18:16:36 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.98 2003/05/09 18:08:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -546,11 +546,7 @@ SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
 		typmod = -1;
 	}
 
-	if (!getTypeOutputInfo(typoid, &foutoid, &typelem, &typisvarlena))
-	{
-		SPI_result = SPI_ERROR_NOOUTFUNC;
-		return NULL;
-	}
+	getTypeOutputInfo(typoid, &foutoid, &typelem, &typisvarlena);
 
 	/*
 	 * If we have a toasted datum, forcibly detoast it here to avoid
diff --git a/src/backend/tcop/fastpath.c b/src/backend/tcop/fastpath.c
index 0720f4e971..74971c49c5 100644
--- a/src/backend/tcop/fastpath.c
+++ b/src/backend/tcop/fastpath.c
@@ -8,26 +8,11 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/tcop/fastpath.c,v 1.62 2003/05/08 18:16:36 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/tcop/fastpath.c,v 1.63 2003/05/09 18:08:48 tgl Exp $
  *
  * NOTES
  *	  This cruft is the server side of PQfn.
  *
- *	  - jolly 07/11/95:
- *
- *	  no longer rely on return sizes provided by the frontend.	Always
- *	  use the true lengths for the catalogs.  Assume that the frontend
- *	  has allocated enough space to handle the result value returned.
- *
- *	  trust that the user knows what he is doing with the args.  If the
- *	  sys catalog says it is a varlena, assume that the user is only sending
- *	  down VARDATA and that the argsize is the VARSIZE.  If the arg is
- *	  fixed len, assume that the argsize given by the user is correct.
- *
- *	  if the function returns by value, then only send 4 bytes value
- *	  back to the frontend.  If the return returns by reference,
- *	  send down only the data portion and set the return size appropriately.
- *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
@@ -35,11 +20,11 @@
 #include <netinet/in.h>
 #include <arpa/inet.h>
 
-#include "access/xact.h"
 #include "catalog/pg_proc.h"
 #include "libpq/libpq.h"
 #include "libpq/pqformat.h"
 #include "miscadmin.h"
+#include "mb/pg_wchar.h"
 #include "tcop/fastpath.h"
 #include "utils/acl.h"
 #include "utils/lsyscache.h"
@@ -62,17 +47,16 @@ 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;
+	Oid			namespace;		/* other stuff from pg_proc */
+	Oid			rettype;
+	Oid			argtypes[FUNC_MAX_ARGS];
 };
 
 
-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);
+static int16 parse_fcall_arguments(StringInfo msgBuf, struct fp_info *fip,
+								   FunctionCallInfo fcinfo);
+static int16 parse_fcall_arguments_20(StringInfo msgBuf, struct fp_info *fip,
+									  FunctionCallInfo fcinfo);
 
 
 /* ----------------
@@ -114,7 +98,7 @@ GetOldFunctionMessage(StringInfo buf)
 			return EOF;
 		appendBinaryStringInfo(buf, (char *) &ibuf, 4);
 		argsize = ntohl(ibuf);
-		if (argsize < 0)
+		if (argsize < -1)
 		{
 			/* FATAL here since no hope of regaining message sync */
 			elog(FATAL, "HandleFunctionRequest: bogus argsize %d",
@@ -139,79 +123,69 @@ GetOldFunctionMessage(StringInfo buf)
 /* ----------------
  *		SendFunctionResult
  *
- * retlen is 0 if returning NULL, else the typlen according to the catalogs
+ * Note: although this routine doesn't check, the format had better be 1
+ * (binary) when talking to a pre-3.0 client.
  * ----------------
  */
 static void
-SendFunctionResult(Datum retval, bool retbyval, int retlen)
+SendFunctionResult(Datum retval, bool isnull, Oid rettype, int16 format)
 {
+	bool		newstyle = (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3);
 	StringInfoData buf;
 
 	pq_beginmessage(&buf, 'V');
 
-	if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
+	if (isnull)
 	{
-		/* 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
-		{
-			/* NULL marker */
+		if (newstyle)
 			pq_sendint(&buf, -1, 4);
-		}
 	}
 	else
 	{
-		/* Old-style message */
-		if (retlen != 0)
-		{
+		if (!newstyle)
 			pq_sendbyte(&buf, 'G');
-			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);
-				}
-			}
+		if (format == 0)
+		{
+			Oid			typoutput,
+						typelem;
+			bool		typisvarlena;
+			char	   *outputstr;
+
+			getTypeOutputInfo(rettype,
+							  &typoutput, &typelem, &typisvarlena);
+			outputstr = DatumGetCString(OidFunctionCall3(typoutput,
+														 retval,
+												 ObjectIdGetDatum(typelem),
+												 Int32GetDatum(-1)));
+			pq_sendcountedtext(&buf, outputstr, strlen(outputstr), false);
+			pfree(outputstr);
 		}
-		pq_sendbyte(&buf, '0');
+		else if (format == 1)
+		{
+			Oid			typsend,
+						typelem;
+			bool		typisvarlena;
+			bytea	   *outputbytes;
+
+			getTypeBinaryOutputInfo(rettype,
+									&typsend, &typelem, &typisvarlena);
+			outputbytes = DatumGetByteaP(OidFunctionCall2(typsend,
+														  retval,
+												  ObjectIdGetDatum(typelem)));
+			/* We assume the result will not have been toasted */
+			pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4);
+			pq_sendbytes(&buf, VARDATA(outputbytes),
+						 VARSIZE(outputbytes) - VARHDRSZ);
+			pfree(outputbytes);
+		}
+		else
+			elog(ERROR, "Invalid format code %d", format);
 	}
 
+	if (!newstyle)
+		pq_sendbyte(&buf, '0');
+
 	pq_endmessage(&buf);
 }
 
@@ -224,11 +198,8 @@ SendFunctionResult(Datum retval, bool retbyval, int retlen)
 static void
 fetch_fp_info(Oid func_id, struct fp_info * fip)
 {
-	Oid		   *argtypes;		/* an oidvector */
-	Oid			rettype;
 	HeapTuple	func_htp;
 	Form_pg_proc pp;
-	int			i;
 
 	Assert(OidIsValid(func_id));
 	Assert(fip != (struct fp_info *) NULL);
@@ -254,20 +225,10 @@ fetch_fp_info(Oid func_id, struct fp_info * fip)
 		elog(ERROR, "fetch_fp_info: cache lookup for function %u failed",
 			 func_id);
 	pp = (Form_pg_proc) GETSTRUCT(func_htp);
-	rettype = pp->prorettype;
-	argtypes = pp->proargtypes;
 
-	for (i = 0; i < pp->pronargs; ++i)
-	{
-		get_typlenbyval(argtypes[i], &fip->arglen[i], &fip->argbyval[i]);
-		/* We don't support cstring in fastpath protocol */
-		if (fip->arglen[i] == -2)
-			elog(ERROR, "CSTRING not supported in fastpath protocol");
-	}
-
-	get_typlenbyval(rettype, &fip->retlen, &fip->retbyval);
-	if (fip->retlen == -2)
-		elog(ERROR, "CSTRING not supported in fastpath protocol");
+	fip->namespace = pp->pronamespace;
+	fip->rettype = pp->prorettype;
+	memcpy(fip->argtypes, pp->proargtypes, FUNC_MAX_ARGS * sizeof(Oid));
 
 	ReleaseSysCache(func_htp);
 
@@ -308,6 +269,7 @@ HandleFunctionRequest(StringInfo msgBuf)
 	Oid			fid;
 	AclResult	aclresult;
 	FunctionCallInfoData fcinfo;
+	int16		rformat;
 	Datum		retval;
 	struct fp_info my_fp;
 	struct fp_info *fip;
@@ -334,7 +296,7 @@ HandleFunctionRequest(StringInfo msgBuf)
 			 "queries ignored until end of transaction block");
 
 	/*
-	 * Parse the buffer contents.
+	 * Begin parsing the buffer contents.
 	 */
 	if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
 		(void) pq_getmsgstring(msgBuf);	/* dummy string */
@@ -348,7 +310,14 @@ HandleFunctionRequest(StringInfo msgBuf)
 	fip = &my_fp;
 	fetch_fp_info(fid, fip);
 
-	/* Check permission to call function */
+	/*
+	 * Check permission to access and call function.  Since we didn't go
+	 * through a normal name lookup, we need to check schema usage too.
+	 */
+	aclresult = pg_namespace_aclcheck(fip->namespace, GetUserId(), ACL_USAGE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, get_namespace_name(fip->namespace));
+
 	aclresult = pg_proc_aclcheck(fid, GetUserId(), ACL_EXECUTE);
 	if (aclresult != ACLCHECK_OK)
 		aclcheck_error(aclresult, get_func_name(fid));
@@ -365,9 +334,9 @@ HandleFunctionRequest(StringInfo msgBuf)
 	fcinfo.flinfo = &fip->flinfo;
 
 	if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
-		parse_fcall_arguments(msgBuf, fip, &fcinfo);
+		rformat = parse_fcall_arguments(msgBuf, fip, &fcinfo);
 	else
-		parse_fcall_arguments_20(msgBuf, fip, &fcinfo);
+		rformat = parse_fcall_arguments_20(msgBuf, fip, &fcinfo);
 
 	/* Verify we reached the end of the message where expected. */
 	pq_getmsgend(msgBuf);
@@ -375,18 +344,18 @@ HandleFunctionRequest(StringInfo msgBuf)
 	/* Okay, do it ... */
 	retval = FunctionCallInvoke(&fcinfo);
 
-	if (fcinfo.isnull)
-		SendFunctionResult(retval, fip->retbyval, 0);
-	else
-		SendFunctionResult(retval, fip->retbyval, fip->retlen);
+	SendFunctionResult(retval, fcinfo.isnull, fip->rettype, rformat);
 
 	return 0;
 }
 
 /*
  * Parse function arguments in a 3.0 protocol message
+ *
+ * Argument values are loaded into *fcinfo, and the desired result format
+ * is returned.
  */
-static void
+static int16
 parse_fcall_arguments(StringInfo msgBuf, struct fp_info *fip,
 					  FunctionCallInfo fcinfo)
 {
@@ -394,6 +363,7 @@ parse_fcall_arguments(StringInfo msgBuf, struct fp_info *fip,
 	int			i;
 	int			numAFormats;
 	int16	   *aformats = NULL;
+	StringInfoData abuf;
 
 	/* Get the argument format codes */
 	numAFormats = pq_getmsgint(msgBuf, 2);
@@ -412,59 +382,110 @@ parse_fcall_arguments(StringInfo msgBuf, struct fp_info *fip,
 
 	fcinfo->nargs = nargs;
 
+	if (numAFormats > 1 && numAFormats != nargs)
+		elog(ERROR, "Function Call message has %d argument formats but %d arguments",
+			 numAFormats, nargs);
+
+	initStringInfo(&abuf);
+
 	/*
 	 * Copy supplied arguments into arg vector.
 	 */
 	for (i = 0; i < nargs; ++i)
 	{
 		int			argsize;
-		char	   *p;
+		int16		aformat;
 
 		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);
+		if (argsize == -1)
+		{
+			fcinfo->argnull[i] = true;
+			continue;
+		}
+		if (argsize < 0)
+			elog(ERROR, "HandleFunctionRequest: bogus argsize %d",
+				 argsize);
+
+		/* Reset abuf to empty, and insert raw data into it */
+		abuf.len = 0;
+		abuf.data[0] = '\0';
+		abuf.cursor = 0;
+
+		appendBinaryStringInfo(&abuf,
+							   pq_getmsgbytes(msgBuf, argsize),
+							   argsize);
+
+		if (numAFormats > 1)
+			aformat = aformats[i];
+		else if (numAFormats > 0)
+			aformat = aformats[0];
+		else
+			aformat = 0;		/* default = text */
+
+		if (aformat == 0)
+		{
+			Oid			typInput;
+			Oid			typElem;
+			char	   *pstring;
+
+			getTypeInputInfo(fip->argtypes[i], &typInput, &typElem);
+			/*
+			 * Since stringinfo.c keeps a trailing null in
+			 * place even for binary data, the contents of
+			 * abuf 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 *) abuf.data,
+									argsize);
+			fcinfo->arg[i] =
+				OidFunctionCall3(typInput,
+								 CStringGetDatum(pstring),
+								 ObjectIdGetDatum(typElem),
+								 Int32GetDatum(-1));
+			/* Free result of encoding conversion, if any */
+			if (pstring != abuf.data)
+				pfree(pstring);
+		}
+		else if (aformat == 1)
+		{
+			Oid			typReceive;
+			Oid			typElem;
+
+			/* Call the argument type's binary input converter */
+			getTypeBinaryInputInfo(fip->argtypes[i], &typReceive, &typElem);
+
+			fcinfo->arg[i] = OidFunctionCall2(typReceive,
+											  PointerGetDatum(&abuf),
+											  ObjectIdGetDatum(typElem));
+
+			/* Trouble if it didn't eat the whole buffer */
+			if (abuf.cursor != abuf.len)
+				elog(ERROR, "Improper binary format in function argument %d",
+					 i + 1);
 		}
 		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);
-		}
+			elog(ERROR, "Invalid format code %d", aformat);
 	}
 
-	/* XXX for the moment, ignore result format code */
-	(void) pq_getmsgint(msgBuf, 2);
+	/* Return result format code */
+	return (int16) pq_getmsgint(msgBuf, 2);
 }
 
 /*
  * Parse function arguments in a 2.0 protocol message
+ *
+ * Argument values are loaded into *fcinfo, and the desired result format
+ * is returned.
  */
-static void
+static int16
 parse_fcall_arguments_20(StringInfo msgBuf, struct fp_info *fip,
 						 FunctionCallInfo fcinfo)
 {
 	int			nargs;
 	int			i;
+	StringInfoData abuf;
 
 	nargs = pq_getmsgint(msgBuf, 4);	/* # of arguments */
 
@@ -474,44 +495,54 @@ parse_fcall_arguments_20(StringInfo msgBuf, struct fp_info *fip,
 
 	fcinfo->nargs = nargs;
 
+	initStringInfo(&abuf);
+
 	/*
-	 * 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.  In protocol 2.0 these are
+	 * always assumed to be supplied in binary format.
+	 *
+	 * Note: although the original protocol 2.0 code did not have any way for
+	 * the frontend to specify a NULL argument, we now choose to interpret
+	 * length == -1 as meaning a NULL.
 	 */
 	for (i = 0; i < nargs; ++i)
 	{
 		int			argsize;
-		char	   *p;
+		Oid			typReceive;
+		Oid			typElem;
 
 		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);
+		if (argsize == -1)
+		{
+			fcinfo->argnull[i] = true;
+			continue;
 		}
+		if (argsize < 0)
+			elog(ERROR, "HandleFunctionRequest: bogus argsize %d",
+				 argsize);
+
+		/* Reset abuf to empty, and insert raw data into it */
+		abuf.len = 0;
+		abuf.data[0] = '\0';
+		abuf.cursor = 0;
+
+		appendBinaryStringInfo(&abuf,
+							   pq_getmsgbytes(msgBuf, argsize),
+							   argsize);
+
+		/* Call the argument type's binary input converter */
+		getTypeBinaryInputInfo(fip->argtypes[i], &typReceive, &typElem);
+
+		fcinfo->arg[i] = OidFunctionCall2(typReceive,
+										  PointerGetDatum(&abuf),
+										  ObjectIdGetDatum(typElem));
+
+		/* Trouble if it didn't eat the whole buffer */
+		if (abuf.cursor != abuf.len)
+			elog(ERROR, "Improper binary format in function argument %d",
+				 i + 1);
 	}
+
+	/* Desired result format is always binary in protocol 2.0 */
+	return 1;
 }
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index dd235e8765..f444302e96 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.341 2003/05/09 15:57:24 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.342 2003/05/09 18:08:48 tgl Exp $
  *
  * NOTES
  *	  this is the "main" module of the postgres backend and
@@ -1342,8 +1342,21 @@ exec_bind_message(StringInfo input_message)
 					}
 					else if (pformat == 1)
 					{
-						/* XXX something similar to above */
-						elog(ERROR, "Binary BIND not implemented yet");
+						Oid			typReceive;
+						Oid			typElem;
+
+						/* Call the parameter type's binary input converter */
+						getTypeBinaryInputInfo(ptype, &typReceive, &typElem);
+
+						params[i].value =
+							OidFunctionCall2(typReceive,
+											 PointerGetDatum(&pbuf),
+											 ObjectIdGetDatum(typElem));
+
+						/* Trouble if it didn't eat the whole buffer */
+						if (pbuf.cursor != pbuf.len)
+							elog(ERROR, "Improper binary format in BIND parameter %d",
+								 i + 1);
 					}
 					else
 					{
@@ -2511,7 +2524,7 @@ PostgresMain(int argc, char *argv[], const char *username)
 	if (!IsUnderPostmaster)
 	{
 		puts("\nPOSTGRES backend interactive interface ");
-		puts("$Revision: 1.341 $ $Date: 2003/05/09 15:57:24 $\n");
+		puts("$Revision: 1.342 $ $Date: 2003/05/09 18:08:48 $\n");
 	}
 
 	/*
@@ -2771,6 +2784,9 @@ PostgresMain(int argc, char *argv[], const char *username)
 				/* start an xact for this function invocation */
 				start_xact_command();
 
+				/* switch back to message context */
+				MemoryContextSwitchTo(MessageContext);
+
 				if (HandleFunctionRequest(input_message) == EOF)
 				{
 					/* lost frontend connection during F message input */
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 26d06c440f..49e545b684 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.92 2003/04/08 23:20:02 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.93 2003/05/09 18:08:48 tgl Exp $
  *
  * NOTES
  *	  Eventually, the index information should go through here, too.
@@ -1361,11 +1361,15 @@ getTypeInputInfo(Oid type, Oid *typInput, Oid *typElem)
 							   ObjectIdGetDatum(type),
 							   0, 0, 0);
 	if (!HeapTupleIsValid(typeTuple))
-		elog(ERROR, "getTypeInputInfo: Cache lookup of type %u failed", type);
+		elog(ERROR, "Cache lookup of type %u failed", type);
 	pt = (Form_pg_type) GETSTRUCT(typeTuple);
 
 	if (!pt->typisdefined)
-		elog(ERROR, "Type \"%s\" is only a shell", NameStr(pt->typname));
+		elog(ERROR, "Type %s is only a shell",
+			 format_type_be(type));
+	if (!OidIsValid(pt->typinput))
+		elog(ERROR, "No input function available for type %s",
+			 format_type_be(type));
 
 	*typInput = pt->typinput;
 	*typElem = pt->typelem;
@@ -1377,10 +1381,8 @@ getTypeInputInfo(Oid type, Oid *typInput, Oid *typElem)
  * getTypeOutputInfo
  *
  *		Get info needed for printing values of a type
- *
- * Returns true if data valid (a false result probably means it's a shell type)
  */
-bool
+void
 getTypeOutputInfo(Oid type, Oid *typOutput, Oid *typElem,
 				  bool *typIsVarlena)
 {
@@ -1391,14 +1393,85 @@ getTypeOutputInfo(Oid type, Oid *typOutput, Oid *typElem,
 							   ObjectIdGetDatum(type),
 							   0, 0, 0);
 	if (!HeapTupleIsValid(typeTuple))
-		elog(ERROR, "getTypeOutputInfo: Cache lookup of type %u failed", type);
+		elog(ERROR, "Cache lookup of type %u failed", type);
 	pt = (Form_pg_type) GETSTRUCT(typeTuple);
 
+	if (!pt->typisdefined)
+		elog(ERROR, "Type %s is only a shell",
+			 format_type_be(type));
+	if (!OidIsValid(pt->typoutput))
+		elog(ERROR, "No output function available for type %s",
+			 format_type_be(type));
+
 	*typOutput = pt->typoutput;
 	*typElem = pt->typelem;
 	*typIsVarlena = (!pt->typbyval) && (pt->typlen == -1);
+
+	ReleaseSysCache(typeTuple);
+}
+
+/*
+ * getTypeBinaryInputInfo
+ *
+ *		Get info needed for binary input of values of a type
+ */
+void
+getTypeBinaryInputInfo(Oid type, Oid *typReceive, Oid *typElem)
+{
+	HeapTuple	typeTuple;
+	Form_pg_type pt;
+
+	typeTuple = SearchSysCache(TYPEOID,
+							   ObjectIdGetDatum(type),
+							   0, 0, 0);
+	if (!HeapTupleIsValid(typeTuple))
+		elog(ERROR, "Cache lookup of type %u failed", type);
+	pt = (Form_pg_type) GETSTRUCT(typeTuple);
+
+	if (!pt->typisdefined)
+		elog(ERROR, "Type %s is only a shell",
+			 format_type_be(type));
+	if (!OidIsValid(pt->typreceive))
+		elog(ERROR, "No binary input function available for type %s",
+			 format_type_be(type));
+
+	*typReceive = pt->typreceive;
+	*typElem = pt->typelem;
+
+	ReleaseSysCache(typeTuple);
+}
+
+/*
+ * getTypeBinaryOutputInfo
+ *
+ *		Get info needed for binary output of values of a type
+ */
+void
+getTypeBinaryOutputInfo(Oid type, Oid *typSend, Oid *typElem,
+						bool *typIsVarlena)
+{
+	HeapTuple	typeTuple;
+	Form_pg_type pt;
+
+	typeTuple = SearchSysCache(TYPEOID,
+							   ObjectIdGetDatum(type),
+							   0, 0, 0);
+	if (!HeapTupleIsValid(typeTuple))
+		elog(ERROR, "Cache lookup of type %u failed", type);
+	pt = (Form_pg_type) GETSTRUCT(typeTuple);
+
+	if (!pt->typisdefined)
+		elog(ERROR, "Type %s is only a shell",
+			 format_type_be(type));
+	if (!OidIsValid(pt->typsend))
+		elog(ERROR, "No binary output function available for type %s",
+			 format_type_be(type));
+
+	*typSend = pt->typsend;
+	*typElem = pt->typelem;
+	*typIsVarlena = (!pt->typbyval) && (pt->typlen == -1);
+
 	ReleaseSysCache(typeTuple);
-	return OidIsValid(*typOutput);
 }
 
 
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index 610b836131..848cc9f146 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.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: lsyscache.h,v 1.68 2003/04/08 23:20:04 tgl Exp $
+ * $Id: lsyscache.h,v 1.69 2003/05/09 18:08:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -61,8 +61,11 @@ extern Oid	get_typ_typrelid(Oid typid);
 extern Oid	get_element_type(Oid typid);
 extern Oid	get_array_type(Oid typid);
 extern void getTypeInputInfo(Oid type, Oid *typInput, Oid *typElem);
-extern bool getTypeOutputInfo(Oid type, Oid *typOutput, Oid *typElem,
+extern void getTypeOutputInfo(Oid type, Oid *typOutput, Oid *typElem,
 				  bool *typIsVarlena);
+extern void getTypeBinaryInputInfo(Oid type, Oid *typReceive, Oid *typElem);
+extern void getTypeBinaryOutputInfo(Oid type, Oid *typSend, Oid *typElem,
+									bool *typIsVarlena);
 extern Oid	getBaseType(Oid typid);
 extern int32 get_typavgwidth(Oid typid, int32 typmod);
 extern int32 get_attavgwidth(Oid relid, AttrNumber attnum);