diff --git a/doc/src/sgml/ref/create_type.sgml b/doc/src/sgml/ref/create_type.sgml
index 9382395182a9776ef9b677e911c452338a000f02..e3b8b44d8f9ddf5a784fd7815aaff5b0fc7fabab 100644
--- a/doc/src/sgml/ref/create_type.sgml
+++ b/doc/src/sgml/ref/create_type.sgml
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/create_type.sgml,v 1.61 2006/02/28 22:37:25 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/create_type.sgml,v 1.62 2006/04/04 19:35:32 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -97,8 +97,7 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
   <para>
    The <replaceable class="parameter">input_function</replaceable>
    converts the type's external textual representation to the internal
-   representation  used by the
-   operators and functions defined for the type.
+   representation used by the operators and functions defined for the type.
    <replaceable class="parameter">output_function</replaceable>
    performs the reverse transformation.  The input function may be
    declared as taking one argument of type <type>cstring</type>,
@@ -110,9 +109,16 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
    and the third is the <literal>typmod</> of the destination column, if known
    (-1 will be passed if not).
    The input function must return a value of the data type itself.
+   Usually, an input function should be declared STRICT; if it is not,
+   it will be called with a NULL first parameter when reading a NULL
+   input value.  The function must still return NULL in this case, unless
+   it raises an error.
+   (This case is mainly meant to support domain input functions, which
+   may need to reject NULL inputs.)
    The output function must be
    declared as taking one argument of the new data type.
    The output function must return type <type>cstring</type>.
+   Output functions are not invoked for NULL values.
   </para>
 
   <para>
@@ -133,6 +139,12 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
    holding the received byte string; the optional arguments are the
    same as for the text input function.
    The receive function must return a value of the data type itself.
+   Usually, a receive function should be declared STRICT; if it is not,
+   it will be called with a NULL first parameter when reading a NULL
+   input value.  The function must still return NULL in this case, unless
+   it raises an error.
+   (This case is mainly meant to support domain receive functions, which
+   may need to reject NULL inputs.)
    Similarly, the optional
    <replaceable class="parameter">send_function</replaceable> converts
    from the internal representation to the external binary representation.
@@ -140,6 +152,7 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
    output.  The send function must be
    declared as taking one argument of the new data type.
    The send function must return type <type>bytea</type>.
+   Send functions are not invoked for NULL values.
   </para>
 
   <para>
diff --git a/src/backend/access/common/printtup.c b/src/backend/access/common/printtup.c
index 3ba606dfaf1cacc8edc52b4737e50e5c06f77115..ba5793b0e7eb3581c39032b5defb8ece5081bb0a 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
- *	  $PostgreSQL: pgsql/src/backend/access/common/printtup.c,v 1.94 2006/03/05 15:58:20 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/access/common/printtup.c,v 1.95 2006/04/04 19:35:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -331,8 +331,7 @@ printtup(TupleTableSlot *slot, DestReceiver *self)
 			/* Text output */
 			char	   *outputstr;
 
-			outputstr = DatumGetCString(FunctionCall1(&thisState->finfo,
-													  attr));
+			outputstr = OutputFunctionCall(&thisState->finfo, attr);
 			pq_sendcountedtext(&buf, outputstr, strlen(outputstr), false);
 			pfree(outputstr);
 		}
@@ -341,9 +340,7 @@ printtup(TupleTableSlot *slot, DestReceiver *self)
 			/* Binary output */
 			bytea	   *outputbytes;
 
-			outputbytes = DatumGetByteaP(FunctionCall1(&thisState->finfo,
-													   attr));
-			/* We assume the result will not have been toasted */
+			outputbytes = SendFunctionCall(&thisState->finfo, attr);
 			pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4);
 			pq_sendbytes(&buf, VARDATA(outputbytes),
 						 VARSIZE(outputbytes) - VARHDRSZ);
@@ -429,8 +426,7 @@ printtup_20(TupleTableSlot *slot, DestReceiver *self)
 		else
 			attr = origattr;
 
-		outputstr = DatumGetCString(FunctionCall1(&thisState->finfo,
-												  attr));
+		outputstr = OutputFunctionCall(&thisState->finfo, attr);
 		pq_sendcountedtext(&buf, outputstr, strlen(outputstr), true);
 		pfree(outputstr);
 
@@ -542,8 +538,7 @@ debugtup(TupleTableSlot *slot, DestReceiver *self)
 		else
 			attr = origattr;
 
-		value = DatumGetCString(OidFunctionCall1(typoutput,
-												 attr));
+		value = OidOutputFunctionCall(typoutput, attr);
 
 		printatt((unsigned) i + 1, typeinfo->attrs[i], value);
 
@@ -632,8 +627,7 @@ printtup_internal_20(TupleTableSlot *slot, DestReceiver *self)
 		else
 			attr = origattr;
 
-		outputbytes = DatumGetByteaP(FunctionCall1(&thisState->finfo,
-												   attr));
+		outputbytes = SendFunctionCall(&thisState->finfo, attr);
 		/* We assume the result will not have been toasted */
 		pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4);
 		pq_sendbytes(&buf, VARDATA(outputbytes),
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 1d48fc44e18524f4900e4bef817729481a61fe63..c1d16b0e8fccd81bf4d8b93921b8cc6aba734774 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.213 2006/03/07 01:03:12 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.214 2006/04/04 19:35:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -826,11 +826,11 @@ InsertOneValue(char *value, int i)
 		if (ap == NULL)
 			elog(ERROR, "could not find atttypid %u in Typ list", typoid);
 
-		/* XXX this should match getTypeIOParam() */
-		if (ap->am_typ.typtype == 'c')
-			typioparam = typoid;
-		else
+		/* XXX this logic should match getTypeIOParam() */
+		if (OidIsValid(ap->am_typ.typelem))
 			typioparam = ap->am_typ.typelem;
+		else
+			typioparam = typoid;
 
 		typinput = ap->am_typ.typinput;
 		typoutput = ap->am_typ.typoutput;
@@ -850,19 +850,18 @@ InsertOneValue(char *value, int i)
 			elog(ERROR, "type oid %u not found", typoid);
 		elog(DEBUG5, "Typ == NULL, typeindex = %u", typeindex);
 
-		/* XXX there are no composite types in TypInfo */
-		typioparam = TypInfo[typeindex].elem;
+		/* XXX this logic should match getTypeIOParam() */
+		if (OidIsValid(TypInfo[typeindex].elem))
+			typioparam = TypInfo[typeindex].elem;
+		else
+			typioparam = typoid;
 
 		typinput = TypInfo[typeindex].inproc;
 		typoutput = TypInfo[typeindex].outproc;
 	}
 
