diff --git a/src/backend/executor/execCurrent.c b/src/backend/executor/execCurrent.c
index 1c8be25..1504c92 100644
*** a/src/backend/executor/execCurrent.c
--- b/src/backend/executor/execCurrent.c
*************** fetch_cursor_param_value(ExprContext *ec
*** 216,226 ****
  	if (paramInfo &&
  		paramId > 0 && paramId <= paramInfo->numParams)
  	{
! 		ParamExternData *prm = &paramInfo->params[paramId - 1];
  
  		/* give hook a chance in case parameter is dynamic */
! 		if (!OidIsValid(prm->ptype) && paramInfo->paramFetch != NULL)
! 			(*paramInfo->paramFetch) (paramInfo, paramId);
  
  		if (OidIsValid(prm->ptype) && !prm->isnull)
  		{
--- 216,228 ----
  	if (paramInfo &&
  		paramId > 0 && paramId <= paramInfo->numParams)
  	{
! 		ParamExternData *prm;
  
  		/* give hook a chance in case parameter is dynamic */
! 		if (paramInfo->paramFetch != NULL)
! 			prm = (*paramInfo->paramFetch) (paramInfo, paramId, 0);
! 		else
! 			prm = &paramInfo->params[paramId - 1];
  
  		if (OidIsValid(prm->ptype) && !prm->isnull)
  		{
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index d94fe58..f558d40 100644
*** a/src/backend/executor/execQual.c
--- b/src/backend/executor/execQual.c
*************** ExecEvalParamExtern(ExprState *exprstate
*** 1137,1147 ****
  	if (paramInfo &&
  		thisParamId > 0 && thisParamId <= paramInfo->numParams)
  	{
! 		ParamExternData *prm = &paramInfo->params[thisParamId - 1];
  
  		/* give hook a chance in case parameter is dynamic */
! 		if (!OidIsValid(prm->ptype) && paramInfo->paramFetch != NULL)
! 			(*paramInfo->paramFetch) (paramInfo, thisParamId);
  
  		if (OidIsValid(prm->ptype))
  		{
--- 1137,1149 ----
  	if (paramInfo &&
  		thisParamId > 0 && thisParamId <= paramInfo->numParams)
  	{
! 		ParamExternData *prm;
  
  		/* give hook a chance in case parameter is dynamic */
! 		if (paramInfo->paramFetch != NULL)
! 			prm = (*paramInfo->paramFetch) (paramInfo, thisParamId, 0);
! 		else
! 			prm = &paramInfo->params[thisParamId - 1];
  
  		if (OidIsValid(prm->ptype))
  		{
diff --git a/src/backend/nodes/params.c b/src/backend/nodes/params.c
index fb803f8..b4916d8 100644
*** a/src/backend/nodes/params.c
--- b/src/backend/nodes/params.c
*************** copyParamList(ParamListInfo from)
*** 52,65 ****
  
  	for (i = 0; i < from->numParams; i++)
  	{
! 		ParamExternData *oprm = &from->params[i];
  		ParamExternData *nprm = &retval->params[i];
  		int16		typLen;
  		bool		typByVal;
  
  		/* give hook a chance in case parameter is dynamic */
! 		if (!OidIsValid(oprm->ptype) && from->paramFetch != NULL)
! 			(*from->paramFetch) (from, i + 1);
  
  		/* flat-copy the parameter info */
  		*nprm = *oprm;
--- 52,67 ----
  
  	for (i = 0; i < from->numParams; i++)
  	{
! 		ParamExternData *oprm;
  		ParamExternData *nprm = &retval->params[i];
  		int16		typLen;
  		bool		typByVal;
  
  		/* give hook a chance in case parameter is dynamic */
! 		if (from->paramFetch != NULL)
! 			oprm = (*from->paramFetch) (from, i + 1, PARAM_CHECK_VALID);
! 		else
! 			oprm = &from->params[i];
  
  		/* flat-copy the parameter info */
  		*nprm = *oprm;
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 84d58ae..c0e086b 100644
*** a/src/backend/optimizer/util/clauses.c
--- b/src/backend/optimizer/util/clauses.c
*************** eval_const_expressions_mutator(Node *nod
*** 2343,2356 ****
  		case T_Param:
  			{
  				Param	   *param = (Param *) node;
  
  				/* Look to see if we've been given a value for this Param */
  				if (param->paramkind == PARAM_EXTERN &&
! 					context->boundParams != NULL &&
! 					param->paramid > 0 &&
! 					param->paramid <= context->boundParams->numParams)
  				{
! 					ParamExternData *prm = &context->boundParams->params[param->paramid - 1];
  
  					if (OidIsValid(prm->ptype))
  					{
--- 2343,2365 ----
  		case T_Param:
  			{
  				Param	   *param = (Param *) node;
+ 				int			thisParamId = param->paramid;
+ 				ParamListInfo paramInfo = context->boundParams;
  
  				/* Look to see if we've been given a value for this Param */
  				if (param->paramkind == PARAM_EXTERN &&
! 					paramInfo != NULL &&
! 					thisParamId > 0 &&
! 					thisParamId <= paramInfo->numParams)
  				{
! 					ParamExternData *prm;
! 
! 					/* give hook a chance in case parameter is dynamic */
! 					if (paramInfo->paramFetch != NULL)
! 						prm = (*paramInfo->paramFetch) (paramInfo, thisParamId,
! 														PARAM_CHECK_SAFE);
! 					else
! 						prm = &paramInfo->params[thisParamId - 1];
  
  					if (OidIsValid(prm->ptype))
  					{
diff --git a/src/include/nodes/params.h b/src/include/nodes/params.h
index a0f7dd0..268876a 100644
*** a/src/include/nodes/params.h
--- b/src/include/nodes/params.h
*************** struct ParseState;
*** 36,53 ****
   *
   *	  There are two hook functions that can be associated with a ParamListInfo
   *	  array to support dynamic parameter handling.  First, if paramFetch
!  *	  isn't null and the executor requires a value for an invalid parameter
!  *	  (one with ptype == InvalidOid), the paramFetch hook is called to give
!  *	  it a chance to fill in the parameter value.  Second, a parserSetup
!  *	  hook can be supplied to re-instantiate the original parsing hooks if
!  *	  a query needs to be re-parsed/planned (as a substitute for supposing
!  *	  that the current ptype values represent a fixed set of parameter types).
! 
   *	  Although the data structure is really an array, not a list, we keep
   *	  the old typedef name to avoid unnecessary code changes.
   * ----------------
   */
  
  #define PARAM_FLAG_CONST	0x0001		/* parameter is constant */
  
  typedef struct ParamExternData
--- 36,63 ----
   *
   *	  There are two hook functions that can be associated with a ParamListInfo
   *	  array to support dynamic parameter handling.  First, if paramFetch
!  *	  isn't null, the paramFetch hook is called whenever a parameter value
!  *	  is required, to give it a chance to fill in the parameter value.
!  *	  (If the hook can't supply a value, it should set ptype to InvalidOid to
!  *	  specify that the parameter is missing, rather than merely being null.)
!  *	  Second, a parserSetup hook can be supplied to re-instantiate the
!  *	  original parsing hooks if a query needs to be re-parsed/planned
!  *	  (otherwise, we assume that the ptype values represent a fixed
!  *	  set of parameter types).
!  *
!  *	  Note that if the paramFetch hook is set, the params[] array is
!  *	  really entirely virtual and can be of any length, since the hook
!  *	  has full control of which entry is accessed.  (As an example,
!  *	  PL/pgSQL uses only a single params[] entry that is multiplexed
!  *	  among all parameters.)  However, numParams must still be valid
!  *	  as it defines the largest possibly-valid parameter number.
!  *
   *	  Although the data structure is really an array, not a list, we keep
   *	  the old typedef name to avoid unnecessary code changes.
   * ----------------
   */
  
+ /* flag bits in ParamExternData.pflags: */
  #define PARAM_FLAG_CONST	0x0001		/* parameter is constant */
  
  typedef struct ParamExternData
*************** typedef struct ParamExternData
*** 60,66 ****
  
  typedef struct ParamListInfoData *ParamListInfo;
  
! typedef void (*ParamFetchHook) (ParamListInfo params, int paramid);
  
  typedef void (*ParserSetupHook) (struct ParseState *pstate, void *arg);
  
--- 70,81 ----
  
  typedef struct ParamListInfoData *ParamListInfo;
  
! /* flag bits for ParamFetchHook's flags argument: */
! #define PARAM_CHECK_VALID	0x0001		/* check paramid validity */
! #define PARAM_CHECK_SAFE	0x0002		/* skip evals that might fail */
! 
! typedef ParamExternData *(*ParamFetchHook) (ParamListInfo params, int paramid,
! 														int flags);
  
  typedef void (*ParserSetupHook) (struct ParseState *pstate, void *arg);
  
*************** typedef struct ParamListInfoData
*** 70,76 ****
  	void	   *paramFetchArg;
  	ParserSetupHook parserSetup;	/* parser setup hook */
  	void	   *parserSetupArg;
! 	int			numParams;		/* number of ParamExternDatas following */
  	ParamExternData params[FLEXIBLE_ARRAY_MEMBER];
  }	ParamListInfoData;
  
--- 85,92 ----
  	void	   *paramFetchArg;
  	ParserSetupHook parserSetup;	/* parser setup hook */
  	void	   *parserSetupArg;
! 	int			numParams;		/* maximum allowable Param number */
! 	/* typically, params[] is of length numParams */
  	ParamExternData params[FLEXIBLE_ARRAY_MEMBER];
  }	ParamListInfoData;
  
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index f24f55a..5b9fe37 100644
*** a/src/pl/plpgsql/src/pl_exec.c
--- b/src/pl/plpgsql/src/pl_exec.c
*************** static int exec_for_query(PLpgSQL_execst
*** 211,217 ****
  			   Portal portal, bool prefetch_ok);
  static ParamListInfo setup_param_list(PLpgSQL_execstate *estate,
  				 PLpgSQL_expr *expr);
! static void plpgsql_param_fetch(ParamListInfo params, int paramid);
  static void exec_move_row(PLpgSQL_execstate *estate,
  			  PLpgSQL_rec *rec,
  			  PLpgSQL_row *row,
--- 211,218 ----
  			   Portal portal, bool prefetch_ok);
  static ParamListInfo setup_param_list(PLpgSQL_execstate *estate,
  				 PLpgSQL_expr *expr);
! static ParamExternData *plpgsql_param_fetch(ParamListInfo params, int paramid,
! 					int flags);
  static void exec_move_row(PLpgSQL_execstate *estate,
  			  PLpgSQL_rec *rec,
  			  PLpgSQL_row *row,
*************** exec_stmt_forc(PLpgSQL_execstate *estate
*** 2197,2206 ****
  		elog(ERROR, "could not open cursor: %s",
  			 SPI_result_code_string(SPI_result));
  
- 	/* don't need paramlist any more */
- 	if (paramLI)
- 		pfree(paramLI);
- 
  	/*
  	 * If cursor variable was NULL, store the generated portal name in it
  	 */
--- 2198,2203 ----
*************** plpgsql_estate_setup(PLpgSQL_execstate *
*** 3169,3174 ****
--- 3166,3186 ----
  	estate->datums = palloc(sizeof(PLpgSQL_datum *) * estate->ndatums);
  	/* caller is expected to fill the datums array */
  
+ 	/*
+ 	 * Create ParamListInfo with one entry (we only need one because
+ 	 * plpgsql_param_fetch() funnels all requests through entry zero).  This
+ 	 * is shared by all SPI invocations from this estate.  We would just make
+ 	 * this a static part of the estate struct, except that doesn't work for
+ 	 * structs containing flexible arrays.
+ 	 */
+ 	estate->paramLI = (ParamListInfo)
+ 		palloc0(offsetof(ParamListInfoData, params) + sizeof(ParamExternData));
+ 	estate->paramLI->paramFetch = plpgsql_param_fetch;
+ 	estate->paramLI->paramFetchArg = (void *) estate;
+ 	estate->paramLI->parserSetup = (ParserSetupHook) plpgsql_parser_setup;
+ 	estate->paramLI->parserSetupArg = NULL;		/* filled during use */
+ 	estate->paramLI->numParams = estate->ndatums;
+ 
  	/* set up for use of appropriate simple-expression EState */
  	if (simple_eval_estate)
  		estate->simple_eval_estate = simple_eval_estate;
*************** plpgsql_estate_setup(PLpgSQL_execstate *
*** 3179,3185 ****
  	estate->eval_processed = 0;
  	estate->eval_lastoid = InvalidOid;
  	estate->eval_econtext = NULL;
- 	estate->cur_expr = NULL;
  
  	estate->err_stmt = NULL;
  	estate->err_text = NULL;
--- 3191,3196 ----
*************** exec_stmt_execsql(PLpgSQL_execstate *est
*** 3495,3503 ****
  					 (rc == SPI_OK_SELECT) ? errhint("If you want to discard the results of a SELECT, use PERFORM instead.") : 0));
  	}
  
- 	if (paramLI)
- 		pfree(paramLI);
- 
  	return PLPGSQL_RC_OK;
  }
  
--- 3506,3511 ----
*************** exec_stmt_open(PLpgSQL_execstate *estate
*** 3864,3871 ****
  
  	if (curname)
  		pfree(curname);
- 	if (paramLI)
- 		pfree(paramLI);
  
  	return PLPGSQL_RC_OK;
  }
--- 3872,3877 ----
*************** exec_run_select(PLpgSQL_execstate *estat
*** 4909,4916 ****
  		if (*portalP == NULL)
  			elog(ERROR, "could not open implicit cursor for query \"%s\": %s",
  				 expr->query, SPI_result_code_string(SPI_result));
- 		if (paramLI)
- 			pfree(paramLI);
  		return SPI_OK_CURSOR;
  	}
  
--- 4915,4920 ----
*************** exec_run_select(PLpgSQL_execstate *estat
*** 4930,4938 ****
  	estate->eval_processed = SPI_processed;
  	estate->eval_lastoid = SPI_lastoid;
  
- 	if (paramLI)
- 		pfree(paramLI);
- 
  	return rc;
  }
  
--- 4934,4939 ----
*************** exec_eval_simple_expr(PLpgSQL_execstate 
*** 5140,5146 ****
  	LocalTransactionId curlxid = MyProc->lxid;
  	CachedPlan *cplan;
  	ParamListInfo paramLI;
- 	PLpgSQL_expr *save_cur_expr;
  	MemoryContext oldcontext;
  
  	/*
--- 5141,5146 ----
*************** exec_eval_simple_expr(PLpgSQL_execstate 
*** 5216,5230 ****
  	}
  
  	/*
! 	 * Create the param list in econtext's temporary memory context. We won't
! 	 * need to free it explicitly, since it will go away at the next reset of
! 	 * that context.
! 	 *
! 	 * Just for paranoia's sake, save and restore the prior value of
! 	 * estate->cur_expr, which setup_param_list() sets.
  	 */
- 	save_cur_expr = estate->cur_expr;
- 
  	paramLI = setup_param_list(estate, expr);
  	econtext->ecxt_param_list_info = paramLI;
  
--- 5216,5223 ----
  	}
  
  	/*
! 	 * Set up param list.
  	 */
  	paramLI = setup_param_list(estate, expr);
  	econtext->ecxt_param_list_info = paramLI;
  
*************** exec_eval_simple_expr(PLpgSQL_execstate 
*** 5244,5251 ****
  	/* Assorted cleanup */
  	expr->expr_simple_in_use = false;
  
- 	estate->cur_expr = save_cur_expr;
- 
  	if (!estate->readonly_func)
  		PopActiveSnapshot();
  
--- 5237,5242 ----
*************** exec_eval_simple_expr(PLpgSQL_execstate 
*** 5266,5285 ****
  
  
  /*
!  * Create a ParamListInfo to pass to SPI
!  *
!  * We fill in the values for any expression parameters that are plain
!  * PLpgSQL_var datums; these are cheap and safe to evaluate, and by setting
!  * them with PARAM_FLAG_CONST flags, we allow the planner to use those values
!  * in custom plans.  However, parameters that are not plain PLpgSQL_vars
!  * should not be evaluated here, because they could throw errors (for example
!  * "no such record field") and we do not want that to happen in a part of
!  * the expression that might never be evaluated at runtime.  To handle those
!  * parameters, we set up a paramFetch hook for the executor to call when it
!  * wants a not-presupplied value.
!  *
!  * The result is a locally palloc'd array that should be pfree'd after use;
!  * but note it can be NULL.
   */
  static ParamListInfo
  setup_param_list(PLpgSQL_execstate *estate, PLpgSQL_expr *expr)
--- 5257,5263 ----
  
  
  /*
!  * Set up a ParamListInfo to pass to SPI for this expression
   */
  static ParamListInfo
  setup_param_list(PLpgSQL_execstate *estate, PLpgSQL_expr *expr)
*************** setup_param_list(PLpgSQL_execstate *esta
*** 5292,5344 ****
  	 */
  	Assert(expr->plan != NULL);
  
! 	/*
! 	 * Could we re-use these arrays instead of palloc'ing a new one each time?
! 	 * However, we'd have to re-fill the array each time anyway, since new
! 	 * values might have been assigned to the variables.
! 	 */
  	if (!bms_is_empty(expr->paramnos))
  	{
! 		int			dno;
  
! 		paramLI = (ParamListInfo)
! 			palloc0(offsetof(ParamListInfoData, params) +
! 					estate->ndatums * sizeof(ParamExternData));
! 		paramLI->paramFetch = plpgsql_param_fetch;
! 		paramLI->paramFetchArg = (void *) estate;
! 		paramLI->parserSetup = (ParserSetupHook) plpgsql_parser_setup;
  		paramLI->parserSetupArg = (void *) expr;
- 		paramLI->numParams = estate->ndatums;
- 
- 		/* Instantiate values for "safe" parameters of the expression */
- 		dno = -1;
- 		while ((dno = bms_next_member(expr->paramnos, dno)) >= 0)
- 		{
- 			PLpgSQL_datum *datum = estate->datums[dno];
- 
- 			if (datum->dtype == PLPGSQL_DTYPE_VAR)
- 			{
- 				PLpgSQL_var *var = (PLpgSQL_var *) datum;
- 				ParamExternData *prm = &paramLI->params[dno];
- 
- 				prm->value = var->value;
- 				prm->isnull = var->isnull;
- 				prm->pflags = PARAM_FLAG_CONST;
- 				prm->ptype = var->datatype->typoid;
- 			}
- 		}
  
  		/*
! 		 * Set up link to active expr where the hook functions can find it.
! 		 * Callers must save and restore cur_expr if there is any chance that
! 		 * they are interrupting an active use of parameters.
! 		 */
! 		estate->cur_expr = expr;
! 
! 		/*
! 		 * Also make sure this is set before parser hooks need it.  There is
! 		 * no need to save and restore, since the value is always correct once
! 		 * set.  (Should be set already, but let's be sure.)
  		 */
  		expr->func = estate->func;
  	}
--- 5270,5287 ----
  	 */
  	Assert(expr->plan != NULL);
  
! 	/* We only need a ParamListInfo if the expression has parameters */
  	if (!bms_is_empty(expr->paramnos))
  	{
! 		/* Use the common ParamListInfo for all evals in this estate */
! 		paramLI = estate->paramLI;
  
! 		/* Make sure it points to this expr while we use it */
  		paramLI->parserSetupArg = (void *) expr;
  
  		/*
! 		 * Make sure this is set before parser hooks need it.  (Should be set
! 		 * already, but let's be sure.)
  		 */
  		expr->func = estate->func;
  	}
*************** setup_param_list(PLpgSQL_execstate *esta
*** 5357,5370 ****
  /*
   * plpgsql_param_fetch		paramFetch callback for dynamic parameter fetch
   */
! static void
! plpgsql_param_fetch(ParamListInfo params, int paramid)
  {
  	int			dno;
  	PLpgSQL_execstate *estate;
  	PLpgSQL_expr *expr;
  	PLpgSQL_datum *datum;
- 	ParamExternData *prm;
  	int32		prmtypmod;
  
  	/* paramid's are 1-based, but dnos are 0-based */
--- 5300,5313 ----
  /*
   * plpgsql_param_fetch		paramFetch callback for dynamic parameter fetch
   */
! static ParamExternData *
! plpgsql_param_fetch(ParamListInfo params, int paramid, int flags)
  {
+ 	ParamExternData *prm = &params->params[0];
  	int			dno;
  	PLpgSQL_execstate *estate;
  	PLpgSQL_expr *expr;
  	PLpgSQL_datum *datum;
  	int32		prmtypmod;
  
  	/* paramid's are 1-based, but dnos are 0-based */
*************** plpgsql_param_fetch(ParamListInfo params
*** 5373,5395 ****
  
  	/* fetch back the hook data */
  	estate = (PLpgSQL_execstate *) params->paramFetchArg;
! 	expr = estate->cur_expr;
  	Assert(params->numParams == estate->ndatums);
  
  	/*
! 	 * Do nothing if asked for a value that's not supposed to be used by this
! 	 * SQL expression.  This avoids unwanted evaluations when functions such
! 	 * as copyParamList try to materialize all the values.
  	 */
! 	if (!bms_is_member(dno, expr->paramnos))
! 		return;
  
! 	/* OK, evaluate the value and store into the appropriate paramlist slot */
! 	datum = estate->datums[dno];
! 	prm = &params->params[dno];
  	exec_eval_datum(estate, datum,
  					&prm->ptype, &prmtypmod,
  					&prm->value, &prm->isnull);
  }
  
  
--- 5316,5361 ----
  
  	/* fetch back the hook data */
  	estate = (PLpgSQL_execstate *) params->paramFetchArg;
! 	expr = (PLpgSQL_expr *) params->parserSetupArg;
  	Assert(params->numParams == estate->ndatums);
  
+ 	/* now we can look up the datum */
+ 	datum = estate->datums[dno];
+ 
  	/*
! 	 * We return null/invalid in two cases:
! 	 *
! 	 * 1. If PARAM_CHECK_VALID is set and we're asked for a value that's not
! 	 * supposed to be used by this SQL expression.  This avoids unwanted
! 	 * evaluations when functions such as copyParamList() try to materialize
! 	 * all the parameter slots.
! 	 *
! 	 * 2. If PARAM_CHECK_SAFE is set and we're not sure evaluation will
! 	 * succeed; the caller would rather we not return a value than cause an
! 	 * unexpected error.
  	 */
! 	if (flags)
! 	{
! 		if (((flags & PARAM_CHECK_VALID) &&
! 			 !bms_is_member(dno, expr->paramnos)) ||
! 			((flags & PARAM_CHECK_SAFE) &&
! 			 datum->dtype != PLPGSQL_DTYPE_VAR))
! 		{
! 			prm->value = (Datum) 0;
! 			prm->isnull = true;
! 			prm->pflags = 0;
! 			prm->ptype = InvalidOid;
! 			return prm;
! 		}
! 	}
  
! 	/* OK, evaluate the value and store into the single paramlist slot */
  	exec_eval_datum(estate, datum,
  					&prm->ptype, &prmtypmod,
  					&prm->value, &prm->isnull);
+ 	/* If we're being called by the planner, OK to treat value as constant */
+ 	prm->pflags = PARAM_FLAG_CONST;
+ 	return prm;
  }
  
  
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 4ec4628..032f283 100644
*** a/src/pl/plpgsql/src/plpgsql.h
--- b/src/pl/plpgsql/src/plpgsql.h
*************** typedef struct PLpgSQL_execstate
*** 784,789 ****
--- 784,792 ----
  	int			ndatums;
  	PLpgSQL_datum **datums;
  
+ 	/* Struct for passing datum values to executor */
+ 	ParamListInfo paramLI;
+ 
  	/* EState to use for "simple" expression evaluation */
  	EState	   *simple_eval_estate;
  
*************** typedef struct PLpgSQL_execstate
*** 792,798 ****
  	uint32		eval_processed;
  	Oid			eval_lastoid;
  	ExprContext *eval_econtext; /* for executing simple expressions */
- 	PLpgSQL_expr *cur_expr;		/* current query/expr being evaluated */
  
  	/* status information for error context reporting */
  	PLpgSQL_stmt *err_stmt;		/* current stmt */
--- 795,800 ----
