diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index f364ce4..1621254 100644
*** a/src/pl/plpgsql/src/pl_comp.c
--- b/src/pl/plpgsql/src/pl_comp.c
*************** do_compile(FunctionCallInfo fcinfo,
*** 559,566 ****
  			{
  				function->fn_retbyval = typeStruct->typbyval;
  				function->fn_rettyplen = typeStruct->typlen;
- 				function->fn_rettypioparam = getTypeIOParam(typeTup);
- 				fmgr_info(typeStruct->typinput, &(function->fn_retinput));
  
  				/*
  				 * install $0 reference, but only for polymorphic return
--- 559,564 ----
*************** plpgsql_compile_inline(char *proc_source
*** 803,809 ****
  	char	   *func_name = "inline_code_block";
  	PLpgSQL_function *function;
  	ErrorContextCallback plerrcontext;
- 	Oid			typinput;
  	PLpgSQL_variable *var;
  	int			parse_rc;
  	MemoryContext func_cxt;
--- 801,806 ----
*************** plpgsql_compile_inline(char *proc_source
*** 876,883 ****
  	/* a bit of hardwired knowledge about type VOID here */
  	function->fn_retbyval = true;
  	function->fn_rettyplen = sizeof(int32);
- 	getTypeInputInfo(VOIDOID, &typinput, &function->fn_rettypioparam);
- 	fmgr_info(typinput, &(function->fn_retinput));
  
  	/*
  	 * Remember if function is STABLE/IMMUTABLE.  XXX would it be better to
--- 873,878 ----
*************** build_datatype(HeapTuple typeTup, int32 
*** 2200,2211 ****
  	}
  	typ->typlen = typeStruct->typlen;
  	typ->typbyval = typeStruct->typbyval;
  	typ->typrelid = typeStruct->typrelid;
- 	typ->typioparam = getTypeIOParam(typeTup);
  	typ->collation = typeStruct->typcollation;
  	if (OidIsValid(collation) && OidIsValid(typ->collation))
  		typ->collation = collation;
- 	fmgr_info(typeStruct->typinput, &(typ->typinput));
  	typ->atttypmod = typmod;
  
  	return typ;
--- 2195,2205 ----
  	}
  	typ->typlen = typeStruct->typlen;
  	typ->typbyval = typeStruct->typbyval;
+ 	typ->typisdomain = (typeStruct->typtype == TYPTYPE_DOMAIN);
  	typ->typrelid = typeStruct->typrelid;
  	typ->collation = typeStruct->typcollation;
  	if (OidIsValid(collation) && OidIsValid(typ->collation))
  		typ->collation = collation;
  	typ->atttypmod = typmod;
  
  	return typ;
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 41a68f8..4b4a442 100644
*** a/src/pl/plpgsql/src/pl_exec.c
--- b/src/pl/plpgsql/src/pl_exec.c
***************
*** 26,31 ****
--- 26,33 ----
  #include "funcapi.h"
  #include "miscadmin.h"
  #include "nodes/nodeFuncs.h"
+ #include "optimizer/planner.h"
+ #include "parser/parse_coerce.h"
  #include "parser/scansup.h"
  #include "storage/proc.h"
  #include "tcop/tcopprot.h"
*************** typedef struct
*** 50,55 ****
--- 52,71 ----
  	bool	   *freevals;		/* which arguments are pfree-able */
  } PreparedParamsData;
  
+ typedef struct
+ {
+ 	/* NB: we assume this struct contains no padding bytes */
+ 	Oid			srctype;		/* source type for cast */
+ 	Oid			dsttype;		/* destination type for cast */
+ 	int32		dsttypmod;		/* destination typmod for cast */
+ } plpgsql_CastHashKey;
+ 
+ typedef struct
+ {
+ 	plpgsql_CastHashKey key;	/* hash key --- MUST BE FIRST */
+ 	ExprState  *cast_exprstate; /* cast expression, or NULL if no-op cast */
+ } plpgsql_CastHashEntry;
+ 
  /*
   * All plpgsql function executions within a single transaction share the same
   * executor EState for evaluating "simple" expressions.  Each function call
*************** static void exec_move_row_from_datum(PLp
*** 211,225 ****
  static char *convert_value_to_string(PLpgSQL_execstate *estate,
  						Datum value, Oid valtype);
  static Datum exec_cast_value(PLpgSQL_execstate *estate,
! 				Datum value, bool isnull,
  				Oid valtype, int32 valtypmod,
! 				Oid reqtype, int32 reqtypmod,
! 				FmgrInfo *reqinput,
! 				Oid reqtypioparam);
! static Datum exec_simple_cast_value(PLpgSQL_execstate *estate,
! 					   Datum value, bool isnull,
! 					   Oid valtype, int32 valtypmod,
! 					   Oid reqtype, int32 reqtypmod);
  static void exec_init_tuple_store(PLpgSQL_execstate *estate);
  static void exec_set_found(PLpgSQL_execstate *estate, bool state);
  static void plpgsql_create_econtext(PLpgSQL_execstate *estate);
--- 227,237 ----
  static char *convert_value_to_string(PLpgSQL_execstate *estate,
  						Datum value, Oid valtype);
  static Datum exec_cast_value(PLpgSQL_execstate *estate,
! 				Datum value, bool *isnull,
  				Oid valtype, int32 valtypmod,
! 				Oid reqtype, int32 reqtypmod);
! static ExprState *get_cast_expression(PLpgSQL_execstate *estate,
! 					Oid srctype, Oid dsttype, int32 dsttypmod);
  static void exec_init_tuple_store(PLpgSQL_execstate *estate);
  static void exec_set_found(PLpgSQL_execstate *estate, bool state);
  static void plpgsql_create_econtext(PLpgSQL_execstate *estate);
*************** plpgsql_exec_function(PLpgSQL_function *
*** 454,466 ****
  			/* Cast value to proper type */
  			estate.retval = exec_cast_value(&estate,
  											estate.retval,
! 											fcinfo->isnull,
  											estate.rettype,
  											-1,
  											func->fn_rettype,
! 											-1,
! 											&(func->fn_retinput),
! 											func->fn_rettypioparam);
  
  			/*
  			 * If the function's return type isn't by value, copy the value
--- 466,476 ----
  			/* Cast value to proper type */
  			estate.retval = exec_cast_value(&estate,
  											estate.retval,
! 											&fcinfo->isnull,
  											estate.rettype,
  											-1,
  											func->fn_rettype,
! 											-1);
  
  			/*
  			 * If the function's return type isn't by value, copy the value
*************** exec_stmt_block(PLpgSQL_execstate *estat
*** 1079,1085 ****
  						 * before the notnull check to be consistent with
  						 * exec_assign_value.)
  						 */
! 						if (!var->datatype->typinput.fn_strict)
  							exec_assign_value(estate,
  											  (PLpgSQL_datum *) var,
  											  (Datum) 0,
--- 1089,1095 ----
  						 * before the notnull check to be consistent with
  						 * exec_assign_value.)
  						 */
! 						if (var->datatype->typisdomain)
  							exec_assign_value(estate,
  											  (PLpgSQL_datum *) var,
  											  (Datum) 0,
*************** exec_stmt_fori(PLpgSQL_execstate *estate
*** 1903,1914 ****
  	 */
  	value = exec_eval_expr(estate, stmt->lower,
  						   &isnull, &valtype, &valtypmod);
! 	value = exec_cast_value(estate, value, isnull,
  							valtype, valtypmod,
  							var->datatype->typoid,
! 							var->datatype->atttypmod,
! 							&(var->datatype->typinput),
! 							var->datatype->typioparam);
  	if (isnull)
  		ereport(ERROR,
  				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
--- 1913,1922 ----
  	 */
  	value = exec_eval_expr(estate, stmt->lower,
  						   &isnull, &valtype, &valtypmod);
! 	value = exec_cast_value(estate, value, &isnull,
  							valtype, valtypmod,
  							var->datatype->typoid,
! 							var->datatype->atttypmod);
  	if (isnull)
  		ereport(ERROR,
  				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
*************** exec_stmt_fori(PLpgSQL_execstate *estate
*** 1921,1932 ****
  	 */
  	value = exec_eval_expr(estate, stmt->upper,
  						   &isnull, &valtype, &valtypmod);
! 	value = exec_cast_value(estate, value, isnull,
  							valtype, valtypmod,
  							var->datatype->typoid,
! 							var->datatype->atttypmod,
! 							&(var->datatype->typinput),
! 							var->datatype->typioparam);
  	if (isnull)
  		ereport(ERROR,
  				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
--- 1929,1938 ----
  	 */
  	value = exec_eval_expr(estate, stmt->upper,
  						   &isnull, &valtype, &valtypmod);
! 	value = exec_cast_value(estate, value, &isnull,
  							valtype, valtypmod,
  							var->datatype->typoid,
! 							var->datatype->atttypmod);
  	if (isnull)
  		ereport(ERROR,
  				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
*************** exec_stmt_fori(PLpgSQL_execstate *estate
*** 1941,1952 ****
  	{
  		value = exec_eval_expr(estate, stmt->step,
  							   &isnull, &valtype, &valtypmod);
! 		value = exec_cast_value(estate, value, isnull,
  								valtype, valtypmod,
  								var->datatype->typoid,
! 								var->datatype->atttypmod,
! 								&(var->datatype->typinput),
! 								var->datatype->typioparam);
  		if (isnull)
  			ereport(ERROR,
  					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
--- 1947,1956 ----
  	{
  		value = exec_eval_expr(estate, stmt->step,
  							   &isnull, &valtype, &valtypmod);
! 		value = exec_cast_value(estate, value, &isnull,
  								valtype, valtypmod,
  								var->datatype->typoid,
! 								var->datatype->atttypmod);
  		if (isnull)
  			ereport(ERROR,
  					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
*************** exec_stmt_return_next(PLpgSQL_execstate 
*** 2614,2626 ****
  						errmsg("wrong result type supplied in RETURN NEXT")));
  
  					/* coerce type if needed */
! 					retval = exec_simple_cast_value(estate,
! 													retval,
! 													isNull,
! 													var->datatype->typoid,
! 													var->datatype->atttypmod,
! 												 tupdesc->attrs[0]->atttypid,
! 											   tupdesc->attrs[0]->atttypmod);
  
  					tuplestore_putvalues(estate->tuple_store, tupdesc,
  										 &retval, &isNull);
--- 2618,2630 ----
  						errmsg("wrong result type supplied in RETURN NEXT")));
  
  					/* coerce type if needed */
! 					retval = exec_cast_value(estate,
! 											 retval,
! 											 &isNull,
! 											 var->datatype->typoid,
! 											 var->datatype->atttypmod,
! 											 tupdesc->attrs[0]->atttypid,
! 											 tupdesc->attrs[0]->atttypmod);
  
  					tuplestore_putvalues(estate->tuple_store, tupdesc,
  										 &retval, &isNull);
*************** exec_stmt_return_next(PLpgSQL_execstate 
*** 2740,2752 ****
  					   errmsg("wrong result type supplied in RETURN NEXT")));
  
  			/* coerce type if needed */
! 			retval = exec_simple_cast_value(estate,
! 											retval,
! 											isNull,
! 											rettype,
! 											rettypmod,
! 											tupdesc->attrs[0]->atttypid,
! 											tupdesc->attrs[0]->atttypmod);
  
  			tuplestore_putvalues(estate->tuple_store, tupdesc,
  								 &retval, &isNull);
--- 2744,2756 ----
  					   errmsg("wrong result type supplied in RETURN NEXT")));
  
  			/* coerce type if needed */
! 			retval = exec_cast_value(estate,
! 									 retval,
! 									 &isNull,
! 									 rettype,
! 									 rettypmod,
! 									 tupdesc->attrs[0]->atttypid,
! 									 tupdesc->attrs[0]->atttypmod);
  
  			tuplestore_putvalues(estate->tuple_store, tupdesc,
  								 &retval, &isNull);
*************** exec_assign_value(PLpgSQL_execstate *est
*** 4070,4082 ****
  
  				newvalue = exec_cast_value(estate,
  										   value,
! 										   isNull,
  										   valtype,
  										   valtypmod,
  										   var->datatype->typoid,
! 										   var->datatype->atttypmod,
! 										   &(var->datatype->typinput),
! 										   var->datatype->typioparam);
  
  				if (isNull && var->notnull)
  					ereport(ERROR,
--- 4074,4084 ----
  
  				newvalue = exec_cast_value(estate,
  										   value,
! 										   &isNull,
  										   valtype,
  										   valtypmod,
  										   var->datatype->typoid,
! 										   var->datatype->atttypmod);
  
  				if (isNull && var->notnull)
  					ereport(ERROR,
*************** exec_assign_value(PLpgSQL_execstate *est
*** 4220,4232 ****
  				 */
  				atttype = rec->tupdesc->attrs[fno]->atttypid;
  				atttypmod = rec->tupdesc->attrs[fno]->atttypmod;
! 				values[fno] = exec_simple_cast_value(estate,
! 													 value,
! 													 isNull,
! 													 valtype,
! 													 valtypmod,
! 													 atttype,
! 													 atttypmod);
  				nulls[fno] = isNull;
  
  				/*
--- 4222,4234 ----
  				 */
  				atttype = rec->tupdesc->attrs[fno]->atttypid;
  				atttypmod = rec->tupdesc->attrs[fno]->atttypmod;
! 				values[fno] = exec_cast_value(estate,
! 											  value,
! 											  &isNull,
! 											  valtype,
! 											  valtypmod,
! 											  atttype,
! 											  atttypmod);
  				nulls[fno] = isNull;
  
  				/*
*************** exec_assign_value(PLpgSQL_execstate *est
*** 4383,4395 ****
  				estate->eval_tuptable = save_eval_tuptable;
  
  				/* Coerce source value to match array element type. */
! 				coerced_value = exec_simple_cast_value(estate,
! 													   value,
! 													   isNull,
! 													   valtype,
! 													   valtypmod,
! 													   arrayelem->elemtypoid,
! 													 arrayelem->arraytypmod);
  
  				/*
  				 * If the original array is null, cons up an empty array so
--- 4385,4397 ----
  				estate->eval_tuptable = save_eval_tuptable;
  
  				/* Coerce source value to match array element type. */
! 				coerced_value = exec_cast_value(estate,
! 												value,
! 												&isNull,
! 												valtype,
! 												valtypmod,
! 												arrayelem->elemtypoid,
! 												arrayelem->arraytypmod);
  
  				/*
  				 * If the original array is null, cons up an empty array so
*************** exec_eval_integer(PLpgSQL_execstate *est
*** 4760,4768 ****
  	int32		exprtypmod;
  
  	exprdatum = exec_eval_expr(estate, expr, isNull, &exprtypeid, &exprtypmod);
! 	exprdatum = exec_simple_cast_value(estate, exprdatum, *isNull,
! 									   exprtypeid, exprtypmod,
! 									   INT4OID, -1);
  	return DatumGetInt32(exprdatum);
  }
  
--- 4762,4770 ----
  	int32		exprtypmod;
  
  	exprdatum = exec_eval_expr(estate, expr, isNull, &exprtypeid, &exprtypmod);
! 	exprdatum = exec_cast_value(estate, exprdatum, isNull,
! 								exprtypeid, exprtypmod,
! 								INT4OID, -1);
  	return DatumGetInt32(exprdatum);
  }
  
*************** exec_eval_boolean(PLpgSQL_execstate *est
*** 4783,4791 ****
  	int32		exprtypmod;
  
  	exprdatum = exec_eval_expr(estate, expr, isNull, &exprtypeid, &exprtypmod);
! 	exprdatum = exec_simple_cast_value(estate, exprdatum, *isNull,
! 									   exprtypeid, exprtypmod,
! 									   BOOLOID, -1);
  	return DatumGetBool(exprdatum);
  }
  
--- 4785,4793 ----
  	int32		exprtypmod;
  
  	exprdatum = exec_eval_expr(estate, expr, isNull, &exprtypeid, &exprtypmod);
! 	exprdatum = exec_cast_value(estate, exprdatum, isNull,
! 								exprtypeid, exprtypmod,
! 								BOOLOID, -1);
  	return DatumGetBool(exprdatum);
  }
  
*************** convert_value_to_string(PLpgSQL_execstat
*** 5705,5710 ****
--- 5707,5716 ----
  /* ----------
   * exec_cast_value			Cast a value if required
   *
+  * Note that *isnull is an input and also an output parameter.  While it's
+  * unlikely that a cast operation would produce null from non-null or vice
+  * versa, that could in principle happen.
+  *
   * Note: the estate's eval_econtext is used for temporary storage, and may
   * also contain the result Datum if we have to do a conversion to a pass-
   * by-reference data type.  Be sure to do an exec_eval_cleanup() call when
*************** convert_value_to_string(PLpgSQL_execstat
*** 5713,5723 ****
   */
  static Datum
  exec_cast_value(PLpgSQL_execstate *estate,
! 				Datum value, bool isnull,
  				Oid valtype, int32 valtypmod,
! 				Oid reqtype, int32 reqtypmod,
! 				FmgrInfo *reqinput,
! 				Oid reqtypioparam)
  {
  	/*
  	 * If the type of the given value isn't what's requested, convert it.
--- 5719,5727 ----
   */
  static Datum
  exec_cast_value(PLpgSQL_execstate *estate,
! 				Datum value, bool *isnull,
  				Oid valtype, int32 valtypmod,
! 				Oid reqtype, int32 reqtypmod)
  {
  	/*
  	 * If the type of the given value isn't what's requested, convert it.
*************** exec_cast_value(PLpgSQL_execstate *estat
*** 5725,5791 ****
  	if (valtype != reqtype ||
  		(valtypmod != reqtypmod && reqtypmod != -1))
  	{
! 		MemoryContext oldcontext;
  
! 		oldcontext = MemoryContextSwitchTo(estate->eval_econtext->ecxt_per_tuple_memory);
! 		if (!isnull)
  		{
! 			char	   *extval;
  
! 			extval = convert_value_to_string(estate, value, valtype);
! 			value = InputFunctionCall(reqinput, extval,
! 									  reqtypioparam, reqtypmod);
! 		}
! 		else
! 		{
! 			value = InputFunctionCall(reqinput, NULL,
! 									  reqtypioparam, reqtypmod);
  		}
- 		MemoryContextSwitchTo(oldcontext);
  	}
  
  	return value;
  }
  
  /* ----------
!  * exec_simple_cast_value			Cast a value if required
   *
!  * As above, but need not supply details about target type.  Note that this
!  * is slower than exec_cast_value with cached type info, and so should be
!  * avoided in heavily used code paths.
   * ----------
   */
! static Datum
! exec_simple_cast_value(PLpgSQL_execstate *estate,
! 					   Datum value, bool isnull,
! 					   Oid valtype, int32 valtypmod,
! 					   Oid reqtype, int32 reqtypmod)
  {
! 	if (valtype != reqtype ||
! 		(valtypmod != reqtypmod && reqtypmod != -1))
  	{
! 		Oid			typinput;
! 		Oid			typioparam;
! 		FmgrInfo	finfo_input;
  
! 		getTypeInputInfo(reqtype, &typinput, &typioparam);
  
! 		fmgr_info(typinput, &finfo_input);
  
! 		value = exec_cast_value(estate,
! 								value,
! 								isnull,
! 								valtype,
! 								valtypmod,
! 								reqtype,
! 								reqtypmod,
! 								&finfo_input,
! 								typioparam);
  	}
  
! 	return value;
! }
  
  
  /* ----------
   * exec_simple_check_node -		Recursively check if an expression
--- 5729,5899 ----
  	if (valtype != reqtype ||
  		(valtypmod != reqtypmod && reqtypmod != -1))
  	{
! 		ExprState  *cast_expr;
  
! 		cast_expr = get_cast_expression(estate, valtype, reqtype, reqtypmod);
! 		if (cast_expr)
  		{
! 			ExprContext *econtext = estate->eval_econtext;
! 			MemoryContext oldcontext;
  
! 			oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
! 			econtext->caseValue_datum = value;
! 			econtext->caseValue_isNull = *isnull;
! 
! 			value = ExecEvalExpr(cast_expr, econtext, isnull, NULL);
! 
! 			MemoryContextSwitchTo(oldcontext);
  		}
  	}
  
  	return value;
  }
  
  /* ----------
!  * get_cast_expression			Look up how to perform a type cast
   *
!  * Returns an expression evaluation tree based on a CaseTestExpr input,
!  * or NULL if the cast is a mere no-op relabeling.
!  *
!  * We cache the results of the lookup in a per-function hash table.
!  * In principle this could probably be a session-wide hash table instead,
!  * but that introduces some corner-case questions that probably aren't
!  * worth dealing with; in particular that re-entrant use of an evaluation
!  * tree might occur.
   * ----------
   */
! static ExprState *
! get_cast_expression(PLpgSQL_execstate *estate,
! 					Oid srctype, Oid dsttype, int32 dsttypmod)
  {
! 	HTAB	   *cast_hash = estate->func->cast_hash;
! 	plpgsql_CastHashKey cast_key;
! 	plpgsql_CastHashEntry *cast_entry;
! 	bool		found;
! 	CaseTestExpr *placeholder;
! 	Node	   *cast_expr;
! 	ExprState  *cast_exprstate;
! 	MemoryContext oldcontext;
! 
! 	/* Create the cast-info hash table if we didn't already */
! 	if (cast_hash == NULL)
  	{
! 		HASHCTL		ctl;
  
! 		memset(&ctl, 0, sizeof(ctl));
! 		ctl.keysize = sizeof(plpgsql_CastHashKey);
! 		ctl.entrysize = sizeof(plpgsql_CastHashEntry);
! 		ctl.hcxt = estate->func->fn_cxt;
! 		cast_hash = hash_create("PLpgSQL cast cache",
! 								16,		/* start small and extend */
! 								&ctl,
! 								HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
! 		estate->func->cast_hash = cast_hash;
! 	}
  
! 	/* Look for existing entry */
! 	cast_key.srctype = srctype;
! 	cast_key.dsttype = dsttype;
! 	cast_key.dsttypmod = dsttypmod;
! 	cast_entry = (plpgsql_CastHashEntry *) hash_search(cast_hash,
! 													   (void *) &cast_key,
! 													   HASH_FIND, NULL);
! 	if (cast_entry)
! 		return cast_entry->cast_exprstate;
  
! 	/* Construct expression tree for coercion in function's context */
! 	oldcontext = MemoryContextSwitchTo(estate->func->fn_cxt);
! 
! 	/*
! 	 * We use a CaseTestExpr as the base of the coercion tree, since it's very
! 	 * cheap to insert the source value for that.
! 	 */
! 	placeholder = makeNode(CaseTestExpr);
! 	placeholder->typeId = srctype;
! 	placeholder->typeMod = -1;
! 	placeholder->collation = get_typcollation(srctype);
! 	if (OidIsValid(estate->func->fn_input_collation) &&
! 		OidIsValid(placeholder->collation))
! 		placeholder->collation = estate->func->fn_input_collation;
! 
! 	/*
! 	 * Apply coercion.  We use ASSIGNMENT coercion because that's the closest
! 	 * match to plpgsql's historical behavior; in particular, EXPLICIT
! 	 * coercion would allow silent truncation to a destination
! 	 * varchar/bpchar's length, which we do not want.
! 	 *
! 	 * If source type is UNKNOWN, coerce_to_target_type will fail (it only
! 	 * expects to see that for Const input nodes), so don't call it; we'll
! 	 * apply CoerceViaIO instead.
! 	 */
! 	if (srctype != UNKNOWNOID)
! 		cast_expr = coerce_to_target_type(NULL,
! 										  (Node *) placeholder, srctype,
! 										  dsttype, dsttypmod,
! 										  COERCION_ASSIGNMENT,
! 										  COERCE_IMPLICIT_CAST,
! 										  -1);
! 	else
! 		cast_expr = NULL;
! 
! 	/*
! 	 * If there's no cast path according to the parser, fall back to using an
! 	 * I/O coercion; this is semantically dubious but matches plpgsql's
! 	 * historical behavior.
! 	 */
! 	if (cast_expr == NULL)
! 	{
! 		CoerceViaIO *iocoerce = makeNode(CoerceViaIO);
! 
! 		iocoerce->arg = (Expr *) placeholder;
! 		iocoerce->resulttype = dsttype;
! 		iocoerce->resultcollid = InvalidOid;
! 		iocoerce->coerceformat = COERCE_IMPLICIT_CAST;
! 		iocoerce->location = -1;
! 		cast_expr = (Node *) iocoerce;
! 		if (dsttypmod != -1)
! 			cast_expr = coerce_to_target_type(NULL,
! 											  cast_expr, dsttype,
! 											  dsttype, dsttypmod,
! 											  COERCION_ASSIGNMENT,
! 											  COERCE_IMPLICIT_CAST,
! 											  -1);
  	}
  
! 	/* Note: we don't bother labeling the expression tree with collation */
! 
! 	/* Detect whether we have a no-op (RelabelType) coercion */
! 	if (IsA(cast_expr, RelabelType) &&
! 		((RelabelType *) cast_expr)->arg == (Expr *) placeholder)
! 		cast_expr = NULL;
! 
! 	if (cast_expr)
! 	{
! 		/* ExecInitExpr assumes we've planned the expression */
! 		cast_expr = (Node *) expression_planner((Expr *) cast_expr);
! 		/* Create an expression eval state tree for it */
! 		cast_exprstate = ExecInitExpr((Expr *) cast_expr, NULL);
! 	}
! 	else
! 		cast_exprstate = NULL;
! 
! 	MemoryContextSwitchTo(oldcontext);
! 
! 	/*
! 	 * Now fill in a hashtable entry.  If we fail anywhere up to/including
! 	 * this step, we've only leaked some memory in the function context, which
! 	 * isn't great but isn't disastrous either.
! 	 */
! 	cast_entry = (plpgsql_CastHashEntry *) hash_search(cast_hash,
! 													   (void *) &cast_key,
! 													   HASH_ENTER, &found);
! 	Assert(!found);				/* wasn't there a moment ago */
! 
! 	cast_entry->cast_exprstate = cast_exprstate;
  
+ 	return cast_exprstate;
+ }
  
  /* ----------
   * exec_simple_check_node -		Recursively check if an expression
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 624c91e..8bac860 100644
*** a/src/pl/plpgsql/src/plpgsql.h
--- b/src/pl/plpgsql/src/plpgsql.h
***************
*** 22,27 ****
--- 22,28 ----
  #include "commands/event_trigger.h"
  #include "commands/trigger.h"
  #include "executor/spi.h"
+ #include "utils/hsearch.h"
  
  /**********************************************************************
   * Definitions
*************** typedef struct
*** 178,187 ****
  	int			ttype;			/* PLPGSQL_TTYPE_ code */
  	int16		typlen;			/* stuff copied from its pg_type entry */
  	bool		typbyval;
  	Oid			typrelid;
- 	Oid			typioparam;
  	Oid			collation;		/* from pg_type, but can be overridden */
- 	FmgrInfo	typinput;		/* lookup info for typinput function */
  	int32		atttypmod;		/* typmod (taken from someplace else) */
  } PLpgSQL_type;
  
--- 179,187 ----
  	int			ttype;			/* PLPGSQL_TTYPE_ code */
  	int16		typlen;			/* stuff copied from its pg_type entry */
  	bool		typbyval;
+ 	bool		typisdomain;
  	Oid			typrelid;
  	Oid			collation;		/* from pg_type, but can be overridden */
  	int32		atttypmod;		/* typmod (taken from someplace else) */
  } PLpgSQL_type;
  
*************** typedef struct PLpgSQL_function
*** 709,716 ****
  	Oid			fn_rettype;
  	int			fn_rettyplen;
  	bool		fn_retbyval;
- 	FmgrInfo	fn_retinput;
- 	Oid			fn_rettypioparam;
  	bool		fn_retistuple;
  	bool		fn_retset;
  	bool		fn_readonly;
--- 709,714 ----
*************** typedef struct PLpgSQL_function
*** 748,753 ****
--- 746,754 ----
  	PLpgSQL_datum **datums;
  	PLpgSQL_stmt_block *action;
  
+ 	/* table for performing casts needed in this function */
+ 	HTAB	   *cast_hash;
+ 
  	/* these fields change when the function is used */
  	struct PLpgSQL_execstate *cur_estate;
  	unsigned long use_count;