-	values[i] = OidFunctionCall3(typinput,
-								 CStringGetDatum(value),
-								 ObjectIdGetDatum(typioparam),
-								 Int32GetDatum(-1));
-	prt = DatumGetCString(OidFunctionCall1(typoutput,
-										   values[i]));
+	values[i] = OidInputFunctionCall(typinput, value, typioparam, -1);
+	prt = OidOutputFunctionCall(typoutput, values[i]);
 	elog(DEBUG4, "inserted -> %s", prt);
 	pfree(prt);
 }
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 23c057af8e08da8d154e59c77239a278823193d0..af21d565f1781bbc392c9234324ca58ad6588a30 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.261 2006/03/23 00:19:29 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.262 2006/04/04 19:35:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1382,8 +1382,8 @@ CopyTo(CopyState cstate)
 			{
 				if (!cstate->binary)
 				{
-					string = DatumGetCString(FunctionCall1(&out_functions[attnum - 1],
-														   value));
+					string = OutputFunctionCall(&out_functions[attnum - 1],
+												value);
 					if (cstate->csv_mode)
 						CopyAttributeOutCSV(cstate, string,
 											force_quote[attnum - 1],
@@ -1395,9 +1395,8 @@ CopyTo(CopyState cstate)
 				{
 					bytea	   *outputbytes;
 
-					outputbytes = DatumGetByteaP(FunctionCall1(&out_functions[attnum - 1],
-															   value));
-					/* We assume the result will not have been toasted */
+					outputbytes = SendFunctionCall(&out_functions[attnum - 1],
+												   value);
 					CopySendInt32(cstate, VARSIZE(outputbytes) - VARHDRSZ);
 					CopySendData(cstate, VARDATA(outputbytes),
 								 VARSIZE(outputbytes) - VARHDRSZ);
@@ -1459,6 +1458,13 @@ copy_in_error_callback(void *arg)
 					   cstate->cur_attname, attval);
 			pfree(attval);
 		}
+		else if (cstate->cur_attname)
+		{
+			/* error is relevant to a particular column, value is NULL */
+			errcontext("COPY %s, line %d, column %s: NULL input",
+					   cstate->cur_relname, cstate->cur_lineno,
+					   cstate->cur_attname);
+		}
 		else
 		{
 			/* error is relevant to a particular line */
@@ -1854,19 +1860,16 @@ CopyFrom(CopyState cstate)
 					string = cstate->null_print;
 				}
 
-				/* If we read an SQL NULL, no need to do anything */
+				cstate->cur_attname = NameStr(attr[m]->attname);
+				cstate->cur_attval = string;
+				values[m] = InputFunctionCall(&in_functions[m],
+											  string,
+											  typioparams[m],
+											  attr[m]->atttypmod);
 				if (string != NULL)
-				{
-					cstate->cur_attname = NameStr(attr[m]->attname);
-					cstate->cur_attval = string;
-					values[m] = FunctionCall3(&in_functions[m],
-											  CStringGetDatum(string),
-											ObjectIdGetDatum(typioparams[m]),
-										  Int32GetDatum(attr[m]->atttypmod));
 					nulls[m] = ' ';
-					cstate->cur_attname = NULL;
-					cstate->cur_attval = NULL;
-				}
+				cstate->cur_attname = NULL;
+				cstate->cur_attval = NULL;
 			}
 
 			Assert(fieldno == nfields);
@@ -2900,7 +2903,7 @@ CopyReadBinaryAttribute(CopyState cstate,
 	if (fld_size == -1)
 	{
 		*isnull = true;
-		return (Datum) 0;
+		return ReceiveFunctionCall(flinfo, NULL, typioparam, typmod);
 	}
 	if (fld_size < 0)
 		ereport(ERROR,
@@ -2924,10 +2927,8 @@ CopyReadBinaryAttribute(CopyState cstate,
 	cstate->attribute_buf.data[fld_size] = '\0';
 
 	/* Call the column type's binary input converter */
-	result = FunctionCall3(flinfo,
-						   PointerGetDatum(&cstate->attribute_buf),
-						   ObjectIdGetDatum(typioparam),
-						   Int32GetDatum(typmod));
+	result = ReceiveFunctionCall(flinfo, &cstate->attribute_buf,
+								 typioparam, typmod);
 
 	/* Trouble if it didn't eat the whole buffer */
 	if (cstate->attribute_buf.cursor != cstate->attribute_buf.len)
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index bd50027f776805cc789251cbb8964418b274be6f..81f589fd44b4a81bbbe28d9a9b4cbac4886b765b 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/execTuples.c,v 1.92 2006/03/05 15:58:26 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/execTuples.c,v 1.93 2006/04/04 19:35:34 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -928,6 +928,7 @@ TupleDescGetAttInMetadata(TupleDesc tupdesc)
 /*
  * BuildTupleFromCStrings - build a HeapTuple given user data in C string form.
  * values is an array of C strings, one for each attribute of the return tuple.
+ * A NULL string pointer indicates we want to create a NULL field.
  */
 HeapTuple
 BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
@@ -937,35 +938,25 @@ BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
 	Datum	   *dvalues;
 	char	   *nulls;
 	int			i;
-	Oid			attioparam;
-	int32		atttypmod;
 	HeapTuple	tuple;
 
 	dvalues = (Datum *) palloc(natts * sizeof(Datum));
 	nulls = (char *) palloc(natts * sizeof(char));
 
-	/* Call the "in" function for each non-null, non-dropped attribute */
+	/* Call the "in" function for each non-dropped attribute */
 	for (i = 0; i < natts; i++)
 	{
 		if (!tupdesc->attrs[i]->attisdropped)
 		{
 			/* Non-dropped attributes */
+			dvalues[i] = InputFunctionCall(&attinmeta->attinfuncs[i],
+										   values[i],
+										   attinmeta->attioparams[i],
+										   attinmeta->atttypmods[i]);
 			if (values[i] != NULL)
-			{
-				attioparam = attinmeta->attioparams[i];
-				atttypmod = attinmeta->atttypmods[i];
-
-				dvalues[i] = FunctionCall3(&attinmeta->attinfuncs[i],
-										   CStringGetDatum(values[i]),
-										   ObjectIdGetDatum(attioparam),
-										   Int32GetDatum(atttypmod));
 				nulls[i] = ' ';
-			}
 			else
-			{
-				dvalues[i] = (Datum) 0;
 				nulls[i] = 'n';
-			}
 		}
 		else
 		{
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index 5b48545aac93eeedd52925983e6bc7da9ad53cb3..a242d53968e6a700bada053035ef2732a05efe0d 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -61,7 +61,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/nodeAgg.c,v 1.138 2006/03/05 15:58:26 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/nodeAgg.c,v 1.139 2006/04/04 19:35:34 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1407,10 +1407,8 @@ GetAggInitVal(Datum textInitVal, Oid transtype)
 
 	getTypeInputInfo(transtype, &typinput, &typioparam);
 	strInitVal = DatumGetCString(DirectFunctionCall1(textout, textInitVal));
-	initVal = OidFunctionCall3(typinput,
-							   CStringGetDatum(strInitVal),
-							   ObjectIdGetDatum(typioparam),
-							   Int32GetDatum(-1));
+	initVal = OidInputFunctionCall(typinput, strInitVal,
+								   typioparam, -1);
 	pfree(strInitVal);
 	return initVal;
 }
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 183bc89b020843c195332c3037bd1a495b735bb5..3563ba23d3ad2f7cbb8f2fed207402b3714936b9 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.149 2006/03/14 22:48:19 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.150 2006/04/04 19:35:34 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -629,9 +629,9 @@ SPI_fname(TupleDesc tupdesc, int fnumber)
 char *
 SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
 {
+	char	   *result;
 	Datum		origval,
-				val,
-				result;
+				val;
 	bool		isnull;
 	Oid			typoid,
 				foutoid;
@@ -666,14 +666,13 @@ SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
 	else
 		val = origval;
 
-	result = OidFunctionCall1(foutoid,
-							  val);
+	result = OidOutputFunctionCall(foutoid, val);
 
 	/* Clean up detoasted copy, if any */
 	if (val != origval)
 		pfree(DatumGetPointer(val));
 
-	return DatumGetCString(result);
+	return result;
 }
 
 Datum
diff --git a/src/backend/nodes/print.c b/src/backend/nodes/print.c
index 18b7535cd06fa1352438ade7c1dc1e32efe66361..c3fbdf242d04895c14591603b12252ac204c9597 100644
--- a/src/backend/nodes/print.c
+++ b/src/backend/nodes/print.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/print.c,v 1.78 2006/03/05 15:58:28 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/print.c,v 1.79 2006/04/04 19:35:34 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -357,8 +357,7 @@ print_expr(Node *expr, List *rtable)
 		getTypeOutputInfo(c->consttype,
 						  &typoutput, &typIsVarlena);
 
-		outputstr = DatumGetCString(OidFunctionCall1(typoutput,
-													 c->constvalue));
+		outputstr = OidOutputFunctionCall(typoutput, c->constvalue);
 		printf("%s", outputstr);
 		pfree(outputstr);
 	}
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index bcb8e0016dfa0fe0826599bf78f83e9768705f90..5a343e768dda1a9492fb68f45a4c259e1ae88925 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.135 2006/03/05 15:58:33 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.136 2006/04/04 19:35:34 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -166,26 +166,21 @@ coerce_type(ParseState *pstate, Node *node,
 		newcon->constbyval = typeByVal(targetType);
 		newcon->constisnull = con->constisnull;
 
+		/*
+		 * We pass typmod -1 to the input routine, primarily because
+		 * existing input routines follow implicit-coercion semantics for
+		 * length checks, which is not always what we want here. Any
+		 * length constraint will be applied later by our caller.
+		 *
+		 * We assume here that UNKNOWN's internal representation is the
+		 * same as CSTRING.
+		 */
 		if (!con->constisnull)
-		{
-			/*
-			 * We assume here that UNKNOWN's internal representation is the
-			 * same as CSTRING
-			 */
-			char	   *val = DatumGetCString(con->constvalue);
-
-			/*
-			 * We pass typmod -1 to the input routine, primarily because
-			 * existing input routines follow implicit-coercion semantics for
-			 * length checks, which is not always what we want here. Any
-			 * length constraint will be applied later by our caller.
-			 *
-			 * Note that we call stringTypeDatum using the domain's pg_type
-			 * row, if it's a domain.  This works because the domain row has
-			 * the same typinput and typelem as the base type --- ugly...
-			 */
-			newcon->constvalue = stringTypeDatum(targetType, val, -1);
-		}
+			newcon->constvalue = stringTypeDatum(targetType,
+											DatumGetCString(con->constvalue),
+												 -1);
+		else
+			newcon->constvalue = stringTypeDatum(targetType, NULL, -1);
 
 		result = (Node *) newcon;
 
diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
index e88e6c37c1e1276d0364c8c9045a0bd12409a7a2..fec4552c9c4129c9b9ce467244e8072ec4c93ae7 100644
--- a/src/backend/parser/parse_type.c
+++ b/src/backend/parser/parse_type.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.79 2006/03/14 22:48:21 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.80 2006/04/04 19:35:35 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -326,7 +326,8 @@ typeTypeRelid(Type typ)
 
 /*
  * Given a type structure and a string, returns the internal representation
- * of that string
+ * of that string.  The "string" can be NULL to perform conversion of a NULL
+ * (which might result in failure, if the input function rejects NULLs).
  */
 Datum
 stringTypeDatum(Type tp, char *string, int32 atttypmod)
@@ -336,10 +337,8 @@ stringTypeDatum(Type tp, char *string, int32 atttypmod)
 
 	typinput = ((Form_pg_type) GETSTRUCT(tp))->typinput;
 	typioparam = getTypeIOParam(tp);
-	return OidFunctionCall3(typinput,
-							CStringGetDatum(string),
-							ObjectIdGetDatum(typioparam),
-							Int32GetDatum(atttypmod));
+	return OidInputFunctionCall(typinput, string,
+								typioparam, atttypmod);
 }
 
 /* given a typeid, return the type's typrelid (associated relation, if any) */
diff --git a/src/backend/tcop/fastpath.c b/src/backend/tcop/fastpath.c
index f9a5d7116b1e685ad3d19a456686fd89323ed9fa..c9fa715600fee72620692f6eb29162c0eb558260 100644
--- a/src/backend/tcop/fastpath.c
+++ b/src/backend/tcop/fastpath.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/tcop/fastpath.c,v 1.85 2006/03/05 15:58:40 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/tcop/fastpath.c,v 1.86 2006/04/04 19:35:35 tgl Exp $
  *
  * NOTES
  *	  This cruft is the server side of PQfn.
@@ -154,8 +154,7 @@ SendFunctionResult(Datum retval, bool isnull, Oid rettype, int16 format)
 			char	   *outputstr;
 
 			getTypeOutputInfo(rettype, &typoutput, &typisvarlena);
-			outputstr = DatumGetCString(OidFunctionCall1(typoutput,
-														 retval));
+			outputstr = OidOutputFunctionCall(typoutput, retval);
 			pq_sendcountedtext(&buf, outputstr, strlen(outputstr), false);
 			pfree(outputstr);
 		}
@@ -166,9 +165,7 @@ SendFunctionResult(Datum retval, bool isnull, Oid rettype, int16 format)
 			bytea	   *outputbytes;
 
 			getTypeBinaryOutputInfo(rettype, &typsend, &typisvarlena);
-			outputbytes = DatumGetByteaP(OidFunctionCall1(typsend,
-														  retval));
-			/* We assume the result will not have been toasted */
+			outputbytes = OidSendFunctionCall(typsend, retval);
 			pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4);
 			pq_sendbytes(&buf, VARDATA(outputbytes),
 						 VARSIZE(outputbytes) - VARHDRSZ);
@@ -433,23 +430,25 @@ parse_fcall_arguments(StringInfo msgBuf, struct fp_info * fip,
 		if (argsize == -1)
 		{
 			fcinfo->argnull[i] = true;
-			continue;
 		}
-		fcinfo->argnull[i] = false;
-		if (argsize < 0)
-			ereport(ERROR,
-					(errcode(ERRCODE_PROTOCOL_VIOLATION),
-				  errmsg("invalid argument size %d in function call message",
-						 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);
+		else
+		{
+			fcinfo->argnull[i] = false;
+			if (argsize < 0)
+				ereport(ERROR,
+						(errcode(ERRCODE_PROTOCOL_VIOLATION),
+						 errmsg("invalid argument size %d in function call message",
+								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];
@@ -472,31 +471,36 @@ parse_fcall_arguments(StringInfo msgBuf, struct fp_info * fip,
 			 * have to do encoding conversion before calling the typinput
 			 * routine, though.
 			 */
-			pstring = pg_client_to_server(abuf.data, argsize);
-			fcinfo->arg[i] =
-				OidFunctionCall3(typinput,
-								 CStringGetDatum(pstring),
-								 ObjectIdGetDatum(typioparam),
-								 Int32GetDatum(-1));
+			if (argsize == -1)
+				pstring = NULL;
+			else
+				pstring = pg_client_to_server(abuf.data, argsize);
+
+			fcinfo->arg[i] = OidInputFunctionCall(typinput, pstring,
+												  typioparam, -1);
 			/* Free result of encoding conversion, if any */
-			if (pstring != abuf.data)
+			if (pstring && pstring != abuf.data)
 				pfree(pstring);
 		}
 		else if (aformat == 1)
 		{
 			Oid			typreceive;
 			Oid			typioparam;
+			StringInfo	bufptr;
 
 			/* Call the argument type's binary input converter */
 			getTypeBinaryInputInfo(fip->argtypes[i], &typreceive, &typioparam);
 
-			fcinfo->arg[i] = OidFunctionCall3(typreceive,
-											  PointerGetDatum(&abuf),
-											  ObjectIdGetDatum(typioparam),
-											  Int32GetDatum(-1));
+			if (argsize == -1)
+				bufptr = NULL;
+			else
+				bufptr = &abuf;
+
+			fcinfo->arg[i] = OidReceiveFunctionCall(typreceive, bufptr,
+													typioparam, -1);
 
 			/* Trouble if it didn't eat the whole buffer */
-			if (abuf.cursor != abuf.len)
+			if (argsize != -1 && abuf.cursor != abuf.len)
 				ereport(ERROR,
 						(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
 				errmsg("incorrect binary data format in function argument %d",
@@ -552,18 +556,22 @@ parse_fcall_arguments_20(StringInfo msgBuf, struct fp_info * fip,
 		Oid			typreceive;
 		Oid			typioparam;
 
+		getTypeBinaryInputInfo(fip->argtypes[i], &typreceive, &typioparam);
+
 		argsize = pq_getmsgint(msgBuf, 4);
 		if (argsize == -1)
 		{
 			fcinfo->argnull[i] = true;
+			fcinfo->arg[i] = OidReceiveFunctionCall(typreceive, NULL,
+													typioparam, -1);
 			continue;
 		}
 		fcinfo->argnull[i] = false;
 		if (argsize < 0)
 			ereport(ERROR,
 					(errcode(ERRCODE_PROTOCOL_VIOLATION),
-				  errmsg("invalid argument size %d in function call message",
-						 argsize)));
+					 errmsg("invalid argument size %d in function call message",
+							argsize)));
 
 		/* Reset abuf to empty, and insert raw data into it */
 		abuf.len = 0;
@@ -574,13 +582,8 @@ parse_fcall_arguments_20(StringInfo msgBuf, struct fp_info * fip,
 							   pq_getmsgbytes(msgBuf, argsize),
 							   argsize);
 
-		/* Call the argument type's binary input converter */
-		getTypeBinaryInputInfo(fip->argtypes[i], &typreceive, &typioparam);
-
-		fcinfo->arg[i] = OidFunctionCall3(typreceive,
-										  PointerGetDatum(&abuf),
-										  ObjectIdGetDatum(typioparam),
-										  Int32GetDatum(-1));
+		fcinfo->arg[i] = OidReceiveFunctionCall(typreceive, &abuf,
+												typioparam, -1);
 
 		/* Trouble if it didn't eat the whole buffer */
 		if (abuf.cursor != abuf.len)
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 78cb8c3f811be947ea00f1ca78438ad0e1ec6820..df9ef8983a041145560f67c1523dba3e0cf48560 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.482 2006/03/14 22:48:21 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.483 2006/04/04 19:35:35 tgl Exp $
  *
  * NOTES
  *	  this is the "main" module of the postgres backend and
@@ -1486,6 +1486,9 @@ exec_bind_message(StringInfo input_message)
 			Oid			ptype = lfirst_oid(l);
 			int32		plength;
 			bool		isNull;
+			StringInfoData pbuf;
+			char		csave;
+			int16		pformat;
 
 			plength = pq_getmsgint(input_message, 4);
 			isNull = (plength == -1);
@@ -1493,16 +1496,6 @@ exec_bind_message(StringInfo input_message)
 			if (!isNull)
 			{
 				const char *pvalue = pq_getmsgbytes(input_message, plength);
-				int16		pformat;
-				StringInfoData pbuf;
-				char		csave;
-
-				if (numPFormats > 1)
-					pformat = pformats[i];
-				else if (numPFormats > 0)
-					pformat = pformats[0];
-				else
-					pformat = 0;	/* default = text */
 
 				/*
 				 * Rather than copying data around, we just set up a phony
@@ -1519,63 +1512,80 @@ exec_bind_message(StringInfo input_message)
 
 				csave = pbuf.data[plength];
 				pbuf.data[plength] = '\0';
+			}
+			else
+			{
+				pbuf.data = NULL;		/* keep compiler quiet */
+				csave = 0;
+			}
 
-				if (pformat == 0)
-				{
-					Oid			typinput;
-					Oid			typioparam;
-					char	   *pstring;
+			if (numPFormats > 1)
+				pformat = pformats[i];
+			else if (numPFormats > 0)
+				pformat = pformats[0];
+			else
+				pformat = 0;	/* default = text */
 
-					getTypeInputInfo(ptype, &typinput, &typioparam);
+			if (pformat == 0)
+			{
+				Oid			typinput;
+				Oid			typioparam;
+				char	   *pstring;
 
-					/*
-					 * We have to do encoding conversion before calling the
-					 * typinput routine.
-					 */
+				getTypeInputInfo(ptype, &typinput, &typioparam);
+
+				/*
+				 * We have to do encoding conversion before calling the
+				 * typinput routine.
+				 */
+				if (isNull)
+					pstring = NULL;
+				else
 					pstring = pg_client_to_server(pbuf.data, plength);
-					params[i].value =
-						OidFunctionCall3(typinput,
-										 CStringGetDatum(pstring),
-										 ObjectIdGetDatum(typioparam),
-										 Int32GetDatum(-1));
-					/* Free result of encoding conversion, if any */
-					if (pstring != pbuf.data)
-						pfree(pstring);
-				}
-				else if (pformat == 1)
-				{
-					Oid			typreceive;
-					Oid			typioparam;
 
-					/*
-					 * Call the parameter type's binary input converter
-					 */
-					getTypeBinaryInputInfo(ptype, &typreceive, &typioparam);
-
-					params[i].value =
-						OidFunctionCall3(typreceive,
-										 PointerGetDatum(&pbuf),
-										 ObjectIdGetDatum(typioparam),
-										 Int32GetDatum(-1));
-
-					/* Trouble if it didn't eat the whole buffer */
-					if (pbuf.cursor != pbuf.len)
-						ereport(ERROR,
-							 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
-							  errmsg("incorrect binary data format in bind parameter %d",
-									 i + 1)));
-				}
+				params[i].value = OidInputFunctionCall(typinput, pstring,
+													   typioparam, -1);
+				/* Free result of encoding conversion, if any */
+				if (pstring && pstring != pbuf.data)
+					pfree(pstring);
+			}
+			else if (pformat == 1)
+			{
+				Oid			typreceive;
+				Oid			typioparam;
+				StringInfo	bufptr;
+
+				/*
+				 * Call the parameter type's binary input converter
+				 */
+				getTypeBinaryInputInfo(ptype, &typreceive, &typioparam);
+
+				if (isNull)
+					bufptr = NULL;
 				else
-				{
+					bufptr = &pbuf;
+
+				params[i].value = OidReceiveFunctionCall(typreceive, bufptr,
+														 typioparam, -1);
+
+				/* Trouble if it didn't eat the whole buffer */
+				if (!isNull && pbuf.cursor != pbuf.len)
 					ereport(ERROR,
-							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-							 errmsg("unsupported format code: %d",
-									pformat)));
-				}
+							(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
+							 errmsg("incorrect binary data format in bind parameter %d",
+									i + 1)));
+			}
+			else
+			{
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						 errmsg("unsupported format code: %d",
+								pformat)));
+			}
 
-				/* Restore message buffer contents */
+			/* Restore message buffer contents */
+			if (!isNull)
 				pbuf.data[plength] = csave;
-			}
 
 			params[i].kind = PARAM_NUM;
 			params[i].id = i + 1;
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 1de3ff13a7dcb82344539a02854cd51f55d9b44b..dba7353ba588152fbc9c5e435d16a1c9793dfd9a 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.127 2006/03/05 15:58:41 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.128 2006/04/04 19:35:35 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -787,14 +787,14 @@ ReadArrayStr(char *arrayStr,
 			pg_strcasecmp(itemstart, "NULL") == 0)
 		{
 			/* it's a NULL item */
+			values[i] = InputFunctionCall(inputproc, NULL,
+										  typioparam, typmod);
 			nulls[i] = true;
 		}
 		else
 		{
-			values[i] = FunctionCall3(inputproc,
-									  CStringGetDatum(itemstart),
-									  ObjectIdGetDatum(typioparam),
-									  Int32GetDatum(typmod));
+			values[i] = InputFunctionCall(inputproc, itemstart,
+										  typioparam, typmod);
 			nulls[i] = false;
 		}
 	}
@@ -1018,8 +1018,7 @@ array_out(PG_FUNCTION_ARGS)
 			Datum		itemvalue;
 
 			itemvalue = fetch_att(p, typbyval, typlen);
-			values[i] = DatumGetCString(FunctionCall1(&my_extra->proc,
-													  itemvalue));
+			values[i] = OutputFunctionCall(&my_extra->proc, itemvalue);
 			p = att_addlength(p, typlen, PointerGetDatum(p));
 			p = (char *) att_align(p, typalign);
 
@@ -1357,6 +1356,8 @@ ReadArrayBinary(StringInfo buf,
 		if (itemlen == -1)
 		{
 			/* -1 length means NULL */
+			values[i] = ReceiveFunctionCall(receiveproc, NULL,
+											typioparam, typmod);
 			nulls[i] = true;
 			continue;
 		}
@@ -1378,10 +1379,8 @@ ReadArrayBinary(StringInfo buf,
 		buf->data[buf->cursor] = '\0';
 
 		/* Now call the element's receiveproc */
-		values[i] = FunctionCall3(receiveproc,
-								  PointerGetDatum(&elem_buf),
-								  ObjectIdGetDatum(typioparam),
-								  Int32GetDatum(typmod));
+		values[i] = ReceiveFunctionCall(receiveproc, &elem_buf,
+										typioparam, typmod);
 		nulls[i] = false;
 
 		/* Trouble if it didn't eat the whole buffer */
@@ -1515,10 +1514,7 @@ array_send(PG_FUNCTION_ARGS)
 			bytea	   *outputbytes;
 
 			itemvalue = fetch_att(p, typbyval, typlen);
-
-			outputbytes = DatumGetByteaP(FunctionCall1(&my_extra->proc,
-													   itemvalue));
-			/* We assume the result will not have been toasted */
+			outputbytes = SendFunctionCall(&my_extra->proc, itemvalue);
 			pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4);
 			pq_sendbytes(&buf, VARDATA(outputbytes),
 						 VARSIZE(outputbytes) - VARHDRSZ);
diff --git a/src/backend/utils/adt/rowtypes.c b/src/backend/utils/adt/rowtypes.c
index 9d7fcaa413a12625e92658c7cf7b35abcd6f4696..bb61dc2956840de9073f278a593d31697e4deae5 100644
--- a/src/backend/utils/adt/rowtypes.c
+++ b/src/backend/utils/adt/rowtypes.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/rowtypes.c,v 1.14 2006/03/05 15:58:43 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/rowtypes.c,v 1.15 2006/04/04 19:35:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -136,6 +136,7 @@ record_in(PG_FUNCTION_ARGS)
 	{
 		ColumnIOData *column_info = &my_extra->columns[i];
 		Oid			column_type = tupdesc->attrs[i]->atttypid;
+		char	   *column_data;
 
 		/* Ignore dropped columns in datatype, but fill with nulls */
 		if (tupdesc->attrs[i]->attisdropped)
@@ -161,7 +162,7 @@ record_in(PG_FUNCTION_ARGS)
 		/* Check for null: completely empty input means null */
 		if (*ptr == ',' || *ptr == ')')
 		{
-			values[i] = (Datum) 0;
+			column_data = NULL;
 			nulls[i] = 'n';
 		}
 		else
@@ -207,26 +208,28 @@ record_in(PG_FUNCTION_ARGS)
 					appendStringInfoChar(&buf, ch);
 			}
 
-			/*
-			 * Convert the column value
-			 */
-			if (column_info->column_type != column_type)
-			{
-				getTypeInputInfo(column_type,
-								 &column_info->typiofunc,
-								 &column_info->typioparam);
-				fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
-							  fcinfo->flinfo->fn_mcxt);
-				column_info->column_type = column_type;
-			}
-
-			values[i] = FunctionCall3(&column_info->proc,
-									  CStringGetDatum(buf.data),
-								   ObjectIdGetDatum(column_info->typioparam),
-								Int32GetDatum(tupdesc->attrs[i]->atttypmod));
+			column_data = buf.data;
 			nulls[i] = ' ';
 		}
 
+		/*
+		 * Convert the column value
+		 */
+		if (column_info->column_type != column_type)
+		{
+			getTypeInputInfo(column_type,
+							 &column_info->typiofunc,
+							 &column_info->typioparam);
+			fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
+						  fcinfo->flinfo->fn_mcxt);
+			column_info->column_type = column_type;
+		}
+
+		values[i] = InputFunctionCall(&column_info->proc,
+									  column_data,
+									  column_info->typioparam,
+									  tupdesc->attrs[i]->atttypmod);
+
 		/*
 		 * Prep for next column
 		 */
@@ -372,8 +375,7 @@ record_out(PG_FUNCTION_ARGS)
 			column_info->column_type = column_type;
 		}
 
-		value = DatumGetCString(FunctionCall1(&column_info->proc,
-											  values[i]));
+		value = OutputFunctionCall(&column_info->proc, values[i]);
 
 		/* Detect whether we need double quotes for this value */
 		nq = (value[0] == '\0');	/* force quotes for empty string */
@@ -505,6 +507,9 @@ record_recv(PG_FUNCTION_ARGS)
 		Oid			column_type = tupdesc->attrs[i]->atttypid;
 		Oid			coltypoid;
 		int			itemlen;
+		StringInfoData item_buf;
+		StringInfo	bufptr;
+		char		csave;
 
 		/* Ignore dropped columns in datatype, but fill with nulls */
 		if (tupdesc->attrs[i]->attisdropped)
@@ -532,8 +537,9 @@ record_recv(PG_FUNCTION_ARGS)
 		if (itemlen == -1)
 		{
 			/* -1 length means NULL */
-			values[i] = (Datum) 0;
+			bufptr = NULL;
 			nulls[i] = 'n';
+			csave = 0;			/* keep compiler quiet */
 		}
 		else
 		{
@@ -543,9 +549,6 @@ record_recv(PG_FUNCTION_ARGS)
 			 * We assume we can scribble on the input buffer so as to maintain
 			 * the convention that StringInfos have a trailing null.
 			 */
-			StringInfoData item_buf;
-			char		csave;
-
 			item_buf.data = &buf->data[buf->cursor];
 			item_buf.maxlen = itemlen + 1;
 			item_buf.len = itemlen;
@@ -556,23 +559,28 @@ record_recv(PG_FUNCTION_ARGS)
 			csave = buf->data[buf->cursor];
 			buf->data[buf->cursor] = '\0';
 
-			/* Now call the column's receiveproc */
-			if (column_info->column_type != column_type)
-			{
-				getTypeBinaryInputInfo(column_type,
-									   &column_info->typiofunc,
-									   &column_info->typioparam);
-				fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
-							  fcinfo->flinfo->fn_mcxt);
-				column_info->column_type = column_type;
-			}
-
-			values[i] = FunctionCall3(&column_info->proc,
-									  PointerGetDatum(&item_buf),
-								   ObjectIdGetDatum(column_info->typioparam),
-								Int32GetDatum(tupdesc->attrs[i]->atttypmod));
+			bufptr = &item_buf;
 			nulls[i] = ' ';
+		}
 
