From 147d4bf3e5e3da2ee0f0cc132718ab1c4912a877 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Tue, 4 Apr 2006 19:35:37 +0000
Subject: [PATCH] Modify all callers of datatype input and receive functions so
 that if these functions are not strict, they will be called (passing a NULL
 first parameter) during any attempt to input a NULL value of their datatype. 
 Currently, all our input functions are strict and so this commit does not
 change any behavior.  However, this will make it possible to build domain
 input functions that centralize checking of domain constraints, thereby
 closing numerous holes in our domain support, as per previous discussion.

While at it, I took the opportunity to introduce convenience functions
InputFunctionCall, OutputFunctionCall, etc to use in code that calls I/O
functions.  This eliminates a lot of grotty-looking casts, but the main
motivation is to make it easier to grep for these places if we ever need
to touch them again.
---
 doc/src/sgml/ref/create_type.sgml    |  19 ++-
 src/backend/access/common/printtup.c |  18 +--
 src/backend/bootstrap/bootstrap.c    |  25 ++--
 src/backend/commands/copy.c          |  45 +++----
 src/backend/executor/execTuples.c    |  23 ++--
 src/backend/executor/nodeAgg.c       |   8 +-
 src/backend/executor/spi.c           |  11 +-
 src/backend/nodes/print.c            |   5 +-
 src/backend/parser/parse_coerce.c    |  35 +++---
 src/backend/parser/parse_type.c      |  11 +-
 src/backend/tcop/fastpath.c          |  89 +++++++-------
 src/backend/tcop/postgres.c          | 128 ++++++++++----------
 src/backend/utils/adt/arrayfuncs.c   |  26 ++---
 src/backend/utils/adt/rowtypes.c     |  91 ++++++++-------
 src/backend/utils/adt/ruleutils.c    |   5 +-
 src/backend/utils/adt/varlena.c      |   5 +-
 src/backend/utils/cache/lsyscache.c  |   8 +-
 src/backend/utils/fmgr/fmgr.c        | 168 ++++++++++++++++++++++++++-
 src/include/fmgr.h                   |  18 ++-
 src/pl/plperl/plperl.c               | 109 +++++++++--------
 src/pl/plpgsql/src/pl_exec.c         |  29 +++--
 src/pl/plpython/plpython.c           |  72 +++++++-----
 src/pl/tcl/pltcl.c                   |  50 ++++----
 23 files changed, 602 insertions(+), 396 deletions(-)

diff --git a/doc/src/sgml/ref/create_type.sgml b/doc/src/sgml/ref/create_type.sgml
index 9382395182a..e3b8b44d8f9 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 3ba606dfaf1..ba5793b0e7e 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 1d48fc44e18..c1d16b0e8fc 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 23c057af8e0..af21d565f17 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 bd50027f776..81f589fd44b 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 5b48545aac9..a242d53968e 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 183bc89b020..3563ba23d3a 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 18b7535cd06..c3fbdf242d0 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 bcb8e0016df..5a343e768dd 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 e88e6c37c1e..fec4552c9c4 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 f9a5d7116b1..c9fa715600f 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 78cb8c3f811..df9ef8983a0 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 1de3ff13a7d..dba7353ba58 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 9d7fcaa413a..bb61dc29568 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 714140e0617..f8daab6eeaf 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 b936b8219dd..79dc0178a80 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 6eb6ea802bf..fb24cc62366 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 e64ede6f566..4a663135dcb 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 9f17a24357d..0d6e72594d2 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 0524d9ebe4e..310df025706 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 e4fa7003a7c..8a82a42fbe6 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 d20a5f72f13..bf96db1776d 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 e1f21109bc5..07cbcc2eb31 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));
-- 
GitLab