+		/* Now call the column's receiveproc */
+		if (column_info->column_type != column_type)
+		{
+			getTypeBinaryInputInfo(column_type,
+								   &column_info->typiofunc,
+								   &column_info->typioparam);
+			fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
+						  fcinfo->flinfo->fn_mcxt);
+			column_info->column_type = column_type;
+		}
+
+		values[i] = ReceiveFunctionCall(&column_info->proc,
+										bufptr,
+										column_info->typioparam,
+										tupdesc->attrs[i]->atttypmod);
+
+		if (bufptr)
+		{
 			/* Trouble if it didn't eat the whole buffer */
 			if (item_buf.cursor != itemlen)
 				ereport(ERROR,
@@ -712,8 +720,7 @@ record_send(PG_FUNCTION_ARGS)
 			column_info->column_type = column_type;
 		}
 
-		outputbytes = DatumGetByteaP(FunctionCall1(&column_info->proc,
-												   values[i]));
+		outputbytes = SendFunctionCall(&column_info->proc, values[i]);
 
 		/* We assume the result will not have been toasted */
 		pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4);
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 714140e06179d315ad64b36802612a90d46418bf..f8daab6eeaf234eec3448bd1657a70d15bda14b5 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -2,7 +2,7 @@
  * ruleutils.c	- Functions to convert stored expressions/querytrees
  *				back to source text
  *
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.217 2006/03/16 00:31:55 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.218 2006/04/04 19:35:36 tgl Exp $
  **********************************************************************/
 
 #include "postgres.h"
@@ -3921,8 +3921,7 @@ get_const_expr(Const *constval, deparse_context *context)
 	getTypeOutputInfo(constval->consttype,
 					  &typoutput, &typIsVarlena);
 
-	extval = DatumGetCString(OidFunctionCall1(typoutput,
-											  constval->constvalue));
+	extval = OidOutputFunctionCall(typoutput, constval->constvalue);
 
 	switch (constval->consttype)
 	{
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index b936b8219dd52e372ad5c5558672bb4c19fff639..79dc0178a80ee284d2ccfde2a72095d3399687ae 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/varlena.c,v 1.145 2006/03/05 15:58:44 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/varlena.c,v 1.146 2006/04/04 19:35:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2580,8 +2580,7 @@ array_to_text(PG_FUNCTION_ARGS)
 		{
 			itemvalue = fetch_att(p, typbyval, typlen);
 
-			value = DatumGetCString(FunctionCall1(&my_extra->proc,
-												  itemvalue));
+			value = OutputFunctionCall(&my_extra->proc, itemvalue);
 
 			if (printed)
 				appendStringInfo(&buf, "%s%s", fldsep, value);
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 6eb6ea802bfd2ab5b816988f9cbff304cd349dad..fb24cc623660bcb7fd162af4262b252f35abcc24 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
- *	  $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.132 2006/03/05 15:58:45 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.133 2006/04/04 19:35:36 tgl Exp $
  *
  * NOTES
  *	  Eventually, the index information should go through here, too.
@@ -1554,10 +1554,8 @@ get_typdefault(Oid typid)
 			strDefaultVal = DatumGetCString(DirectFunctionCall1(textout,
 																datum));
 			/* Convert C string to a value of the given type */
-			datum = OidFunctionCall3(type->typinput,
-									 CStringGetDatum(strDefaultVal),
-								 ObjectIdGetDatum(getTypeIOParam(typeTuple)),
-									 Int32GetDatum(-1));
+			datum = OidInputFunctionCall(type->typinput, strDefaultVal,
+										 getTypeIOParam(typeTuple), -1);
 			/* Build a Const node containing the value */
 			expr = (Node *) makeConst(typid,
 									  type->typlen,
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index e64ede6f5661c7c13a8d20a112aee1bc8ab72469..4a663135dcbe6d63a9cb11cb5eb90f0cca035e40 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/fmgr/fmgr.c,v 1.99 2006/03/05 15:58:46 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/fmgr/fmgr.c,v 1.100 2006/04/04 19:35:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1682,6 +1682,172 @@ OidFunctionCall9(Oid functionId, Datum arg1, Datum arg2,
 }
 
 
+/*
+ * Special cases for convenient invocation of datatype I/O functions.
+ */
+
+/*
+ * Call a previously-looked-up datatype input function.
+ *
+ * "str" may be NULL to indicate we are reading a NULL.  In this case
+ * the caller should assume the result is NULL, but we'll call the input
+ * function anyway if it's not strict.  So this is almost but not quite
+ * the same as FunctionCall3.
+ */
+Datum
+InputFunctionCall(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod)
+{
+	FunctionCallInfoData fcinfo;
+	Datum		result;
+
+	if (str == NULL && flinfo->fn_strict)
+		return (Datum) 0;		/* just return null result */
+
+	InitFunctionCallInfoData(fcinfo, flinfo, 3, NULL, NULL);
+
+	fcinfo.arg[0] = CStringGetDatum(str);
+	fcinfo.arg[1] = ObjectIdGetDatum(typioparam);
+	fcinfo.arg[2] = Int32GetDatum(typmod);
+	fcinfo.argnull[0] = (str == NULL);
+	fcinfo.argnull[1] = false;
+	fcinfo.argnull[2] = false;
+
+	result = FunctionCallInvoke(&fcinfo);
+
+	/* Should get null result if and only if str is NULL */
+	if (str == NULL)
+	{
+		if (!fcinfo.isnull)
+			elog(ERROR, "input function %u returned non-NULL",
+				 fcinfo.flinfo->fn_oid);
+	}
+	else
+	{
+		if (fcinfo.isnull)
+			elog(ERROR, "input function %u returned NULL",
+				 fcinfo.flinfo->fn_oid);
+	}
+
+	return result;
+}
+
+/*
+ * Call a previously-looked-up datatype output function.
+ *
+ * Do not call this on NULL datums.
+ *
+ * This is mere window dressing for FunctionCall1, but its use is recommended
+ * anyway so that code invoking output functions can be identified easily.
+ */
+char *
+OutputFunctionCall(FmgrInfo *flinfo, Datum val)
+{
+	return DatumGetCString(FunctionCall1(flinfo, val));
+}
+
+/*
+ * Call a previously-looked-up datatype binary-input function.
+ *
+ * "buf" may be NULL to indicate we are reading a NULL.  In this case
+ * the caller should assume the result is NULL, but we'll call the receive
+ * function anyway if it's not strict.  So this is almost but not quite
+ * the same as FunctionCall3.
+ */
+Datum
+ReceiveFunctionCall(FmgrInfo *flinfo, StringInfo buf,
+					Oid typioparam, int32 typmod)
+{
+	FunctionCallInfoData fcinfo;
+	Datum		result;
+
+	if (buf == NULL && flinfo->fn_strict)
+		return (Datum) 0;		/* just return null result */
+
+	InitFunctionCallInfoData(fcinfo, flinfo, 3, NULL, NULL);
+
+	fcinfo.arg[0] = PointerGetDatum(buf);
+	fcinfo.arg[1] = ObjectIdGetDatum(typioparam);
+	fcinfo.arg[2] = Int32GetDatum(typmod);
+	fcinfo.argnull[0] = (buf == NULL);
+	fcinfo.argnull[1] = false;
+	fcinfo.argnull[2] = false;
+
+	result = FunctionCallInvoke(&fcinfo);
+
+	/* Should get null result if and only if buf is NULL */
+	if (buf == NULL)
+	{
+		if (!fcinfo.isnull)
+			elog(ERROR, "receive function %u returned non-NULL",
+				 fcinfo.flinfo->fn_oid);
+	}
+	else
+	{
+		if (fcinfo.isnull)
+			elog(ERROR, "receive function %u returned NULL",
+				 fcinfo.flinfo->fn_oid);
+	}
+
+	return result;
+}
+
+/*
+ * Call a previously-looked-up datatype binary-output function.
+ *
+ * Do not call this on NULL datums.
+ *
+ * This is little more than window dressing for FunctionCall1, but its use is
+ * recommended anyway so that code invoking output functions can be identified
+ * easily.  Note however that it does guarantee a non-toasted result.
+ */
+bytea *
+SendFunctionCall(FmgrInfo *flinfo, Datum val)
+{
+	return DatumGetByteaP(FunctionCall1(flinfo, val));
+}
+
+/*
+ * As above, for I/O functions identified by OID.  These are only to be used
+ * in seldom-executed code paths.  They are not only slow but leak memory.
+ */
+Datum
+OidInputFunctionCall(Oid functionId, char *str, Oid typioparam, int32 typmod)
+{
+	FmgrInfo	flinfo;
+
+	fmgr_info(functionId, &flinfo);
+	return InputFunctionCall(&flinfo, str, typioparam, typmod);
+}
+
+char *
+OidOutputFunctionCall(Oid functionId, Datum val)
+{
+	FmgrInfo	flinfo;
+
+	fmgr_info(functionId, &flinfo);
+	return OutputFunctionCall(&flinfo, val);
+}
+
+Datum
+OidReceiveFunctionCall(Oid functionId, StringInfo buf,
+					   Oid typioparam, int32 typmod)
+{
+	FmgrInfo	flinfo;
+
+	fmgr_info(functionId, &flinfo);
+	return ReceiveFunctionCall(&flinfo, buf, typioparam, typmod);
+}
+
+bytea *
+OidSendFunctionCall(Oid functionId, Datum val)
+{
+	FmgrInfo	flinfo;
+
+	fmgr_info(functionId, &flinfo);
+	return SendFunctionCall(&flinfo, val);
+}
+
+
 /*
  * !!! OLD INTERFACE !!!
  *
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index 9f17a24357d9d168ebac69753a34b391868e757e..0d6e72594d2cbc810ddea8311568f19a0e27f44d 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -11,7 +11,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/fmgr.h,v 1.42 2006/03/05 15:58:52 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/fmgr.h,v 1.43 2006/04/04 19:35:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,6 +20,8 @@
 
 /* We don't want to include primnodes.h here, so make a stub reference */
 typedef struct Node *fmNodePtr;
+/* Likewise, avoid including stringinfo.h here */
+typedef struct StringInfoData *fmStringInfo;
 
 
 /*
@@ -394,6 +396,20 @@ extern Datum OidFunctionCall9(Oid functionId, Datum arg1, Datum arg2,
 				 Datum arg6, Datum arg7, Datum arg8,
 				 Datum arg9);
 
+/* Special cases for convenient invocation of datatype I/O functions. */
+extern Datum InputFunctionCall(FmgrInfo *flinfo, char *str,
+							   Oid typioparam, int32 typmod);
+extern Datum OidInputFunctionCall(Oid functionId, char *str,
+								  Oid typioparam, int32 typmod);
+extern char *OutputFunctionCall(FmgrInfo *flinfo, Datum val);
+extern char *OidOutputFunctionCall(Oid functionId, Datum val);
+extern Datum ReceiveFunctionCall(FmgrInfo *flinfo, fmStringInfo buf,
+								 Oid typioparam, int32 typmod);
+extern Datum OidReceiveFunctionCall(Oid functionId, fmStringInfo buf,
+									Oid typioparam, int32 typmod);
+extern bytea *SendFunctionCall(FmgrInfo *flinfo, Datum val);
+extern bytea *OidSendFunctionCall(Oid functionId, Datum val);
+
 
 /*
  * Routines in fmgr.c
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index 0524d9ebe4e4026b92c2a27e2dbca0aca2930ae2..310df025706e5f31714459bdc8fad7fb93d39468 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -1,7 +1,7 @@
 /**********************************************************************
  * plperl.c - perl as a procedural language for PostgreSQL
  *
- *	  $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.107 2006/03/19 22:22:56 neilc Exp $
+ *	  $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.108 2006/04/04 19:35:37 tgl Exp $
  *
  **********************************************************************/
 
@@ -585,31 +585,35 @@ plperl_modify_tuple(HV *hvTD, TriggerData *tdata, HeapTuple otup)
 	while ((val = hv_iternextsv(hvNew, &key, &klen)))
 	{
 		int			attn = SPI_fnumber(tupdesc, key);
+		Oid			typinput;
+		Oid			typioparam;
+		int32		atttypmod;
+		FmgrInfo	finfo;
 
 		if (attn <= 0 || tupdesc->attrs[attn - 1]->attisdropped)
 			ereport(ERROR,
 					(errcode(ERRCODE_UNDEFINED_COLUMN),
 					 errmsg("Perl hash contains nonexistent column \"%s\"",
 							key)));
+		/* XXX would be better to cache these lookups */
+		getTypeInputInfo(tupdesc->attrs[attn - 1]->atttypid,
+						 &typinput, &typioparam);
+		fmgr_info(typinput, &finfo);
+		atttypmod = tupdesc->attrs[attn - 1]->atttypmod;
 		if (SvOK(val) && SvTYPE(val) != SVt_NULL)
 		{
-			Oid			typinput;
-			Oid			typioparam;
-			FmgrInfo	finfo;
-
-			/* XXX would be better to cache these lookups */
-			getTypeInputInfo(tupdesc->attrs[attn - 1]->atttypid,
-							 &typinput, &typioparam);
-			fmgr_info(typinput, &finfo);
-			modvalues[slotsused] = FunctionCall3(&finfo,
-										   CStringGetDatum(SvPV(val, PL_na)),
-												 ObjectIdGetDatum(typioparam),
-						 Int32GetDatum(tupdesc->attrs[attn - 1]->atttypmod));
+			modvalues[slotsused] = InputFunctionCall(&finfo,
+													 SvPV(val, PL_na),
+													 typioparam,
+													 atttypmod);
 			modnulls[slotsused] = ' ';
 		}
 		else
 		{
-			modvalues[slotsused] = (Datum) 0;
+			modvalues[slotsused] = InputFunctionCall(&finfo,
+													 NULL,
+													 typioparam,
+													 atttypmod);
 			modnulls[slotsused] = 'n';
 		}
 		modattrs[slotsused] = attn;
@@ -897,8 +901,8 @@ plperl_call_perl_func(plperl_proc_desc *desc, FunctionCallInfo fcinfo)
 		{
 			char	   *tmp;
 
-			tmp = DatumGetCString(FunctionCall1(&(desc->arg_out_func[i]),
-												fcinfo->arg[i]));
+			tmp = OutputFunctionCall(&(desc->arg_out_func[i]),
+									 fcinfo->arg[i]);
 			sv = newSVpv(tmp, 0);
 #if PERL_BCDVERSION >= 0x5006000L
 			if (GetDatabaseEncoding() == PG_UTF8)
@@ -1091,8 +1095,9 @@ plperl_func_handler(PG_FUNCTION_ARGS)
 		/* Return NULL if Perl code returned undef */
 		if (rsi && IsA(rsi, ReturnSetInfo))
 			rsi->isDone = ExprEndResult;
+		retval = InputFunctionCall(&prodesc->result_in_func, NULL,
+								   prodesc->result_typioparam, -1);
 		fcinfo->isnull = true;
-		retval = (Datum) 0;
 	}
 	else if (prodesc->fn_retistuple)
 	{
@@ -1138,10 +1143,8 @@ plperl_func_handler(PG_FUNCTION_ARGS)
 
 		val = SvPV(perlret, PL_na);
 
-		retval = FunctionCall3(&prodesc->result_in_func,
-							   CStringGetDatum(val),
-							   ObjectIdGetDatum(prodesc->result_typioparam),
-							   Int32GetDatum(-1));
+		retval = InputFunctionCall(&prodesc->result_in_func, val,
+								   prodesc->result_typioparam, -1);
 	}
 
 	if (array_ret == NULL)
@@ -1534,7 +1537,7 @@ plperl_hash_from_tuple(HeapTuple tuple, TupleDesc tupdesc)
 		getTypeOutputInfo(tupdesc->attrs[i]->atttypid,
 						  &typoutput, &typisvarlena);
 
-		outputstr = DatumGetCString(OidFunctionCall1(typoutput, attr));
+		outputstr = OidOutputFunctionCall(typoutput, attr);
 
 		sv = newSVpv(outputstr, 0);
 #if PERL_BCDVERSION >= 0x5006000L
@@ -1750,19 +1753,23 @@ plperl_return_next(SV *sv)
 	}
 	else
 	{
-		Datum		ret = (Datum) 0;
-		bool		isNull = true;
+		Datum		ret;
+		bool		isNull;
 
 		if (SvOK(sv) && SvTYPE(sv) != SVt_NULL)
 		{
 			char	   *val = SvPV(sv, PL_na);
 
-			ret = FunctionCall3(&prodesc->result_in_func,
-								PointerGetDatum(val),
-								ObjectIdGetDatum(prodesc->result_typioparam),
-								Int32GetDatum(-1));
+			ret = InputFunctionCall(&prodesc->result_in_func, val,
+									prodesc->result_typioparam, -1);
 			isNull = false;
 		}
+		else
+		{
+			ret = InputFunctionCall(&prodesc->result_in_func, NULL,
+									prodesc->result_typioparam, -1);
+			isNull = true;
+		}
 
 		tuple = heap_form_tuple(current_call_data->ret_tdesc, &ret, &isNull);
 	}
@@ -2118,9 +2125,9 @@ plperl_spi_exec_prepared(char* query, HV * attr, int argc, SV ** argv)
 		/************************************************************
 		 * Set up arguments
 		 ************************************************************/
-		if ( argc > 0) 
+		if (argc > 0) 
 		{
-			nulls = (char *)palloc( argc);
+			nulls = (char *) palloc(argc);
 			argvalues = (Datum *) palloc(argc * sizeof(Datum));
 		} 
 		else 
@@ -2129,21 +2136,22 @@ plperl_spi_exec_prepared(char* query, HV * attr, int argc, SV ** argv)
 			argvalues = NULL;
 		}
 
-		for ( i = 0; i < argc; i++) 
+		for (i = 0; i < argc; i++) 
 		{
-			if ( SvTYPE( argv[i]) != SVt_NULL) 
+			if (SvTYPE(argv[i]) != SVt_NULL) 
 			{
-				argvalues[i] =
-					FunctionCall3( &qdesc->arginfuncs[i],
-						  CStringGetDatum( SvPV( argv[i], PL_na)),
-						  ObjectIdGetDatum( qdesc->argtypioparams[i]),
-						  Int32GetDatum(-1)
-					);
+				argvalues[i] = InputFunctionCall(&qdesc->arginfuncs[i],
+												 SvPV(argv[i], PL_na),
+												 qdesc->argtypioparams[i],
+												 -1);
 				nulls[i] = ' ';
 			} 
 			else 
 			{
-				argvalues[i] = (Datum) 0;
+				argvalues[i] = InputFunctionCall(&qdesc->arginfuncs[i],
+												 NULL,
+												 qdesc->argtypioparams[i],
+												 -1);
 				nulls[i] = 'n';
 			}
 		}
@@ -2247,9 +2255,9 @@ plperl_spi_query_prepared(char* query, int argc, SV ** argv)
 		/************************************************************
 		 * Set up arguments
 		 ************************************************************/
-		if ( argc > 0) 
+		if (argc > 0) 
 		{
-			nulls = (char *)palloc( argc);
+			nulls = (char *) palloc(argc);
 			argvalues = (Datum *) palloc(argc * sizeof(Datum));
 		} 
 		else 
@@ -2258,21 +2266,22 @@ plperl_spi_query_prepared(char* query, int argc, SV ** argv)
 			argvalues = NULL;
 		}
 
-		for ( i = 0; i < argc; i++) 
+		for (i = 0; i < argc; i++) 
 		{
-			if ( SvTYPE( argv[i]) != SVt_NULL) 
+			if (SvTYPE(argv[i]) != SVt_NULL) 
 			{
-				argvalues[i] =
-					FunctionCall3( &qdesc->arginfuncs[i],
-						  CStringGetDatum( SvPV( argv[i], PL_na)),
-						  ObjectIdGetDatum( qdesc->argtypioparams[i]),
-						  Int32GetDatum(-1)
-					);
+				argvalues[i] = InputFunctionCall(&qdesc->arginfuncs[i],
+												 SvPV(argv[i], PL_na),
+												 qdesc->argtypioparams[i],
+												 -1);
 				nulls[i] = ' ';
 			} 
 			else 
 			{
-				argvalues[i] = (Datum) 0;
+				argvalues[i] = InputFunctionCall(&qdesc->arginfuncs[i],
+												 NULL,
+												 qdesc->argtypioparams[i],
+												 -1);
 				nulls[i] = 'n';
 			}
 		}
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index e4fa7003a7ca2210a1a53ba8f0b2c94ffd193d81..8a82a42fbe6be756e8ffc0cf06e8ac5a18a226e6 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.162 2006/03/09 21:29:36 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.163 2006/04/04 19:35:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -4043,6 +4043,8 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
  *
  * Note: callers generally assume that the result is a palloc'd string and
  * should be pfree'd.  This is not all that safe an assumption ...
+ *
+ * Note: not caching the conversion function lookup is bad for performance.
  * ----------
  */
 static char *
@@ -4053,7 +4055,7 @@ convert_value_to_string(Datum value, Oid valtype)
 
 	getTypeOutputInfo(valtype, &typoutput, &typIsVarlena);
 
-	return DatumGetCString(OidFunctionCall1(typoutput, value));
+	return OidOutputFunctionCall(typoutput, value);
 }
 
 /* ----------
@@ -4068,23 +4070,26 @@ exec_cast_value(Datum value, Oid valtype,
 				int32 reqtypmod,
 				bool isnull)
 {
-	if (!isnull)
+	/*
+	 * If the type of the queries return value isn't that of the variable,
+	 * convert it.
+	 */
+	if (valtype != reqtype || reqtypmod != -1)
 	{
-		/*
-		 * If the type of the queries return value isn't that of the variable,
-		 * convert it.
-		 */
-		if (valtype != reqtype || reqtypmod != -1)
+		if (!isnull)
 		{
 			char	   *extval;
 
 			extval = convert_value_to_string(value, valtype);
-			value = FunctionCall3(reqinput,
-								  CStringGetDatum(extval),
-								  ObjectIdGetDatum(reqtypioparam),
-								  Int32GetDatum(reqtypmod));
+			value = InputFunctionCall(reqinput, extval,
+									  reqtypioparam, reqtypmod);
 			pfree(extval);
 		}
+		else
+		{
+			value = InputFunctionCall(reqinput, NULL,
+									  reqtypioparam, reqtypmod);
+		}
 	}
 
 	return value;
diff --git a/src/pl/plpython/plpython.c b/src/pl/plpython/plpython.c
index d20a5f72f1301954324ff53377294a1da6d67e5e..bf96db1776df51532ef5084e13c78cc48a5b884d 100644
--- a/src/pl/plpython/plpython.c
+++ b/src/pl/plpython/plpython.c
@@ -1,7 +1,7 @@
 /**********************************************************************
  * plpython.c - python as a procedural language for PostgreSQL
  *
- *	$PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.76 2006/03/14 22:48:24 tgl Exp $
+ *	$PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.77 2006/04/04 19:35:37 tgl Exp $
  *
  *********************************************************************
  */
@@ -482,17 +482,24 @@ PLy_modify_tuple(PLyProcedure * proc, PyObject * pltd, TriggerData *tdata,
 
 			modattrs[i] = attn;
 
-			if (plval != Py_None && !tupdesc->attrs[atti]->attisdropped)
+			if (tupdesc->attrs[atti]->attisdropped)
+			{
+				modvalues[i] = (Datum) 0;
+				modnulls[i] = 'n';
+			}
+			else if (plval != Py_None)
 			{
 				plstr = PyObject_Str(plval);
 				if (!plstr)
-					PLy_elog(ERROR, "function \"%s\" could not modify tuple", proc->proname);
+					PLy_elog(ERROR, "function \"%s\" could not modify tuple",
+							 proc->proname);
 				src = PyString_AsString(plstr);
 
-				modvalues[i] = FunctionCall3(&proc->result.out.r.atts[atti].typfunc,
-											 CStringGetDatum(src),
-				  ObjectIdGetDatum(proc->result.out.r.atts[atti].typioparam),
-							 Int32GetDatum(tupdesc->attrs[atti]->atttypmod));
+				modvalues[i] =
+					InputFunctionCall(&proc->result.out.r.atts[atti].typfunc,
+									  src,
+									  proc->result.out.r.atts[atti].typioparam,
+									  tupdesc->attrs[atti]->atttypmod);
 				modnulls[i] = ' ';
 
 				Py_DECREF(plstr);
@@ -500,7 +507,11 @@ PLy_modify_tuple(PLyProcedure * proc, PyObject * pltd, TriggerData *tdata,
 			}
 			else
 			{
-				modvalues[i] = PointerGetDatum(NULL);
+				modvalues[i] =
+					InputFunctionCall(&proc->result.out.r.atts[atti].typfunc,
+									  NULL,
+									  proc->result.out.r.atts[atti].typioparam,
+									  tupdesc->attrs[atti]->atttypmod);
 				modnulls[i] = 'n';
 			}
 
@@ -751,7 +762,10 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure * proc)
 		else if (plrv == Py_None)
 		{
 			fcinfo->isnull = true;
-			rv = PointerGetDatum(NULL);
+			rv = InputFunctionCall(&proc->result.out.d.typfunc,
+								   NULL,
+								   proc->result.out.d.typioparam,
+								   -1);
 		}
 		else
 		{
@@ -760,10 +774,10 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure * proc)
 			if (!plrv_so)
 				PLy_elog(ERROR, "function \"%s\" could not create return value", proc->proname);
 			plrv_sc = PyString_AsString(plrv_so);
-			rv = FunctionCall3(&proc->result.out.d.typfunc,
-							   PointerGetDatum(plrv_sc),
-							   ObjectIdGetDatum(proc->result.out.d.typioparam),
-							   Int32GetDatum(-1));
+			rv = InputFunctionCall(&proc->result.out.d.typfunc,
+								   plrv_sc,
+								   proc->result.out.d.typioparam,
+								   -1);
 		}
 	}
 	PG_CATCH();
@@ -861,13 +875,9 @@ PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc)
 				else
 				{
 					char	   *ct;
-					Datum		dt;
 
-					dt = FunctionCall3(&(proc->args[i].in.d.typfunc),
-									   fcinfo->arg[i],
-							 ObjectIdGetDatum(proc->args[i].in.d.typioparam),
-									   Int32GetDatum(-1));
-					ct = DatumGetCString(dt);
+					ct = OutputFunctionCall(&(proc->args[i].in.d.typfunc),
+											fcinfo->arg[i]);
 					arg = (proc->args[i].in.d.func) (ct);
 					pfree(ct);
 				}
@@ -1454,8 +1464,7 @@ PLyDict_FromTuple(PLyTypeInfo * info, HeapTuple tuple, TupleDesc desc)
 		{
 			char	   *key,
 					   *vsrc;
-			Datum		vattr,
-						vdat;
+			Datum		vattr;
 			bool		is_null;
 			PyObject   *value;
 
@@ -1469,11 +1478,8 @@ PLyDict_FromTuple(PLyTypeInfo * info, HeapTuple tuple, TupleDesc desc)
 				PyDict_SetItemString(dict, key, Py_None);
 			else
 			{
-				vdat = FunctionCall3(&info->in.r.atts[i].typfunc,
-									 vattr,
-							 ObjectIdGetDatum(info->in.r.atts[i].typioparam),
-								   Int32GetDatum(desc->attrs[i]->atttypmod));
-				vsrc = DatumGetCString(vdat);
+				vsrc = OutputFunctionCall(&info->in.r.atts[i].typfunc,
+										  vattr);
 
 				/*
 				 * no exceptions allowed
@@ -2035,10 +2041,10 @@ PLy_spi_execute_plan(PyObject * ob, PyObject * list, long limit)
 					char *sv = PyString_AsString(so);
 
 					plan->values[i] =
-						FunctionCall3(&(plan->args[i].out.d.typfunc),
-									  CStringGetDatum(sv),
-								ObjectIdGetDatum(plan->args[i].out.d.typioparam),
-									  Int32GetDatum(-1));
+						InputFunctionCall(&(plan->args[i].out.d.typfunc),
+										  sv,
+										  plan->args[i].out.d.typioparam,
+										  -1);
 				}
 				PG_CATCH();
 				{
@@ -2053,7 +2059,11 @@ PLy_spi_execute_plan(PyObject * ob, PyObject * list, long limit)
 			else
 			{
 				Py_DECREF(elem);
-				plan->values[i] = PointerGetDatum(NULL);
+				plan->values[i] =
+					InputFunctionCall(&(plan->args[i].out.d.typfunc),
+									  NULL,
+									  plan->args[i].out.d.typioparam,
+									  -1);
 				nulls[i] = 'n';
 			}
 		}
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index e1f21109bc5ef259c406b51507a71dbae4ed4bfe..07cbcc2eb315ab643d50fa7b8a6e33dfdfd70b0b 100644
--- a/src/pl/tcl/pltcl.c
+++ b/src/pl/tcl/pltcl.c
@@ -2,7 +2,7 @@
  * pltcl.c		- PostgreSQL support for Tcl as
  *				  procedural language (PL)
  *
- *	  $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.101 2006/03/14 22:48:24 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.102 2006/04/04 19:35:37 tgl Exp $
  *
  **********************************************************************/
 
@@ -524,8 +524,8 @@ pltcl_func_handler(PG_FUNCTION_ARGS)
 				{
 					char	   *tmp;
 
-					tmp = DatumGetCString(FunctionCall1(&prodesc->arg_out_func[i],
-														fcinfo->arg[i]));
+					tmp = OutputFunctionCall(&prodesc->arg_out_func[i],
+											 fcinfo->arg[i]);
 					UTF_BEGIN;
 					Tcl_DStringAppendElement(&tcl_cmd, UTF_E2U(tmp));
 					UTF_END;
@@ -578,14 +578,17 @@ pltcl_func_handler(PG_FUNCTION_ARGS)
 		elog(ERROR, "SPI_finish() failed");
 
 	if (fcinfo->isnull)
-		retval = (Datum) 0;
+		retval = InputFunctionCall(&prodesc->result_in_func,
+								   NULL,
+								   prodesc->result_typioparam,
+								   -1);
 	else
 	{
 		UTF_BEGIN;
-		retval = FunctionCall3(&prodesc->result_in_func,
-							   PointerGetDatum(UTF_U2E(interp->result)),
-							   ObjectIdGetDatum(prodesc->result_typioparam),
-							   Int32GetDatum(-1));
+		retval = InputFunctionCall(&prodesc->result_in_func,
+								   UTF_U2E(interp->result),
+								   prodesc->result_typioparam,
+								   -1);
 		UTF_END;
 	}
 
@@ -805,7 +808,6 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS)
 	/* Use a TRY to ensure ret_values will get freed */
 	PG_TRY();
 	{
-
 		if (ret_numvals % 2 != 0)
 			elog(ERROR, "invalid return list from trigger - must have even # of elements");
 
@@ -871,11 +873,10 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS)
 			modnulls[attnum - 1] = ' ';
 			fmgr_info(typinput, &finfo);
 			UTF_BEGIN;
-			modvalues[attnum - 1] =
-				FunctionCall3(&finfo,
-							  CStringGetDatum(UTF_U2E(ret_value)),
-							  ObjectIdGetDatum(typioparam),
-					   Int32GetDatum(tupdesc->attrs[attnum - 1]->atttypmod));
+			modvalues[attnum - 1] = InputFunctionCall(&finfo,
+													  (char *) UTF_U2E(ret_value),
+													  typioparam,
+													  tupdesc->attrs[attnum - 1]->atttypmod);
 			UTF_END;
 		}
 
@@ -2041,17 +2042,18 @@ pltcl_SPI_execute_plan(ClientData cdata, Tcl_Interp *interp,
 		{
 			if (nulls && nulls[j] == 'n')
 			{
-				/* don't try to convert the input for a null */
-				argvalues[j] = (Datum) 0;
+				argvalues[j] = InputFunctionCall(&qdesc->arginfuncs[j],
+												 NULL,
+												 qdesc->argtypioparams[j],
+												 -1);
 			}
 			else
 			{
 				UTF_BEGIN;
-				argvalues[j] =
-					FunctionCall3(&qdesc->arginfuncs[j],
-								  CStringGetDatum(UTF_U2E(callargs[j])),
-								  ObjectIdGetDatum(qdesc->argtypioparams[j]),
-								  Int32GetDatum(-1));
+				argvalues[j] = InputFunctionCall(&qdesc->arginfuncs[j],
+												 (char *) UTF_U2E(callargs[j]),
+												 qdesc->argtypioparams[j],
+												 -1);
 				UTF_END;
 			}
 		}
@@ -2185,8 +2187,7 @@ pltcl_set_tuple_values(Tcl_Interp *interp, CONST84 char *arrayname,
 		 ************************************************************/
 		if (!isnull && OidIsValid(typoutput))
 		{
-			outputstr = DatumGetCString(OidFunctionCall1(typoutput,
-														 attr));
+			outputstr = OidOutputFunctionCall(typoutput, attr);
 			UTF_BEGIN;
 			Tcl_SetVar2(interp, *arrptr, *nameptr, UTF_E2U(outputstr), 0);
 			UTF_END;
@@ -2255,8 +2256,7 @@ pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc,
 		 ************************************************************/
 		if (!isnull && OidIsValid(typoutput))
 		{
-			outputstr = DatumGetCString(OidFunctionCall1(typoutput,
-														 attr));
+			outputstr = OidOutputFunctionCall(typoutput, attr);
 			Tcl_DStringAppendElement(retval, attname);
 			UTF_BEGIN;
 			Tcl_DStringAppendElement(retval, UTF_E2U(outputstr));