Variable-length FunctionCallInfoData

Started by Andres Freundover 7 years ago18 messages
#1Andres Freund
andres@anarazel.de
1 attachment(s)

Hi,

While prototyping codegen improvements for JITed expression evaluation,
I once more hit the issue that the FunctionCallInfoData structs are
really large (936 bytes), despite arguments beyond the fourth barely
every being used. I think we should fix that.

What I think we should do is convert
FunctionCallInfoData->{arg,argisnull} into an array of NullableDatum
(new type, a struct of Datum and bool), and then use a variable length
array for the arguments. In the super common case of 2 arguments that
reduces the size of the array from 936 to 64 bytes. Besides the size
reduction this also noticably reduces the number of cachelines accessed
- before it's absolutely guaranteed that the arg and argnull arrays for
the same argument aren't on the same cacheline, after it's almost
guaranteed to be the case.

Attached is a *PROTOTYPE* patch doing so. Note I was too lazy to fully
fix up the jit code, I didn't want to do the legwork before we've some
agreement on this. We also can get rid of FUNC_MAX_ARGS after this, but
there's surrounding code that still relies on it.

There's some added uglyness, which I hope we can polish a bit
further. Right now we allocate a good number of FunctionCallInfoData
struct on the stack - which doesn't quite work afterwards anymore. So
the stack allocations, for the majoroity cases where the argument number
is known, currently looks like:

union {
FunctionCallInfoData fcinfo;
char *fcinfo_data[SizeForFunctionCallInfoData(0)];
} fcinfodata;
FunctionCallInfo fcinfo = &fcinfodata.fcinfo;

that's not pretty, but also not that bad.

It's a bit unfortunate that this'll break some extensions, but I don't
really see a way around that. The current approach, to me, clearly
doesn't have a future. I wonder if we should add a bunch of accessor
macros / inline functions that we (or extension authors) can backpatch
to reduce the pain of maintaining different code paths.

Besides the change here, I think we should also go much further with the
conversion to NullableDatum. There's two main areas of change: I want
to move the execExpr.c related code so steps return data into
NullableDatums - that removes a good chunk of pointer dereferences and
allocations. Secondly I think we should move TupleTableSlot to this
format - the issue with nulls / datums being on separate cachelines is
noticeable in profiles, but more importantly the code looks more
consistent with it.

As an example for the difference in memory usage, here's the memory
consumption at ExecutorRun time, for TPCH's Q01:

master:
TopPortalContext: 8192 total in 1 blocks; 7664 free (0 chunks); 528 used
PortalContext: 1024 total in 1 blocks; 576 free (0 chunks); 448 used:
ExecutorState: 90744 total in 5 blocks; 31568 free (2 chunks); 59176 used
ExprContext: 8192 total in 1 blocks; 7936 free (0 chunks); 256 used
ExprContext: 8192 total in 1 blocks; 7936 free (0 chunks); 256 used
ExprContext: 8192 total in 1 blocks; 7936 free (0 chunks); 256 used
ExprContext: 8192 total in 1 blocks; 3488 free (0 chunks); 4704 used
ExprContext: 8192 total in 1 blocks; 7936 free (0 chunks); 256 used
ExprContext: 8192 total in 1 blocks; 7936 free (0 chunks); 256 used
Grand total: 149112 bytes in 13 blocks; 82976 free (2 chunks); 66136 used

patch:
TopPortalContext: 8192 total in 1 blocks; 7664 free (0 chunks); 528 used
PortalContext: 1024 total in 1 blocks; 576 free (0 chunks); 448 used:
ExecutorState: 65536 total in 4 blocks; 33536 free (6 chunks); 32000 used
ExprContext: 8192 total in 1 blocks; 7936 free (0 chunks); 256 used
ExprContext: 8192 total in 1 blocks; 7936 free (0 chunks); 256 used
ExprContext: 8192 total in 1 blocks; 7936 free (0 chunks); 256 used
ExprContext: 8192 total in 1 blocks; 5408 free (0 chunks); 2784 used
ExprContext: 8192 total in 1 blocks; 7936 free (0 chunks); 256 used
ExprContext: 8192 total in 1 blocks; 7936 free (0 chunks); 256 used
Grand total: 123904 bytes in 12 blocks; 86864 free (6 chunks); 37040 used

As you can see, the ExecutorState context uses nearly half the amount of
memory as before. In a lot of cases a good chunk of the benefit is going
to be hidden due to memory context sizing, but I'd expect that to matter
much less for more complex statements and plpgsql functions etc.

Comments?

Greetings,

Andres Freund

Attachments:

v1-0001-Variable-length-FunctionCallInfoData.patchtext/x-diff; charset=us-asciiDownload
From 50ac86917da52cbab61cffaabb1d443184b3aa87 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Tue, 5 Jun 2018 10:24:31 -0700
Subject: [PATCH v1] Variable length FunctionCallInfoData

Author: Andres Freund
Reviewed-By:
Discussion: https://postgr.es/m/
Backpatch:
---
 src/backend/commands/event_trigger.c  |  12 +-
 src/backend/commands/functioncmds.c   |  16 +-
 src/backend/commands/tablecmds.c      |  12 +-
 src/backend/commands/trigger.c        |  16 +-
 src/backend/executor/execExpr.c       |  81 +--
 src/backend/executor/execExprInterp.c | 131 ++--
 src/backend/executor/execSRF.c        |  42 +-
 src/backend/executor/functions.c      |   4 +-
 src/backend/executor/nodeAgg.c        |  74 ++-
 src/backend/executor/nodeWindowAgg.c  |  80 ++-
 src/backend/jit/llvm/llvmjit_expr.c   |  20 +-
 src/backend/tcop/fastpath.c           |  47 +-
 src/backend/utils/adt/arrayfuncs.c    | 153 +++--
 src/backend/utils/adt/int.c           |  23 +-
 src/backend/utils/adt/oid.c           |  23 +-
 src/backend/utils/adt/rowtypes.c      |  38 +-
 src/backend/utils/fmgr/fmgr.c         | 917 ++++++++++++--------------
 src/backend/utils/sort/sortsupport.c  |  13 +-
 src/include/executor/execExpr.h       |   6 +-
 src/include/executor/nodeAgg.h        |   6 +-
 src/include/fmgr.h                    |  15 +-
 src/include/nodes/execnodes.h         |   2 +-
 src/include/postgres.h                |   7 +
 src/pl/plperl/plperl.c                |  24 +-
 src/pl/plpgsql/src/pl_exec.c          |   8 +-
 src/pl/plpython/plpy_exec.c           |   4 +-
 src/pl/tcl/pltcl.c                    |  21 +-
 27 files changed, 912 insertions(+), 883 deletions(-)

diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index eecc85d14e5..46af596fa18 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -1050,7 +1050,11 @@ EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata)
 	{
 		Oid			fnoid = lfirst_oid(lc);
 		FmgrInfo	flinfo;
-		FunctionCallInfoData fcinfo;
+		union {
+			FunctionCallInfoData fcinfo;
+			char *fcinfo_data[SizeForFunctionCallInfoData(0)];
+		} fcinfodata;
+		FunctionCallInfo fcinfo = &fcinfodata.fcinfo;
 		PgStat_FunctionCallUsage fcusage;
 
 		elog(DEBUG1, "EventTriggerInvoke %u", fnoid);
@@ -1070,10 +1074,10 @@ EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata)
 		fmgr_info(fnoid, &flinfo);
 
 		/* Call the function, passing no arguments but setting a context. */
-		InitFunctionCallInfoData(fcinfo, &flinfo, 0,
+		InitFunctionCallInfoData(*fcinfo, &flinfo, 0,
 								 InvalidOid, (Node *) trigdata, NULL);
-		pgstat_init_function_usage(&fcinfo, &fcusage);
-		FunctionCallInvoke(&fcinfo);
+		pgstat_init_function_usage(fcinfo, &fcusage);
+		FunctionCallInvoke(fcinfo);
 		pgstat_end_function_usage(&fcusage, true);
 
 		/* Reclaim memory. */
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 8864d9ae446..0e52997e95e 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -2213,7 +2213,11 @@ ExecuteCallStmt(CallStmt *stmt, ParamListInfo params, bool atomic, DestReceiver
 	int			i;
 	AclResult	aclresult;
 	FmgrInfo	flinfo;
-	FunctionCallInfoData fcinfo;
+	union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(3)];
+	} fcinfodata;
+	FunctionCallInfo fcinfo = &fcinfodata.fcinfo;
 	CallContext *callcontext;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2264,7 +2268,7 @@ ExecuteCallStmt(CallStmt *stmt, ParamListInfo params, bool atomic, DestReceiver
 	/* Initialize function call structure */
 	InvokeFunctionExecuteHook(fexpr->funcid);
 	fmgr_info(fexpr->funcid, &flinfo);
-	InitFunctionCallInfoData(fcinfo, &flinfo, nargs, fexpr->inputcollid, (Node *) callcontext, NULL);
+	InitFunctionCallInfoData(*fcinfo, &flinfo, nargs, fexpr->inputcollid, (Node *) callcontext, NULL);
 
 	/*
 	 * Evaluate procedure arguments inside a suitable execution context.  Note
@@ -2285,13 +2289,13 @@ ExecuteCallStmt(CallStmt *stmt, ParamListInfo params, bool atomic, DestReceiver
 
 		val = ExecEvalExprSwitchContext(exprstate, econtext, &isnull);
 
-		fcinfo.arg[i] = val;
-		fcinfo.argnull[i] = isnull;
+		fcinfo->args[i].datum = val;
+		fcinfo->args[i].isnull = isnull;
 
 		i++;
 	}
 
-	retval = FunctionCallInvoke(&fcinfo);
+	retval = FunctionCallInvoke(fcinfo);
 
 	if (fexpr->funcresulttype == VOIDOID)
 	{
@@ -2311,7 +2315,7 @@ ExecuteCallStmt(CallStmt *stmt, ParamListInfo params, bool atomic, DestReceiver
 		TupOutputState *tstate;
 		TupleTableSlot *slot;
 
-		if (fcinfo.isnull)
+		if (fcinfo->isnull)
 			elog(ERROR, "procedure returned null record");
 
 		td = DatumGetHeapTupleHeader(retval);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 0e95037dcf4..97c54a6ff80 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8556,7 +8556,11 @@ validateForeignKeyConstraint(char *conname,
 
 	while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
 	{
-		FunctionCallInfoData fcinfo;
+		union {
+			FunctionCallInfoData fcinfo;
+			char *fcinfo_data[SizeForFunctionCallInfoData(0)];
+		} fcinfodata;
+		FunctionCallInfo fcinfo = &fcinfodata.fcinfo;
 		TriggerData trigdata;
 
 		/*
@@ -8564,7 +8568,7 @@ validateForeignKeyConstraint(char *conname,
 		 *
 		 * No parameters are passed, but we do set a context
 		 */
-		MemSet(&fcinfo, 0, sizeof(fcinfo));
+		MemSet(&fcinfodata, 0, sizeof(fcinfodata));
 
 		/*
 		 * We assume RI_FKey_check_ins won't look at flinfo...
@@ -8578,9 +8582,9 @@ validateForeignKeyConstraint(char *conname,
 		trigdata.tg_trigtuplebuf = scan->rs_cbuf;
 		trigdata.tg_newtuplebuf = InvalidBuffer;
 
-		fcinfo.context = (Node *) &trigdata;
+		fcinfo->context = (Node *) &trigdata;
 
-		RI_FKey_check_ins(&fcinfo);
+		RI_FKey_check_ins(fcinfo);
 	}
 
 	heap_endscan(scan);
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 57519fe8d64..f6059e43908 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2356,7 +2356,11 @@ ExecCallTriggerFunc(TriggerData *trigdata,
 					Instrumentation *instr,
 					MemoryContext per_tuple_context)
 {
-	FunctionCallInfoData fcinfo;
+	union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(0)];
+	} fcinfodata;
+	FunctionCallInfo fcinfo = &fcinfodata.fcinfo;
 	PgStat_FunctionCallUsage fcusage;
 	Datum		result;
 	MemoryContext oldContext;
@@ -2401,15 +2405,15 @@ ExecCallTriggerFunc(TriggerData *trigdata,
 	/*
 	 * Call the function, passing no arguments but setting a context.
 	 */
-	InitFunctionCallInfoData(fcinfo, finfo, 0,
+	InitFunctionCallInfoData(*fcinfo, finfo, 0,
 							 InvalidOid, (Node *) trigdata, NULL);
 
-	pgstat_init_function_usage(&fcinfo, &fcusage);
+	pgstat_init_function_usage(fcinfo, &fcusage);
 
 	MyTriggerDepth++;
 	PG_TRY();
 	{
-		result = FunctionCallInvoke(&fcinfo);
+		result = FunctionCallInvoke(fcinfo);
 	}
 	PG_CATCH();
 	{
@@ -2427,11 +2431,11 @@ ExecCallTriggerFunc(TriggerData *trigdata,
 	 * Trigger protocol allows function to return a null pointer, but NOT to
 	 * set the isnull result flag.
 	 */
-	if (fcinfo.isnull)
+	if (fcinfo->isnull)
 		ereport(ERROR,
 				(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
 				 errmsg("trigger function %u returned null value",
-						fcinfo.flinfo->fn_oid)));
+						fcinfo->flinfo->fn_oid)));
 
 	/*
 	 * If doing EXPLAIN ANALYZE, stop charging time to this trigger, and count
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index 67ebe0f6967..d3c606f3c11 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -970,7 +970,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 
 				/* Set up the primary fmgr lookup information */
 				finfo = palloc0(sizeof(FmgrInfo));
-				fcinfo = palloc0(sizeof(FunctionCallInfoData));
+				fcinfo = palloc0(SizeForFunctionCallInfoData(2));
 				fmgr_info(opexpr->opfuncid, finfo);
 				fmgr_info_set_expr((Node *) node, finfo);
 				InitFunctionCallInfoData(*fcinfo, finfo, 2,
@@ -978,7 +978,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 
 				/* Evaluate scalar directly into left function argument */
 				ExecInitExprRec(scalararg, state,
-								&fcinfo->arg[0], &fcinfo->argnull[0]);
+								&fcinfo->args[0].datum, &fcinfo->args[0].isnull);
 
 				/*
 				 * Evaluate array argument into our return value.  There's no
@@ -1267,7 +1267,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 
 				/* lookup the source type's output function */
 				scratch.d.iocoerce.finfo_out = palloc0(sizeof(FmgrInfo));
-				scratch.d.iocoerce.fcinfo_data_out = palloc0(sizeof(FunctionCallInfoData));
+				scratch.d.iocoerce.fcinfo_data_out = palloc0(SizeForFunctionCallInfoData(1));
 
 				getTypeOutputInfo(exprType((Node *) iocoerce->arg),
 								  &iofunc, &typisvarlena);
@@ -1279,7 +1279,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 
 				/* lookup the result type's input function */
 				scratch.d.iocoerce.finfo_in = palloc0(sizeof(FmgrInfo));
-				scratch.d.iocoerce.fcinfo_data_in = palloc0(sizeof(FunctionCallInfoData));
+				scratch.d.iocoerce.fcinfo_data_in = palloc0(SizeForFunctionCallInfoData(3));
 
 				getTypeInputInfo(iocoerce->resulttype,
 								 &iofunc, &typioparam);
@@ -1294,10 +1294,10 @@ ExecInitExprRec(Expr *node, ExprState *state,
 				 * function, since they're constants.
 				 */
 				fcinfo_in = scratch.d.iocoerce.fcinfo_data_in;
-				fcinfo_in->arg[1] = ObjectIdGetDatum(typioparam);
-				fcinfo_in->argnull[1] = false;
-				fcinfo_in->arg[2] = Int32GetDatum(-1);
-				fcinfo_in->argnull[2] = false;
+				fcinfo_in->args[1].datum = ObjectIdGetDatum(typioparam);
+				fcinfo_in->args[1].isnull = false;
+				fcinfo_in->args[2].datum = Int32GetDatum(-1);
+				fcinfo_in->args[2].isnull = false;
 
 				ExprEvalPushStep(state, &scratch);
 				break;
@@ -1738,7 +1738,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 
 					/* Set up the primary fmgr lookup information */
 					finfo = palloc0(sizeof(FmgrInfo));
-					fcinfo = palloc0(sizeof(FunctionCallInfoData));
+					fcinfo = palloc0(SizeForFunctionCallInfoData(2));
 					fmgr_info(proc, finfo);
 					fmgr_info_set_expr((Node *) node, finfo);
 					InitFunctionCallInfoData(*fcinfo, finfo, 2,
@@ -1753,9 +1753,9 @@ ExecInitExprRec(Expr *node, ExprState *state,
 
 					/* evaluate left and right args directly into fcinfo */
 					ExecInitExprRec(left_expr, state,
-									&fcinfo->arg[0], &fcinfo->argnull[0]);
+									&fcinfo->args[0].datum, &fcinfo->args[0].isnull);
 					ExecInitExprRec(right_expr, state,
-									&fcinfo->arg[1], &fcinfo->argnull[1]);
+									&fcinfo->args[1].datum, &fcinfo->args[1].isnull);
 
 					scratch.opcode = EEOP_ROWCOMPARE_STEP;
 					scratch.d.rowcompare_step.finfo = finfo;
@@ -1881,7 +1881,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 
 				/* Perform function lookup */
 				finfo = palloc0(sizeof(FmgrInfo));
-				fcinfo = palloc0(sizeof(FunctionCallInfoData));
+				fcinfo = palloc0(SizeForFunctionCallInfoData(2));
 				fmgr_info(typentry->cmp_proc, finfo);
 				fmgr_info_set_expr((Node *) node, finfo);
 				InitFunctionCallInfoData(*fcinfo, finfo, 2,
@@ -2190,7 +2190,7 @@ ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid,
 
 	/* Allocate function lookup data and parameter workspace for this call */
 	scratch->d.func.finfo = palloc0(sizeof(FmgrInfo));
-	scratch->d.func.fcinfo_data = palloc0(sizeof(FunctionCallInfoData));
+	scratch->d.func.fcinfo_data = palloc0(SizeForFunctionCallInfoData(nargs));
 	flinfo = scratch->d.func.finfo;
 	fcinfo = scratch->d.func.fcinfo_data;
 
@@ -2229,13 +2229,14 @@ ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid,
 			 */
 			Const	   *con = (Const *) arg;
 
-			fcinfo->arg[argno] = con->constvalue;
-			fcinfo->argnull[argno] = con->constisnull;
+			fcinfo->args[argno].datum = con->constvalue;
+			fcinfo->args[argno].isnull = con->constisnull;
 		}
 		else
 		{
 			ExecInitExprRec(arg, state,
-							&fcinfo->arg[argno], &fcinfo->argnull[argno]);
+							&fcinfo->args[argno].datum,
+							&fcinfo->args[argno].isnull);
 		}
 		argno++;
 	}
@@ -2868,10 +2869,11 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
 		int			numInputs = pertrans->numInputs;
 		int			argno;
 		int			setno;
-		FunctionCallInfo trans_fcinfo = &pertrans->transfn_fcinfo;
+		FunctionCallInfo trans_fcinfo = pertrans->transfn_fcinfo;
 		ListCell   *arg;
 		ListCell   *bail;
 		List	   *adjust_bailout = NIL;
+		NullableDatum   *strictargs = NULL;
 		bool	   *strictnulls = NULL;
 
 		/*
@@ -2909,7 +2911,7 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
 			Assert(pertrans->numSortCols == 0);
 			Assert(list_length(pertrans->aggref->args) == 1);
 
-			strictnulls = trans_fcinfo->argnull + 1;
+			strictargs = trans_fcinfo->args + 1;
 			source_tle = (TargetEntry *) linitial(pertrans->aggref->args);
 
 			/*
@@ -2923,21 +2925,21 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
 				 * value
 				 */
 				ExecInitExprRec(source_tle->expr, state,
-								&trans_fcinfo->arg[argno + 1],
-								&trans_fcinfo->argnull[argno + 1]);
+								&trans_fcinfo->args[argno + 1].datum,
+								&trans_fcinfo->args[argno + 1].isnull);
 			}
 			else
 			{
-				FunctionCallInfo ds_fcinfo = &pertrans->deserialfn_fcinfo;
+				FunctionCallInfo ds_fcinfo = pertrans->deserialfn_fcinfo;
 
 				/* evaluate argument */
 				ExecInitExprRec(source_tle->expr, state,
-								&ds_fcinfo->arg[0],
-								&ds_fcinfo->argnull[0]);
+								&ds_fcinfo->args[0].datum,
+								&ds_fcinfo->args[0].isnull);
 
 				/* Dummy second argument for type-safety reasons */
-				ds_fcinfo->arg[1] = PointerGetDatum(NULL);
-				ds_fcinfo->argnull[1] = false;
+				ds_fcinfo->args[1].datum = PointerGetDatum(NULL);
+				ds_fcinfo->args[1].isnull = false;
 
 				/*
 				 * Don't call a strict deserialization function with NULL
@@ -2951,8 +2953,8 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
 				scratch.d.agg_deserialize.aggstate = aggstate;
 				scratch.d.agg_deserialize.fcinfo_data = ds_fcinfo;
 				scratch.d.agg_deserialize.jumpnull = -1;	/* adjust later */
-				scratch.resvalue = &trans_fcinfo->arg[argno + 1];
-				scratch.resnull = &trans_fcinfo->argnull[argno + 1];
+				scratch.resvalue = &trans_fcinfo->args[argno + 1].datum;
+				scratch.resnull = &trans_fcinfo->args[argno + 1].isnull;
 
 				ExprEvalPushStep(state, &scratch);
 				adjust_bailout = lappend_int(adjust_bailout,
@@ -2969,7 +2971,7 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
 			/*
 			 * Normal transition function without ORDER BY / DISTINCT.
 			 */
-			strictnulls = trans_fcinfo->argnull + 1;
+			strictargs = trans_fcinfo->args + 1;
 
 			foreach(arg, pertrans->aggref->args)
 			{
@@ -2980,8 +2982,8 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
 				 * value
 				 */
 				ExecInitExprRec(source_tle->expr, state,
-								&trans_fcinfo->arg[argno + 1],
-								&trans_fcinfo->argnull[argno + 1]);
+								&trans_fcinfo->args[argno + 1].datum,
+								&trans_fcinfo->args[argno + 1].isnull);
 				argno++;
 			}
 		}
@@ -3029,8 +3031,12 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
 		 */
 		if (trans_fcinfo->flinfo->fn_strict && numInputs > 0)
 		{
-			scratch.opcode = EEOP_AGG_STRICT_INPUT_CHECK;
+			if (strictnulls)
+				scratch.opcode = EEOP_AGG_STRICT_INPUT_CHECK_NULLS;
+			else
+				scratch.opcode = EEOP_AGG_STRICT_INPUT_CHECK_ARGS;
 			scratch.d.agg_strict_input_check.nulls = strictnulls;
+			scratch.d.agg_strict_input_check.args = strictargs;
 			scratch.d.agg_strict_input_check.jumpnull = -1; /* adjust later */
 			scratch.d.agg_strict_input_check.nargs = numInputs;
 			ExprEvalPushStep(state, &scratch);
@@ -3084,7 +3090,8 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
 				Assert(as->d.jump.jumpdone == -1);
 				as->d.jump.jumpdone = state->steps_len;
 			}
-			else if (as->opcode == EEOP_AGG_STRICT_INPUT_CHECK)
+			else if (as->opcode == EEOP_AGG_STRICT_INPUT_CHECK_ARGS ||
+					 as->opcode == EEOP_AGG_STRICT_INPUT_CHECK_NULLS)
 			{
 				Assert(as->d.agg_strict_input_check.jumpnull == -1);
 				as->d.agg_strict_input_check.jumpnull = state->steps_len;
@@ -3289,7 +3296,7 @@ ExecBuildGroupingEqual(TupleDesc ldesc, TupleDesc rdesc,
 
 		/* Set up the primary fmgr lookup information */
 		finfo = palloc0(sizeof(FmgrInfo));
-		fcinfo = palloc0(sizeof(FunctionCallInfoData));
+		fcinfo = palloc0(SizeForFunctionCallInfoData(2));
 		fmgr_info(foid, finfo);
 		fmgr_info_set_expr(NULL, finfo);
 		InitFunctionCallInfoData(*fcinfo, finfo, 2,
@@ -3299,16 +3306,16 @@ ExecBuildGroupingEqual(TupleDesc ldesc, TupleDesc rdesc,
 		scratch.opcode = EEOP_INNER_VAR;
 		scratch.d.var.attnum = attno - 1;
 		scratch.d.var.vartype = latt->atttypid;
-		scratch.resvalue = &fcinfo->arg[0];
-		scratch.resnull = &fcinfo->argnull[0];
+		scratch.resvalue = &fcinfo->args[0].datum;
+		scratch.resnull = &fcinfo->args[0].isnull;
 		ExprEvalPushStep(state, &scratch);
 
 		/* right arg */
 		scratch.opcode = EEOP_OUTER_VAR;
 		scratch.d.var.attnum = attno - 1;
 		scratch.d.var.vartype = ratt->atttypid;
-		scratch.resvalue = &fcinfo->arg[1];
-		scratch.resnull = &fcinfo->argnull[1];
+		scratch.resvalue = &fcinfo->args[1].datum;
+		scratch.resnull = &fcinfo->args[1].isnull;
 		ExprEvalPushStep(state, &scratch);
 
 		/* evaluate distinctness */
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 76ad078fe66..9207becc1b2 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -386,7 +386,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 		&&CASE_EEOP_ALTERNATIVE_SUBPLAN,
 		&&CASE_EEOP_AGG_STRICT_DESERIALIZE,
 		&&CASE_EEOP_AGG_DESERIALIZE,
-		&&CASE_EEOP_AGG_STRICT_INPUT_CHECK,
+		&&CASE_EEOP_AGG_STRICT_INPUT_CHECK_ARGS,
+		&&CASE_EEOP_AGG_STRICT_INPUT_CHECK_NULLS,
 		&&CASE_EEOP_AGG_INIT_TRANS,
 		&&CASE_EEOP_AGG_STRICT_TRANS_CHECK,
 		&&CASE_EEOP_AGG_PLAIN_TRANS_BYVAL,
@@ -661,14 +662,14 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 		EEO_CASE(EEOP_FUNCEXPR_STRICT)
 		{
 			FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
-			bool	   *argnull = fcinfo->argnull;
+			NullableDatum *args = fcinfo->args;
 			int			argno;
 			Datum		d;
 
 			/* strict function, so check for NULL args */
 			for (argno = 0; argno < op->d.func.nargs; argno++)
 			{
-				if (argnull[argno])
+				if (args[argno].isnull)
 				{
 					*op->resnull = true;
 					goto strictfail;
@@ -1084,8 +1085,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 				FunctionCallInfo fcinfo_out;
 
 				fcinfo_out = op->d.iocoerce.fcinfo_data_out;
-				fcinfo_out->arg[0] = *op->resvalue;
-				fcinfo_out->argnull[0] = false;
+				fcinfo_out->args[0].datum = *op->resvalue;
+				fcinfo_out->args[0].isnull = false;
 
 				fcinfo_out->isnull = false;
 				str = DatumGetCString(FunctionCallInvoke(fcinfo_out));
@@ -1101,8 +1102,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 				Datum		d;
 
 				fcinfo_in = op->d.iocoerce.fcinfo_data_in;
-				fcinfo_in->arg[0] = PointerGetDatum(str);
-				fcinfo_in->argnull[0] = *op->resnull;
+				fcinfo_in->args[0].datum = PointerGetDatum(str);
+				fcinfo_in->args[0].isnull = *op->resnull;
 				/* second and third arguments are already set up */
 
 				fcinfo_in->isnull = false;
@@ -1139,13 +1140,13 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 			FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
 
 			/* check function arguments for NULLness */
-			if (fcinfo->argnull[0] && fcinfo->argnull[1])
+			if (fcinfo->args[0].isnull && fcinfo->args[1].isnull)
 			{
 				/* Both NULL? Then is not distinct... */
 				*op->resvalue = BoolGetDatum(false);
 				*op->resnull = false;
 			}
-			else if (fcinfo->argnull[0] || fcinfo->argnull[1])
+			else if (fcinfo->args[0].isnull || fcinfo->args[1].isnull)
 			{
 				/* Only one is NULL? Then is distinct... */
 				*op->resvalue = BoolGetDatum(true);
@@ -1171,12 +1172,12 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 		{
 			FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
 
-			if (fcinfo->argnull[0] && fcinfo->argnull[1])
+			if (fcinfo->args[0].isnull && fcinfo->args[1].isnull)
 			{
 				*op->resvalue = BoolGetDatum(true);
 				*op->resnull = false;
 			}
-			else if (fcinfo->argnull[0] || fcinfo->argnull[1])
+			else if (fcinfo->args[0].isnull || fcinfo->args[1].isnull)
 			{
 				*op->resvalue = BoolGetDatum(false);
 				*op->resnull = false;
@@ -1202,7 +1203,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 			FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
 
 			/* if either argument is NULL they can't be equal */
-			if (!fcinfo->argnull[0] && !fcinfo->argnull[1])
+			if (!fcinfo->args[0].isnull && !fcinfo->args[1].isnull)
 			{
 				Datum		result;
 
@@ -1220,8 +1221,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 			}
 
 			/* Arguments aren't equal, so return the first one */
-			*op->resvalue = fcinfo->arg[0];
-			*op->resnull = fcinfo->argnull[0];
+			*op->resvalue = fcinfo->args[0].datum;
+			*op->resnull = fcinfo->args[0].isnull;
 
 			EEO_NEXT();
 		}
@@ -1287,7 +1288,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 
 			/* force NULL result if strict fn and NULL input */
 			if (op->d.rowcompare_step.finfo->fn_strict &&
-				(fcinfo->argnull[0] || fcinfo->argnull[1]))
+				(fcinfo->args[0].isnull || fcinfo->args[1].isnull))
 			{
 				*op->resnull = true;
 				EEO_JUMP(op->d.rowcompare_step.jumpnull);
@@ -1526,10 +1527,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 		/* evaluate a strict aggregate deserialization function */
 		EEO_CASE(EEOP_AGG_STRICT_DESERIALIZE)
 		{
-			bool	   *argnull = op->d.agg_deserialize.fcinfo_data->argnull;
-
 			/* Don't call a strict deserialization function with NULL input */
-			if (argnull[0])
+			if (op->d.agg_deserialize.fcinfo_data->args[0].isnull)
 				EEO_JUMP(op->d.agg_deserialize.jumpnull);
 
 			/* fallthrough */
@@ -1559,15 +1558,29 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 		 * Check that a strict aggregate transition / combination function's
 		 * input is not NULL.
 		 */
-		EEO_CASE(EEOP_AGG_STRICT_INPUT_CHECK)
+		EEO_CASE(EEOP_AGG_STRICT_INPUT_CHECK_ARGS)
 		{
 			int			argno;
-			bool	   *nulls = op->d.agg_strict_input_check.nulls;
+			NullableDatum *args = op->d.agg_strict_input_check.args;
 			int			nargs = op->d.agg_strict_input_check.nargs;
 
 			for (argno = 0; argno < nargs; argno++)
 			{
-				if (nulls[argno])
+				if (args[argno].isnull)
+					EEO_JUMP(op->d.agg_strict_input_check.jumpnull);
+			}
+			EEO_NEXT();
+		}
+
+		EEO_CASE(EEOP_AGG_STRICT_INPUT_CHECK_NULLS)
+		{
+			int			argno;
+			bool		*isnull = op->d.agg_strict_input_check.nulls;
+			int			nargs = op->d.agg_strict_input_check.nargs;
+
+			for (argno = 0; argno < nargs; argno++)
+			{
+				if (isnull[argno])
 					EEO_JUMP(op->d.agg_strict_input_check.jumpnull);
 			}
 			EEO_NEXT();
@@ -1643,7 +1656,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 
 			Assert(pertrans->transtypeByVal);
 
-			fcinfo = &pertrans->transfn_fcinfo;
+			fcinfo = pertrans->transfn_fcinfo;
 
 			/* cf. select_current_set() */
 			aggstate->curaggcontext = op->d.agg_trans.aggcontext;
@@ -1655,8 +1668,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 			/* invoke transition function in per-tuple context */
 			oldContext = MemoryContextSwitchTo(aggstate->tmpcontext->ecxt_per_tuple_memory);
 
-			fcinfo->arg[0] = pergroup->transValue;
-			fcinfo->argnull[0] = pergroup->transValueIsNull;
+			fcinfo->args[0].datum = pergroup->transValue;
+			fcinfo->args[0].isnull = pergroup->transValueIsNull;
 			fcinfo->isnull = false; /* just in case transfn doesn't set it */
 
 			newVal = FunctionCallInvoke(fcinfo);
@@ -1694,7 +1707,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 
 			Assert(!pertrans->transtypeByVal);
 
-			fcinfo = &pertrans->transfn_fcinfo;
+			fcinfo = pertrans->transfn_fcinfo;
 
 			/* cf. select_current_set() */
 			aggstate->curaggcontext = op->d.agg_trans.aggcontext;
@@ -1706,8 +1719,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 			/* invoke transition function in per-tuple context */
 			oldContext = MemoryContextSwitchTo(aggstate->tmpcontext->ecxt_per_tuple_memory);
 
-			fcinfo->arg[0] = pergroup->transValue;
-			fcinfo->argnull[0] = pergroup->transValueIsNull;
+			fcinfo->args[0].datum = pergroup->transValue;
+			fcinfo->args[0].isnull = pergroup->transValueIsNull;
 			fcinfo->isnull = false; /* just in case transfn doesn't set it */
 
 			newVal = FunctionCallInvoke(fcinfo);
@@ -2060,7 +2073,7 @@ ExecJustApplyFuncToCase(ExprState *state, ExprContext *econtext, bool *isnull)
 {
 	ExprEvalStep *op = &state->steps[0];
 	FunctionCallInfo fcinfo;
-	bool	   *argnull;
+	NullableDatum *args;
 	int			argno;
 	Datum		d;
 
@@ -2074,12 +2087,12 @@ ExecJustApplyFuncToCase(ExprState *state, ExprContext *econtext, bool *isnull)
 	op++;
 
 	fcinfo = op->d.func.fcinfo_data;
-	argnull = fcinfo->argnull;
+	args = fcinfo->args;
 
 	/* strict function, so check for NULL args */
 	for (argno = 0; argno < op->d.func.nargs; argno++)
 	{
-		if (argnull[argno])
+		if (args[argno].isnull)
 		{
 			*isnull = true;
 			return (Datum) 0;
@@ -2205,14 +2218,14 @@ ExecEvalFuncExprStrictFusage(ExprState *state, ExprEvalStep *op,
 
 	FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
 	PgStat_FunctionCallUsage fcusage;
-	bool	   *argnull = fcinfo->argnull;
+	NullableDatum *args = fcinfo->args;
 	int			argno;
 	Datum		d;
 
 	/* strict function, so check for NULL args */
 	for (argno = 0; argno < op->d.func.nargs; argno++)
 	{
-		if (argnull[argno])
+		if (args[argno].isnull)
 		{
 			*op->resnull = true;
 			return;
@@ -2330,7 +2343,11 @@ void
 ExecEvalSQLValueFunction(ExprState *state, ExprEvalStep *op)
 {
 	SQLValueFunction *svf = op->d.sqlvaluefunction.svf;
-	FunctionCallInfoData fcinfo;
+	union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(0)];
+	} fcinfodata;
+	FunctionCallInfo fcinfo = &fcinfodata.fcinfo;
 
 	*op->resnull = false;
 
@@ -2362,24 +2379,24 @@ ExecEvalSQLValueFunction(ExprState *state, ExprEvalStep *op)
 		case SVFOP_CURRENT_ROLE:
 		case SVFOP_CURRENT_USER:
 		case SVFOP_USER:
-			InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
-			*op->resvalue = current_user(&fcinfo);
-			*op->resnull = fcinfo.isnull;
+			InitFunctionCallInfoData(*fcinfo, NULL, 0, InvalidOid, NULL, NULL);
+			*op->resvalue = current_user(fcinfo);
+			*op->resnull = fcinfo->isnull;
 			break;
 		case SVFOP_SESSION_USER:
-			InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
-			*op->resvalue = session_user(&fcinfo);
-			*op->resnull = fcinfo.isnull;
+			InitFunctionCallInfoData(*fcinfo, NULL, 0, InvalidOid, NULL, NULL);
+			*op->resvalue = session_user(fcinfo);
+			*op->resnull = fcinfo->isnull;
 			break;
 		case SVFOP_CURRENT_CATALOG:
-			InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
-			*op->resvalue = current_database(&fcinfo);
-			*op->resnull = fcinfo.isnull;
+			InitFunctionCallInfoData(*fcinfo, NULL, 0, InvalidOid, NULL, NULL);
+			*op->resvalue = current_database(fcinfo);
+			*op->resnull = fcinfo->isnull;
 			break;
 		case SVFOP_CURRENT_SCHEMA:
-			InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
-			*op->resvalue = current_schema(&fcinfo);
-			*op->resnull = fcinfo.isnull;
+			InitFunctionCallInfoData(*fcinfo, NULL, 0, InvalidOid, NULL, NULL);
+			*op->resvalue = current_schema(fcinfo);
+			*op->resnull = fcinfo->isnull;
 			break;
 	}
 }
@@ -2812,8 +2829,8 @@ ExecEvalMinMax(ExprState *state, ExprEvalStep *op)
 	int			off;
 
 	/* set at initialization */
-	Assert(fcinfo->argnull[0] == false);
-	Assert(fcinfo->argnull[1] == false);
+	Assert(fcinfo->args[0].isnull == false);
+	Assert(fcinfo->args[1].isnull == false);
 
 	/* default to null result */
 	*op->resnull = true;
@@ -2835,8 +2852,8 @@ ExecEvalMinMax(ExprState *state, ExprEvalStep *op)
 			int			cmpresult;
 
 			/* apply comparison function */
-			fcinfo->arg[0] = *op->resvalue;
-			fcinfo->arg[1] = values[off];
+			fcinfo->args[0].datum = *op->resvalue;
+			fcinfo->args[1].datum = values[off];
 
 			fcinfo->isnull = false;
 			cmpresult = DatumGetInt32(FunctionCallInvoke(fcinfo));
@@ -3388,7 +3405,7 @@ ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op)
 	 * If the scalar is NULL, and the function is strict, return NULL; no
 	 * point in iterating the loop.
 	 */
-	if (fcinfo->argnull[0] && strictfunc)
+	if (fcinfo->args[0].isnull && strictfunc)
 	{
 		*op->resnull = true;
 		return;
@@ -3428,20 +3445,20 @@ ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op)
 		/* Get array element, checking for NULL */
 		if (bitmap && (*bitmap & bitmask) == 0)
 		{
-			fcinfo->arg[1] = (Datum) 0;
-			fcinfo->argnull[1] = true;
+			fcinfo->args[1].datum = (Datum) 0;
+			fcinfo->args[1].isnull = true;
 		}
 		else
 		{
 			elt = fetch_att(s, typbyval, typlen);
 			s = att_addlength_pointer(s, typlen, s);
 			s = (char *) att_align_nominal(s, typalign);
-			fcinfo->arg[1] = elt;
-			fcinfo->argnull[1] = false;
+			fcinfo->args[1].datum = elt;
+			fcinfo->args[1].isnull = false;
 		}
 
 		/* Call comparison function */
-		if (fcinfo->argnull[1] && strictfunc)
+		if (fcinfo->args[1].isnull && strictfunc)
 		{
 			fcinfo->isnull = true;
 			thisresult = (Datum) 0;
@@ -4040,7 +4057,7 @@ ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
 void
 ExecAggInitGroup(AggState *aggstate, AggStatePerTrans pertrans, AggStatePerGroup pergroup)
 {
-	FunctionCallInfo fcinfo = &pertrans->transfn_fcinfo;
+	FunctionCallInfo fcinfo = pertrans->transfn_fcinfo;
 	MemoryContext oldContext;
 
 	/*
@@ -4051,7 +4068,7 @@ ExecAggInitGroup(AggState *aggstate, AggStatePerTrans pertrans, AggStatePerGroup
 	 */
 	oldContext = MemoryContextSwitchTo(
 									   aggstate->curaggcontext->ecxt_per_tuple_memory);
-	pergroup->transValue = datumCopy(fcinfo->arg[1],
+	pergroup->transValue = datumCopy(fcinfo->args[1].datum,
 									 pertrans->transtypeByVal,
 									 pertrans->transtypeLen);
 	pergroup->transValueIsNull = false;
diff --git a/src/backend/executor/execSRF.c b/src/backend/executor/execSRF.c
index b97b8d797ec..882cb6b3e38 100644
--- a/src/backend/executor/execSRF.c
+++ b/src/backend/executor/execSRF.c
@@ -109,7 +109,10 @@ ExecMakeTableFunctionResult(SetExprState *setexpr,
 	Oid			funcrettype;
 	bool		returnsTuple;
 	bool		returnsSet = false;
-	FunctionCallInfoData fcinfo;
+	union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(3)];
+	} fcinfo;
 	PgStat_FunctionCallUsage fcusage;
 	ReturnSetInfo rsinfo;
 	HeapTupleData tmptup;
@@ -157,9 +160,9 @@ ExecMakeTableFunctionResult(SetExprState *setexpr,
 		 * This path is similar to ExecMakeFunctionResultSet.
 		 */
 		returnsSet = setexpr->funcReturnsSet;
-		InitFunctionCallInfoData(fcinfo, &(setexpr->func),
+		InitFunctionCallInfoData(fcinfo.fcinfo, &(setexpr->func),
 								 list_length(setexpr->args),
-								 setexpr->fcinfo_data.fncollation,
+								 setexpr->fcinfo->fncollation,
 								 NULL, (Node *) &rsinfo);
 
 		/*
@@ -174,7 +177,7 @@ ExecMakeTableFunctionResult(SetExprState *setexpr,
 		 */
 		MemoryContextReset(argContext);
 		oldcontext = MemoryContextSwitchTo(argContext);
-		ExecEvalFuncArgs(&fcinfo, setexpr->args, econtext);
+		ExecEvalFuncArgs(&fcinfo.fcinfo, setexpr->args, econtext);
 		MemoryContextSwitchTo(oldcontext);
 
 		/*
@@ -186,9 +189,9 @@ ExecMakeTableFunctionResult(SetExprState *setexpr,
 		{
 			int			i;
 
-			for (i = 0; i < fcinfo.nargs; i++)
+			for (i = 0; i < fcinfo.fcinfo.nargs; i++)
 			{
-				if (fcinfo.argnull[i])
+				if (fcinfo.fcinfo.args[i].isnull)
 					goto no_function_result;
 			}
 		}
@@ -196,7 +199,7 @@ ExecMakeTableFunctionResult(SetExprState *setexpr,
 	else
 	{
 		/* Treat setexpr as a generic expression */
-		InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
+		InitFunctionCallInfoData(fcinfo.fcinfo, NULL, 0, InvalidOid, NULL, NULL);
 	}
 
 	/*
@@ -224,11 +227,11 @@ ExecMakeTableFunctionResult(SetExprState *setexpr,
 		/* Call the function or expression one time */
 		if (!setexpr->elidedFuncState)
 		{
-			pgstat_init_function_usage(&fcinfo, &fcusage);
+			pgstat_init_function_usage(&fcinfo.fcinfo, &fcusage);
 
-			fcinfo.isnull = false;
+			fcinfo.fcinfo.isnull = false;
 			rsinfo.isDone = ExprSingleResult;
-			result = FunctionCallInvoke(&fcinfo);
+			result = FunctionCallInvoke(&fcinfo.fcinfo);
 
 			pgstat_end_function_usage(&fcusage,
 									  rsinfo.isDone != ExprMultipleResult);
@@ -236,7 +239,7 @@ ExecMakeTableFunctionResult(SetExprState *setexpr,
 		else
 		{
 			result =
-				ExecEvalExpr(setexpr->elidedFuncState, econtext, &fcinfo.isnull);
+				ExecEvalExpr(setexpr->elidedFuncState, econtext, &fcinfo.fcinfo.isnull);
 			rsinfo.isDone = ExprSingleResult;
 		}
 
@@ -277,7 +280,7 @@ ExecMakeTableFunctionResult(SetExprState *setexpr,
 			 */
 			if (returnsTuple)
 			{
-				if (!fcinfo.isnull)
+				if (!fcinfo.fcinfo.isnull)
 				{
 					HeapTupleHeader td = DatumGetHeapTupleHeader(result);
 
@@ -338,7 +341,7 @@ ExecMakeTableFunctionResult(SetExprState *setexpr,
 			else
 			{
 				/* Scalar-type case: just store the function result */
-				tuplestore_putvalues(tupstore, tupdesc, &result, &fcinfo.isnull);
+				tuplestore_putvalues(tupstore, tupdesc, &result, &fcinfo.fcinfo.isnull);
 			}
 
 			/*
@@ -547,7 +550,7 @@ restart:
 	 * rows from this SRF have been returned, otherwise ValuePerCall SRFs
 	 * would reference freed memory after the first returned row.
 	 */
-	fcinfo = &fcache->fcinfo_data;
+	fcinfo = fcache->fcinfo;
 	arguments = fcache->args;
 	if (!fcache->setArgsValid)
 	{
@@ -587,7 +590,7 @@ restart:
 	{
 		for (i = 0; i < fcinfo->nargs; i++)
 		{
-			if (fcinfo->argnull[i])
+			if (fcinfo->args[i].isnull)
 			{
 				callit = false;
 				break;
@@ -704,7 +707,8 @@ init_sexpr(Oid foid, Oid input_collation, Expr *node,
 	fmgr_info_set_expr((Node *) sexpr->expr, &(sexpr->func));
 
 	/* Initialize the function call parameter struct as well */
-	InitFunctionCallInfoData(sexpr->fcinfo_data, &(sexpr->func),
+	sexpr->fcinfo = (FunctionCallInfo) palloc(SizeForFunctionCallInfoData(list_length(sexpr->args)));
+	InitFunctionCallInfoData(*sexpr->fcinfo, &(sexpr->func),
 							 list_length(sexpr->args),
 							 input_collation, NULL, NULL);
 
@@ -820,9 +824,9 @@ ExecEvalFuncArgs(FunctionCallInfo fcinfo,
 	{
 		ExprState  *argstate = (ExprState *) lfirst(arg);
 
-		fcinfo->arg[i] = ExecEvalExpr(argstate,
-									  econtext,
-									  &fcinfo->argnull[i]);
+		fcinfo->args[i].datum = ExecEvalExpr(argstate,
+											 econtext,
+											 &fcinfo->args[i].isnull);
 		i++;
 	}
 
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 23545896d4d..672dcd981cd 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -933,8 +933,8 @@ postquel_sub_params(SQLFunctionCachePtr fcache,
 		{
 			ParamExternData *prm = &paramLI->params[i];
 
-			prm->value = fcinfo->arg[i];
-			prm->isnull = fcinfo->argnull[i];
+			prm->value = fcinfo->args[i].datum;
+			prm->isnull = fcinfo->args[i].isnull;
 			prm->pflags = 0;
 			prm->ptype = fcache->pinfo->argtypes[i];
 		}
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index 0fe0c22c1ea..90a5e1ba2ad 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -553,7 +553,7 @@ advance_transition_function(AggState *aggstate,
 							AggStatePerTrans pertrans,
 							AggStatePerGroup pergroupstate)
 {
-	FunctionCallInfo fcinfo = &pertrans->transfn_fcinfo;
+	FunctionCallInfo fcinfo = pertrans->transfn_fcinfo;
 	MemoryContext oldContext;
 	Datum		newVal;
 
@@ -568,7 +568,7 @@ advance_transition_function(AggState *aggstate,
 
 		for (i = 1; i <= numTransInputs; i++)
 		{
-			if (fcinfo->argnull[i])
+			if (fcinfo->args[i].isnull)
 				return;
 		}
 		if (pergroupstate->noTransValue)
@@ -584,7 +584,7 @@ advance_transition_function(AggState *aggstate,
 			 */
 			oldContext = MemoryContextSwitchTo(
 											   aggstate->curaggcontext->ecxt_per_tuple_memory);
-			pergroupstate->transValue = datumCopy(fcinfo->arg[1],
+			pergroupstate->transValue = datumCopy(fcinfo->args[1].datum,
 												  pertrans->transtypeByVal,
 												  pertrans->transtypeLen);
 			pergroupstate->transValueIsNull = false;
@@ -613,8 +613,8 @@ advance_transition_function(AggState *aggstate,
 	/*
 	 * OK to call the transition function
 	 */
-	fcinfo->arg[0] = pergroupstate->transValue;
-	fcinfo->argnull[0] = pergroupstate->transValueIsNull;
+	fcinfo->args[0].datum = pergroupstate->transValue;
+	fcinfo->args[0].isnull = pergroupstate->transValueIsNull;
 	fcinfo->isnull = false;		/* just in case transfn doesn't set it */
 
 	newVal = FunctionCallInvoke(fcinfo);
@@ -717,7 +717,7 @@ process_ordered_aggregate_single(AggState *aggstate,
 	bool		isDistinct = (pertrans->numDistinctCols > 0);
 	Datum		newAbbrevVal = (Datum) 0;
 	Datum		oldAbbrevVal = (Datum) 0;
-	FunctionCallInfo fcinfo = &pertrans->transfn_fcinfo;
+	FunctionCallInfo fcinfo = pertrans->transfn_fcinfo;
 	Datum	   *newVal;
 	bool	   *isNull;
 
@@ -726,8 +726,8 @@ process_ordered_aggregate_single(AggState *aggstate,
 	tuplesort_performsort(pertrans->sortstates[aggstate->current_set]);
 
 	/* Load the column into argument 1 (arg 0 will be transition value) */
-	newVal = fcinfo->arg + 1;
-	isNull = fcinfo->argnull + 1;
+	newVal = &fcinfo->args[1].datum;
+	isNull = &fcinfo->args[1].isnull;
 
 	/*
 	 * Note: if input type is pass-by-ref, the datums returned by the sort are
@@ -803,7 +803,7 @@ process_ordered_aggregate_multi(AggState *aggstate,
 								AggStatePerGroup pergroupstate)
 {
 	ExprContext *tmpcontext = aggstate->tmpcontext;
-	FunctionCallInfo fcinfo = &pertrans->transfn_fcinfo;
+	FunctionCallInfo fcinfo = pertrans->transfn_fcinfo;
 	TupleTableSlot *slot1 = pertrans->sortslot;
 	TupleTableSlot *slot2 = pertrans->uniqslot;
 	int			numTransInputs = pertrans->numTransInputs;
@@ -843,8 +843,8 @@ process_ordered_aggregate_multi(AggState *aggstate,
 			/* Start from 1, since the 0th arg will be the transition value */
 			for (i = 0; i < numTransInputs; i++)
 			{
-				fcinfo->arg[i + 1] = slot1->tts_values[i];
-				fcinfo->argnull[i + 1] = slot1->tts_isnull[i];
+				fcinfo->args[i + 1].datum = slot1->tts_values[i];
+				fcinfo->args[i + 1].isnull = slot1->tts_isnull[i];
 			}
 
 			advance_transition_function(aggstate, pertrans, pergroupstate);
@@ -897,7 +897,11 @@ finalize_aggregate(AggState *aggstate,
 				   AggStatePerGroup pergroupstate,
 				   Datum *resultVal, bool *resultIsNull)
 {
-	FunctionCallInfoData fcinfo;
+	union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(3)];
+	} fcinfodata;
+	FunctionCallInfo fcinfo = &fcinfodata.fcinfo;
 	bool		anynull = false;
 	MemoryContext oldContext;
 	int			i;
@@ -917,10 +921,10 @@ finalize_aggregate(AggState *aggstate,
 	{
 		ExprState  *expr = (ExprState *) lfirst(lc);
 
-		fcinfo.arg[i] = ExecEvalExpr(expr,
-									 aggstate->ss.ps.ps_ExprContext,
-									 &fcinfo.argnull[i]);
-		anynull |= fcinfo.argnull[i];
+		fcinfo->args[i].datum = ExecEvalExpr(expr,
+											aggstate->ss.ps.ps_ExprContext,
+											&fcinfo->args[i].isnull);
+		anynull |= fcinfo->args[i].isnull;
 		i++;
 	}
 
@@ -934,27 +938,27 @@ finalize_aggregate(AggState *aggstate,
 		/* set up aggstate->curperagg for AggGetAggref() */
 		aggstate->curperagg = peragg;
 
-		InitFunctionCallInfoData(fcinfo, &peragg->finalfn,
+		InitFunctionCallInfoData(*fcinfo, &peragg->finalfn,
 								 numFinalArgs,
 								 pertrans->aggCollation,
 								 (void *) aggstate, NULL);
 
 		/* Fill in the transition state value */
-		fcinfo.arg[0] = MakeExpandedObjectReadOnly(pergroupstate->transValue,
+		fcinfo->args[0].datum = MakeExpandedObjectReadOnly(pergroupstate->transValue,
 												   pergroupstate->transValueIsNull,
 												   pertrans->transtypeLen);
-		fcinfo.argnull[0] = pergroupstate->transValueIsNull;
+		fcinfo->args[0].isnull = pergroupstate->transValueIsNull;
 		anynull |= pergroupstate->transValueIsNull;
 
 		/* Fill any remaining argument positions with nulls */
 		for (; i < numFinalArgs; i++)
 		{
-			fcinfo.arg[i] = (Datum) 0;
-			fcinfo.argnull[i] = true;
+			fcinfo->args[i].datum = (Datum) 0;
+			fcinfo->args[i].isnull = true;
 			anynull = true;
 		}
 
-		if (fcinfo.flinfo->fn_strict && anynull)
+		if (fcinfo->flinfo->fn_strict && anynull)
 		{
 			/* don't call a strict function with NULL inputs */
 			*resultVal = (Datum) 0;
@@ -962,8 +966,8 @@ finalize_aggregate(AggState *aggstate,
 		}
 		else
 		{
-			*resultVal = FunctionCallInvoke(&fcinfo);
-			*resultIsNull = fcinfo.isnull;
+			*resultVal = FunctionCallInvoke(fcinfo);
+			*resultIsNull = fcinfo->isnull;
 		}
 		aggstate->curperagg = NULL;
 	}
@@ -1018,12 +1022,12 @@ finalize_partialaggregate(AggState *aggstate,
 		}
 		else
 		{
-			FunctionCallInfo fcinfo = &pertrans->serialfn_fcinfo;
+			FunctionCallInfo fcinfo = pertrans->serialfn_fcinfo;
 
-			fcinfo->arg[0] = MakeExpandedObjectReadOnly(pergroupstate->transValue,
-														pergroupstate->transValueIsNull,
-														pertrans->transtypeLen);
-			fcinfo->argnull[0] = pergroupstate->transValueIsNull;
+			fcinfo->args[0].datum = MakeExpandedObjectReadOnly(pergroupstate->transValue,
+															   pergroupstate->transValueIsNull,
+															   pertrans->transtypeLen);
+			fcinfo->args[0].isnull = pergroupstate->transValueIsNull;
 
 			*resultVal = FunctionCallInvoke(fcinfo);
 			*resultIsNull = fcinfo->isnull;
@@ -2926,7 +2930,8 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
 		fmgr_info(aggtransfn, &pertrans->transfn);
 		fmgr_info_set_expr((Node *) combinefnexpr, &pertrans->transfn);
 
-		InitFunctionCallInfoData(pertrans->transfn_fcinfo,
+		pertrans->transfn_fcinfo = (FunctionCallInfo) palloc(SizeForFunctionCallInfoData(2));
+		InitFunctionCallInfoData(*pertrans->transfn_fcinfo,
 								 &pertrans->transfn,
 								 2,
 								 pertrans->aggCollation,
@@ -2964,7 +2969,8 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
 		fmgr_info(aggtransfn, &pertrans->transfn);
 		fmgr_info_set_expr((Node *) transfnexpr, &pertrans->transfn);
 
-		InitFunctionCallInfoData(pertrans->transfn_fcinfo,
+		pertrans->transfn_fcinfo = (FunctionCallInfo) palloc(SizeForFunctionCallInfoData(pertrans->numTransInputs + 1));
+		InitFunctionCallInfoData(*pertrans->transfn_fcinfo,
 								 &pertrans->transfn,
 								 pertrans->numTransInputs + 1,
 								 pertrans->aggCollation,
@@ -3002,7 +3008,8 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
 		fmgr_info(aggserialfn, &pertrans->serialfn);
 		fmgr_info_set_expr((Node *) serialfnexpr, &pertrans->serialfn);
 
-		InitFunctionCallInfoData(pertrans->serialfn_fcinfo,
+		pertrans->serialfn_fcinfo = (FunctionCallInfo) palloc(SizeForFunctionCallInfoData(1));
+		InitFunctionCallInfoData(*pertrans->serialfn_fcinfo,
 								 &pertrans->serialfn,
 								 1,
 								 InvalidOid,
@@ -3016,7 +3023,8 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
 		fmgr_info(aggdeserialfn, &pertrans->deserialfn);
 		fmgr_info_set_expr((Node *) deserialfnexpr, &pertrans->deserialfn);
 
-		InitFunctionCallInfoData(pertrans->deserialfn_fcinfo,
+		pertrans->deserialfn_fcinfo = (FunctionCallInfo) palloc(SizeForFunctionCallInfoData(2));
+		InitFunctionCallInfoData(*pertrans->deserialfn_fcinfo,
 								 &pertrans->deserialfn,
 								 2,
 								 InvalidOid,
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index fe5369a0c7b..9c1fd24065c 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -243,8 +243,11 @@ advance_windowaggregate(WindowAggState *winstate,
 {
 	WindowFuncExprState *wfuncstate = perfuncstate->wfuncstate;
 	int			numArguments = perfuncstate->numArguments;
-	FunctionCallInfoData fcinfodata;
-	FunctionCallInfo fcinfo = &fcinfodata;
+	union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(3)];
+	} fcinfodata;
+	FunctionCallInfo fcinfo = &fcinfodata.fcinfo;
 	Datum		newVal;
 	ListCell   *arg;
 	int			i;
@@ -273,8 +276,8 @@ advance_windowaggregate(WindowAggState *winstate,
 	{
 		ExprState  *argstate = (ExprState *) lfirst(arg);
 
-		fcinfo->arg[i] = ExecEvalExpr(argstate, econtext,
-									  &fcinfo->argnull[i]);
+		fcinfo->args[i].datum = ExecEvalExpr(argstate, econtext,
+											 &fcinfo->args[i].isnull);
 		i++;
 	}
 
@@ -287,7 +290,7 @@ advance_windowaggregate(WindowAggState *winstate,
 		 */
 		for (i = 1; i <= numArguments; i++)
 		{
-			if (fcinfo->argnull[i])
+			if (fcinfo->args[i].isnull)
 			{
 				MemoryContextSwitchTo(oldContext);
 				return;
@@ -306,7 +309,7 @@ advance_windowaggregate(WindowAggState *winstate,
 		if (peraggstate->transValueCount == 0 && peraggstate->transValueIsNull)
 		{
 			MemoryContextSwitchTo(peraggstate->aggcontext);
-			peraggstate->transValue = datumCopy(fcinfo->arg[1],
+			peraggstate->transValue = datumCopy(fcinfo->args[1].datum,
 												peraggstate->transtypeByVal,
 												peraggstate->transtypeLen);
 			peraggstate->transValueIsNull = false;
@@ -339,8 +342,8 @@ advance_windowaggregate(WindowAggState *winstate,
 							 numArguments + 1,
 							 perfuncstate->winCollation,
 							 (void *) winstate, NULL);
-	fcinfo->arg[0] = peraggstate->transValue;
-	fcinfo->argnull[0] = peraggstate->transValueIsNull;
+	fcinfo->args[0].datum = peraggstate->transValue;
+	fcinfo->args[0].isnull = peraggstate->transValueIsNull;
 	winstate->curaggcontext = peraggstate->aggcontext;
 	newVal = FunctionCallInvoke(fcinfo);
 	winstate->curaggcontext = NULL;
@@ -420,8 +423,11 @@ advance_windowaggregate_base(WindowAggState *winstate,
 {
 	WindowFuncExprState *wfuncstate = perfuncstate->wfuncstate;
 	int			numArguments = perfuncstate->numArguments;
-	FunctionCallInfoData fcinfodata;
-	FunctionCallInfo fcinfo = &fcinfodata;
+	union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(3)];
+	} fcinfodata;
+	FunctionCallInfo fcinfo = &fcinfodata.fcinfo;
 	Datum		newVal;
 	ListCell   *arg;
 	int			i;
@@ -450,8 +456,8 @@ advance_windowaggregate_base(WindowAggState *winstate,
 	{
 		ExprState  *argstate = (ExprState *) lfirst(arg);
 
-		fcinfo->arg[i] = ExecEvalExpr(argstate, econtext,
-									  &fcinfo->argnull[i]);
+		fcinfo->args[i].datum = ExecEvalExpr(argstate, econtext,
+											 &fcinfo->args[i].isnull);
 		i++;
 	}
 
@@ -464,7 +470,7 @@ advance_windowaggregate_base(WindowAggState *winstate,
 		 */
 		for (i = 1; i <= numArguments; i++)
 		{
-			if (fcinfo->argnull[i])
+			if (fcinfo->args[i].isnull)
 			{
 				MemoryContextSwitchTo(oldContext);
 				return true;
@@ -510,8 +516,8 @@ advance_windowaggregate_base(WindowAggState *winstate,
 							 numArguments + 1,
 							 perfuncstate->winCollation,
 							 (void *) winstate, NULL);
-	fcinfo->arg[0] = peraggstate->transValue;
-	fcinfo->argnull[0] = peraggstate->transValueIsNull;
+	fcinfo->args[0].datum = peraggstate->transValue;
+	fcinfo->args[0].isnull = peraggstate->transValueIsNull;
 	winstate->curaggcontext = peraggstate->aggcontext;
 	newVal = FunctionCallInvoke(fcinfo);
 	winstate->curaggcontext = NULL;
@@ -592,29 +598,33 @@ finalize_windowaggregate(WindowAggState *winstate,
 	if (OidIsValid(peraggstate->finalfn_oid))
 	{
 		int			numFinalArgs = peraggstate->numFinalArgs;
-		FunctionCallInfoData fcinfo;
+		union {
+			FunctionCallInfoData fcinfo;
+			char *fcinfo_data[SizeForFunctionCallInfoData(3)];
+		} fcinfodata;
+		FunctionCallInfo fcinfo = &fcinfodata.fcinfo;
 		bool		anynull;
 		int			i;
 
-		InitFunctionCallInfoData(fcinfo, &(peraggstate->finalfn),
+		InitFunctionCallInfoData(fcinfodata.fcinfo, &(peraggstate->finalfn),
 								 numFinalArgs,
 								 perfuncstate->winCollation,
 								 (void *) winstate, NULL);
-		fcinfo.arg[0] = MakeExpandedObjectReadOnly(peraggstate->transValue,
-												   peraggstate->transValueIsNull,
-												   peraggstate->transtypeLen);
-		fcinfo.argnull[0] = peraggstate->transValueIsNull;
+		fcinfo->args[0].datum = MakeExpandedObjectReadOnly(peraggstate->transValue,
+														   peraggstate->transValueIsNull,
+														   peraggstate->transtypeLen);
+		fcinfo->args[0].isnull = peraggstate->transValueIsNull;
 		anynull = peraggstate->transValueIsNull;
 
 		/* Fill any remaining argument positions with nulls */
 		for (i = 1; i < numFinalArgs; i++)
 		{
-			fcinfo.arg[i] = (Datum) 0;
-			fcinfo.argnull[i] = true;
+			fcinfo->args[i].datum = (Datum) 0;
+			fcinfo->args[i].isnull = true;
 			anynull = true;
 		}
 
-		if (fcinfo.flinfo->fn_strict && anynull)
+		if (fcinfo->flinfo->fn_strict && anynull)
 		{
 			/* don't call a strict function with NULL inputs */
 			*result = (Datum) 0;
@@ -623,9 +633,9 @@ finalize_windowaggregate(WindowAggState *winstate,
 		else
 		{
 			winstate->curaggcontext = peraggstate->aggcontext;
-			*result = FunctionCallInvoke(&fcinfo);
+			*result = FunctionCallInvoke(fcinfo);
 			winstate->curaggcontext = NULL;
-			*isnull = fcinfo.isnull;
+			*isnull = fcinfo->isnull;
 		}
 	}
 	else
@@ -1032,8 +1042,13 @@ static void
 eval_windowfunction(WindowAggState *winstate, WindowStatePerFunc perfuncstate,
 					Datum *result, bool *isnull)
 {
-	FunctionCallInfoData fcinfo;
+	union {
+		FunctionCallInfoData fcinfo;
+		/* FIXME: variable length allocation */
+		char *fcinfo_data[SizeForFunctionCallInfoData(10)];
+	} d;
 	MemoryContext oldContext;
+	int argno;
 
 	oldContext = MemoryContextSwitchTo(winstate->ss.ps.ps_ExprContext->ecxt_per_tuple_memory);
 
@@ -1043,24 +1058,25 @@ eval_windowfunction(WindowAggState *winstate, WindowStatePerFunc perfuncstate,
 	 * implementations to support varying numbers of arguments.  The real info
 	 * goes through the WindowObject, which is passed via fcinfo->context.
 	 */
-	InitFunctionCallInfoData(fcinfo, &(perfuncstate->flinfo),
+	InitFunctionCallInfoData(d.fcinfo, &(perfuncstate->flinfo),
 							 perfuncstate->numArguments,
 							 perfuncstate->winCollation,
 							 (void *) perfuncstate->winobj, NULL);
 	/* Just in case, make all the regular argument slots be null */
-	memset(fcinfo.argnull, true, perfuncstate->numArguments);
+	for (argno = 0; argno < perfuncstate->numArguments; argno++)
+		d.fcinfo.args[argno].isnull = true;
 	/* Window functions don't have a current aggregate context, either */
 	winstate->curaggcontext = NULL;
 
-	*result = FunctionCallInvoke(&fcinfo);
-	*isnull = fcinfo.isnull;
+	*result = FunctionCallInvoke(&d.fcinfo);
+	*isnull = d.fcinfo.isnull;
 
 	/*
 	 * Make sure pass-by-ref data is allocated in the appropriate context. (We
 	 * need this in case the function returns a pointer into some short-lived
 	 * tuple, as is entirely possible.)
 	 */
-	if (!perfuncstate->resulttypeByVal && !fcinfo.isnull &&
+	if (!perfuncstate->resulttypeByVal && !d.fcinfo.isnull &&
 		!MemoryContextContains(CurrentMemoryContext,
 							   DatumGetPointer(*result)))
 		*result = datumCopy(*result,
diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c
index 36c5f7d500e..04bfd162227 100644
--- a/src/backend/jit/llvm/llvmjit_expr.c
+++ b/src/backend/jit/llvm/llvmjit_expr.c
@@ -2117,15 +2117,16 @@ llvm_compile_expr(ExprState *state)
 			case EEOP_AGG_STRICT_INPUT_CHECK:
 				{
 					int			nargs = op->d.agg_strict_input_check.nargs;
-					bool	   *nulls = op->d.agg_strict_input_check.nulls;
+					NullableDatum *args = op->d.agg_strict_input_check.args;
 					int			jumpnull;
 					int			argno;
 
-					LLVMValueRef v_nullp;
+					LLVMValueRef v_argp;
 					LLVMBasicBlockRef *b_checknulls;
 
 					jumpnull = op->d.agg_strict_input_check.jumpnull;
-					v_nullp = l_ptr_const(nulls, l_ptr(TypeStorageBool));
+					/* FIXME: This isn't correct, it's a NullableDatum now */
+					v_argp = l_ptr_const(args, l_ptr(TypeStorageBool));
 
 					/* create blocks for checking args */
 					b_checknulls = palloc(sizeof(LLVMBasicBlockRef *) * nargs);
@@ -2153,7 +2154,8 @@ llvm_compile_expr(ExprState *state)
 						else
 							b_argnotnull = b_checknulls[argno + 1];
 
-						v_argisnull = l_load_gep1(b, v_nullp, v_argno, "");
+						/* FIXME: This isn't correct */
+						v_argisnull = l_load_gep1(b, v_argp, v_argno, "");
 
 						LLVMBuildCondBr(b,
 										LLVMBuildICmp(b,
@@ -2330,7 +2332,7 @@ llvm_compile_expr(ExprState *state)
 					aggstate = op->d.agg_trans.aggstate;
 					pertrans = op->d.agg_trans.pertrans;
 
-					fcinfo = &pertrans->transfn_fcinfo;
+					fcinfo = pertrans->transfn_fcinfo;
 
 					v_aggstatep = l_ptr_const(aggstate,
 											  l_ptr(StructAggState));
@@ -2598,12 +2600,8 @@ BuildV1Call(LLVMJitContext *context, LLVMBuilderRef b,
 		LLVMValueRef v_lifetime = create_LifetimeEnd(mod);
 		LLVMValueRef params[2];
 
-		params[0] = l_int64_const(sizeof(fcinfo->arg));
-		params[1] = l_ptr_const(fcinfo->arg, l_ptr(LLVMInt8Type()));
-		LLVMBuildCall(b, v_lifetime, params, lengthof(params), "");
-
-		params[0] = l_int64_const(sizeof(fcinfo->argnull));
-		params[1] = l_ptr_const(fcinfo->argnull, l_ptr(LLVMInt8Type()));
+		params[0] = l_int64_const(sizeof(NullableDatum) * fcinfo->nargs);
+		params[1] = l_ptr_const(fcinfo->args, l_ptr(LLVMInt8Type()));
 		LLVMBuildCall(b, v_lifetime, params, lengthof(params), "");
 
 		params[0] = l_int64_const(sizeof(fcinfo->isnull));
diff --git a/src/backend/tcop/fastpath.c b/src/backend/tcop/fastpath.c
index d16ba5ec927..2394f800fce 100644
--- a/src/backend/tcop/fastpath.c
+++ b/src/backend/tcop/fastpath.c
@@ -260,7 +260,11 @@ HandleFunctionRequest(StringInfo msgBuf)
 {
 	Oid			fid;
 	AclResult	aclresult;
-	FunctionCallInfoData fcinfo;
+	union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(3)];
+	} fcinfodata;
+	FunctionCallInfo fcinfo = &fcinfodata.fcinfo;
 	int16		rformat;
 	Datum		retval;
 	struct fp_info my_fp;
@@ -332,12 +336,12 @@ HandleFunctionRequest(StringInfo msgBuf)
 	 * functions can't be called this way.  Perhaps we should pass
 	 * DEFAULT_COLLATION_OID, instead?
 	 */
-	InitFunctionCallInfoData(fcinfo, &fip->flinfo, 0, InvalidOid, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, &fip->flinfo, 0, InvalidOid, NULL, NULL);
 
 	if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
-		rformat = parse_fcall_arguments(msgBuf, fip, &fcinfo);
+		rformat = parse_fcall_arguments(msgBuf, fip, fcinfo);
 	else
-		rformat = parse_fcall_arguments_20(msgBuf, fip, &fcinfo);
+		rformat = parse_fcall_arguments_20(msgBuf, fip, fcinfo);
 
 	/* Verify we reached the end of the message where expected. */
 	pq_getmsgend(msgBuf);
@@ -350,9 +354,9 @@ HandleFunctionRequest(StringInfo msgBuf)
 	{
 		int			i;
 
-		for (i = 0; i < fcinfo.nargs; i++)
+		for (i = 0; i < fcinfo->nargs; i++)
 		{
-			if (fcinfo.argnull[i])
+			if (fcinfo->args[i].isnull)
 			{
 				callit = false;
 				break;
@@ -363,18 +367,18 @@ HandleFunctionRequest(StringInfo msgBuf)
 	if (callit)
 	{
 		/* Okay, do it ... */
-		retval = FunctionCallInvoke(&fcinfo);
+		retval = FunctionCallInvoke(fcinfo);
 	}
 	else
 	{
-		fcinfo.isnull = true;
+		fcinfo->isnull = true;
 		retval = (Datum) 0;
 	}
 
 	/* ensure we do at least one CHECK_FOR_INTERRUPTS per function call */
 	CHECK_FOR_INTERRUPTS();
 
-	SendFunctionResult(retval, fcinfo.isnull, fip->rettype, rformat);
+	SendFunctionResult(retval, fcinfo->isnull, fip->rettype, rformat);
 
 	/* We no longer need the snapshot */
 	PopActiveSnapshot();
@@ -450,11 +454,11 @@ parse_fcall_arguments(StringInfo msgBuf, struct fp_info *fip,
 		argsize = pq_getmsgint(msgBuf, 4);
 		if (argsize == -1)
 		{
-			fcinfo->argnull[i] = true;
+			fcinfo->args[i].isnull = true;
 		}
 		else
 		{
-			fcinfo->argnull[i] = false;
+			fcinfo->args[i].isnull = false;
 			if (argsize < 0)
 				ereport(ERROR,
 						(errcode(ERRCODE_PROTOCOL_VIOLATION),
@@ -494,8 +498,8 @@ parse_fcall_arguments(StringInfo msgBuf, struct fp_info *fip,
 			else
 				pstring = pg_client_to_server(abuf.data, argsize);
 
-			fcinfo->arg[i] = OidInputFunctionCall(typinput, pstring,
-												  typioparam, -1);
+			fcinfo->args[i].datum = OidInputFunctionCall(typinput, pstring,
+														 typioparam, -1);
 			/* Free result of encoding conversion, if any */
 			if (pstring && pstring != abuf.data)
 				pfree(pstring);
@@ -514,8 +518,8 @@ parse_fcall_arguments(StringInfo msgBuf, struct fp_info *fip,
 			else
 				bufptr = &abuf;
 
-			fcinfo->arg[i] = OidReceiveFunctionCall(typreceive, bufptr,
-													typioparam, -1);
+			fcinfo->args[i].datum = OidReceiveFunctionCall(typreceive, bufptr,
+														   typioparam, -1);
 
 			/* Trouble if it didn't eat the whole buffer */
 			if (argsize != -1 && abuf.cursor != abuf.len)
@@ -579,12 +583,13 @@ parse_fcall_arguments_20(StringInfo msgBuf, struct fp_info *fip,
 		argsize = pq_getmsgint(msgBuf, 4);
 		if (argsize == -1)
 		{
-			fcinfo->argnull[i] = true;
-			fcinfo->arg[i] = OidReceiveFunctionCall(typreceive, NULL,
-													typioparam, -1);
+			/* FIXME: huh? */
+			fcinfo->args[i].isnull = true;
+			fcinfo->args[i].datum = OidReceiveFunctionCall(typreceive, NULL,
+														   typioparam, -1);
 			continue;
 		}
-		fcinfo->argnull[i] = false;
+		fcinfo->args[i].isnull = false;
 		if (argsize < 0)
 			ereport(ERROR,
 					(errcode(ERRCODE_PROTOCOL_VIOLATION),
@@ -597,8 +602,8 @@ parse_fcall_arguments_20(StringInfo msgBuf, struct fp_info *fip,
 							   pq_getmsgbytes(msgBuf, argsize),
 							   argsize);
 
-		fcinfo->arg[i] = OidReceiveFunctionCall(typreceive, &abuf,
-												typioparam, -1);
+		fcinfo->args[i].datum = OidReceiveFunctionCall(typreceive, &abuf,
+													   typioparam, -1);
 
 		/* Trouble if it didn't eat the whole buffer */
 		if (abuf.cursor != abuf.len)
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 0cbdbe5587e..602348d04d5 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -3581,7 +3581,10 @@ array_eq(PG_FUNCTION_ARGS)
 	array_iter	it1;
 	array_iter	it2;
 	int			i;
-	FunctionCallInfoData locfcinfo;
+	union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(2)];
+	} locfcinfo;
 
 	if (element_type != AARR_ELEMTYPE(array2))
 		ereport(ERROR,
@@ -3621,7 +3624,7 @@ array_eq(PG_FUNCTION_ARGS)
 		/*
 		 * apply the operator to each pair of array elements.
 		 */
-		InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
+		InitFunctionCallInfoData(locfcinfo.fcinfo, &typentry->eq_opr_finfo, 2,
 								 collation, NULL, NULL);
 
 		/* Loop over source data */
@@ -3657,12 +3660,12 @@ array_eq(PG_FUNCTION_ARGS)
 			/*
 			 * Apply the operator to the element pair
 			 */
-			locfcinfo.arg[0] = elt1;
-			locfcinfo.arg[1] = elt2;
-			locfcinfo.argnull[0] = false;
-			locfcinfo.argnull[1] = false;
-			locfcinfo.isnull = false;
-			oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
+			locfcinfo.fcinfo.args[0].datum = elt1;
+			locfcinfo.fcinfo.args[0].isnull = false;
+			locfcinfo.fcinfo.args[1].datum = elt2;
+			locfcinfo.fcinfo.args[1].isnull = false;
+			locfcinfo.fcinfo.isnull = false;
+			oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo.fcinfo));
 			if (!oprresult)
 			{
 				result = false;
@@ -3752,7 +3755,11 @@ array_cmp(FunctionCallInfo fcinfo)
 	array_iter	it1;
 	array_iter	it2;
 	int			i;
-	FunctionCallInfoData locfcinfo;
+		union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(2)];
+	} locfcinfo;
+
 
 	if (element_type != AARR_ELEMTYPE(array2))
 		ereport(ERROR,
@@ -3785,7 +3792,7 @@ array_cmp(FunctionCallInfo fcinfo)
 	/*
 	 * apply the operator to each pair of array elements.
 	 */
-	InitFunctionCallInfoData(locfcinfo, &typentry->cmp_proc_finfo, 2,
+	InitFunctionCallInfoData(locfcinfo.fcinfo, &typentry->cmp_proc_finfo, 2,
 							 collation, NULL, NULL);
 
 	/* Loop over source data */
@@ -3824,12 +3831,12 @@ array_cmp(FunctionCallInfo fcinfo)
 		}
 
 		/* Compare the pair of elements */
-		locfcinfo.arg[0] = elt1;
-		locfcinfo.arg[1] = elt2;
-		locfcinfo.argnull[0] = false;
-		locfcinfo.argnull[1] = false;
-		locfcinfo.isnull = false;
-		cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
+		locfcinfo.fcinfo.args[0].datum = elt1;
+		locfcinfo.fcinfo.args[0].isnull = false;
+		locfcinfo.fcinfo.args[1].datum = elt2;
+		locfcinfo.fcinfo.args[1].isnull = false;
+		locfcinfo.fcinfo.isnull = false;
+		cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo.fcinfo));
 
 		if (cmpresult == 0)
 			continue;			/* equal */
@@ -3916,7 +3923,11 @@ hash_array(PG_FUNCTION_ARGS)
 	char		typalign;
 	int			i;
 	array_iter	iter;
-	FunctionCallInfoData locfcinfo;
+		union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(1)];
+	} locfcinfo;
+
 
 	/*
 	 * We arrange to look up the hash function only once per series of calls,
@@ -3944,7 +3955,7 @@ hash_array(PG_FUNCTION_ARGS)
 	/*
 	 * apply the hash function to each array element.
 	 */
-	InitFunctionCallInfoData(locfcinfo, &typentry->hash_proc_finfo, 1,
+	InitFunctionCallInfoData(locfcinfo.fcinfo, &typentry->hash_proc_finfo, 1,
 							 InvalidOid, NULL, NULL);
 
 	/* Loop over source data */
@@ -3968,10 +3979,10 @@ hash_array(PG_FUNCTION_ARGS)
 		else
 		{
 			/* Apply the hash function */
-			locfcinfo.arg[0] = elt;
-			locfcinfo.argnull[0] = false;
-			locfcinfo.isnull = false;
-			elthash = DatumGetUInt32(FunctionCallInvoke(&locfcinfo));
+			locfcinfo.fcinfo.args[0].datum = elt;
+			locfcinfo.fcinfo.args[0].isnull = false;
+			locfcinfo.fcinfo.isnull = false;
+			elthash = DatumGetUInt32(FunctionCallInvoke(&locfcinfo.fcinfo));
 		}
 
 		/*
@@ -4014,7 +4025,11 @@ hash_array_extended(PG_FUNCTION_ARGS)
 	char		typalign;
 	int			i;
 	array_iter	iter;
-	FunctionCallInfoData locfcinfo;
+		union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(2)];
+	} locfcinfo;
+
 
 	typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
 	if (typentry == NULL ||
@@ -4033,7 +4048,7 @@ hash_array_extended(PG_FUNCTION_ARGS)
 	typbyval = typentry->typbyval;
 	typalign = typentry->typalign;
 
-	InitFunctionCallInfoData(locfcinfo, &typentry->hash_extended_proc_finfo, 2,
+	InitFunctionCallInfoData(locfcinfo.fcinfo, &typentry->hash_extended_proc_finfo, 2,
 							 InvalidOid, NULL, NULL);
 
 	/* Loop over source data */
@@ -4056,12 +4071,11 @@ hash_array_extended(PG_FUNCTION_ARGS)
 		else
 		{
 			/* Apply the hash function */
-			locfcinfo.arg[0] = elt;
-			locfcinfo.arg[1] = Int64GetDatum(seed);
-			locfcinfo.argnull[0] = false;
-			locfcinfo.argnull[1] = false;
-			locfcinfo.isnull = false;
-			elthash = DatumGetUInt64(FunctionCallInvoke(&locfcinfo));
+			locfcinfo.fcinfo.args[0].datum = elt;
+			locfcinfo.fcinfo.args[0].isnull = false;
+			locfcinfo.fcinfo.args[1].datum = Int64GetDatum(seed);
+			locfcinfo.fcinfo.args[1].isnull = false;
+			elthash = DatumGetUInt64(FunctionCallInvoke(&locfcinfo.fcinfo));
 		}
 
 		result = (result << 5) - result + elthash;
@@ -4104,7 +4118,11 @@ array_contain_compare(AnyArrayType *array1, AnyArrayType *array2, Oid collation,
 	int			i;
 	int			j;
 	array_iter	it1;
-	FunctionCallInfoData locfcinfo;
+		union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(2)];
+	} locfcinfo;
+
 
 	if (element_type != AARR_ELEMTYPE(array2))
 		ereport(ERROR,
@@ -4155,7 +4173,7 @@ array_contain_compare(AnyArrayType *array1, AnyArrayType *array2, Oid collation,
 	/*
 	 * Apply the comparison operator to each pair of array elements.
 	 */
-	InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
+	InitFunctionCallInfoData(locfcinfo.fcinfo, &typentry->eq_opr_finfo, 2,
 							 collation, NULL, NULL);
 
 	/* Loop over source data */
@@ -4197,12 +4215,12 @@ array_contain_compare(AnyArrayType *array1, AnyArrayType *array2, Oid collation,
 			/*
 			 * Apply the operator to the element pair
 			 */
-			locfcinfo.arg[0] = elt1;
-			locfcinfo.arg[1] = elt2;
-			locfcinfo.argnull[0] = false;
-			locfcinfo.argnull[1] = false;
-			locfcinfo.isnull = false;
-			oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
+			locfcinfo.fcinfo.args[0].datum = elt1;
+			locfcinfo.fcinfo.args[0].isnull = false;
+			locfcinfo.fcinfo.args[1].datum = elt2;
+			locfcinfo.fcinfo.args[1].isnull = false;
+			locfcinfo.fcinfo.isnull = false;
+			oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo.fcinfo));
 			if (oprresult)
 				break;
 		}
@@ -6053,7 +6071,11 @@ array_replace_internal(ArrayType *array,
 	int			bitmask;
 	bool		changed = false;
 	TypeCacheEntry *typentry;
-	FunctionCallInfoData locfcinfo;
+		union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(2)];
+	} locfcinfo;
+
 
 	element_type = ARR_ELEMTYPE(array);
 	ndim = ARR_NDIM(array);
@@ -6108,7 +6130,7 @@ array_replace_internal(ArrayType *array,
 	}
 
 	/* Prepare to apply the comparison operator */
-	InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
+	InitFunctionCallInfoData(locfcinfo.fcinfo, &typentry->eq_opr_finfo, 2,
 							 collation, NULL, NULL);
 
 	/* Allocate temporary arrays for new values */
@@ -6166,12 +6188,12 @@ array_replace_internal(ArrayType *array,
 				/*
 				 * Apply the operator to the element pair
 				 */
-				locfcinfo.arg[0] = elt;
-				locfcinfo.arg[1] = search;
-				locfcinfo.argnull[0] = false;
-				locfcinfo.argnull[1] = false;
-				locfcinfo.isnull = false;
-				oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
+				locfcinfo.fcinfo.args[0].datum = elt;
+				locfcinfo.fcinfo.args[0].isnull = false;
+				locfcinfo.fcinfo.args[1].datum = search;
+				locfcinfo.fcinfo.args[1].isnull = false;
+				locfcinfo.fcinfo.isnull = false;
+				oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo.fcinfo));
 				if (!oprresult)
 				{
 					/* no match, keep element */
@@ -6451,7 +6473,11 @@ width_bucket_array_fixed(Datum operand,
 	char	   *thresholds_data;
 	int			typlen = typentry->typlen;
 	bool		typbyval = typentry->typbyval;
-	FunctionCallInfoData locfcinfo;
+		union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(2)];
+	} locfcinfo;
+
 	int			left;
 	int			right;
 
@@ -6461,7 +6487,7 @@ width_bucket_array_fixed(Datum operand,
 	 */
 	thresholds_data = (char *) ARR_DATA_PTR(thresholds);
 
-	InitFunctionCallInfoData(locfcinfo, &typentry->cmp_proc_finfo, 2,
+	InitFunctionCallInfoData(locfcinfo.fcinfo, &typentry->cmp_proc_finfo, 2,
 							 collation, NULL, NULL);
 
 	/* Find the bucket */
@@ -6475,13 +6501,13 @@ width_bucket_array_fixed(Datum operand,
 
 		ptr = thresholds_data + mid * typlen;
 
-		locfcinfo.arg[0] = operand;
-		locfcinfo.arg[1] = fetch_att(ptr, typbyval, typlen);
-		locfcinfo.argnull[0] = false;
-		locfcinfo.argnull[1] = false;
-		locfcinfo.isnull = false;
+		locfcinfo.fcinfo.args[0].datum = operand;
+		locfcinfo.fcinfo.args[0].isnull = false;
+		locfcinfo.fcinfo.args[1].datum = fetch_att(ptr, typbyval, typlen);
+		locfcinfo.fcinfo.args[1].isnull = false;
+		locfcinfo.fcinfo.isnull = false;
 
-		cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
+		cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo.fcinfo));
 
 		if (cmpresult < 0)
 			right = mid;
@@ -6505,13 +6531,17 @@ width_bucket_array_variable(Datum operand,
 	int			typlen = typentry->typlen;
 	bool		typbyval = typentry->typbyval;
 	char		typalign = typentry->typalign;
-	FunctionCallInfoData locfcinfo;
+		union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(2)];
+	} locfcinfo;
+
 	int			left;
 	int			right;
 
 	thresholds_data = (char *) ARR_DATA_PTR(thresholds);
 
-	InitFunctionCallInfoData(locfcinfo, &typentry->cmp_proc_finfo, 2,
+	InitFunctionCallInfoData(locfcinfo.fcinfo, &typentry->cmp_proc_finfo, 2,
 							 collation, NULL, NULL);
 
 	/* Find the bucket */
@@ -6532,13 +6562,12 @@ width_bucket_array_variable(Datum operand,
 			ptr = (char *) att_align_nominal(ptr, typalign);
 		}
 
-		locfcinfo.arg[0] = operand;
-		locfcinfo.arg[1] = fetch_att(ptr, typbyval, typlen);
-		locfcinfo.argnull[0] = false;
-		locfcinfo.argnull[1] = false;
-		locfcinfo.isnull = false;
+		locfcinfo.fcinfo.args[0].datum = operand;
+		locfcinfo.fcinfo.args[0].isnull = false;
+		locfcinfo.fcinfo.args[1].datum = fetch_att(ptr, typbyval, typlen);
+		locfcinfo.fcinfo.args[1].isnull = false;
 
-		cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
+		cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo.fcinfo));
 
 		if (cmpresult < 0)
 			right = mid;
diff --git a/src/backend/utils/adt/int.c b/src/backend/utils/adt/int.c
index 559c365fecd..ac90ad280df 100644
--- a/src/backend/utils/adt/int.c
+++ b/src/backend/utils/adt/int.c
@@ -202,7 +202,10 @@ Datum
 int2vectorrecv(PG_FUNCTION_ARGS)
 {
 	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
-	FunctionCallInfoData locfcinfo;
+	union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(3)];
+	} locfcinfo;
 	int2vector *result;
 
 	/*
@@ -211,19 +214,19 @@ int2vectorrecv(PG_FUNCTION_ARGS)
 	 * fcinfo->flinfo->fn_extra.  So we need to pass it our own flinfo
 	 * parameter.
 	 */
-	InitFunctionCallInfoData(locfcinfo, fcinfo->flinfo, 3,
+	InitFunctionCallInfoData(locfcinfo.fcinfo, fcinfo->flinfo, 3,
 							 InvalidOid, NULL, NULL);
 
-	locfcinfo.arg[0] = PointerGetDatum(buf);
-	locfcinfo.arg[1] = ObjectIdGetDatum(INT2OID);
-	locfcinfo.arg[2] = Int32GetDatum(-1);
-	locfcinfo.argnull[0] = false;
-	locfcinfo.argnull[1] = false;
-	locfcinfo.argnull[2] = false;
+	locfcinfo.fcinfo.args[0].datum = PointerGetDatum(buf);
+	locfcinfo.fcinfo.args[0].isnull = false;
+	locfcinfo.fcinfo.args[1].datum = ObjectIdGetDatum(INT2OID);
+	locfcinfo.fcinfo.args[1].isnull = false;
+	locfcinfo.fcinfo.args[2].datum = Int32GetDatum(-1);
+	locfcinfo.fcinfo.args[2].isnull = false;
 
-	result = (int2vector *) DatumGetPointer(array_recv(&locfcinfo));
+	result = (int2vector *) DatumGetPointer(array_recv(&locfcinfo.fcinfo));
 
-	Assert(!locfcinfo.isnull);
+	Assert(!locfcinfo.fcinfo.isnull);
 
 	/* sanity checks: int2vector must be 1-D, 0-based, no nulls */
 	if (ARR_NDIM(result) != 1 ||
diff --git a/src/backend/utils/adt/oid.c b/src/backend/utils/adt/oid.c
index b0670e0d9f7..3f9fbab0330 100644
--- a/src/backend/utils/adt/oid.c
+++ b/src/backend/utils/adt/oid.c
@@ -257,7 +257,10 @@ Datum
 oidvectorrecv(PG_FUNCTION_ARGS)
 {
 	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
-	FunctionCallInfoData locfcinfo;
+	union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(3)];
+	} locfcinfo;
 	oidvector  *result;
 
 	/*
@@ -266,19 +269,19 @@ oidvectorrecv(PG_FUNCTION_ARGS)
 	 * fcinfo->flinfo->fn_extra.  So we need to pass it our own flinfo
 	 * parameter.
 	 */
-	InitFunctionCallInfoData(locfcinfo, fcinfo->flinfo, 3,
+	InitFunctionCallInfoData(locfcinfo.fcinfo, fcinfo->flinfo, 3,
 							 InvalidOid, NULL, NULL);
 
-	locfcinfo.arg[0] = PointerGetDatum(buf);
-	locfcinfo.arg[1] = ObjectIdGetDatum(OIDOID);
-	locfcinfo.arg[2] = Int32GetDatum(-1);
-	locfcinfo.argnull[0] = false;
-	locfcinfo.argnull[1] = false;
-	locfcinfo.argnull[2] = false;
+	locfcinfo.fcinfo.args[0].datum = PointerGetDatum(buf);
+	locfcinfo.fcinfo.args[0].isnull = false;
+	locfcinfo.fcinfo.args[1].datum = ObjectIdGetDatum(OIDOID);
+	locfcinfo.fcinfo.args[1].isnull = false;
+	locfcinfo.fcinfo.args[2].datum = Int32GetDatum(-1);
+	locfcinfo.fcinfo.args[2].isnull = false;
 
-	result = (oidvector *) DatumGetPointer(array_recv(&locfcinfo));
+	result = (oidvector *) DatumGetPointer(array_recv(&locfcinfo.fcinfo));
 
-	Assert(!locfcinfo.isnull);
+	Assert(!locfcinfo.fcinfo.isnull);
 
 	/* sanity checks: oidvector must be 1-D, 0-based, no nulls */
 	if (ARR_NDIM(result) != 1 ||
diff --git a/src/backend/utils/adt/rowtypes.c b/src/backend/utils/adt/rowtypes.c
index 5f729342f8d..9b10a6b13c0 100644
--- a/src/backend/utils/adt/rowtypes.c
+++ b/src/backend/utils/adt/rowtypes.c
@@ -942,7 +942,10 @@ record_cmp(FunctionCallInfo fcinfo)
 		 */
 		if (!nulls1[i1] || !nulls2[i2])
 		{
-			FunctionCallInfoData locfcinfo;
+			union {
+				FunctionCallInfoData fcinfo;
+				char *fcinfo_data[SizeForFunctionCallInfoData(3)];
+			} locfcinfo;
 			int32		cmpresult;
 
 			if (nulls1[i1])
@@ -959,14 +962,14 @@ record_cmp(FunctionCallInfo fcinfo)
 			}
 
 			/* Compare the pair of elements */
-			InitFunctionCallInfoData(locfcinfo, &typentry->cmp_proc_finfo, 2,
+			InitFunctionCallInfoData(locfcinfo.fcinfo, &typentry->cmp_proc_finfo, 2,
 									 collation, NULL, NULL);
-			locfcinfo.arg[0] = values1[i1];
-			locfcinfo.arg[1] = values2[i2];
-			locfcinfo.argnull[0] = false;
-			locfcinfo.argnull[1] = false;
-			locfcinfo.isnull = false;
-			cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
+			locfcinfo.fcinfo.args[0].datum = values1[i1];
+			locfcinfo.fcinfo.args[0].isnull = false;
+			locfcinfo.fcinfo.args[1].datum = values2[i2];
+			locfcinfo.fcinfo.args[1].isnull = false;
+			locfcinfo.fcinfo.isnull = false;
+			cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo.fcinfo));
 
 			if (cmpresult < 0)
 			{
@@ -1123,7 +1126,10 @@ record_eq(PG_FUNCTION_ARGS)
 		Form_pg_attribute att2;
 		TypeCacheEntry *typentry;
 		Oid			collation;
-		FunctionCallInfoData locfcinfo;
+		union {
+			FunctionCallInfoData fcinfo;
+			char *fcinfo_data[SizeForFunctionCallInfoData(3)];
+		} locfcinfo;
 		bool		oprresult;
 
 		/*
@@ -1193,14 +1199,14 @@ record_eq(PG_FUNCTION_ARGS)
 			}
 
 			/* Compare the pair of elements */
-			InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
+			InitFunctionCallInfoData(locfcinfo.fcinfo, &typentry->eq_opr_finfo, 2,
 									 collation, NULL, NULL);
-			locfcinfo.arg[0] = values1[i1];
-			locfcinfo.arg[1] = values2[i2];
-			locfcinfo.argnull[0] = false;
-			locfcinfo.argnull[1] = false;
-			locfcinfo.isnull = false;
-			oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
+			locfcinfo.fcinfo.args[0].datum = values1[i1];
+			locfcinfo.fcinfo.args[0].isnull = false;
+			locfcinfo.fcinfo.args[1].datum = values2[i2];
+			locfcinfo.fcinfo.args[1].isnull = false;
+			locfcinfo.fcinfo.isnull = false;
+			oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo.fcinfo));
 			if (!oprresult)
 			{
 				result = false;
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index 6cbbd5b78b0..738979c03f1 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -782,7 +782,6 @@ fmgr_security_definer(PG_FUNCTION_ARGS)
  *		Support routines for callers of fmgr-compatible functions
  *-------------------------------------------------------------------------
  */
-
 /*
  * These are for invocation of a specifically named function with a
  * directly-computed parameter list.  Note that neither arguments nor result
@@ -792,18 +791,21 @@ fmgr_security_definer(PG_FUNCTION_ARGS)
 Datum
 DirectFunctionCall1Coll(PGFunction func, Oid collation, Datum arg1)
 {
-	FunctionCallInfoData fcinfo;
+	union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(1)];
+	} d;
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, NULL, 1, collation, NULL, NULL);
+	InitFunctionCallInfoData(d.fcinfo, NULL, 1, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.argnull[0] = false;
+	d.fcinfo.args[0].datum = arg1;
+	d.fcinfo.args[0].isnull = false;
 
-	result = (*func) (&fcinfo);
+	result = (*func) (&d.fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
+	if (d.fcinfo.isnull)
 		elog(ERROR, "function %p returned NULL", (void *) func);
 
 	return result;
@@ -812,20 +814,23 @@ DirectFunctionCall1Coll(PGFunction func, Oid collation, Datum arg1)
 Datum
 DirectFunctionCall2Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2)
 {
-	FunctionCallInfoData fcinfo;
+	union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(2)];
+	} d;
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, NULL, 2, collation, NULL, NULL);
+	InitFunctionCallInfoData(d.fcinfo, NULL, 2, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
+	d.fcinfo.args[0].datum = arg1;
+	d.fcinfo.args[0].isnull = false;
+	d.fcinfo.args[1].datum = arg2;
+	d.fcinfo.args[1].isnull = false;
 
-	result = (*func) (&fcinfo);
+	result = (*func) (&d.fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
+	if (d.fcinfo.isnull)
 		elog(ERROR, "function %p returned NULL", (void *) func);
 
 	return result;
@@ -835,22 +840,25 @@ Datum
 DirectFunctionCall3Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
 						Datum arg3)
 {
-	FunctionCallInfoData fcinfo;
+	union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(3)];
+	} d;
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, NULL, 3, collation, NULL, NULL);
+	InitFunctionCallInfoData(d.fcinfo, NULL, 3, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
+	d.fcinfo.args[0].datum = arg1;
+	d.fcinfo.args[0].isnull = false;
+	d.fcinfo.args[1].datum = arg2;
+	d.fcinfo.args[1].isnull = false;
+	d.fcinfo.args[2].datum = arg3;
+	d.fcinfo.args[2].isnull = false;
 
-	result = (*func) (&fcinfo);
+	result = (*func) (&d.fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
+	if (d.fcinfo.isnull)
 		elog(ERROR, "function %p returned NULL", (void *) func);
 
 	return result;
@@ -860,24 +868,27 @@ Datum
 DirectFunctionCall4Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
 						Datum arg3, Datum arg4)
 {
-	FunctionCallInfoData fcinfo;
+	union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(4)];
+	} d;
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, NULL, 4, collation, NULL, NULL);
+	InitFunctionCallInfoData(d.fcinfo, NULL, 4, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
+	d.fcinfo.args[0].datum = arg1;
+	d.fcinfo.args[0].isnull = false;
+	d.fcinfo.args[1].datum = arg2;
+	d.fcinfo.args[1].isnull = false;
+	d.fcinfo.args[2].datum = arg3;
+	d.fcinfo.args[2].isnull = false;
+	d.fcinfo.args[3].datum = arg4;
+	d.fcinfo.args[3].isnull = false;
 
-	result = (*func) (&fcinfo);
+	result = (*func) (&d.fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
+	if (d.fcinfo.isnull)
 		elog(ERROR, "function %p returned NULL", (void *) func);
 
 	return result;
@@ -887,26 +898,29 @@ Datum
 DirectFunctionCall5Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
 						Datum arg3, Datum arg4, Datum arg5)
 {
-	FunctionCallInfoData fcinfo;
+	union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(5)];
+	} d;
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, NULL, 5, collation, NULL, NULL);
+	InitFunctionCallInfoData(d.fcinfo, NULL, 5, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
+	d.fcinfo.args[0].datum = arg1;
+	d.fcinfo.args[0].isnull = false;
+	d.fcinfo.args[1].datum = arg2;
+	d.fcinfo.args[1].isnull = false;
+	d.fcinfo.args[2].datum = arg3;
+	d.fcinfo.args[2].isnull = false;
+	d.fcinfo.args[3].datum = arg4;
+	d.fcinfo.args[3].isnull = false;
+	d.fcinfo.args[4].datum = arg5;
+	d.fcinfo.args[4].isnull = false;
 
-	result = (*func) (&fcinfo);
+	result = (*func) (&d.fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
+	if (d.fcinfo.isnull)
 		elog(ERROR, "function %p returned NULL", (void *) func);
 
 	return result;
@@ -917,28 +931,31 @@ DirectFunctionCall6Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
 						Datum arg3, Datum arg4, Datum arg5,
 						Datum arg6)
 {
-	FunctionCallInfoData fcinfo;
+	union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(6)];
+	} d;
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, NULL, 6, collation, NULL, NULL);
+	InitFunctionCallInfoData(d.fcinfo, NULL, 6, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.arg[5] = arg6;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
-	fcinfo.argnull[5] = false;
+	d.fcinfo.args[0].datum = arg1;
+	d.fcinfo.args[0].isnull = false;
+	d.fcinfo.args[1].datum = arg2;
+	d.fcinfo.args[1].isnull = false;
+	d.fcinfo.args[2].datum = arg3;
+	d.fcinfo.args[2].isnull = false;
+	d.fcinfo.args[3].datum = arg4;
+	d.fcinfo.args[3].isnull = false;
+	d.fcinfo.args[4].datum = arg5;
+	d.fcinfo.args[4].isnull = false;
+	d.fcinfo.args[5].datum = arg6;
+	d.fcinfo.args[5].isnull = false;
 
-	result = (*func) (&fcinfo);
+	result = (*func) (&d.fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
+	if (d.fcinfo.isnull)
 		elog(ERROR, "function %p returned NULL", (void *) func);
 
 	return result;
@@ -949,30 +966,33 @@ DirectFunctionCall7Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
 						Datum arg3, Datum arg4, Datum arg5,
 						Datum arg6, Datum arg7)
 {
-	FunctionCallInfoData fcinfo;
+	union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(7)];
+	} d;
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, NULL, 7, collation, NULL, NULL);
+	InitFunctionCallInfoData(d.fcinfo, NULL, 7, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.arg[5] = arg6;
-	fcinfo.arg[6] = arg7;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
-	fcinfo.argnull[5] = false;
-	fcinfo.argnull[6] = false;
+	d.fcinfo.args[0].datum = arg1;
+	d.fcinfo.args[0].isnull = false;
+	d.fcinfo.args[1].datum = arg2;
+	d.fcinfo.args[1].isnull = false;
+	d.fcinfo.args[2].datum = arg3;
+	d.fcinfo.args[2].isnull = false;
+	d.fcinfo.args[3].datum = arg4;
+	d.fcinfo.args[3].isnull = false;
+	d.fcinfo.args[4].datum = arg5;
+	d.fcinfo.args[4].isnull = false;
+	d.fcinfo.args[5].datum = arg6;
+	d.fcinfo.args[5].isnull = false;
+	d.fcinfo.args[6].datum = arg7;
+	d.fcinfo.args[6].isnull = false;
 
-	result = (*func) (&fcinfo);
+	result = (*func) (&d.fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
+	if (d.fcinfo.isnull)
 		elog(ERROR, "function %p returned NULL", (void *) func);
 
 	return result;
@@ -983,32 +1003,35 @@ DirectFunctionCall8Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
 						Datum arg3, Datum arg4, Datum arg5,
 						Datum arg6, Datum arg7, Datum arg8)
 {
-	FunctionCallInfoData fcinfo;
+	union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(8)];
+	} d;
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, NULL, 8, collation, NULL, NULL);
+	InitFunctionCallInfoData(d.fcinfo, NULL, 8, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.arg[5] = arg6;
-	fcinfo.arg[6] = arg7;
-	fcinfo.arg[7] = arg8;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
-	fcinfo.argnull[5] = false;
-	fcinfo.argnull[6] = false;
-	fcinfo.argnull[7] = false;
+	d.fcinfo.args[0].datum = arg1;
+	d.fcinfo.args[0].isnull = false;
+	d.fcinfo.args[1].datum = arg2;
+	d.fcinfo.args[1].isnull = false;
+	d.fcinfo.args[2].datum = arg3;
+	d.fcinfo.args[2].isnull = false;
+	d.fcinfo.args[3].datum = arg4;
+	d.fcinfo.args[3].isnull = false;
+	d.fcinfo.args[4].datum = arg5;
+	d.fcinfo.args[4].isnull = false;
+	d.fcinfo.args[5].datum = arg6;
+	d.fcinfo.args[5].isnull = false;
+	d.fcinfo.args[6].datum = arg7;
+	d.fcinfo.args[6].isnull = false;
+	d.fcinfo.args[7].datum = arg8;
+	d.fcinfo.args[7].isnull = false;
 
-	result = (*func) (&fcinfo);
+	result = (*func) (&d.fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
+	if (d.fcinfo.isnull)
 		elog(ERROR, "function %p returned NULL", (void *) func);
 
 	return result;
@@ -1020,34 +1043,37 @@ DirectFunctionCall9Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
 						Datum arg6, Datum arg7, Datum arg8,
 						Datum arg9)
 {
-	FunctionCallInfoData fcinfo;
+	union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(9)];
+	} d;
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, NULL, 9, collation, NULL, NULL);
+	InitFunctionCallInfoData(d.fcinfo, NULL, 9, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.arg[5] = arg6;
-	fcinfo.arg[6] = arg7;
-	fcinfo.arg[7] = arg8;
-	fcinfo.arg[8] = arg9;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
-	fcinfo.argnull[5] = false;
-	fcinfo.argnull[6] = false;
-	fcinfo.argnull[7] = false;
-	fcinfo.argnull[8] = false;
+	d.fcinfo.args[0].datum = arg1;
+	d.fcinfo.args[0].isnull = false;
+	d.fcinfo.args[1].datum = arg2;
+	d.fcinfo.args[1].isnull = false;
+	d.fcinfo.args[2].datum = arg3;
+	d.fcinfo.args[2].isnull = false;
+	d.fcinfo.args[3].datum = arg4;
+	d.fcinfo.args[3].isnull = false;
+	d.fcinfo.args[4].datum = arg5;
+	d.fcinfo.args[4].isnull = false;
+	d.fcinfo.args[5].datum = arg6;
+	d.fcinfo.args[5].isnull = false;
+	d.fcinfo.args[6].datum = arg7;
+	d.fcinfo.args[6].isnull = false;
+	d.fcinfo.args[7].datum = arg8;
+	d.fcinfo.args[7].isnull = false;
+	d.fcinfo.args[8].datum = arg9;
+	d.fcinfo.args[8].isnull = false;
 
-	result = (*func) (&fcinfo);
+	result = (*func) (&d.fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
+	if (d.fcinfo.isnull)
 		elog(ERROR, "function %p returned NULL", (void *) func);
 
 	return result;
@@ -1065,18 +1091,21 @@ DirectFunctionCall9Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
 Datum
 CallerFInfoFunctionCall1(PGFunction func, FmgrInfo *flinfo, Oid collation, Datum arg1)
 {
-	FunctionCallInfoData fcinfo;
+	union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(1)];
+	} d;
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, flinfo, 1, collation, NULL, NULL);
+	InitFunctionCallInfoData(d.fcinfo, flinfo, 1, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.argnull[0] = false;
+	d.fcinfo.args[0].datum = arg1;
+	d.fcinfo.args[0].isnull = false;
 
-	result = (*func) (&fcinfo);
+	result = (*func) (&d.fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
+	if (d.fcinfo.isnull)
 		elog(ERROR, "function %p returned NULL", (void *) func);
 
 	return result;
@@ -1085,25 +1114,53 @@ CallerFInfoFunctionCall1(PGFunction func, FmgrInfo *flinfo, Oid collation, Datum
 Datum
 CallerFInfoFunctionCall2(PGFunction func, FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
 {
-	FunctionCallInfoData fcinfo;
+	union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(2)];
+	} d;
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, flinfo, 2, collation, NULL, NULL);
+	InitFunctionCallInfoData(d.fcinfo, flinfo, 2, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
+	d.fcinfo.args[0].datum = arg1;
+	d.fcinfo.args[0].isnull = false;
+	d.fcinfo.args[1].datum = arg2;
+	d.fcinfo.args[1].isnull = false;
 
-	result = (*func) (&fcinfo);
+	result = (*func) (&d.fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
+	if (d.fcinfo.isnull)
 		elog(ERROR, "function %p returned NULL", (void *) func);
 
 	return result;
 }
 
+/*
+ * These are for invocation of a previously-looked-up function with a
+ * directly-computed parameter list.  Note that neither arguments nor result
+ * are allowed to be NULL.
+ */
+Datum
+FunctionCall0Coll(FmgrInfo *flinfo, Oid collation)
+{
+	union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(0)];
+	} d;
+	Datum		result;
+
+	InitFunctionCallInfoData(d.fcinfo, flinfo, 0, collation, NULL, NULL);
+
+	result = FunctionCallInvoke(&d.fcinfo);
+
+	/* Check for null result, since caller is clearly not expecting one */
+	if (d.fcinfo.isnull)
+		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
+
+	return result;
+}
+
 /*
  * These are for invocation of a previously-looked-up function with a
  * directly-computed parameter list.  Note that neither arguments nor result
@@ -1112,19 +1169,22 @@ CallerFInfoFunctionCall2(PGFunction func, FmgrInfo *flinfo, Oid collation, Datum
 Datum
 FunctionCall1Coll(FmgrInfo *flinfo, Oid collation, Datum arg1)
 {
-	FunctionCallInfoData fcinfo;
+	union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(1)];
+	} d;
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, flinfo, 1, collation, NULL, NULL);
+	InitFunctionCallInfoData(d.fcinfo, flinfo, 1, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.argnull[0] = false;
+	d.fcinfo.args[0].datum = arg1;
+	d.fcinfo.args[0].isnull = false;
 
-	result = FunctionCallInvoke(&fcinfo);
+	result = FunctionCallInvoke(&d.fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid);
+	if (d.fcinfo.isnull)
+		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
 
 	return result;
 }
@@ -1132,21 +1192,24 @@ FunctionCall1Coll(FmgrInfo *flinfo, Oid collation, Datum arg1)
 Datum
 FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
 {
-	FunctionCallInfoData fcinfo;
+	union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(2)];
+	} d;
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, flinfo, 2, collation, NULL, NULL);
+	InitFunctionCallInfoData(d.fcinfo, flinfo, 2, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
+	d.fcinfo.args[0].datum = arg1;
+	d.fcinfo.args[0].isnull = false;
+	d.fcinfo.args[1].datum = arg2;
+	d.fcinfo.args[1].isnull = false;
 
-	result = FunctionCallInvoke(&fcinfo);
+	result = FunctionCallInvoke(&d.fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid);
+	if (d.fcinfo.isnull)
+		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
 
 	return result;
 }
@@ -1155,23 +1218,26 @@ Datum
 FunctionCall3Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
 				  Datum arg3)
 {
-	FunctionCallInfoData fcinfo;
+	union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(3)];
+	} d;
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, flinfo, 3, collation, NULL, NULL);
+	InitFunctionCallInfoData(d.fcinfo, flinfo, 3, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
+	d.fcinfo.args[0].datum = arg1;
+	d.fcinfo.args[0].isnull = false;
+	d.fcinfo.args[1].datum = arg2;
+	d.fcinfo.args[1].isnull = false;
+	d.fcinfo.args[2].datum = arg3;
+	d.fcinfo.args[2].isnull = false;
 
-	result = FunctionCallInvoke(&fcinfo);
+	result = FunctionCallInvoke(&d.fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid);
+	if (d.fcinfo.isnull)
+		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
 
 	return result;
 }
@@ -1180,25 +1246,28 @@ Datum
 FunctionCall4Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
 				  Datum arg3, Datum arg4)
 {
-	FunctionCallInfoData fcinfo;
+	union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(4)];
+	} d;
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, flinfo, 4, collation, NULL, NULL);
+	InitFunctionCallInfoData(d.fcinfo, flinfo, 4, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
+	d.fcinfo.args[0].datum = arg1;
+	d.fcinfo.args[0].isnull = false;
+	d.fcinfo.args[1].datum = arg2;
+	d.fcinfo.args[1].isnull = false;
+	d.fcinfo.args[2].datum = arg3;
+	d.fcinfo.args[2].isnull = false;
+	d.fcinfo.args[3].datum = arg4;
+	d.fcinfo.args[3].isnull = false;
 
-	result = FunctionCallInvoke(&fcinfo);
+	result = FunctionCallInvoke(&d.fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid);
+	if (d.fcinfo.isnull)
+		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
 
 	return result;
 }
@@ -1207,27 +1276,30 @@ Datum
 FunctionCall5Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
 				  Datum arg3, Datum arg4, Datum arg5)
 {
-	FunctionCallInfoData fcinfo;
+	union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(5)];
+	} d;
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, flinfo, 5, collation, NULL, NULL);
+	InitFunctionCallInfoData(d.fcinfo, flinfo, 5, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
+	d.fcinfo.args[0].datum = arg1;
+	d.fcinfo.args[0].isnull = false;
+	d.fcinfo.args[1].datum = arg2;
+	d.fcinfo.args[1].isnull = false;
+	d.fcinfo.args[2].datum = arg3;
+	d.fcinfo.args[2].isnull = false;
+	d.fcinfo.args[3].datum = arg4;
+	d.fcinfo.args[3].isnull = false;
+	d.fcinfo.args[4].datum = arg5;
+	d.fcinfo.args[4].isnull = false;
 
-	result = FunctionCallInvoke(&fcinfo);
+	result = FunctionCallInvoke(&d.fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid);
+	if (d.fcinfo.isnull)
+		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
 
 	return result;
 }
@@ -1237,29 +1309,32 @@ FunctionCall6Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
 				  Datum arg3, Datum arg4, Datum arg5,
 				  Datum arg6)
 {
-	FunctionCallInfoData fcinfo;
+	union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(6)];
+	} d;
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, flinfo, 6, collation, NULL, NULL);
+	InitFunctionCallInfoData(d.fcinfo, flinfo, 6, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.arg[5] = arg6;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
-	fcinfo.argnull[5] = false;
+	d.fcinfo.args[0].datum = arg1;
+	d.fcinfo.args[0].isnull = false;
+	d.fcinfo.args[1].datum = arg2;
+	d.fcinfo.args[1].isnull = false;
+	d.fcinfo.args[2].datum = arg3;
+	d.fcinfo.args[2].isnull = false;
+	d.fcinfo.args[3].datum = arg4;
+	d.fcinfo.args[3].isnull = false;
+	d.fcinfo.args[4].datum = arg5;
+	d.fcinfo.args[4].isnull = false;
+	d.fcinfo.args[5].datum = arg6;
+	d.fcinfo.args[5].isnull = false;
 
-	result = FunctionCallInvoke(&fcinfo);
+	result = FunctionCallInvoke(&d.fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid);
+	if (d.fcinfo.isnull)
+		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
 
 	return result;
 }
@@ -1269,31 +1344,34 @@ FunctionCall7Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
 				  Datum arg3, Datum arg4, Datum arg5,
 				  Datum arg6, Datum arg7)
 {
-	FunctionCallInfoData fcinfo;
+	union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(7)];
+	} d;
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, flinfo, 7, collation, NULL, NULL);
+	InitFunctionCallInfoData(d.fcinfo, flinfo, 7, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.arg[5] = arg6;
-	fcinfo.arg[6] = arg7;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
-	fcinfo.argnull[5] = false;
-	fcinfo.argnull[6] = false;
+	d.fcinfo.args[0].datum = arg1;
+	d.fcinfo.args[0].isnull = false;
+	d.fcinfo.args[1].datum = arg2;
+	d.fcinfo.args[1].isnull = false;
+	d.fcinfo.args[2].datum = arg3;
+	d.fcinfo.args[2].isnull = false;
+	d.fcinfo.args[3].datum = arg4;
+	d.fcinfo.args[3].isnull = false;
+	d.fcinfo.args[4].datum = arg5;
+	d.fcinfo.args[4].isnull = false;
+	d.fcinfo.args[5].datum = arg6;
+	d.fcinfo.args[5].isnull = false;
+	d.fcinfo.args[6].datum = arg7;
+	d.fcinfo.args[6].isnull = false;
 
-	result = FunctionCallInvoke(&fcinfo);
+	result = FunctionCallInvoke(&d.fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid);
+	if (d.fcinfo.isnull)
+		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
 
 	return result;
 }
@@ -1303,33 +1381,36 @@ FunctionCall8Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
 				  Datum arg3, Datum arg4, Datum arg5,
 				  Datum arg6, Datum arg7, Datum arg8)
 {
-	FunctionCallInfoData fcinfo;
+	union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(8)];
+	} d;
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, flinfo, 8, collation, NULL, NULL);
+	InitFunctionCallInfoData(d.fcinfo, flinfo, 8, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.arg[5] = arg6;
-	fcinfo.arg[6] = arg7;
-	fcinfo.arg[7] = arg8;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
-	fcinfo.argnull[5] = false;
-	fcinfo.argnull[6] = false;
-	fcinfo.argnull[7] = false;
+	d.fcinfo.args[0].datum = arg1;
+	d.fcinfo.args[0].isnull = false;
+	d.fcinfo.args[1].datum = arg2;
+	d.fcinfo.args[1].isnull = false;
+	d.fcinfo.args[2].datum = arg3;
+	d.fcinfo.args[2].isnull = false;
+	d.fcinfo.args[3].datum = arg4;
+	d.fcinfo.args[3].isnull = false;
+	d.fcinfo.args[4].datum = arg5;
+	d.fcinfo.args[4].isnull = false;
+	d.fcinfo.args[5].datum = arg6;
+	d.fcinfo.args[5].isnull = false;
+	d.fcinfo.args[6].datum = arg7;
+	d.fcinfo.args[6].isnull = false;
+	d.fcinfo.args[7].datum = arg8;
+	d.fcinfo.args[7].isnull = false;
 
-	result = FunctionCallInvoke(&fcinfo);
+	result = FunctionCallInvoke(&d.fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid);
+	if (d.fcinfo.isnull)
+		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
 
 	return result;
 }
@@ -1340,35 +1421,38 @@ FunctionCall9Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
 				  Datum arg6, Datum arg7, Datum arg8,
 				  Datum arg9)
 {
-	FunctionCallInfoData fcinfo;
+	union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(9)];
+	} d;
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, flinfo, 9, collation, NULL, NULL);
+	InitFunctionCallInfoData(d.fcinfo, flinfo, 9, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.arg[5] = arg6;
-	fcinfo.arg[6] = arg7;
-	fcinfo.arg[7] = arg8;
-	fcinfo.arg[8] = arg9;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
-	fcinfo.argnull[5] = false;
-	fcinfo.argnull[6] = false;
-	fcinfo.argnull[7] = false;
-	fcinfo.argnull[8] = false;
+	d.fcinfo.args[0].datum = arg1;
+	d.fcinfo.args[0].isnull = false;
+	d.fcinfo.args[1].datum = arg2;
+	d.fcinfo.args[1].isnull = false;
+	d.fcinfo.args[2].datum = arg3;
+	d.fcinfo.args[2].isnull = false;
+	d.fcinfo.args[3].datum = arg4;
+	d.fcinfo.args[3].isnull = false;
+	d.fcinfo.args[4].datum = arg5;
+	d.fcinfo.args[4].isnull = false;
+	d.fcinfo.args[5].datum = arg6;
+	d.fcinfo.args[5].isnull = false;
+	d.fcinfo.args[6].datum = arg7;
+	d.fcinfo.args[6].isnull = false;
+	d.fcinfo.args[7].datum = arg8;
+	d.fcinfo.args[7].isnull = false;
+	d.fcinfo.args[8].datum = arg9;
+	d.fcinfo.args[8].isnull = false;
 
-	result = FunctionCallInvoke(&fcinfo);
+	result = FunctionCallInvoke(&d.fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid);
+	if (d.fcinfo.isnull)
+		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
 
 	return result;
 }
@@ -1385,68 +1469,30 @@ Datum
 OidFunctionCall0Coll(Oid functionId, Oid collation)
 {
 	FmgrInfo	flinfo;
-	FunctionCallInfoData fcinfo;
-	Datum		result;
 
 	fmgr_info(functionId, &flinfo);
 
-	InitFunctionCallInfoData(fcinfo, &flinfo, 0, collation, NULL, NULL);
-
-	result = FunctionCallInvoke(&fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
-	return result;
+	return FunctionCall0Coll(&flinfo, collation);
 }
 
 Datum
 OidFunctionCall1Coll(Oid functionId, Oid collation, Datum arg1)
 {
 	FmgrInfo	flinfo;
-	FunctionCallInfoData fcinfo;
-	Datum		result;
 
 	fmgr_info(functionId, &flinfo);
 
-	InitFunctionCallInfoData(fcinfo, &flinfo, 1, collation, NULL, NULL);
-
-	fcinfo.arg[0] = arg1;
-	fcinfo.argnull[0] = false;
-
-	result = FunctionCallInvoke(&fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
-	return result;
+	return FunctionCall1Coll(&flinfo, collation, arg1);
 }
 
 Datum
 OidFunctionCall2Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2)
 {
 	FmgrInfo	flinfo;
-	FunctionCallInfoData fcinfo;
-	Datum		result;
 
 	fmgr_info(functionId, &flinfo);
 
-	InitFunctionCallInfoData(fcinfo, &flinfo, 2, collation, NULL, NULL);
-
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-
-	result = FunctionCallInvoke(&fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
-	return result;
+	return FunctionCall2Coll(&flinfo, collation, arg1, arg2);
 }
 
 Datum
@@ -1454,27 +1500,10 @@ OidFunctionCall3Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
 					 Datum arg3)
 {
 	FmgrInfo	flinfo;
-	FunctionCallInfoData fcinfo;
-	Datum		result;
 
 	fmgr_info(functionId, &flinfo);
 
-	InitFunctionCallInfoData(fcinfo, &flinfo, 3, collation, NULL, NULL);
-
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-
-	result = FunctionCallInvoke(&fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
-	return result;
+	return FunctionCall3Coll(&flinfo, collation, arg1, arg2, arg3);
 }
 
 Datum
@@ -1482,29 +1511,10 @@ OidFunctionCall4Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
 					 Datum arg3, Datum arg4)
 {
 	FmgrInfo	flinfo;
-	FunctionCallInfoData fcinfo;
-	Datum		result;
 
 	fmgr_info(functionId, &flinfo);
 
-	InitFunctionCallInfoData(fcinfo, &flinfo, 4, collation, NULL, NULL);
-
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-
-	result = FunctionCallInvoke(&fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
-	return result;
+	return FunctionCall4Coll(&flinfo, collation, arg1, arg2, arg3, arg4);
 }
 
 Datum
@@ -1512,31 +1522,10 @@ OidFunctionCall5Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
 					 Datum arg3, Datum arg4, Datum arg5)
 {
 	FmgrInfo	flinfo;
-	FunctionCallInfoData fcinfo;
-	Datum		result;
 
 	fmgr_info(functionId, &flinfo);
 
-	InitFunctionCallInfoData(fcinfo, &flinfo, 5, collation, NULL, NULL);
-
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
-
-	result = FunctionCallInvoke(&fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
-	return result;
+	return FunctionCall5Coll(&flinfo, collation, arg1, arg2, arg3, arg4, arg5);
 }
 
 Datum
@@ -1545,33 +1534,11 @@ OidFunctionCall6Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
 					 Datum arg6)
 {
 	FmgrInfo	flinfo;
-	FunctionCallInfoData fcinfo;
-	Datum		result;
 
 	fmgr_info(functionId, &flinfo);
 
-	InitFunctionCallInfoData(fcinfo, &flinfo, 6, collation, NULL, NULL);
-
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.arg[5] = arg6;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
-	fcinfo.argnull[5] = false;
-
-	result = FunctionCallInvoke(&fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
-	return result;
+	return FunctionCall6Coll(&flinfo, collation, arg1, arg2, arg3, arg4, arg5,
+							 arg6);
 }
 
 Datum
@@ -1580,35 +1547,11 @@ OidFunctionCall7Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
 					 Datum arg6, Datum arg7)
 {
 	FmgrInfo	flinfo;
-	FunctionCallInfoData fcinfo;
-	Datum		result;
 
 	fmgr_info(functionId, &flinfo);
 
-	InitFunctionCallInfoData(fcinfo, &flinfo, 7, collation, NULL, NULL);
-
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.arg[5] = arg6;
-	fcinfo.arg[6] = arg7;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
-	fcinfo.argnull[5] = false;
-	fcinfo.argnull[6] = false;
-
-	result = FunctionCallInvoke(&fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
-	return result;
+	return FunctionCall7Coll(&flinfo, collation, arg1, arg2, arg3, arg4, arg5,
+							 arg6, arg7);
 }
 
 Datum
@@ -1617,37 +1560,11 @@ OidFunctionCall8Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
 					 Datum arg6, Datum arg7, Datum arg8)
 {
 	FmgrInfo	flinfo;
-	FunctionCallInfoData fcinfo;
-	Datum		result;
 
 	fmgr_info(functionId, &flinfo);
 
-	InitFunctionCallInfoData(fcinfo, &flinfo, 8, collation, NULL, NULL);
-
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.arg[5] = arg6;
-	fcinfo.arg[6] = arg7;
-	fcinfo.arg[7] = arg8;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
-	fcinfo.argnull[5] = false;
-	fcinfo.argnull[6] = false;
-	fcinfo.argnull[7] = false;
-
-	result = FunctionCallInvoke(&fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
-	return result;
+	return FunctionCall8Coll(&flinfo, collation, arg1, arg2, arg3, arg4, arg5,
+							 arg6, arg7, arg8);
 }
 
 Datum
@@ -1657,39 +1574,11 @@ OidFunctionCall9Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
 					 Datum arg9)
 {
 	FmgrInfo	flinfo;
-	FunctionCallInfoData fcinfo;
-	Datum		result;
 
 	fmgr_info(functionId, &flinfo);
 
-	InitFunctionCallInfoData(fcinfo, &flinfo, 9, collation, NULL, NULL);
-
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.arg[5] = arg6;
-	fcinfo.arg[6] = arg7;
-	fcinfo.arg[7] = arg8;
-	fcinfo.arg[8] = arg9;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
-	fcinfo.argnull[5] = false;
-	fcinfo.argnull[6] = false;
-	fcinfo.argnull[7] = false;
-	fcinfo.argnull[8] = false;
-
-	result = FunctionCallInvoke(&fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
-	return result;
+	return FunctionCall9Coll(&flinfo, collation, arg1, arg2, arg3, arg4, arg5,
+							 arg6, arg7, arg8, arg9);
 }
 
 
@@ -1708,35 +1597,38 @@ OidFunctionCall9Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
 Datum
 InputFunctionCall(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod)
 {
-	FunctionCallInfoData fcinfo;
+	union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(3)];
+	} d;
 	Datum		result;
 
 	if (str == NULL && flinfo->fn_strict)
 		return (Datum) 0;		/* just return null result */
 
-	InitFunctionCallInfoData(fcinfo, flinfo, 3, InvalidOid, NULL, NULL);
+	InitFunctionCallInfoData(d.fcinfo, flinfo, 3, InvalidOid, 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;
+	d.fcinfo.args[0].datum = CStringGetDatum(str);
+	d.fcinfo.args[0].isnull = false;
+	d.fcinfo.args[1].datum = ObjectIdGetDatum(typioparam);
+	d.fcinfo.args[1].isnull = false;
+	d.fcinfo.args[2].datum = Int32GetDatum(typmod);
+	d.fcinfo.args[2].isnull = false;
 
-	result = FunctionCallInvoke(&fcinfo);
+	result = FunctionCallInvoke(&d.fcinfo);
 
 	/* Should get null result if and only if str is NULL */
 	if (str == NULL)
 	{
-		if (!fcinfo.isnull)
+		if (!d.fcinfo.isnull)
 			elog(ERROR, "input function %u returned non-NULL",
-				 fcinfo.flinfo->fn_oid);
+				 flinfo->fn_oid);
 	}
 	else
 	{
-		if (fcinfo.isnull)
+		if (d.fcinfo.isnull)
 			elog(ERROR, "input function %u returned NULL",
-				 fcinfo.flinfo->fn_oid);
+				 flinfo->fn_oid);
 	}
 
 	return result;
@@ -1767,35 +1659,38 @@ Datum
 ReceiveFunctionCall(FmgrInfo *flinfo, StringInfo buf,
 					Oid typioparam, int32 typmod)
 {
-	FunctionCallInfoData fcinfo;
+	union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(3)];
+	} d;
 	Datum		result;
 
 	if (buf == NULL && flinfo->fn_strict)
 		return (Datum) 0;		/* just return null result */
 
-	InitFunctionCallInfoData(fcinfo, flinfo, 3, InvalidOid, NULL, NULL);
+	InitFunctionCallInfoData(d.fcinfo, flinfo, 3, InvalidOid, 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;
+	d.fcinfo.args[0].datum = PointerGetDatum(buf);
+	d.fcinfo.args[0].isnull = false;
+	d.fcinfo.args[1].datum = ObjectIdGetDatum(typioparam);
+	d.fcinfo.args[1].isnull = false;
+	d.fcinfo.args[2].datum = Int32GetDatum(typmod);
+	d.fcinfo.args[2].isnull = false;
 
-	result = FunctionCallInvoke(&fcinfo);
+	result = FunctionCallInvoke(&d.fcinfo);
 
 	/* Should get null result if and only if buf is NULL */
 	if (buf == NULL)
 	{
-		if (!fcinfo.isnull)
+		if (!d.fcinfo.isnull)
 			elog(ERROR, "receive function %u returned non-NULL",
-				 fcinfo.flinfo->fn_oid);
+				 flinfo->fn_oid);
 	}
 	else
 	{
-		if (fcinfo.isnull)
+		if (d.fcinfo.isnull)
 			elog(ERROR, "receive function %u returned NULL",
-				 fcinfo.flinfo->fn_oid);
+				 flinfo->fn_oid);
 	}
 
 	return result;
diff --git a/src/backend/utils/sort/sortsupport.c b/src/backend/utils/sort/sortsupport.c
index 8c09fe4a430..31aa4100d67 100644
--- a/src/backend/utils/sort/sortsupport.c
+++ b/src/backend/utils/sort/sortsupport.c
@@ -26,10 +26,11 @@
 /* Info needed to use an old-style comparison function as a sort comparator */
 typedef struct
 {
-	FunctionCallInfoData fcinfo;	/* reusable callinfo structure */
 	FmgrInfo	flinfo;			/* lookup data for comparison function */
+	FunctionCallInfoData fcinfo;	/* reusable callinfo structure */
 } SortShimExtra;
 
+#define SizeForSortShimExtra(nargs) (offsetof(SortShimExtra, fcinfo) + SizeForFunctionCallInfoData(nargs))
 
 /*
  * Shim function for calling an old-style comparator
@@ -44,8 +45,8 @@ comparison_shim(Datum x, Datum y, SortSupport ssup)
 	SortShimExtra *extra = (SortShimExtra *) ssup->ssup_extra;
 	Datum		result;
 
-	extra->fcinfo.arg[0] = x;
-	extra->fcinfo.arg[1] = y;
+	extra->fcinfo.args[0].datum = x;
+	extra->fcinfo.args[1].datum = y;
 
 	/* just for paranoia's sake, we reset isnull each time */
 	extra->fcinfo.isnull = false;
@@ -69,7 +70,7 @@ PrepareSortSupportComparisonShim(Oid cmpFunc, SortSupport ssup)
 	SortShimExtra *extra;
 
 	extra = (SortShimExtra *) MemoryContextAlloc(ssup->ssup_cxt,
-												 sizeof(SortShimExtra));
+												 SizeForSortShimExtra(2));
 
 	/* Lookup the comparison function */
 	fmgr_info_cxt(cmpFunc, &extra->flinfo, ssup->ssup_cxt);
@@ -77,8 +78,8 @@ PrepareSortSupportComparisonShim(Oid cmpFunc, SortSupport ssup)
 	/* We can initialize the callinfo just once and re-use it */
 	InitFunctionCallInfoData(extra->fcinfo, &extra->flinfo, 2,
 							 ssup->ssup_collation, NULL, NULL);
-	extra->fcinfo.argnull[0] = false;
-	extra->fcinfo.argnull[1] = false;
+	extra->fcinfo.args[0].isnull = false;
+	extra->fcinfo.args[1].isnull = false;
 
 	ssup->ssup_extra = extra;
 	ssup->comparator = comparison_shim;
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index 233d9d4094e..414a5193547 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -224,7 +224,8 @@ typedef enum ExprEvalOp
 	/* aggregation related nodes */
 	EEOP_AGG_STRICT_DESERIALIZE,
 	EEOP_AGG_DESERIALIZE,
-	EEOP_AGG_STRICT_INPUT_CHECK,
+	EEOP_AGG_STRICT_INPUT_CHECK_ARGS,
+	EEOP_AGG_STRICT_INPUT_CHECK_NULLS,
 	EEOP_AGG_INIT_TRANS,
 	EEOP_AGG_STRICT_TRANS_CHECK,
 	EEOP_AGG_PLAIN_TRANS_BYVAL,
@@ -600,7 +601,8 @@ typedef struct ExprEvalStep
 		/* for EEOP_AGG_STRICT_INPUT_CHECK */
 		struct
 		{
-			bool	   *nulls;
+			NullableDatum *args;
+			bool *nulls;
 			int			nargs;
 			int			jumpnull;
 		}			agg_strict_input_check;
diff --git a/src/include/executor/nodeAgg.h b/src/include/executor/nodeAgg.h
index 8fb8c8fe802..51aec33fd8d 100644
--- a/src/include/executor/nodeAgg.h
+++ b/src/include/executor/nodeAgg.h
@@ -158,12 +158,12 @@ typedef struct AggStatePerTransData
 	 * re-initializing the unchanging fields; which isn't much, but it seems
 	 * worth the extra space consumption.
 	 */
-	FunctionCallInfoData transfn_fcinfo;
+	FunctionCallInfo transfn_fcinfo;
 
 	/* Likewise for serialization and deserialization functions */
-	FunctionCallInfoData serialfn_fcinfo;
+	FunctionCallInfo serialfn_fcinfo;
 
-	FunctionCallInfoData deserialfn_fcinfo;
+	FunctionCallInfo deserialfn_fcinfo;
 }			AggStatePerTransData;
 
 /*
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index 101f513ba67..0928af0e954 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -84,11 +84,14 @@ typedef struct FunctionCallInfoData
 	bool		isnull;			/* function must set true if result is NULL */
 	short		nargs;			/* # arguments actually passed */
 #define FIELDNO_FUNCTIONCALLINFODATA_ARG 6
-	Datum		arg[FUNC_MAX_ARGS]; /* Arguments passed to function */
-#define FIELDNO_FUNCTIONCALLINFODATA_ARGNULL 7
-	bool		argnull[FUNC_MAX_ARGS]; /* T if arg[i] is actually NULL */
+#define FIELDNO_FUNCTIONCALLINFODATA_ARGNULL 6
+
+	NullableDatum args[FLEXIBLE_ARRAY_MEMBER];
+
 } FunctionCallInfoData;
 
+#define SizeForFunctionCallInfoData(nargs) (offsetof(FunctionCallInfoData, args) + sizeof(NullableDatum) * (nargs))
+
 /*
  * This routine fills a FmgrInfo struct, given the OID
  * of the function to be called.
@@ -141,7 +144,6 @@ extern void fmgr_symbol(Oid functionId, char **mod, char **fn);
  */
 #define FunctionCallInvoke(fcinfo)	((* (fcinfo)->flinfo->fn_addr) (fcinfo))
 
-
 /*-------------------------------------------------------------------------
  *		Support macros to ease writing fmgr-compatible functions
  *
@@ -176,7 +178,7 @@ extern void fmgr_symbol(Oid functionId, char **mod, char **fn);
  * If function is not marked "proisstrict" in pg_proc, it must check for
  * null arguments using this macro.  Do not try to GETARG a null argument!
  */
-#define PG_ARGISNULL(n)  (fcinfo->argnull[n])
+#define PG_ARGISNULL(n)  (fcinfo->args[n].isnull)
 
 /*
  * Support for fetching detoasted copies of toastable datatypes (all of
@@ -235,7 +237,7 @@ extern struct varlena *pg_detoast_datum_packed(struct varlena *datum);
 
 /* Macros for fetching arguments of standard types */
 
-#define PG_GETARG_DATUM(n)	 (fcinfo->arg[n])
+#define PG_GETARG_DATUM(n)	 (fcinfo->args[n].datum)
 #define PG_GETARG_INT32(n)	 DatumGetInt32(PG_GETARG_DATUM(n))
 #define PG_GETARG_UINT32(n)  DatumGetUInt32(PG_GETARG_DATUM(n))
 #define PG_GETARG_INT16(n)	 DatumGetInt16(PG_GETARG_DATUM(n))
@@ -514,6 +516,7 @@ extern Datum CallerFInfoFunctionCall2(PGFunction func, FmgrInfo *flinfo,
  * directly-computed parameter list.  Note that neither arguments nor result
  * are allowed to be NULL.
  */
+extern Datum FunctionCall0Coll(FmgrInfo *flinfo, Oid collation);
 extern Datum FunctionCall1Coll(FmgrInfo *flinfo, Oid collation,
 				  Datum arg1);
 extern Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation,
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index da7f52cab09..af029c42af9 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -807,7 +807,7 @@ typedef struct SetExprState
 	 * (by InitFunctionCallInfoData) if func.fn_oid is valid.  It also saves
 	 * argument values between calls, when setArgsValid is true.
 	 */
-	FunctionCallInfoData fcinfo_data;
+	FunctionCallInfo fcinfo;
 } SetExprState;
 
 /* ----------------
diff --git a/src/include/postgres.h b/src/include/postgres.h
index b596fcb513e..b3ff25dc6e2 100644
--- a/src/include/postgres.h
+++ b/src/include/postgres.h
@@ -366,6 +366,13 @@ typedef struct
 
 typedef uintptr_t Datum;
 
+typedef struct NullableDatum
+{
+	Datum datum;
+	bool isnull;
+	/* FIXME: space for flags? */
+} NullableDatum;
+
 #define SIZEOF_DATUM SIZEOF_VOID_P
 
 /*
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index 4342c02b272..d3547e7835c 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -1875,7 +1875,11 @@ Datum
 plperl_inline_handler(PG_FUNCTION_ARGS)
 {
 	InlineCodeBlock *codeblock = (InlineCodeBlock *) PG_GETARG_POINTER(0);
-	FunctionCallInfoData fake_fcinfo;
+	union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(0)];
+	} fake_fcinfodata;
+	FunctionCallInfo fake_fcinfo = &fake_fcinfodata.fcinfo;
 	FmgrInfo	flinfo;
 	plperl_proc_desc desc;
 	plperl_call_data *volatile save_call_data = current_call_data;
@@ -1897,10 +1901,10 @@ plperl_inline_handler(PG_FUNCTION_ARGS)
 	 * plperl_call_perl_func().  In particular note that this sets things up
 	 * with no arguments passed, and a result type of VOID.
 	 */
-	MemSet(&fake_fcinfo, 0, sizeof(fake_fcinfo));
+	MemSet(&fake_fcinfodata, 0, sizeof(fake_fcinfodata));
 	MemSet(&flinfo, 0, sizeof(flinfo));
 	MemSet(&desc, 0, sizeof(desc));
-	fake_fcinfo.flinfo = &flinfo;
+	fake_fcinfo->flinfo = &flinfo;
 	flinfo.fn_oid = InvalidOid;
 	flinfo.fn_mcxt = CurrentMemoryContext;
 
@@ -1918,7 +1922,7 @@ plperl_inline_handler(PG_FUNCTION_ARGS)
 	desc.nargs = 0;
 	desc.reference = NULL;
 
-	this_call_data.fcinfo = &fake_fcinfo;
+	this_call_data.fcinfo = fake_fcinfo;
 	this_call_data.prodesc = &desc;
 	/* we do not bother with refcounting the fake prodesc */
 
@@ -1938,7 +1942,7 @@ plperl_inline_handler(PG_FUNCTION_ARGS)
 		if (!desc.reference)	/* can this happen? */
 			elog(ERROR, "could not create internal procedure for anonymous code block");
 
-		perlret = plperl_call_perl_func(&desc, &fake_fcinfo);
+		perlret = plperl_call_perl_func(&desc, fake_fcinfo);
 
 		SvREFCNT_dec_current(perlret);
 
@@ -2192,11 +2196,11 @@ plperl_call_perl_func(plperl_proc_desc *desc, FunctionCallInfo fcinfo)
 
 	for (i = 0; i < desc->nargs; i++)
 	{
-		if (fcinfo->argnull[i])
+		if (fcinfo->args[i].isnull)
 			PUSHs(&PL_sv_undef);
 		else if (desc->arg_is_rowtype[i])
 		{
-			SV		   *sv = plperl_hash_from_datum(fcinfo->arg[i]);
+			SV		   *sv = plperl_hash_from_datum(fcinfo->args[i].datum);
 
 			PUSHs(sv_2mortal(sv));
 		}
@@ -2206,15 +2210,15 @@ plperl_call_perl_func(plperl_proc_desc *desc, FunctionCallInfo fcinfo)
 			Oid			funcid;
 
 			if (OidIsValid(desc->arg_arraytype[i]))
-				sv = plperl_ref_from_pg_array(fcinfo->arg[i], desc->arg_arraytype[i]);
+				sv = plperl_ref_from_pg_array(fcinfo->args[i].datum, desc->arg_arraytype[i]);
 			else if ((funcid = get_transform_fromsql(argtypes[i], current_call_data->prodesc->lang_oid, current_call_data->prodesc->trftypes)))
-				sv = (SV *) DatumGetPointer(OidFunctionCall1(funcid, fcinfo->arg[i]));
+				sv = (SV *) DatumGetPointer(OidFunctionCall1(funcid, fcinfo->args[i].datum));
 			else
 			{
 				char	   *tmp;
 
 				tmp = OutputFunctionCall(&(desc->arg_out_func[i]),
-										 fcinfo->arg[i]);
+										 fcinfo->args[i].datum);
 				sv = cstr2sv(tmp);
 				pfree(tmp);
 			}
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 3f2c3fbf9c2..2749f2a51f5 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -488,8 +488,8 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo,
 					PLpgSQL_var *var = (PLpgSQL_var *) estate.datums[n];
 
 					assign_simple_var(&estate, var,
-									  fcinfo->arg[i],
-									  fcinfo->argnull[i],
+									  fcinfo->args[i].datum,
+									  fcinfo->args[i].isnull,
 									  false);
 
 					/*
@@ -540,12 +540,12 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo,
 				{
 					PLpgSQL_rec *rec = (PLpgSQL_rec *) estate.datums[n];
 
-					if (!fcinfo->argnull[i])
+					if (!fcinfo->args[i].isnull)
 					{
 						/* Assign row value from composite datum */
 						exec_move_row_from_datum(&estate,
 												 (PLpgSQL_variable *) rec,
-												 fcinfo->arg[i]);
+												 fcinfo->args[i].datum);
 					}
 					else
 					{
diff --git a/src/pl/plpython/plpy_exec.c b/src/pl/plpython/plpy_exec.c
index 47ed95dcc60..d1a8c395ffd 100644
--- a/src/pl/plpython/plpy_exec.c
+++ b/src/pl/plpython/plpy_exec.c
@@ -436,10 +436,10 @@ PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure *proc)
 		{
 			PLyDatumToOb *arginfo = &proc->args[i];
 
-			if (fcinfo->argnull[i])
+			if (fcinfo->args[i].isnull)
 				arg = NULL;
 			else
-				arg = PLy_input_convert(arginfo, fcinfo->arg[i]);
+				arg = PLy_input_convert(arginfo, fcinfo->args[i].datum);
 
 			if (arg == NULL)
 			{
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index 12f7b137809..2865598d099 100644
--- a/src/pl/tcl/pltcl.c
+++ b/src/pl/tcl/pltcl.c
@@ -601,7 +601,12 @@ call_pltcl_start_proc(Oid prolang, bool pltrusted)
 	Form_pg_proc procStruct;
 	AclResult	aclresult;
 	FmgrInfo	finfo;
-	FunctionCallInfoData fcinfo;
+	union {
+		FunctionCallInfoData fcinfo;
+		char *fcinfo_data[SizeForFunctionCallInfoData(3)];
+	} fcinfodata;
+	FunctionCallInfo fcinfo = &fcinfodata.fcinfo;
+
 	PgStat_FunctionCallUsage fcusage;
 
 	/* select appropriate GUC */
@@ -662,11 +667,11 @@ call_pltcl_start_proc(Oid prolang, bool pltrusted)
 	 */
 	InvokeFunctionExecuteHook(procOid);
 	fmgr_info(procOid, &finfo);
-	InitFunctionCallInfoData(fcinfo, &finfo,
+	InitFunctionCallInfoData(*fcinfo, &finfo,
 							 0,
 							 InvalidOid, NULL, NULL);
-	pgstat_init_function_usage(&fcinfo, &fcusage);
-	(void) FunctionCallInvoke(&fcinfo);
+	pgstat_init_function_usage(fcinfo, &fcusage);
+	(void) FunctionCallInvoke(fcinfo);
 	pgstat_end_function_usage(&fcusage, true);
 
 	/* Pop the error context stack */
@@ -873,7 +878,7 @@ pltcl_func_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
 				/**************************************************
 				 * For tuple values, add a list for 'array set ...'
 				 **************************************************/
-				if (fcinfo->argnull[i])
+				if (fcinfo->args[i].isnull)
 					Tcl_ListObjAppendElement(NULL, tcl_cmd, Tcl_NewObj());
 				else
 				{
@@ -884,7 +889,7 @@ pltcl_func_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
 					HeapTupleData tmptup;
 					Tcl_Obj    *list_tmp;
 
-					td = DatumGetHeapTupleHeader(fcinfo->arg[i]);
+					td = DatumGetHeapTupleHeader(fcinfo->args[i].datum);
 					/* Extract rowtype info and find a tupdesc */
 					tupType = HeapTupleHeaderGetTypeId(td);
 					tupTypmod = HeapTupleHeaderGetTypMod(td);
@@ -905,14 +910,14 @@ pltcl_func_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
 				 * Single values are added as string element
 				 * of their external representation
 				 **************************************************/
-				if (fcinfo->argnull[i])
+				if (fcinfo->args[i].isnull)
 					Tcl_ListObjAppendElement(NULL, tcl_cmd, Tcl_NewObj());
 				else
 				{
 					char	   *tmp;
 
 					tmp = OutputFunctionCall(&prodesc->arg_out_func[i],
-											 fcinfo->arg[i]);
+											 fcinfo->args[i].datum);
 					UTF_BEGIN;
 					Tcl_ListObjAppendElement(NULL, tcl_cmd,
 											 Tcl_NewStringObj(UTF_E2U(tmp), -1));
-- 
2.17.0.rc1.dirty

#2Noname
serge@rielau.com
In reply to: Andres Freund (#1)
RE: Variable-length FunctionCallInfoData

Big +1 on this one.

Here is what we did. It's very crude, but minimized the amount of pain:

It helps that the C compiler treats arrays and pointers the same.

I can dig for the complete patch if you want...

Cheers
Serge
/*
* This struct is the data actually passed to an fmgr-called function.
* There are three flavors:
* FunctionCallInfoData:
* Used when the number of arguments is both known and fixed small
* This structure is used for direct function calls involving
* builtin functions
* This structure must be initialized with: InitFunctionCallInfoData()
* FunctionCallInfoDataVariable:
* Used when the number of arguments is unknown and possibly large
* This structure must be allocated with allocFCInfoVar() and initialized with
* InitFunctionCallInfoData().
* FunctionCallInfoDataLarge:
* Used when the number of arguments is unknown, possibly large and
* the struct is embedded somewhere where a variable length is not acceptable
* This structure must be initialized with: InitFunctionCallInfoData()
*
* All structures have the same header and the arg/argnull fields shoule not be
* accessed directly but via the below accessor macros.
*/
typedef struct FunctionCallInfoData
{
FmgrInfo *flinfo; /* ptr to lookup info used for this call */
fmNodePtr context; /* pass info about context of call */
fmNodePtr resultinfo; /* pass or return extra info about result */
Oid fncollation; /* collation for function to use */
bool isnull; /* function must set true if result is NULL */
bool isFixed; /* Must be true */
short nargs; /* # arguments actually passed */
Datum *arg; /* pointer to function arg array */
bool *argnull; /* pointer to null indicator array */
Datum __arg[FUNC_MAX_ARGS_FIX]; /* Arguments passed to function */
bool __argnull[FUNC_MAX_ARGS_FIX]; /* T if arg[i] is actually NULL */
} FunctionCallInfoData;
typedef struct FunctionCallInfoDataVariable
{
FmgrInfo *flinfo; /* ptr to lookup info used for this call */
fmNodePtr context; /* pass info about context of call */
fmNodePtr resultinfo; /* pass or return extra info about result */
Oid fncollation; /* collation for function to use */
bool isnull; /* function must set true if result is NULL */
bool isFixed; /* Must be false */
short nargs; /* # arguments actually passed */
Datum *arg; /* pointer to function arg array */
bool *argnull; /* pointer to null indicator array */
} FunctionCallInfoDataVariable;

typedef struct FunctionCallInfoDataLarge
{
FmgrInfo *flinfo; /* ptr to lookup info used for this call */
fmNodePtr context; /* pass info about context of call */
fmNodePtr resultinfo; /* pass or return extra info about result */
Oid fncollation; /* collation for function to use */
bool isnull; /* function must set true if result is NULL */
bool isFixed; /* Must be false */
short nargs; /* # arguments actually passed */
Datum *arg; /* pointer to function arg array */
bool *argnull; /* pointer to null indicator array */
Datum __arg[FUNC_MAX_ARGS]; /* Arguments passed to function */
bool __argnull[FUNC_MAX_ARGS]; /* T if arg[i] is actually NULL */
} FunctionCallInfoDataLarge;

#3Andres Freund
andres@anarazel.de
In reply to: Noname (#2)
Re: Variable-length FunctionCallInfoData

Hi,

On 2018-06-05 10:40:22 -0700, serge@rielau.com wrote:

Big +1 on this one.

Cool.

Here is what we did. It's very crude, but minimized the amount of pain:

I think I'd rather go for my approach in core though. Needlessly
carrying around a bunch of pointers, and adding the necessary
indirections on accesses, and more allocations don't seem to buy us that
much. Nor do I really like the hackyness...

Greetings,

Andres Freund

#4Serge Rielau
serge@rielau.com
In reply to: Andres Freund (#3)
Re: Variable-length FunctionCallInfoData

Yeah, our approach had to mergeable. You can rip that bandaid.

#5Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: Andres Freund (#1)
Re: Variable-length FunctionCallInfoData

On 6/5/18 13:29, Andres Freund wrote:

Besides the change here, I think we should also go much further with the
conversion to NullableDatum. There's two main areas of change: I want
to move the execExpr.c related code so steps return data into
NullableDatums - that removes a good chunk of pointer dereferences and
allocations. Secondly I think we should move TupleTableSlot to this
format - the issue with nulls / datums being on separate cachelines is
noticeable in profiles, but more importantly the code looks more
consistent with it.

There are also a variety of utility functions that return a Datum and
have an extra bool *isnull argument. What are your thoughts on using
NullableDatum more in those cases? Is returning structs a problem for
low-level performance?

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#6Andres Freund
andres@anarazel.de
In reply to: Peter Eisentraut (#5)
Re: Variable-length FunctionCallInfoData

Hi,

On 2018-06-05 15:08:33 -0400, Peter Eisentraut wrote:

On 6/5/18 13:29, Andres Freund wrote:

Besides the change here, I think we should also go much further with the
conversion to NullableDatum. There's two main areas of change: I want
to move the execExpr.c related code so steps return data into
NullableDatums - that removes a good chunk of pointer dereferences and
allocations. Secondly I think we should move TupleTableSlot to this
format - the issue with nulls / datums being on separate cachelines is
noticeable in profiles, but more importantly the code looks more
consistent with it.

There are also a variety of utility functions that return a Datum and
have an extra bool *isnull argument.

I was thinking of, for now at least, not touching those.

What are your thoughts on using NullableDatum more in those cases? Is
returning structs a problem for low-level performance?

It vastly depends on architecture, compiler and ABI. It'd be a lot
better if we were using C++ (because the caller reserves the space for
such things, and then it's basically free, see return value optimization
/ copy elision). Thus I'm not wild in immediately changing all of them.
I think there aren't that many that aren't performance critical however,
so I guess an argument could be made to change this regardless.

Greetings,

Andres Freund

#7Andres Freund
andres@anarazel.de
In reply to: Andres Freund (#1)
2 attachment(s)
Re: Variable-length FunctionCallInfoData

Hi,

Here's an updated version of the patch. Besides a rebase the biggest
change is that I've wrapped:

On 2018-06-05 10:29:52 -0700, Andres Freund wrote:

There's some added uglyness, which I hope we can polish a bit
further. Right now we allocate a good number of FunctionCallInfoData
struct on the stack - which doesn't quite work afterwards anymore. So
the stack allocations, for the majoroity cases where the argument number
is known, currently looks like:

union {
FunctionCallInfoData fcinfo;
char *fcinfo_data[SizeForFunctionCallInfoData(0)];
} fcinfodata;
FunctionCallInfo fcinfo = &fcinfodata.fcinfo;

that's not pretty, but also not that bad.

into a macro STACK_FCINFO_FOR_ARGS(varname, numargs). That makes the
code look much nicer.

I think we should go for this. If there's some agreement on that I'll
perform a bit more polishing.

I think it'd probably good to add accessors for value/nullness in
arguments that hide the difference between <v12 and v12, for the sake of
extension authors. Would probably mostly make sense if we backpatched
those for compatibility.

Also attached is a second patch that avoids all the duplication in
fmgr.[ch]. While the savings are nice, I'm a bit doubtful that the
amount of magic here is reasonable. Any opinions?

Greetings,

Andres Freund

Attachments:

v2-0001-Variable-length-FunctionCallInfoData.patchtext/x-diff; charset=us-asciiDownload
From 2e0cd41ac8f2daa9f818fa60fe1b2d136c1d9359 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Tue, 5 Jun 2018 10:24:31 -0700
Subject: [PATCH v2 1/2] Variable length FunctionCallInfoData.

Author: Andres Freund
Reviewed-By:
Discussion: https://postgr.es/m/
Backpatch:
---
 src/backend/commands/event_trigger.c  |   8 +-
 src/backend/commands/functioncmds.c   |  14 +-
 src/backend/commands/tablecmds.c      |   8 +-
 src/backend/commands/trigger.c        |  12 +-
 src/backend/executor/execExpr.c       |  81 +--
 src/backend/executor/execExprInterp.c | 127 ++--
 src/backend/executor/execSRF.c        |  39 +-
 src/backend/executor/functions.c      |   4 +-
 src/backend/executor/nodeAgg.c        |  74 +--
 src/backend/executor/nodeWindowAgg.c  |  65 +-
 src/backend/jit/llvm/llvmjit_expr.c   |  20 +-
 src/backend/tcop/fastpath.c           |  43 +-
 src/backend/utils/adt/arrayfuncs.c    | 123 ++--
 src/backend/utils/adt/int.c           |  20 +-
 src/backend/utils/adt/oid.c           |  20 +-
 src/backend/utils/adt/rowtypes.c      |  32 +-
 src/backend/utils/fmgr/fmgr.c         | 848 ++++++++++----------------
 src/backend/utils/sort/sortsupport.c  |  13 +-
 src/include/executor/execExpr.h       |   6 +-
 src/include/executor/nodeAgg.h        |   6 +-
 src/include/fmgr.h                    |  30 +-
 src/include/nodes/execnodes.h         |   2 +-
 src/include/postgres.h                |   7 +
 src/pl/plperl/plperl.c                |  20 +-
 src/pl/plpgsql/src/pl_exec.c          |   8 +-
 src/pl/plpython/plpy_exec.c           |   4 +-
 src/pl/tcl/pltcl.c                    |  17 +-
 27 files changed, 764 insertions(+), 887 deletions(-)

diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 9a702e4097e..6564cd7fe11 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -1050,7 +1050,7 @@ EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata)
 	{
 		Oid			fnoid = lfirst_oid(lc);
 		FmgrInfo	flinfo;
-		FunctionCallInfoData fcinfo;
+		STACK_FCINFO_FOR_ARGS(fcinfo, 0);
 		PgStat_FunctionCallUsage fcusage;
 
 		elog(DEBUG1, "EventTriggerInvoke %u", fnoid);
@@ -1070,10 +1070,10 @@ EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata)
 		fmgr_info(fnoid, &flinfo);
 
 		/* Call the function, passing no arguments but setting a context. */
-		InitFunctionCallInfoData(fcinfo, &flinfo, 0,
+		InitFunctionCallInfoData(*fcinfo, &flinfo, 0,
 								 InvalidOid, (Node *) trigdata, NULL);
-		pgstat_init_function_usage(&fcinfo, &fcusage);
-		FunctionCallInvoke(&fcinfo);
+		pgstat_init_function_usage(fcinfo, &fcusage);
+		FunctionCallInvoke(fcinfo);
 		pgstat_end_function_usage(&fcusage, true);
 
 		/* Reclaim memory. */
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 3fd73a69f04..1eaf8011a96 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -2215,7 +2215,7 @@ ExecuteCallStmt(CallStmt *stmt, ParamListInfo params, bool atomic, DestReceiver
 	int			i;
 	AclResult	aclresult;
 	FmgrInfo	flinfo;
-	FunctionCallInfoData fcinfo;
+	STACK_FCINFO_FOR_ARGS(fcinfo, 3);
 	CallContext *callcontext;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2277,7 +2277,7 @@ ExecuteCallStmt(CallStmt *stmt, ParamListInfo params, bool atomic, DestReceiver
 	InvokeFunctionExecuteHook(fexpr->funcid);
 	fmgr_info(fexpr->funcid, &flinfo);
 	fmgr_info_set_expr((Node *) fexpr, &flinfo);
-	InitFunctionCallInfoData(fcinfo, &flinfo, nargs, fexpr->inputcollid, (Node *) callcontext, NULL);
+	InitFunctionCallInfoData(*fcinfo, &flinfo, nargs, fexpr->inputcollid, (Node *) callcontext, NULL);
 
 	/*
 	 * Evaluate procedure arguments inside a suitable execution context.  Note
@@ -2298,14 +2298,14 @@ ExecuteCallStmt(CallStmt *stmt, ParamListInfo params, bool atomic, DestReceiver
 
 		val = ExecEvalExprSwitchContext(exprstate, econtext, &isnull);
 
-		fcinfo.arg[i] = val;
-		fcinfo.argnull[i] = isnull;
+		fcinfo->args[i].datum = val;
+		fcinfo->args[i].isnull = isnull;
 
 		i++;
 	}
 
-	pgstat_init_function_usage(&fcinfo, &fcusage);
-	retval = FunctionCallInvoke(&fcinfo);
+	pgstat_init_function_usage(fcinfo, &fcusage);
+	retval = FunctionCallInvoke(fcinfo);
 	pgstat_end_function_usage(&fcusage, true);
 
 	if (fexpr->funcresulttype == VOIDOID)
@@ -2326,7 +2326,7 @@ ExecuteCallStmt(CallStmt *stmt, ParamListInfo params, bool atomic, DestReceiver
 		TupOutputState *tstate;
 		TupleTableSlot *slot;
 
-		if (fcinfo.isnull)
+		if (fcinfo->isnull)
 			elog(ERROR, "procedure returned null record");
 
 		td = DatumGetHeapTupleHeader(retval);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index e10d3dbf3dd..9a1a3895522 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8600,7 +8600,7 @@ validateForeignKeyConstraint(char *conname,
 
 	while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
 	{
-		FunctionCallInfoData fcinfo;
+		STACK_FCINFO_FOR_ARGS(fcinfo, 0);
 		TriggerData trigdata;
 
 		/*
@@ -8608,7 +8608,7 @@ validateForeignKeyConstraint(char *conname,
 		 *
 		 * No parameters are passed, but we do set a context
 		 */
-		MemSet(&fcinfo, 0, sizeof(fcinfo));
+		MemSet(&fcinfodata, 0, sizeof(fcinfodata));
 
 		/*
 		 * We assume RI_FKey_check_ins won't look at flinfo...
@@ -8622,9 +8622,9 @@ validateForeignKeyConstraint(char *conname,
 		trigdata.tg_trigtuplebuf = scan->rs_cbuf;
 		trigdata.tg_newtuplebuf = InvalidBuffer;
 
-		fcinfo.context = (Node *) &trigdata;
+		fcinfo->context = (Node *) &trigdata;
 
-		RI_FKey_check_ins(&fcinfo);
+		RI_FKey_check_ins(fcinfo);
 	}
 
 	heap_endscan(scan);
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 240e85e3910..19fcd543caf 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2358,7 +2358,7 @@ ExecCallTriggerFunc(TriggerData *trigdata,
 					Instrumentation *instr,
 					MemoryContext per_tuple_context)
 {
-	FunctionCallInfoData fcinfo;
+	STACK_FCINFO_FOR_ARGS(fcinfo, 0);
 	PgStat_FunctionCallUsage fcusage;
 	Datum		result;
 	MemoryContext oldContext;
@@ -2403,15 +2403,15 @@ ExecCallTriggerFunc(TriggerData *trigdata,
 	/*
 	 * Call the function, passing no arguments but setting a context.
 	 */
-	InitFunctionCallInfoData(fcinfo, finfo, 0,
+	InitFunctionCallInfoData(*fcinfo, finfo, 0,
 							 InvalidOid, (Node *) trigdata, NULL);
 
-	pgstat_init_function_usage(&fcinfo, &fcusage);
+	pgstat_init_function_usage(fcinfo, &fcusage);
 
 	MyTriggerDepth++;
 	PG_TRY();
 	{
-		result = FunctionCallInvoke(&fcinfo);
+		result = FunctionCallInvoke(fcinfo);
 	}
 	PG_CATCH();
 	{
@@ -2429,11 +2429,11 @@ ExecCallTriggerFunc(TriggerData *trigdata,
 	 * Trigger protocol allows function to return a null pointer, but NOT to
 	 * set the isnull result flag.
 	 */
-	if (fcinfo.isnull)
+	if (fcinfo->isnull)
 		ereport(ERROR,
 				(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
 				 errmsg("trigger function %u returned null value",
-						fcinfo.flinfo->fn_oid)));
+						fcinfo->flinfo->fn_oid)));
 
 	/*
 	 * If doing EXPLAIN ANALYZE, stop charging time to this trigger, and count
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index e284fd71d75..a39c3eabf35 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -965,7 +965,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 
 				/* Set up the primary fmgr lookup information */
 				finfo = palloc0(sizeof(FmgrInfo));
-				fcinfo = palloc0(sizeof(FunctionCallInfoData));
+				fcinfo = palloc0(SizeForFunctionCallInfoData(2));
 				fmgr_info(opexpr->opfuncid, finfo);
 				fmgr_info_set_expr((Node *) node, finfo);
 				InitFunctionCallInfoData(*fcinfo, finfo, 2,
@@ -973,7 +973,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 
 				/* Evaluate scalar directly into left function argument */
 				ExecInitExprRec(scalararg, state,
-								&fcinfo->arg[0], &fcinfo->argnull[0]);
+								&fcinfo->args[0].datum, &fcinfo->args[0].isnull);
 
 				/*
 				 * Evaluate array argument into our return value.  There's no
@@ -1262,7 +1262,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 
 				/* lookup the source type's output function */
 				scratch.d.iocoerce.finfo_out = palloc0(sizeof(FmgrInfo));
-				scratch.d.iocoerce.fcinfo_data_out = palloc0(sizeof(FunctionCallInfoData));
+				scratch.d.iocoerce.fcinfo_data_out = palloc0(SizeForFunctionCallInfoData(1));
 
 				getTypeOutputInfo(exprType((Node *) iocoerce->arg),
 								  &iofunc, &typisvarlena);
@@ -1274,7 +1274,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 
 				/* lookup the result type's input function */
 				scratch.d.iocoerce.finfo_in = palloc0(sizeof(FmgrInfo));
-				scratch.d.iocoerce.fcinfo_data_in = palloc0(sizeof(FunctionCallInfoData));
+				scratch.d.iocoerce.fcinfo_data_in = palloc0(SizeForFunctionCallInfoData(3));
 
 				getTypeInputInfo(iocoerce->resulttype,
 								 &iofunc, &typioparam);
@@ -1289,10 +1289,10 @@ ExecInitExprRec(Expr *node, ExprState *state,
 				 * function, since they're constants.
 				 */
 				fcinfo_in = scratch.d.iocoerce.fcinfo_data_in;
-				fcinfo_in->arg[1] = ObjectIdGetDatum(typioparam);
-				fcinfo_in->argnull[1] = false;
-				fcinfo_in->arg[2] = Int32GetDatum(-1);
-				fcinfo_in->argnull[2] = false;
+				fcinfo_in->args[1].datum = ObjectIdGetDatum(typioparam);
+				fcinfo_in->args[1].isnull = false;
+				fcinfo_in->args[2].datum = Int32GetDatum(-1);
+				fcinfo_in->args[2].isnull = false;
 
 				ExprEvalPushStep(state, &scratch);
 				break;
@@ -1733,7 +1733,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 
 					/* Set up the primary fmgr lookup information */
 					finfo = palloc0(sizeof(FmgrInfo));
-					fcinfo = palloc0(sizeof(FunctionCallInfoData));
+					fcinfo = palloc0(SizeForFunctionCallInfoData(2));
 					fmgr_info(proc, finfo);
 					fmgr_info_set_expr((Node *) node, finfo);
 					InitFunctionCallInfoData(*fcinfo, finfo, 2,
@@ -1748,9 +1748,9 @@ ExecInitExprRec(Expr *node, ExprState *state,
 
 					/* evaluate left and right args directly into fcinfo */
 					ExecInitExprRec(left_expr, state,
-									&fcinfo->arg[0], &fcinfo->argnull[0]);
+									&fcinfo->args[0].datum, &fcinfo->args[0].isnull);
 					ExecInitExprRec(right_expr, state,
-									&fcinfo->arg[1], &fcinfo->argnull[1]);
+									&fcinfo->args[1].datum, &fcinfo->args[1].isnull);
 
 					scratch.opcode = EEOP_ROWCOMPARE_STEP;
 					scratch.d.rowcompare_step.finfo = finfo;
@@ -1876,7 +1876,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 
 				/* Perform function lookup */
 				finfo = palloc0(sizeof(FmgrInfo));
-				fcinfo = palloc0(sizeof(FunctionCallInfoData));
+				fcinfo = palloc0(SizeForFunctionCallInfoData(2));
 				fmgr_info(typentry->cmp_proc, finfo);
 				fmgr_info_set_expr((Node *) node, finfo);
 				InitFunctionCallInfoData(*fcinfo, finfo, 2,
@@ -2185,7 +2185,7 @@ ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid,
 
 	/* Allocate function lookup data and parameter workspace for this call */
 	scratch->d.func.finfo = palloc0(sizeof(FmgrInfo));
-	scratch->d.func.fcinfo_data = palloc0(sizeof(FunctionCallInfoData));
+	scratch->d.func.fcinfo_data = palloc0(SizeForFunctionCallInfoData(nargs));
 	flinfo = scratch->d.func.finfo;
 	fcinfo = scratch->d.func.fcinfo_data;
 
@@ -2224,13 +2224,14 @@ ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid,
 			 */
 			Const	   *con = (Const *) arg;
 
-			fcinfo->arg[argno] = con->constvalue;
-			fcinfo->argnull[argno] = con->constisnull;
+			fcinfo->args[argno].datum = con->constvalue;
+			fcinfo->args[argno].isnull = con->constisnull;
 		}
 		else
 		{
 			ExecInitExprRec(arg, state,
-							&fcinfo->arg[argno], &fcinfo->argnull[argno]);
+							&fcinfo->args[argno].datum,
+							&fcinfo->args[argno].isnull);
 		}
 		argno++;
 	}
@@ -2863,10 +2864,11 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
 		int			numInputs = pertrans->numInputs;
 		int			argno;
 		int			setno;
-		FunctionCallInfo trans_fcinfo = &pertrans->transfn_fcinfo;
+		FunctionCallInfo trans_fcinfo = pertrans->transfn_fcinfo;
 		ListCell   *arg;
 		ListCell   *bail;
 		List	   *adjust_bailout = NIL;
+		NullableDatum   *strictargs = NULL;
 		bool	   *strictnulls = NULL;
 
 		/*
@@ -2904,7 +2906,7 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
 			Assert(pertrans->numSortCols == 0);
 			Assert(list_length(pertrans->aggref->args) == 1);
 
-			strictnulls = trans_fcinfo->argnull + 1;
+			strictargs = trans_fcinfo->args + 1;
 			source_tle = (TargetEntry *) linitial(pertrans->aggref->args);
 
 			/*
@@ -2918,21 +2920,21 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
 				 * value
 				 */
 				ExecInitExprRec(source_tle->expr, state,
-								&trans_fcinfo->arg[argno + 1],
-								&trans_fcinfo->argnull[argno + 1]);
+								&trans_fcinfo->args[argno + 1].datum,
+								&trans_fcinfo->args[argno + 1].isnull);
 			}
 			else
 			{
-				FunctionCallInfo ds_fcinfo = &pertrans->deserialfn_fcinfo;
+				FunctionCallInfo ds_fcinfo = pertrans->deserialfn_fcinfo;
 
 				/* evaluate argument */
 				ExecInitExprRec(source_tle->expr, state,
-								&ds_fcinfo->arg[0],
-								&ds_fcinfo->argnull[0]);
+								&ds_fcinfo->args[0].datum,
+								&ds_fcinfo->args[0].isnull);
 
 				/* Dummy second argument for type-safety reasons */
-				ds_fcinfo->arg[1] = PointerGetDatum(NULL);
-				ds_fcinfo->argnull[1] = false;
+				ds_fcinfo->args[1].datum = PointerGetDatum(NULL);
+				ds_fcinfo->args[1].isnull = false;
 
 				/*
 				 * Don't call a strict deserialization function with NULL
@@ -2946,8 +2948,8 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
 				scratch.d.agg_deserialize.aggstate = aggstate;
 				scratch.d.agg_deserialize.fcinfo_data = ds_fcinfo;
 				scratch.d.agg_deserialize.jumpnull = -1;	/* adjust later */
-				scratch.resvalue = &trans_fcinfo->arg[argno + 1];
-				scratch.resnull = &trans_fcinfo->argnull[argno + 1];
+				scratch.resvalue = &trans_fcinfo->args[argno + 1].datum;
+				scratch.resnull = &trans_fcinfo->args[argno + 1].isnull;
 
 				ExprEvalPushStep(state, &scratch);
 				adjust_bailout = lappend_int(adjust_bailout,
@@ -2964,7 +2966,7 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
 			/*
 			 * Normal transition function without ORDER BY / DISTINCT.
 			 */
-			strictnulls = trans_fcinfo->argnull + 1;
+			strictargs = trans_fcinfo->args + 1;
 
 			foreach(arg, pertrans->aggref->args)
 			{
@@ -2975,8 +2977,8 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
 				 * value
 				 */
 				ExecInitExprRec(source_tle->expr, state,
-								&trans_fcinfo->arg[argno + 1],
-								&trans_fcinfo->argnull[argno + 1]);
+								&trans_fcinfo->args[argno + 1].datum,
+								&trans_fcinfo->args[argno + 1].isnull);
 				argno++;
 			}
 		}
@@ -3024,8 +3026,12 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
 		 */
 		if (trans_fcinfo->flinfo->fn_strict && numInputs > 0)
 		{
-			scratch.opcode = EEOP_AGG_STRICT_INPUT_CHECK;
+			if (strictnulls)
+				scratch.opcode = EEOP_AGG_STRICT_INPUT_CHECK_NULLS;
+			else
+				scratch.opcode = EEOP_AGG_STRICT_INPUT_CHECK_ARGS;
 			scratch.d.agg_strict_input_check.nulls = strictnulls;
+			scratch.d.agg_strict_input_check.args = strictargs;
 			scratch.d.agg_strict_input_check.jumpnull = -1; /* adjust later */
 			scratch.d.agg_strict_input_check.nargs = numInputs;
 			ExprEvalPushStep(state, &scratch);
@@ -3079,7 +3085,8 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
 				Assert(as->d.jump.jumpdone == -1);
 				as->d.jump.jumpdone = state->steps_len;
 			}
-			else if (as->opcode == EEOP_AGG_STRICT_INPUT_CHECK)
+			else if (as->opcode == EEOP_AGG_STRICT_INPUT_CHECK_ARGS ||
+					 as->opcode == EEOP_AGG_STRICT_INPUT_CHECK_NULLS)
 			{
 				Assert(as->d.agg_strict_input_check.jumpnull == -1);
 				as->d.agg_strict_input_check.jumpnull = state->steps_len;
@@ -3284,7 +3291,7 @@ ExecBuildGroupingEqual(TupleDesc ldesc, TupleDesc rdesc,
 
 		/* Set up the primary fmgr lookup information */
 		finfo = palloc0(sizeof(FmgrInfo));
-		fcinfo = palloc0(sizeof(FunctionCallInfoData));
+		fcinfo = palloc0(SizeForFunctionCallInfoData(2));
 		fmgr_info(foid, finfo);
 		fmgr_info_set_expr(NULL, finfo);
 		InitFunctionCallInfoData(*fcinfo, finfo, 2,
@@ -3294,16 +3301,16 @@ ExecBuildGroupingEqual(TupleDesc ldesc, TupleDesc rdesc,
 		scratch.opcode = EEOP_INNER_VAR;
 		scratch.d.var.attnum = attno - 1;
 		scratch.d.var.vartype = latt->atttypid;
-		scratch.resvalue = &fcinfo->arg[0];
-		scratch.resnull = &fcinfo->argnull[0];
+		scratch.resvalue = &fcinfo->args[0].datum;
+		scratch.resnull = &fcinfo->args[0].isnull;
 		ExprEvalPushStep(state, &scratch);
 
 		/* right arg */
 		scratch.opcode = EEOP_OUTER_VAR;
 		scratch.d.var.attnum = attno - 1;
 		scratch.d.var.vartype = ratt->atttypid;
-		scratch.resvalue = &fcinfo->arg[1];
-		scratch.resnull = &fcinfo->argnull[1];
+		scratch.resvalue = &fcinfo->args[1].datum;
+		scratch.resnull = &fcinfo->args[1].isnull;
 		ExprEvalPushStep(state, &scratch);
 
 		/* evaluate distinctness */
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 1b0946b02f2..3716585901e 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -386,7 +386,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 		&&CASE_EEOP_ALTERNATIVE_SUBPLAN,
 		&&CASE_EEOP_AGG_STRICT_DESERIALIZE,
 		&&CASE_EEOP_AGG_DESERIALIZE,
-		&&CASE_EEOP_AGG_STRICT_INPUT_CHECK,
+		&&CASE_EEOP_AGG_STRICT_INPUT_CHECK_ARGS,
+		&&CASE_EEOP_AGG_STRICT_INPUT_CHECK_NULLS,
 		&&CASE_EEOP_AGG_INIT_TRANS,
 		&&CASE_EEOP_AGG_STRICT_TRANS_CHECK,
 		&&CASE_EEOP_AGG_PLAIN_TRANS_BYVAL,
@@ -661,14 +662,14 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 		EEO_CASE(EEOP_FUNCEXPR_STRICT)
 		{
 			FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
-			bool	   *argnull = fcinfo->argnull;
+			NullableDatum *args = fcinfo->args;
 			int			argno;
 			Datum		d;
 
 			/* strict function, so check for NULL args */
 			for (argno = 0; argno < op->d.func.nargs; argno++)
 			{
-				if (argnull[argno])
+				if (args[argno].isnull)
 				{
 					*op->resnull = true;
 					goto strictfail;
@@ -1084,8 +1085,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 				FunctionCallInfo fcinfo_out;
 
 				fcinfo_out = op->d.iocoerce.fcinfo_data_out;
-				fcinfo_out->arg[0] = *op->resvalue;
-				fcinfo_out->argnull[0] = false;
+				fcinfo_out->args[0].datum = *op->resvalue;
+				fcinfo_out->args[0].isnull = false;
 
 				fcinfo_out->isnull = false;
 				str = DatumGetCString(FunctionCallInvoke(fcinfo_out));
@@ -1101,8 +1102,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 				Datum		d;
 
 				fcinfo_in = op->d.iocoerce.fcinfo_data_in;
-				fcinfo_in->arg[0] = PointerGetDatum(str);
-				fcinfo_in->argnull[0] = *op->resnull;
+				fcinfo_in->args[0].datum = PointerGetDatum(str);
+				fcinfo_in->args[0].isnull = *op->resnull;
 				/* second and third arguments are already set up */
 
 				fcinfo_in->isnull = false;
@@ -1139,13 +1140,13 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 			FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
 
 			/* check function arguments for NULLness */
-			if (fcinfo->argnull[0] && fcinfo->argnull[1])
+			if (fcinfo->args[0].isnull && fcinfo->args[1].isnull)
 			{
 				/* Both NULL? Then is not distinct... */
 				*op->resvalue = BoolGetDatum(false);
 				*op->resnull = false;
 			}
-			else if (fcinfo->argnull[0] || fcinfo->argnull[1])
+			else if (fcinfo->args[0].isnull || fcinfo->args[1].isnull)
 			{
 				/* Only one is NULL? Then is distinct... */
 				*op->resvalue = BoolGetDatum(true);
@@ -1171,12 +1172,12 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 		{
 			FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
 
-			if (fcinfo->argnull[0] && fcinfo->argnull[1])
+			if (fcinfo->args[0].isnull && fcinfo->args[1].isnull)
 			{
 				*op->resvalue = BoolGetDatum(true);
 				*op->resnull = false;
 			}
-			else if (fcinfo->argnull[0] || fcinfo->argnull[1])
+			else if (fcinfo->args[0].isnull || fcinfo->args[1].isnull)
 			{
 				*op->resvalue = BoolGetDatum(false);
 				*op->resnull = false;
@@ -1202,7 +1203,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 			FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
 
 			/* if either argument is NULL they can't be equal */
-			if (!fcinfo->argnull[0] && !fcinfo->argnull[1])
+			if (!fcinfo->args[0].isnull && !fcinfo->args[1].isnull)
 			{
 				Datum		result;
 
@@ -1220,8 +1221,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 			}
 
 			/* Arguments aren't equal, so return the first one */
-			*op->resvalue = fcinfo->arg[0];
-			*op->resnull = fcinfo->argnull[0];
+			*op->resvalue = fcinfo->args[0].datum;
+			*op->resnull = fcinfo->args[0].isnull;
 
 			EEO_NEXT();
 		}
@@ -1287,7 +1288,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 
 			/* force NULL result if strict fn and NULL input */
 			if (op->d.rowcompare_step.finfo->fn_strict &&
-				(fcinfo->argnull[0] || fcinfo->argnull[1]))
+				(fcinfo->args[0].isnull || fcinfo->args[1].isnull))
 			{
 				*op->resnull = true;
 				EEO_JUMP(op->d.rowcompare_step.jumpnull);
@@ -1526,10 +1527,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 		/* evaluate a strict aggregate deserialization function */
 		EEO_CASE(EEOP_AGG_STRICT_DESERIALIZE)
 		{
-			bool	   *argnull = op->d.agg_deserialize.fcinfo_data->argnull;
-
 			/* Don't call a strict deserialization function with NULL input */
-			if (argnull[0])
+			if (op->d.agg_deserialize.fcinfo_data->args[0].isnull)
 				EEO_JUMP(op->d.agg_deserialize.jumpnull);
 
 			/* fallthrough */
@@ -1559,15 +1558,29 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 		 * Check that a strict aggregate transition / combination function's
 		 * input is not NULL.
 		 */
-		EEO_CASE(EEOP_AGG_STRICT_INPUT_CHECK)
+		EEO_CASE(EEOP_AGG_STRICT_INPUT_CHECK_ARGS)
 		{
 			int			argno;
-			bool	   *nulls = op->d.agg_strict_input_check.nulls;
+			NullableDatum *args = op->d.agg_strict_input_check.args;
 			int			nargs = op->d.agg_strict_input_check.nargs;
 
 			for (argno = 0; argno < nargs; argno++)
 			{
-				if (nulls[argno])
+				if (args[argno].isnull)
+					EEO_JUMP(op->d.agg_strict_input_check.jumpnull);
+			}
+			EEO_NEXT();
+		}
+
+		EEO_CASE(EEOP_AGG_STRICT_INPUT_CHECK_NULLS)
+		{
+			int			argno;
+			bool		*isnull = op->d.agg_strict_input_check.nulls;
+			int			nargs = op->d.agg_strict_input_check.nargs;
+
+			for (argno = 0; argno < nargs; argno++)
+			{
+				if (isnull[argno])
 					EEO_JUMP(op->d.agg_strict_input_check.jumpnull);
 			}
 			EEO_NEXT();
@@ -1643,7 +1656,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 
 			Assert(pertrans->transtypeByVal);
 
-			fcinfo = &pertrans->transfn_fcinfo;
+			fcinfo = pertrans->transfn_fcinfo;
 
 			/* cf. select_current_set() */
 			aggstate->curaggcontext = op->d.agg_trans.aggcontext;
@@ -1655,8 +1668,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 			/* invoke transition function in per-tuple context */
 			oldContext = MemoryContextSwitchTo(aggstate->tmpcontext->ecxt_per_tuple_memory);
 
-			fcinfo->arg[0] = pergroup->transValue;
-			fcinfo->argnull[0] = pergroup->transValueIsNull;
+			fcinfo->args[0].datum = pergroup->transValue;
+			fcinfo->args[0].isnull = pergroup->transValueIsNull;
 			fcinfo->isnull = false; /* just in case transfn doesn't set it */
 
 			newVal = FunctionCallInvoke(fcinfo);
@@ -1694,7 +1707,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 
 			Assert(!pertrans->transtypeByVal);
 
-			fcinfo = &pertrans->transfn_fcinfo;
+			fcinfo = pertrans->transfn_fcinfo;
 
 			/* cf. select_current_set() */
 			aggstate->curaggcontext = op->d.agg_trans.aggcontext;
@@ -1706,8 +1719,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 			/* invoke transition function in per-tuple context */
 			oldContext = MemoryContextSwitchTo(aggstate->tmpcontext->ecxt_per_tuple_memory);
 
-			fcinfo->arg[0] = pergroup->transValue;
-			fcinfo->argnull[0] = pergroup->transValueIsNull;
+			fcinfo->args[0].datum = pergroup->transValue;
+			fcinfo->args[0].isnull = pergroup->transValueIsNull;
 			fcinfo->isnull = false; /* just in case transfn doesn't set it */
 
 			newVal = FunctionCallInvoke(fcinfo);
@@ -2060,7 +2073,7 @@ ExecJustApplyFuncToCase(ExprState *state, ExprContext *econtext, bool *isnull)
 {
 	ExprEvalStep *op = &state->steps[0];
 	FunctionCallInfo fcinfo;
-	bool	   *argnull;
+	NullableDatum *args;
 	int			argno;
 	Datum		d;
 
@@ -2074,12 +2087,12 @@ ExecJustApplyFuncToCase(ExprState *state, ExprContext *econtext, bool *isnull)
 	op++;
 
 	fcinfo = op->d.func.fcinfo_data;
-	argnull = fcinfo->argnull;
+	args = fcinfo->args;
 
 	/* strict function, so check for NULL args */
 	for (argno = 0; argno < op->d.func.nargs; argno++)
 	{
-		if (argnull[argno])
+		if (args[argno].isnull)
 		{
 			*isnull = true;
 			return (Datum) 0;
@@ -2205,14 +2218,14 @@ ExecEvalFuncExprStrictFusage(ExprState *state, ExprEvalStep *op,
 
 	FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
 	PgStat_FunctionCallUsage fcusage;
-	bool	   *argnull = fcinfo->argnull;
+	NullableDatum *args = fcinfo->args;
 	int			argno;
 	Datum		d;
 
 	/* strict function, so check for NULL args */
 	for (argno = 0; argno < op->d.func.nargs; argno++)
 	{
-		if (argnull[argno])
+		if (args[argno].isnull)
 		{
 			*op->resnull = true;
 			return;
@@ -2303,7 +2316,7 @@ void
 ExecEvalSQLValueFunction(ExprState *state, ExprEvalStep *op)
 {
 	SQLValueFunction *svf = op->d.sqlvaluefunction.svf;
-	FunctionCallInfoData fcinfo;
+	STACK_FCINFO_FOR_ARGS(fcinfo, 0);
 
 	*op->resnull = false;
 
@@ -2335,24 +2348,24 @@ ExecEvalSQLValueFunction(ExprState *state, ExprEvalStep *op)
 		case SVFOP_CURRENT_ROLE:
 		case SVFOP_CURRENT_USER:
 		case SVFOP_USER:
-			InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
-			*op->resvalue = current_user(&fcinfo);
-			*op->resnull = fcinfo.isnull;
+			InitFunctionCallInfoData(*fcinfo, NULL, 0, InvalidOid, NULL, NULL);
+			*op->resvalue = current_user(fcinfo);
+			*op->resnull = fcinfo->isnull;
 			break;
 		case SVFOP_SESSION_USER:
-			InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
-			*op->resvalue = session_user(&fcinfo);
-			*op->resnull = fcinfo.isnull;
+			InitFunctionCallInfoData(*fcinfo, NULL, 0, InvalidOid, NULL, NULL);
+			*op->resvalue = session_user(fcinfo);
+			*op->resnull = fcinfo->isnull;
 			break;
 		case SVFOP_CURRENT_CATALOG:
-			InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
-			*op->resvalue = current_database(&fcinfo);
-			*op->resnull = fcinfo.isnull;
+			InitFunctionCallInfoData(*fcinfo, NULL, 0, InvalidOid, NULL, NULL);
+			*op->resvalue = current_database(fcinfo);
+			*op->resnull = fcinfo->isnull;
 			break;
 		case SVFOP_CURRENT_SCHEMA:
-			InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
-			*op->resvalue = current_schema(&fcinfo);
-			*op->resnull = fcinfo.isnull;
+			InitFunctionCallInfoData(*fcinfo, NULL, 0, InvalidOid, NULL, NULL);
+			*op->resvalue = current_schema(fcinfo);
+			*op->resnull = fcinfo->isnull;
 			break;
 	}
 }
@@ -2785,8 +2798,8 @@ ExecEvalMinMax(ExprState *state, ExprEvalStep *op)
 	int			off;
 
 	/* set at initialization */
-	Assert(fcinfo->argnull[0] == false);
-	Assert(fcinfo->argnull[1] == false);
+	Assert(fcinfo->args[0].isnull == false);
+	Assert(fcinfo->args[1].isnull == false);
 
 	/* default to null result */
 	*op->resnull = true;
@@ -2808,8 +2821,8 @@ ExecEvalMinMax(ExprState *state, ExprEvalStep *op)
 			int			cmpresult;
 
 			/* apply comparison function */
-			fcinfo->arg[0] = *op->resvalue;
-			fcinfo->arg[1] = values[off];
+			fcinfo->args[0].datum = *op->resvalue;
+			fcinfo->args[1].datum = values[off];
 
 			fcinfo->isnull = false;
 			cmpresult = DatumGetInt32(FunctionCallInvoke(fcinfo));
@@ -3361,7 +3374,7 @@ ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op)
 	 * If the scalar is NULL, and the function is strict, return NULL; no
 	 * point in iterating the loop.
 	 */
-	if (fcinfo->argnull[0] && strictfunc)
+	if (fcinfo->args[0].isnull && strictfunc)
 	{
 		*op->resnull = true;
 		return;
@@ -3401,20 +3414,20 @@ ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op)
 		/* Get array element, checking for NULL */
 		if (bitmap && (*bitmap & bitmask) == 0)
 		{
-			fcinfo->arg[1] = (Datum) 0;
-			fcinfo->argnull[1] = true;
+			fcinfo->args[1].datum = (Datum) 0;
+			fcinfo->args[1].isnull = true;
 		}
 		else
 		{
 			elt = fetch_att(s, typbyval, typlen);
 			s = att_addlength_pointer(s, typlen, s);
 			s = (char *) att_align_nominal(s, typalign);
-			fcinfo->arg[1] = elt;
-			fcinfo->argnull[1] = false;
+			fcinfo->args[1].datum = elt;
+			fcinfo->args[1].isnull = false;
 		}
 
 		/* Call comparison function */
-		if (fcinfo->argnull[1] && strictfunc)
+		if (fcinfo->args[1].isnull && strictfunc)
 		{
 			fcinfo->isnull = true;
 			thisresult = (Datum) 0;
@@ -4013,7 +4026,7 @@ ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
 void
 ExecAggInitGroup(AggState *aggstate, AggStatePerTrans pertrans, AggStatePerGroup pergroup)
 {
-	FunctionCallInfo fcinfo = &pertrans->transfn_fcinfo;
+	FunctionCallInfo fcinfo = pertrans->transfn_fcinfo;
 	MemoryContext oldContext;
 
 	/*
@@ -4024,7 +4037,7 @@ ExecAggInitGroup(AggState *aggstate, AggStatePerTrans pertrans, AggStatePerGroup
 	 */
 	oldContext = MemoryContextSwitchTo(
 									   aggstate->curaggcontext->ecxt_per_tuple_memory);
-	pergroup->transValue = datumCopy(fcinfo->arg[1],
+	pergroup->transValue = datumCopy(fcinfo->args[1].datum,
 									 pertrans->transtypeByVal,
 									 pertrans->transtypeLen);
 	pergroup->transValueIsNull = false;
diff --git a/src/backend/executor/execSRF.c b/src/backend/executor/execSRF.c
index b97b8d797ec..a50670f537a 100644
--- a/src/backend/executor/execSRF.c
+++ b/src/backend/executor/execSRF.c
@@ -109,7 +109,7 @@ ExecMakeTableFunctionResult(SetExprState *setexpr,
 	Oid			funcrettype;
 	bool		returnsTuple;
 	bool		returnsSet = false;
-	FunctionCallInfoData fcinfo;
+	STACK_FCINFO_FOR_ARGS(fcinfo, 3);
 	PgStat_FunctionCallUsage fcusage;
 	ReturnSetInfo rsinfo;
 	HeapTupleData tmptup;
@@ -157,9 +157,9 @@ ExecMakeTableFunctionResult(SetExprState *setexpr,
 		 * This path is similar to ExecMakeFunctionResultSet.
 		 */
 		returnsSet = setexpr->funcReturnsSet;
-		InitFunctionCallInfoData(fcinfo, &(setexpr->func),
+		InitFunctionCallInfoData(*fcinfo, &(setexpr->func),
 								 list_length(setexpr->args),
-								 setexpr->fcinfo_data.fncollation,
+								 setexpr->fcinfo->fncollation,
 								 NULL, (Node *) &rsinfo);
 
 		/*
@@ -174,7 +174,7 @@ ExecMakeTableFunctionResult(SetExprState *setexpr,
 		 */
 		MemoryContextReset(argContext);
 		oldcontext = MemoryContextSwitchTo(argContext);
-		ExecEvalFuncArgs(&fcinfo, setexpr->args, econtext);
+		ExecEvalFuncArgs(fcinfo, setexpr->args, econtext);
 		MemoryContextSwitchTo(oldcontext);
 
 		/*
@@ -186,9 +186,9 @@ ExecMakeTableFunctionResult(SetExprState *setexpr,
 		{
 			int			i;
 
-			for (i = 0; i < fcinfo.nargs; i++)
+			for (i = 0; i < fcinfo->nargs; i++)
 			{
-				if (fcinfo.argnull[i])
+				if (fcinfo->args[i].isnull)
 					goto no_function_result;
 			}
 		}
@@ -196,7 +196,7 @@ ExecMakeTableFunctionResult(SetExprState *setexpr,
 	else
 	{
 		/* Treat setexpr as a generic expression */
-		InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
+		InitFunctionCallInfoData(*fcinfo, NULL, 0, InvalidOid, NULL, NULL);
 	}
 
 	/*
@@ -224,11 +224,11 @@ ExecMakeTableFunctionResult(SetExprState *setexpr,
 		/* Call the function or expression one time */
 		if (!setexpr->elidedFuncState)
 		{
-			pgstat_init_function_usage(&fcinfo, &fcusage);
+			pgstat_init_function_usage(fcinfo, &fcusage);
 
-			fcinfo.isnull = false;
+			fcinfo->isnull = false;
 			rsinfo.isDone = ExprSingleResult;
-			result = FunctionCallInvoke(&fcinfo);
+			result = FunctionCallInvoke(fcinfo);
 
 			pgstat_end_function_usage(&fcusage,
 									  rsinfo.isDone != ExprMultipleResult);
@@ -236,7 +236,7 @@ ExecMakeTableFunctionResult(SetExprState *setexpr,
 		else
 		{
 			result =
-				ExecEvalExpr(setexpr->elidedFuncState, econtext, &fcinfo.isnull);
+				ExecEvalExpr(setexpr->elidedFuncState, econtext, &fcinfo->isnull);
 			rsinfo.isDone = ExprSingleResult;
 		}
 
@@ -277,7 +277,7 @@ ExecMakeTableFunctionResult(SetExprState *setexpr,
 			 */
 			if (returnsTuple)
 			{
-				if (!fcinfo.isnull)
+				if (!fcinfo->isnull)
 				{
 					HeapTupleHeader td = DatumGetHeapTupleHeader(result);
 
@@ -338,7 +338,7 @@ ExecMakeTableFunctionResult(SetExprState *setexpr,
 			else
 			{
 				/* Scalar-type case: just store the function result */
-				tuplestore_putvalues(tupstore, tupdesc, &result, &fcinfo.isnull);
+				tuplestore_putvalues(tupstore, tupdesc, &result, &fcinfo->isnull);
 			}
 
 			/*
@@ -547,7 +547,7 @@ restart:
 	 * rows from this SRF have been returned, otherwise ValuePerCall SRFs
 	 * would reference freed memory after the first returned row.
 	 */
-	fcinfo = &fcache->fcinfo_data;
+	fcinfo = fcache->fcinfo;
 	arguments = fcache->args;
 	if (!fcache->setArgsValid)
 	{
@@ -587,7 +587,7 @@ restart:
 	{
 		for (i = 0; i < fcinfo->nargs; i++)
 		{
-			if (fcinfo->argnull[i])
+			if (fcinfo->args[i].isnull)
 			{
 				callit = false;
 				break;
@@ -704,7 +704,8 @@ init_sexpr(Oid foid, Oid input_collation, Expr *node,
 	fmgr_info_set_expr((Node *) sexpr->expr, &(sexpr->func));
 
 	/* Initialize the function call parameter struct as well */
-	InitFunctionCallInfoData(sexpr->fcinfo_data, &(sexpr->func),
+	sexpr->fcinfo = (FunctionCallInfo) palloc(SizeForFunctionCallInfoData(list_length(sexpr->args)));
+	InitFunctionCallInfoData(*sexpr->fcinfo, &(sexpr->func),
 							 list_length(sexpr->args),
 							 input_collation, NULL, NULL);
 
@@ -820,9 +821,9 @@ ExecEvalFuncArgs(FunctionCallInfo fcinfo,
 	{
 		ExprState  *argstate = (ExprState *) lfirst(arg);
 
-		fcinfo->arg[i] = ExecEvalExpr(argstate,
-									  econtext,
-									  &fcinfo->argnull[i]);
+		fcinfo->args[i].datum = ExecEvalExpr(argstate,
+											 econtext,
+											 &fcinfo->args[i].isnull);
 		i++;
 	}
 
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 23545896d4d..672dcd981cd 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -933,8 +933,8 @@ postquel_sub_params(SQLFunctionCachePtr fcache,
 		{
 			ParamExternData *prm = &paramLI->params[i];
 
-			prm->value = fcinfo->arg[i];
-			prm->isnull = fcinfo->argnull[i];
+			prm->value = fcinfo->args[i].datum;
+			prm->isnull = fcinfo->args[i].isnull;
 			prm->pflags = 0;
 			prm->ptype = fcache->pinfo->argtypes[i];
 		}
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index 98d8483b720..e9bea4a46a2 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -553,7 +553,7 @@ advance_transition_function(AggState *aggstate,
 							AggStatePerTrans pertrans,
 							AggStatePerGroup pergroupstate)
 {
-	FunctionCallInfo fcinfo = &pertrans->transfn_fcinfo;
+	FunctionCallInfo fcinfo = pertrans->transfn_fcinfo;
 	MemoryContext oldContext;
 	Datum		newVal;
 
@@ -568,7 +568,7 @@ advance_transition_function(AggState *aggstate,
 
 		for (i = 1; i <= numTransInputs; i++)
 		{
-			if (fcinfo->argnull[i])
+			if (fcinfo->args[i].isnull)
 				return;
 		}
 		if (pergroupstate->noTransValue)
@@ -584,7 +584,7 @@ advance_transition_function(AggState *aggstate,
 			 */
 			oldContext = MemoryContextSwitchTo(
 											   aggstate->curaggcontext->ecxt_per_tuple_memory);
-			pergroupstate->transValue = datumCopy(fcinfo->arg[1],
+			pergroupstate->transValue = datumCopy(fcinfo->args[1].datum,
 												  pertrans->transtypeByVal,
 												  pertrans->transtypeLen);
 			pergroupstate->transValueIsNull = false;
@@ -613,8 +613,8 @@ advance_transition_function(AggState *aggstate,
 	/*
 	 * OK to call the transition function
 	 */
-	fcinfo->arg[0] = pergroupstate->transValue;
-	fcinfo->argnull[0] = pergroupstate->transValueIsNull;
+	fcinfo->args[0].datum = pergroupstate->transValue;
+	fcinfo->args[0].isnull = pergroupstate->transValueIsNull;
 	fcinfo->isnull = false;		/* just in case transfn doesn't set it */
 
 	newVal = FunctionCallInvoke(fcinfo);
@@ -717,7 +717,7 @@ process_ordered_aggregate_single(AggState *aggstate,
 	bool		isDistinct = (pertrans->numDistinctCols > 0);
 	Datum		newAbbrevVal = (Datum) 0;
 	Datum		oldAbbrevVal = (Datum) 0;
-	FunctionCallInfo fcinfo = &pertrans->transfn_fcinfo;
+	FunctionCallInfo fcinfo = pertrans->transfn_fcinfo;
 	Datum	   *newVal;
 	bool	   *isNull;
 
@@ -726,8 +726,8 @@ process_ordered_aggregate_single(AggState *aggstate,
 	tuplesort_performsort(pertrans->sortstates[aggstate->current_set]);
 
 	/* Load the column into argument 1 (arg 0 will be transition value) */
-	newVal = fcinfo->arg + 1;
-	isNull = fcinfo->argnull + 1;
+	newVal = &fcinfo->args[1].datum;
+	isNull = &fcinfo->args[1].isnull;
 
 	/*
 	 * Note: if input type is pass-by-ref, the datums returned by the sort are
@@ -803,7 +803,7 @@ process_ordered_aggregate_multi(AggState *aggstate,
 								AggStatePerGroup pergroupstate)
 {
 	ExprContext *tmpcontext = aggstate->tmpcontext;
-	FunctionCallInfo fcinfo = &pertrans->transfn_fcinfo;
+	FunctionCallInfo fcinfo = pertrans->transfn_fcinfo;
 	TupleTableSlot *slot1 = pertrans->sortslot;
 	TupleTableSlot *slot2 = pertrans->uniqslot;
 	int			numTransInputs = pertrans->numTransInputs;
@@ -843,8 +843,8 @@ process_ordered_aggregate_multi(AggState *aggstate,
 			/* Start from 1, since the 0th arg will be the transition value */
 			for (i = 0; i < numTransInputs; i++)
 			{
-				fcinfo->arg[i + 1] = slot1->tts_values[i];
-				fcinfo->argnull[i + 1] = slot1->tts_isnull[i];
+				fcinfo->args[i + 1].datum = slot1->tts_values[i];
+				fcinfo->args[i + 1].isnull = slot1->tts_isnull[i];
 			}
 
 			advance_transition_function(aggstate, pertrans, pergroupstate);
@@ -897,7 +897,7 @@ finalize_aggregate(AggState *aggstate,
 				   AggStatePerGroup pergroupstate,
 				   Datum *resultVal, bool *resultIsNull)
 {
-	FunctionCallInfoData fcinfo;
+	STACK_FCINFO_FOR_ARGS(fcinfo, 3);
 	bool		anynull = false;
 	MemoryContext oldContext;
 	int			i;
@@ -917,10 +917,10 @@ finalize_aggregate(AggState *aggstate,
 	{
 		ExprState  *expr = (ExprState *) lfirst(lc);
 
-		fcinfo.arg[i] = ExecEvalExpr(expr,
-									 aggstate->ss.ps.ps_ExprContext,
-									 &fcinfo.argnull[i]);
-		anynull |= fcinfo.argnull[i];
+		fcinfo->args[i].datum = ExecEvalExpr(expr,
+											 aggstate->ss.ps.ps_ExprContext,
+											 &fcinfo->args[i].isnull);
+		anynull |= fcinfo->args[i].isnull;
 		i++;
 	}
 
@@ -934,27 +934,27 @@ finalize_aggregate(AggState *aggstate,
 		/* set up aggstate->curperagg for AggGetAggref() */
 		aggstate->curperagg = peragg;
 
-		InitFunctionCallInfoData(fcinfo, &peragg->finalfn,
+		InitFunctionCallInfoData(*fcinfo, &peragg->finalfn,
 								 numFinalArgs,
 								 pertrans->aggCollation,
 								 (void *) aggstate, NULL);
 
 		/* Fill in the transition state value */
-		fcinfo.arg[0] = MakeExpandedObjectReadOnly(pergroupstate->transValue,
-												   pergroupstate->transValueIsNull,
-												   pertrans->transtypeLen);
-		fcinfo.argnull[0] = pergroupstate->transValueIsNull;
+		fcinfo->args[0].datum = MakeExpandedObjectReadOnly(pergroupstate->transValue,
+														   pergroupstate->transValueIsNull,
+														   pertrans->transtypeLen);
+		fcinfo->args[0].isnull = pergroupstate->transValueIsNull;
 		anynull |= pergroupstate->transValueIsNull;
 
 		/* Fill any remaining argument positions with nulls */
 		for (; i < numFinalArgs; i++)
 		{
-			fcinfo.arg[i] = (Datum) 0;
-			fcinfo.argnull[i] = true;
+			fcinfo->args[i].datum = (Datum) 0;
+			fcinfo->args[i].isnull = true;
 			anynull = true;
 		}
 
-		if (fcinfo.flinfo->fn_strict && anynull)
+		if (fcinfo->flinfo->fn_strict && anynull)
 		{
 			/* don't call a strict function with NULL inputs */
 			*resultVal = (Datum) 0;
@@ -962,8 +962,8 @@ finalize_aggregate(AggState *aggstate,
 		}
 		else
 		{
-			*resultVal = FunctionCallInvoke(&fcinfo);
-			*resultIsNull = fcinfo.isnull;
+			*resultVal = FunctionCallInvoke(fcinfo);
+			*resultIsNull = fcinfo->isnull;
 		}
 		aggstate->curperagg = NULL;
 	}
@@ -1018,12 +1018,12 @@ finalize_partialaggregate(AggState *aggstate,
 		}
 		else
 		{
-			FunctionCallInfo fcinfo = &pertrans->serialfn_fcinfo;
+			FunctionCallInfo fcinfo = pertrans->serialfn_fcinfo;
 
-			fcinfo->arg[0] = MakeExpandedObjectReadOnly(pergroupstate->transValue,
-														pergroupstate->transValueIsNull,
-														pertrans->transtypeLen);
-			fcinfo->argnull[0] = pergroupstate->transValueIsNull;
+			fcinfo->args[0].datum = MakeExpandedObjectReadOnly(pergroupstate->transValue,
+															   pergroupstate->transValueIsNull,
+															   pertrans->transtypeLen);
+			fcinfo->args[0].isnull = pergroupstate->transValueIsNull;
 
 			*resultVal = FunctionCallInvoke(fcinfo);
 			*resultIsNull = fcinfo->isnull;
@@ -2925,7 +2925,8 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
 		fmgr_info(aggtransfn, &pertrans->transfn);
 		fmgr_info_set_expr((Node *) combinefnexpr, &pertrans->transfn);
 
-		InitFunctionCallInfoData(pertrans->transfn_fcinfo,
+		pertrans->transfn_fcinfo = (FunctionCallInfo) palloc(SizeForFunctionCallInfoData(2));
+		InitFunctionCallInfoData(*pertrans->transfn_fcinfo,
 								 &pertrans->transfn,
 								 2,
 								 pertrans->aggCollation,
@@ -2963,7 +2964,8 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
 		fmgr_info(aggtransfn, &pertrans->transfn);
 		fmgr_info_set_expr((Node *) transfnexpr, &pertrans->transfn);
 
-		InitFunctionCallInfoData(pertrans->transfn_fcinfo,
+		pertrans->transfn_fcinfo = (FunctionCallInfo) palloc(SizeForFunctionCallInfoData(pertrans->numTransInputs + 1));
+		InitFunctionCallInfoData(*pertrans->transfn_fcinfo,
 								 &pertrans->transfn,
 								 pertrans->numTransInputs + 1,
 								 pertrans->aggCollation,
@@ -3001,7 +3003,8 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
 		fmgr_info(aggserialfn, &pertrans->serialfn);
 		fmgr_info_set_expr((Node *) serialfnexpr, &pertrans->serialfn);
 
-		InitFunctionCallInfoData(pertrans->serialfn_fcinfo,
+		pertrans->serialfn_fcinfo = (FunctionCallInfo) palloc(SizeForFunctionCallInfoData(1));
+		InitFunctionCallInfoData(*pertrans->serialfn_fcinfo,
 								 &pertrans->serialfn,
 								 1,
 								 InvalidOid,
@@ -3015,7 +3018,8 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
 		fmgr_info(aggdeserialfn, &pertrans->deserialfn);
 		fmgr_info_set_expr((Node *) deserialfnexpr, &pertrans->deserialfn);
 
-		InitFunctionCallInfoData(pertrans->deserialfn_fcinfo,
+		pertrans->deserialfn_fcinfo = (FunctionCallInfo) palloc(SizeForFunctionCallInfoData(2));
+		InitFunctionCallInfoData(*pertrans->deserialfn_fcinfo,
 								 &pertrans->deserialfn,
 								 2,
 								 InvalidOid,
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index 729c376d86b..032598bd5bb 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -243,8 +243,7 @@ advance_windowaggregate(WindowAggState *winstate,
 {
 	WindowFuncExprState *wfuncstate = perfuncstate->wfuncstate;
 	int			numArguments = perfuncstate->numArguments;
-	FunctionCallInfoData fcinfodata;
-	FunctionCallInfo fcinfo = &fcinfodata;
+	STACK_FCINFO_FOR_ARGS(fcinfo, 3);
 	Datum		newVal;
 	ListCell   *arg;
 	int			i;
@@ -273,8 +272,8 @@ advance_windowaggregate(WindowAggState *winstate,
 	{
 		ExprState  *argstate = (ExprState *) lfirst(arg);
 
-		fcinfo->arg[i] = ExecEvalExpr(argstate, econtext,
-									  &fcinfo->argnull[i]);
+		fcinfo->args[i].datum = ExecEvalExpr(argstate, econtext,
+											 &fcinfo->args[i].isnull);
 		i++;
 	}
 
@@ -287,7 +286,7 @@ advance_windowaggregate(WindowAggState *winstate,
 		 */
 		for (i = 1; i <= numArguments; i++)
 		{
-			if (fcinfo->argnull[i])
+			if (fcinfo->args[i].isnull)
 			{
 				MemoryContextSwitchTo(oldContext);
 				return;
@@ -306,7 +305,7 @@ advance_windowaggregate(WindowAggState *winstate,
 		if (peraggstate->transValueCount == 0 && peraggstate->transValueIsNull)
 		{
 			MemoryContextSwitchTo(peraggstate->aggcontext);
-			peraggstate->transValue = datumCopy(fcinfo->arg[1],
+			peraggstate->transValue = datumCopy(fcinfo->args[1].datum,
 												peraggstate->transtypeByVal,
 												peraggstate->transtypeLen);
 			peraggstate->transValueIsNull = false;
@@ -339,8 +338,8 @@ advance_windowaggregate(WindowAggState *winstate,
 							 numArguments + 1,
 							 perfuncstate->winCollation,
 							 (void *) winstate, NULL);
-	fcinfo->arg[0] = peraggstate->transValue;
-	fcinfo->argnull[0] = peraggstate->transValueIsNull;
+	fcinfo->args[0].datum = peraggstate->transValue;
+	fcinfo->args[0].isnull = peraggstate->transValueIsNull;
 	winstate->curaggcontext = peraggstate->aggcontext;
 	newVal = FunctionCallInvoke(fcinfo);
 	winstate->curaggcontext = NULL;
@@ -420,8 +419,7 @@ advance_windowaggregate_base(WindowAggState *winstate,
 {
 	WindowFuncExprState *wfuncstate = perfuncstate->wfuncstate;
 	int			numArguments = perfuncstate->numArguments;
-	FunctionCallInfoData fcinfodata;
-	FunctionCallInfo fcinfo = &fcinfodata;
+	STACK_FCINFO_FOR_ARGS(fcinfo, 3);
 	Datum		newVal;
 	ListCell   *arg;
 	int			i;
@@ -450,8 +448,8 @@ advance_windowaggregate_base(WindowAggState *winstate,
 	{
 		ExprState  *argstate = (ExprState *) lfirst(arg);
 
-		fcinfo->arg[i] = ExecEvalExpr(argstate, econtext,
-									  &fcinfo->argnull[i]);
+		fcinfo->args[i].datum = ExecEvalExpr(argstate, econtext,
+											 &fcinfo->args[i].isnull);
 		i++;
 	}
 
@@ -464,7 +462,7 @@ advance_windowaggregate_base(WindowAggState *winstate,
 		 */
 		for (i = 1; i <= numArguments; i++)
 		{
-			if (fcinfo->argnull[i])
+			if (fcinfo->args[i].isnull)
 			{
 				MemoryContextSwitchTo(oldContext);
 				return true;
@@ -510,8 +508,8 @@ advance_windowaggregate_base(WindowAggState *winstate,
 							 numArguments + 1,
 							 perfuncstate->winCollation,
 							 (void *) winstate, NULL);
-	fcinfo->arg[0] = peraggstate->transValue;
-	fcinfo->argnull[0] = peraggstate->transValueIsNull;
+	fcinfo->args[0].datum = peraggstate->transValue;
+	fcinfo->args[0].isnull = peraggstate->transValueIsNull;
 	winstate->curaggcontext = peraggstate->aggcontext;
 	newVal = FunctionCallInvoke(fcinfo);
 	winstate->curaggcontext = NULL;
@@ -592,29 +590,29 @@ finalize_windowaggregate(WindowAggState *winstate,
 	if (OidIsValid(peraggstate->finalfn_oid))
 	{
 		int			numFinalArgs = peraggstate->numFinalArgs;
-		FunctionCallInfoData fcinfo;
+		STACK_FCINFO_FOR_ARGS(fcinfo, 3);
 		bool		anynull;
 		int			i;
 
-		InitFunctionCallInfoData(fcinfo, &(peraggstate->finalfn),
+		InitFunctionCallInfoData(fcinfodata.fcinfo, &(peraggstate->finalfn),
 								 numFinalArgs,
 								 perfuncstate->winCollation,
 								 (void *) winstate, NULL);
-		fcinfo.arg[0] = MakeExpandedObjectReadOnly(peraggstate->transValue,
-												   peraggstate->transValueIsNull,
-												   peraggstate->transtypeLen);
-		fcinfo.argnull[0] = peraggstate->transValueIsNull;
+		fcinfo->args[0].datum = MakeExpandedObjectReadOnly(peraggstate->transValue,
+														   peraggstate->transValueIsNull,
+														   peraggstate->transtypeLen);
+		fcinfo->args[0].isnull = peraggstate->transValueIsNull;
 		anynull = peraggstate->transValueIsNull;
 
 		/* Fill any remaining argument positions with nulls */
 		for (i = 1; i < numFinalArgs; i++)
 		{
-			fcinfo.arg[i] = (Datum) 0;
-			fcinfo.argnull[i] = true;
+			fcinfo->args[i].datum = (Datum) 0;
+			fcinfo->args[i].isnull = true;
 			anynull = true;
 		}
 
-		if (fcinfo.flinfo->fn_strict && anynull)
+		if (fcinfo->flinfo->fn_strict && anynull)
 		{
 			/* don't call a strict function with NULL inputs */
 			*result = (Datum) 0;
@@ -623,9 +621,9 @@ finalize_windowaggregate(WindowAggState *winstate,
 		else
 		{
 			winstate->curaggcontext = peraggstate->aggcontext;
-			*result = FunctionCallInvoke(&fcinfo);
+			*result = FunctionCallInvoke(fcinfo);
 			winstate->curaggcontext = NULL;
-			*isnull = fcinfo.isnull;
+			*isnull = fcinfo->isnull;
 		}
 	}
 	else
@@ -1032,8 +1030,9 @@ static void
 eval_windowfunction(WindowAggState *winstate, WindowStatePerFunc perfuncstate,
 					Datum *result, bool *isnull)
 {
-	FunctionCallInfoData fcinfo;
+	FunctionCallInfo fcinfo;
 	MemoryContext oldContext;
+	int argno;
 
 	oldContext = MemoryContextSwitchTo(winstate->ss.ps.ps_ExprContext->ecxt_per_tuple_memory);
 
@@ -1043,24 +1042,26 @@ eval_windowfunction(WindowAggState *winstate, WindowStatePerFunc perfuncstate,
 	 * implementations to support varying numbers of arguments.  The real info
 	 * goes through the WindowObject, which is passed via fcinfo->context.
 	 */
-	InitFunctionCallInfoData(fcinfo, &(perfuncstate->flinfo),
+	fcinfo = palloc(SizeForFunctionCallInfoData(perfuncstate->numArguments));
+	InitFunctionCallInfoData(*fcinfo, &(perfuncstate->flinfo),
 							 perfuncstate->numArguments,
 							 perfuncstate->winCollation,
 							 (void *) perfuncstate->winobj, NULL);
 	/* Just in case, make all the regular argument slots be null */
-	memset(fcinfo.argnull, true, perfuncstate->numArguments);
+	for (argno = 0; argno < perfuncstate->numArguments; argno++)
+		fcinfo->args[argno].isnull = true;
 	/* Window functions don't have a current aggregate context, either */
 	winstate->curaggcontext = NULL;
 
-	*result = FunctionCallInvoke(&fcinfo);
-	*isnull = fcinfo.isnull;
+	*result = FunctionCallInvoke(fcinfo);
+	*isnull = fcinfo->isnull;
 
 	/*
 	 * Make sure pass-by-ref data is allocated in the appropriate context. (We
 	 * need this in case the function returns a pointer into some short-lived
 	 * tuple, as is entirely possible.)
 	 */
-	if (!perfuncstate->resulttypeByVal && !fcinfo.isnull &&
+	if (!perfuncstate->resulttypeByVal && !fcinfo->isnull &&
 		!MemoryContextContains(CurrentMemoryContext,
 							   DatumGetPointer(*result)))
 		*result = datumCopy(*result,
diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c
index 99e0cee157f..13aedc93016 100644
--- a/src/backend/jit/llvm/llvmjit_expr.c
+++ b/src/backend/jit/llvm/llvmjit_expr.c
@@ -2117,15 +2117,16 @@ llvm_compile_expr(ExprState *state)
 			case EEOP_AGG_STRICT_INPUT_CHECK:
 				{
 					int			nargs = op->d.agg_strict_input_check.nargs;
-					bool	   *nulls = op->d.agg_strict_input_check.nulls;
+					NullableDatum *args = op->d.agg_strict_input_check.args;
 					int			jumpnull;
 					int			argno;
 
-					LLVMValueRef v_nullp;
+					LLVMValueRef v_argp;
 					LLVMBasicBlockRef *b_checknulls;
 
 					jumpnull = op->d.agg_strict_input_check.jumpnull;
-					v_nullp = l_ptr_const(nulls, l_ptr(TypeStorageBool));
+					/* FIXME: This isn't correct, it's a NullableDatum now */
+					v_argp = l_ptr_const(args, l_ptr(TypeStorageBool));
 
 					/* create blocks for checking args */
 					b_checknulls = palloc(sizeof(LLVMBasicBlockRef *) * nargs);
@@ -2153,7 +2154,8 @@ llvm_compile_expr(ExprState *state)
 						else
 							b_argnotnull = b_checknulls[argno + 1];
 
-						v_argisnull = l_load_gep1(b, v_nullp, v_argno, "");
+						/* FIXME: This isn't correct */
+						v_argisnull = l_load_gep1(b, v_argp, v_argno, "");
 
 						LLVMBuildCondBr(b,
 										LLVMBuildICmp(b,
@@ -2352,7 +2354,7 @@ llvm_compile_expr(ExprState *state)
 					aggstate = op->d.agg_trans.aggstate;
 					pertrans = op->d.agg_trans.pertrans;
 
-					fcinfo = &pertrans->transfn_fcinfo;
+					fcinfo = pertrans->transfn_fcinfo;
 
 					v_aggstatep = l_ptr_const(aggstate,
 											  l_ptr(StructAggState));
@@ -2622,12 +2624,8 @@ BuildV1Call(LLVMJitContext *context, LLVMBuilderRef b,
 		LLVMValueRef v_lifetime = create_LifetimeEnd(mod);
 		LLVMValueRef params[2];
 
-		params[0] = l_int64_const(sizeof(fcinfo->arg));
-		params[1] = l_ptr_const(fcinfo->arg, l_ptr(LLVMInt8Type()));
-		LLVMBuildCall(b, v_lifetime, params, lengthof(params), "");
-
-		params[0] = l_int64_const(sizeof(fcinfo->argnull));
-		params[1] = l_ptr_const(fcinfo->argnull, l_ptr(LLVMInt8Type()));
+		params[0] = l_int64_const(sizeof(NullableDatum) * fcinfo->nargs);
+		params[1] = l_ptr_const(fcinfo->args, l_ptr(LLVMInt8Type()));
 		LLVMBuildCall(b, v_lifetime, params, lengthof(params), "");
 
 		params[0] = l_int64_const(sizeof(fcinfo->isnull));
diff --git a/src/backend/tcop/fastpath.c b/src/backend/tcop/fastpath.c
index d16ba5ec927..4d2b687fb1f 100644
--- a/src/backend/tcop/fastpath.c
+++ b/src/backend/tcop/fastpath.c
@@ -260,7 +260,7 @@ HandleFunctionRequest(StringInfo msgBuf)
 {
 	Oid			fid;
 	AclResult	aclresult;
-	FunctionCallInfoData fcinfo;
+	STACK_FCINFO_FOR_ARGS(fcinfo, 3);
 	int16		rformat;
 	Datum		retval;
 	struct fp_info my_fp;
@@ -332,12 +332,12 @@ HandleFunctionRequest(StringInfo msgBuf)
 	 * functions can't be called this way.  Perhaps we should pass
 	 * DEFAULT_COLLATION_OID, instead?
 	 */
-	InitFunctionCallInfoData(fcinfo, &fip->flinfo, 0, InvalidOid, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, &fip->flinfo, 0, InvalidOid, NULL, NULL);
 
 	if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
-		rformat = parse_fcall_arguments(msgBuf, fip, &fcinfo);
+		rformat = parse_fcall_arguments(msgBuf, fip, fcinfo);
 	else
-		rformat = parse_fcall_arguments_20(msgBuf, fip, &fcinfo);
+		rformat = parse_fcall_arguments_20(msgBuf, fip, fcinfo);
 
 	/* Verify we reached the end of the message where expected. */
 	pq_getmsgend(msgBuf);
@@ -350,9 +350,9 @@ HandleFunctionRequest(StringInfo msgBuf)
 	{
 		int			i;
 
-		for (i = 0; i < fcinfo.nargs; i++)
+		for (i = 0; i < fcinfo->nargs; i++)
 		{
-			if (fcinfo.argnull[i])
+			if (fcinfo->args[i].isnull)
 			{
 				callit = false;
 				break;
@@ -363,18 +363,18 @@ HandleFunctionRequest(StringInfo msgBuf)
 	if (callit)
 	{
 		/* Okay, do it ... */
-		retval = FunctionCallInvoke(&fcinfo);
+		retval = FunctionCallInvoke(fcinfo);
 	}
 	else
 	{
-		fcinfo.isnull = true;
+		fcinfo->isnull = true;
 		retval = (Datum) 0;
 	}
 
 	/* ensure we do at least one CHECK_FOR_INTERRUPTS per function call */
 	CHECK_FOR_INTERRUPTS();
 
-	SendFunctionResult(retval, fcinfo.isnull, fip->rettype, rformat);
+	SendFunctionResult(retval, fcinfo->isnull, fip->rettype, rformat);
 
 	/* We no longer need the snapshot */
 	PopActiveSnapshot();
@@ -450,11 +450,11 @@ parse_fcall_arguments(StringInfo msgBuf, struct fp_info *fip,
 		argsize = pq_getmsgint(msgBuf, 4);
 		if (argsize == -1)
 		{
-			fcinfo->argnull[i] = true;
+			fcinfo->args[i].isnull = true;
 		}
 		else
 		{
-			fcinfo->argnull[i] = false;
+			fcinfo->args[i].isnull = false;
 			if (argsize < 0)
 				ereport(ERROR,
 						(errcode(ERRCODE_PROTOCOL_VIOLATION),
@@ -494,8 +494,8 @@ parse_fcall_arguments(StringInfo msgBuf, struct fp_info *fip,
 			else
 				pstring = pg_client_to_server(abuf.data, argsize);
 
-			fcinfo->arg[i] = OidInputFunctionCall(typinput, pstring,
-												  typioparam, -1);
+			fcinfo->args[i].datum = OidInputFunctionCall(typinput, pstring,
+														 typioparam, -1);
 			/* Free result of encoding conversion, if any */
 			if (pstring && pstring != abuf.data)
 				pfree(pstring);
@@ -514,8 +514,8 @@ parse_fcall_arguments(StringInfo msgBuf, struct fp_info *fip,
 			else
 				bufptr = &abuf;
 
-			fcinfo->arg[i] = OidReceiveFunctionCall(typreceive, bufptr,
-													typioparam, -1);
+			fcinfo->args[i].datum = OidReceiveFunctionCall(typreceive, bufptr,
+														   typioparam, -1);
 
 			/* Trouble if it didn't eat the whole buffer */
 			if (argsize != -1 && abuf.cursor != abuf.len)
@@ -579,12 +579,13 @@ parse_fcall_arguments_20(StringInfo msgBuf, struct fp_info *fip,
 		argsize = pq_getmsgint(msgBuf, 4);
 		if (argsize == -1)
 		{
-			fcinfo->argnull[i] = true;
-			fcinfo->arg[i] = OidReceiveFunctionCall(typreceive, NULL,
-													typioparam, -1);
+			/* FIXME: huh? */
+			fcinfo->args[i].isnull = true;
+			fcinfo->args[i].datum = OidReceiveFunctionCall(typreceive, NULL,
+														   typioparam, -1);
 			continue;
 		}
-		fcinfo->argnull[i] = false;
+		fcinfo->args[i].isnull = false;
 		if (argsize < 0)
 			ereport(ERROR,
 					(errcode(ERRCODE_PROTOCOL_VIOLATION),
@@ -597,8 +598,8 @@ parse_fcall_arguments_20(StringInfo msgBuf, struct fp_info *fip,
 							   pq_getmsgbytes(msgBuf, argsize),
 							   argsize);
 
-		fcinfo->arg[i] = OidReceiveFunctionCall(typreceive, &abuf,
-												typioparam, -1);
+		fcinfo->args[i].datum = OidReceiveFunctionCall(typreceive, &abuf,
+													   typioparam, -1);
 
 		/* Trouble if it didn't eat the whole buffer */
 		if (abuf.cursor != abuf.len)
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index ce1ded888f1..577e8637768 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -3590,7 +3590,7 @@ array_eq(PG_FUNCTION_ARGS)
 	array_iter	it1;
 	array_iter	it2;
 	int			i;
-	FunctionCallInfoData locfcinfo;
+	STACK_FCINFO_FOR_ARGS(locfcinfo, 3);
 
 	if (element_type != AARR_ELEMTYPE(array2))
 		ereport(ERROR,
@@ -3630,7 +3630,7 @@ array_eq(PG_FUNCTION_ARGS)
 		/*
 		 * apply the operator to each pair of array elements.
 		 */
-		InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
+		InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
 								 collation, NULL, NULL);
 
 		/* Loop over source data */
@@ -3666,12 +3666,12 @@ array_eq(PG_FUNCTION_ARGS)
 			/*
 			 * Apply the operator to the element pair
 			 */
-			locfcinfo.arg[0] = elt1;
-			locfcinfo.arg[1] = elt2;
-			locfcinfo.argnull[0] = false;
-			locfcinfo.argnull[1] = false;
-			locfcinfo.isnull = false;
-			oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
+			locfcinfo->args[0].datum = elt1;
+			locfcinfo->args[0].isnull = false;
+			locfcinfo->args[1].datum = elt2;
+			locfcinfo->args[1].isnull = false;
+			locfcinfo->isnull = false;
+			oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
 			if (!oprresult)
 			{
 				result = false;
@@ -3761,7 +3761,7 @@ array_cmp(FunctionCallInfo fcinfo)
 	array_iter	it1;
 	array_iter	it2;
 	int			i;
-	FunctionCallInfoData locfcinfo;
+	STACK_FCINFO_FOR_ARGS(locfcinfo, 2);
 
 	if (element_type != AARR_ELEMTYPE(array2))
 		ereport(ERROR,
@@ -3794,7 +3794,7 @@ array_cmp(FunctionCallInfo fcinfo)
 	/*
 	 * apply the operator to each pair of array elements.
 	 */
-	InitFunctionCallInfoData(locfcinfo, &typentry->cmp_proc_finfo, 2,
+	InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
 							 collation, NULL, NULL);
 
 	/* Loop over source data */
@@ -3833,12 +3833,12 @@ array_cmp(FunctionCallInfo fcinfo)
 		}
 
 		/* Compare the pair of elements */
-		locfcinfo.arg[0] = elt1;
-		locfcinfo.arg[1] = elt2;
-		locfcinfo.argnull[0] = false;
-		locfcinfo.argnull[1] = false;
-		locfcinfo.isnull = false;
-		cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
+		locfcinfo->args[0].datum = elt1;
+		locfcinfo->args[0].isnull = false;
+		locfcinfo->args[1].datum = elt2;
+		locfcinfo->args[1].isnull = false;
+		locfcinfo->isnull = false;
+		cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
 
 		if (cmpresult == 0)
 			continue;			/* equal */
@@ -3925,7 +3925,7 @@ hash_array(PG_FUNCTION_ARGS)
 	char		typalign;
 	int			i;
 	array_iter	iter;
-	FunctionCallInfoData locfcinfo;
+	STACK_FCINFO_FOR_ARGS(locfcinfo, 1);
 
 	/*
 	 * We arrange to look up the hash function only once per series of calls,
@@ -3953,7 +3953,7 @@ hash_array(PG_FUNCTION_ARGS)
 	/*
 	 * apply the hash function to each array element.
 	 */
-	InitFunctionCallInfoData(locfcinfo, &typentry->hash_proc_finfo, 1,
+	InitFunctionCallInfoData(*locfcinfo, &typentry->hash_proc_finfo, 1,
 							 InvalidOid, NULL, NULL);
 
 	/* Loop over source data */
@@ -3977,10 +3977,10 @@ hash_array(PG_FUNCTION_ARGS)
 		else
 		{
 			/* Apply the hash function */
-			locfcinfo.arg[0] = elt;
-			locfcinfo.argnull[0] = false;
-			locfcinfo.isnull = false;
-			elthash = DatumGetUInt32(FunctionCallInvoke(&locfcinfo));
+			locfcinfo->args[0].datum = elt;
+			locfcinfo->args[0].isnull = false;
+			locfcinfo->isnull = false;
+			elthash = DatumGetUInt32(FunctionCallInvoke(locfcinfo));
 		}
 
 		/*
@@ -4023,7 +4023,7 @@ hash_array_extended(PG_FUNCTION_ARGS)
 	char		typalign;
 	int			i;
 	array_iter	iter;
-	FunctionCallInfoData locfcinfo;
+	STACK_FCINFO_FOR_ARGS(locfcinfo, 2);
 
 	typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
 	if (typentry == NULL ||
@@ -4042,7 +4042,7 @@ hash_array_extended(PG_FUNCTION_ARGS)
 	typbyval = typentry->typbyval;
 	typalign = typentry->typalign;
 
-	InitFunctionCallInfoData(locfcinfo, &typentry->hash_extended_proc_finfo, 2,
+	InitFunctionCallInfoData(*locfcinfo, &typentry->hash_extended_proc_finfo, 2,
 							 InvalidOid, NULL, NULL);
 
 	/* Loop over source data */
@@ -4065,12 +4065,11 @@ hash_array_extended(PG_FUNCTION_ARGS)
 		else
 		{
 			/* Apply the hash function */
-			locfcinfo.arg[0] = elt;
-			locfcinfo.arg[1] = Int64GetDatum(seed);
-			locfcinfo.argnull[0] = false;
-			locfcinfo.argnull[1] = false;
-			locfcinfo.isnull = false;
-			elthash = DatumGetUInt64(FunctionCallInvoke(&locfcinfo));
+			locfcinfo->args[0].datum = elt;
+			locfcinfo->args[0].isnull = false;
+			locfcinfo->args[1].datum = Int64GetDatum(seed);
+			locfcinfo->args[1].isnull = false;
+			elthash = DatumGetUInt64(FunctionCallInvoke(locfcinfo));
 		}
 
 		result = (result << 5) - result + elthash;
@@ -4113,7 +4112,7 @@ array_contain_compare(AnyArrayType *array1, AnyArrayType *array2, Oid collation,
 	int			i;
 	int			j;
 	array_iter	it1;
-	FunctionCallInfoData locfcinfo;
+	STACK_FCINFO_FOR_ARGS(locfcinfo, 2);
 
 	if (element_type != AARR_ELEMTYPE(array2))
 		ereport(ERROR,
@@ -4164,7 +4163,7 @@ array_contain_compare(AnyArrayType *array1, AnyArrayType *array2, Oid collation,
 	/*
 	 * Apply the comparison operator to each pair of array elements.
 	 */
-	InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
+	InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
 							 collation, NULL, NULL);
 
 	/* Loop over source data */
@@ -4206,12 +4205,12 @@ array_contain_compare(AnyArrayType *array1, AnyArrayType *array2, Oid collation,
 			/*
 			 * Apply the operator to the element pair
 			 */
-			locfcinfo.arg[0] = elt1;
-			locfcinfo.arg[1] = elt2;
-			locfcinfo.argnull[0] = false;
-			locfcinfo.argnull[1] = false;
-			locfcinfo.isnull = false;
-			oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
+			locfcinfo->args[0].datum = elt1;
+			locfcinfo->args[0].isnull = false;
+			locfcinfo->args[1].datum = elt2;
+			locfcinfo->args[1].isnull = false;
+			locfcinfo->isnull = false;
+			oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
 			if (oprresult)
 				break;
 		}
@@ -6062,7 +6061,7 @@ array_replace_internal(ArrayType *array,
 	int			bitmask;
 	bool		changed = false;
 	TypeCacheEntry *typentry;
-	FunctionCallInfoData locfcinfo;
+	STACK_FCINFO_FOR_ARGS(locfcinfo, 2);
 
 	element_type = ARR_ELEMTYPE(array);
 	ndim = ARR_NDIM(array);
@@ -6117,7 +6116,7 @@ array_replace_internal(ArrayType *array,
 	}
 
 	/* Prepare to apply the comparison operator */
-	InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
+	InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
 							 collation, NULL, NULL);
 
 	/* Allocate temporary arrays for new values */
@@ -6175,12 +6174,12 @@ array_replace_internal(ArrayType *array,
 				/*
 				 * Apply the operator to the element pair
 				 */
-				locfcinfo.arg[0] = elt;
-				locfcinfo.arg[1] = search;
-				locfcinfo.argnull[0] = false;
-				locfcinfo.argnull[1] = false;
-				locfcinfo.isnull = false;
-				oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
+				locfcinfo->args[0].datum = elt;
+				locfcinfo->args[0].isnull = false;
+				locfcinfo->args[1].datum = search;
+				locfcinfo->args[1].isnull = false;
+				locfcinfo->isnull = false;
+				oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
 				if (!oprresult)
 				{
 					/* no match, keep element */
@@ -6460,7 +6459,8 @@ width_bucket_array_fixed(Datum operand,
 	char	   *thresholds_data;
 	int			typlen = typentry->typlen;
 	bool		typbyval = typentry->typbyval;
-	FunctionCallInfoData locfcinfo;
+	STACK_FCINFO_FOR_ARGS(locfcinfo, 2);
+
 	int			left;
 	int			right;
 
@@ -6470,7 +6470,7 @@ width_bucket_array_fixed(Datum operand,
 	 */
 	thresholds_data = (char *) ARR_DATA_PTR(thresholds);
 
-	InitFunctionCallInfoData(locfcinfo, &typentry->cmp_proc_finfo, 2,
+	InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
 							 collation, NULL, NULL);
 
 	/* Find the bucket */
@@ -6484,13 +6484,13 @@ width_bucket_array_fixed(Datum operand,
 
 		ptr = thresholds_data + mid * typlen;
 
-		locfcinfo.arg[0] = operand;
-		locfcinfo.arg[1] = fetch_att(ptr, typbyval, typlen);
-		locfcinfo.argnull[0] = false;
-		locfcinfo.argnull[1] = false;
-		locfcinfo.isnull = false;
+		locfcinfo->args[0].datum = operand;
+		locfcinfo->args[0].isnull = false;
+		locfcinfo->args[1].datum = fetch_att(ptr, typbyval, typlen);
+		locfcinfo->args[1].isnull = false;
+		locfcinfo->isnull = false;
 
-		cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
+		cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
 
 		if (cmpresult < 0)
 			right = mid;
@@ -6514,13 +6514,13 @@ width_bucket_array_variable(Datum operand,
 	int			typlen = typentry->typlen;
 	bool		typbyval = typentry->typbyval;
 	char		typalign = typentry->typalign;
-	FunctionCallInfoData locfcinfo;
+	STACK_FCINFO_FOR_ARGS(locfcinfo, 2);
 	int			left;
 	int			right;
 
 	thresholds_data = (char *) ARR_DATA_PTR(thresholds);
 
-	InitFunctionCallInfoData(locfcinfo, &typentry->cmp_proc_finfo, 2,
+	InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
 							 collation, NULL, NULL);
 
 	/* Find the bucket */
@@ -6541,13 +6541,12 @@ width_bucket_array_variable(Datum operand,
 			ptr = (char *) att_align_nominal(ptr, typalign);
 		}
 
-		locfcinfo.arg[0] = operand;
-		locfcinfo.arg[1] = fetch_att(ptr, typbyval, typlen);
-		locfcinfo.argnull[0] = false;
-		locfcinfo.argnull[1] = false;
-		locfcinfo.isnull = false;
+		locfcinfo->args[0].datum = operand;
+		locfcinfo->args[0].isnull = false;
+		locfcinfo->args[1].datum = fetch_att(ptr, typbyval, typlen);
+		locfcinfo->args[1].isnull = false;
 
-		cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
+		cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
 
 		if (cmpresult < 0)
 			right = mid;
diff --git a/src/backend/utils/adt/int.c b/src/backend/utils/adt/int.c
index 8149dc1369b..cb92b0f98bb 100644
--- a/src/backend/utils/adt/int.c
+++ b/src/backend/utils/adt/int.c
@@ -202,7 +202,7 @@ Datum
 int2vectorrecv(PG_FUNCTION_ARGS)
 {
 	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
-	FunctionCallInfoData locfcinfo;
+	STACK_FCINFO_FOR_ARGS(locfcinfo, 3);
 	int2vector *result;
 
 	/*
@@ -211,19 +211,19 @@ int2vectorrecv(PG_FUNCTION_ARGS)
 	 * fcinfo->flinfo->fn_extra.  So we need to pass it our own flinfo
 	 * parameter.
 	 */
-	InitFunctionCallInfoData(locfcinfo, fcinfo->flinfo, 3,
+	InitFunctionCallInfoData(*locfcinfo, fcinfo->flinfo, 3,
 							 InvalidOid, NULL, NULL);
 
-	locfcinfo.arg[0] = PointerGetDatum(buf);
-	locfcinfo.arg[1] = ObjectIdGetDatum(INT2OID);
-	locfcinfo.arg[2] = Int32GetDatum(-1);
-	locfcinfo.argnull[0] = false;
-	locfcinfo.argnull[1] = false;
-	locfcinfo.argnull[2] = false;
+	locfcinfo->args[0].datum = PointerGetDatum(buf);
+	locfcinfo->args[0].isnull = false;
+	locfcinfo->args[1].datum = ObjectIdGetDatum(INT2OID);
+	locfcinfo->args[1].isnull = false;
+	locfcinfo->args[2].datum = Int32GetDatum(-1);
+	locfcinfo->args[2].isnull = false;
 
-	result = (int2vector *) DatumGetPointer(array_recv(&locfcinfo));
+	result = (int2vector *) DatumGetPointer(array_recv(locfcinfo));
 
-	Assert(!locfcinfo.isnull);
+	Assert(!locfcinfo->isnull);
 
 	/* sanity checks: int2vector must be 1-D, 0-based, no nulls */
 	if (ARR_NDIM(result) != 1 ||
diff --git a/src/backend/utils/adt/oid.c b/src/backend/utils/adt/oid.c
index b0670e0d9f7..cf50f843115 100644
--- a/src/backend/utils/adt/oid.c
+++ b/src/backend/utils/adt/oid.c
@@ -257,7 +257,7 @@ Datum
 oidvectorrecv(PG_FUNCTION_ARGS)
 {
 	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
-	FunctionCallInfoData locfcinfo;
+	STACK_FCINFO_FOR_ARGS(locfcinfo, 3);
 	oidvector  *result;
 
 	/*
@@ -266,19 +266,19 @@ oidvectorrecv(PG_FUNCTION_ARGS)
 	 * fcinfo->flinfo->fn_extra.  So we need to pass it our own flinfo
 	 * parameter.
 	 */
-	InitFunctionCallInfoData(locfcinfo, fcinfo->flinfo, 3,
+	InitFunctionCallInfoData(*locfcinfo, fcinfo->flinfo, 3,
 							 InvalidOid, NULL, NULL);
 
-	locfcinfo.arg[0] = PointerGetDatum(buf);
-	locfcinfo.arg[1] = ObjectIdGetDatum(OIDOID);
-	locfcinfo.arg[2] = Int32GetDatum(-1);
-	locfcinfo.argnull[0] = false;
-	locfcinfo.argnull[1] = false;
-	locfcinfo.argnull[2] = false;
+	locfcinfo->args[0].datum = PointerGetDatum(buf);
+	locfcinfo->args[0].isnull = false;
+	locfcinfo->args[1].datum = ObjectIdGetDatum(OIDOID);
+	locfcinfo->args[1].isnull = false;
+	locfcinfo->args[2].datum = Int32GetDatum(-1);
+	locfcinfo->args[2].isnull = false;
 
-	result = (oidvector *) DatumGetPointer(array_recv(&locfcinfo));
+	result = (oidvector *) DatumGetPointer(array_recv(locfcinfo));
 
-	Assert(!locfcinfo.isnull);
+	Assert(!locfcinfo->isnull);
 
 	/* sanity checks: oidvector must be 1-D, 0-based, no nulls */
 	if (ARR_NDIM(result) != 1 ||
diff --git a/src/backend/utils/adt/rowtypes.c b/src/backend/utils/adt/rowtypes.c
index 5f729342f8d..de2bd560eea 100644
--- a/src/backend/utils/adt/rowtypes.c
+++ b/src/backend/utils/adt/rowtypes.c
@@ -942,7 +942,7 @@ record_cmp(FunctionCallInfo fcinfo)
 		 */
 		if (!nulls1[i1] || !nulls2[i2])
 		{
-			FunctionCallInfoData locfcinfo;
+			STACK_FCINFO_FOR_ARGS(locfcinfo, 3);
 			int32		cmpresult;
 
 			if (nulls1[i1])
@@ -959,14 +959,14 @@ record_cmp(FunctionCallInfo fcinfo)
 			}
 
 			/* Compare the pair of elements */
-			InitFunctionCallInfoData(locfcinfo, &typentry->cmp_proc_finfo, 2,
+			InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
 									 collation, NULL, NULL);
-			locfcinfo.arg[0] = values1[i1];
-			locfcinfo.arg[1] = values2[i2];
-			locfcinfo.argnull[0] = false;
-			locfcinfo.argnull[1] = false;
-			locfcinfo.isnull = false;
-			cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
+			locfcinfo->args[0].datum = values1[i1];
+			locfcinfo->args[0].isnull = false;
+			locfcinfo->args[1].datum = values2[i2];
+			locfcinfo->args[1].isnull = false;
+			locfcinfo->isnull = false;
+			cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
 
 			if (cmpresult < 0)
 			{
@@ -1123,7 +1123,7 @@ record_eq(PG_FUNCTION_ARGS)
 		Form_pg_attribute att2;
 		TypeCacheEntry *typentry;
 		Oid			collation;
-		FunctionCallInfoData locfcinfo;
+		STACK_FCINFO_FOR_ARGS(locfcinfo, 3);
 		bool		oprresult;
 
 		/*
@@ -1193,14 +1193,14 @@ record_eq(PG_FUNCTION_ARGS)
 			}
 
 			/* Compare the pair of elements */
-			InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
+			InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
 									 collation, NULL, NULL);
-			locfcinfo.arg[0] = values1[i1];
-			locfcinfo.arg[1] = values2[i2];
-			locfcinfo.argnull[0] = false;
-			locfcinfo.argnull[1] = false;
-			locfcinfo.isnull = false;
-			oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
+			locfcinfo->args[0].datum = values1[i1];
+			locfcinfo->args[0].isnull = false;
+			locfcinfo->args[1].datum = values2[i2];
+			locfcinfo->args[1].isnull = false;
+			locfcinfo->isnull = false;
+			oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
 			if (!oprresult)
 			{
 				result = false;
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index 6cbbd5b78b0..86d5cef8108 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -782,7 +782,6 @@ fmgr_security_definer(PG_FUNCTION_ARGS)
  *		Support routines for callers of fmgr-compatible functions
  *-------------------------------------------------------------------------
  */
-
 /*
  * These are for invocation of a specifically named function with a
  * directly-computed parameter list.  Note that neither arguments nor result
@@ -792,18 +791,18 @@ fmgr_security_definer(PG_FUNCTION_ARGS)
 Datum
 DirectFunctionCall1Coll(PGFunction func, Oid collation, Datum arg1)
 {
-	FunctionCallInfoData fcinfo;
+	STACK_FCINFO_FOR_ARGS(fcinfo, 1);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, NULL, 1, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, NULL, 1, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.argnull[0] = false;
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
 
-	result = (*func) (&fcinfo);
+	result = (*func) (fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
+	if (fcinfo->isnull)
 		elog(ERROR, "function %p returned NULL", (void *) func);
 
 	return result;
@@ -812,20 +811,20 @@ DirectFunctionCall1Coll(PGFunction func, Oid collation, Datum arg1)
 Datum
 DirectFunctionCall2Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2)
 {
-	FunctionCallInfoData fcinfo;
+	STACK_FCINFO_FOR_ARGS(fcinfo, 2);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, NULL, 2, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, NULL, 2, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
+	fcinfo->args[1].datum = arg2;
+	fcinfo->args[1].isnull = false;
 
-	result = (*func) (&fcinfo);
+	result = (*func) (fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
+	if (fcinfo->isnull)
 		elog(ERROR, "function %p returned NULL", (void *) func);
 
 	return result;
@@ -835,22 +834,22 @@ Datum
 DirectFunctionCall3Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
 						Datum arg3)
 {
-	FunctionCallInfoData fcinfo;
+	STACK_FCINFO_FOR_ARGS(fcinfo, 3);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, NULL, 3, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, NULL, 3, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
+	fcinfo->args[1].datum = arg2;
+	fcinfo->args[1].isnull = false;
+	fcinfo->args[2].datum = arg3;
+	fcinfo->args[2].isnull = false;
 
-	result = (*func) (&fcinfo);
+	result = (*func) (fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
+	if (fcinfo->isnull)
 		elog(ERROR, "function %p returned NULL", (void *) func);
 
 	return result;
@@ -860,24 +859,24 @@ Datum
 DirectFunctionCall4Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
 						Datum arg3, Datum arg4)
 {
-	FunctionCallInfoData fcinfo;
+	STACK_FCINFO_FOR_ARGS(fcinfo, 4);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, NULL, 4, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, NULL, 4, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
+	fcinfo->args[1].datum = arg2;
+	fcinfo->args[1].isnull = false;
+	fcinfo->args[2].datum = arg3;
+	fcinfo->args[2].isnull = false;
+	fcinfo->args[3].datum = arg4;
+	fcinfo->args[3].isnull = false;
 
-	result = (*func) (&fcinfo);
+	result = (*func) (fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
+	if (fcinfo->isnull)
 		elog(ERROR, "function %p returned NULL", (void *) func);
 
 	return result;
@@ -887,26 +886,26 @@ Datum
 DirectFunctionCall5Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
 						Datum arg3, Datum arg4, Datum arg5)
 {
-	FunctionCallInfoData fcinfo;
+	STACK_FCINFO_FOR_ARGS(fcinfo, 5);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, NULL, 5, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, NULL, 5, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
+	fcinfo->args[1].datum = arg2;
+	fcinfo->args[1].isnull = false;
+	fcinfo->args[2].datum = arg3;
+	fcinfo->args[2].isnull = false;
+	fcinfo->args[3].datum = arg4;
+	fcinfo->args[3].isnull = false;
+	fcinfo->args[4].datum = arg5;
+	fcinfo->args[4].isnull = false;
 
-	result = (*func) (&fcinfo);
+	result = (*func) (fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
+	if (fcinfo->isnull)
 		elog(ERROR, "function %p returned NULL", (void *) func);
 
 	return result;
@@ -917,28 +916,28 @@ DirectFunctionCall6Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
 						Datum arg3, Datum arg4, Datum arg5,
 						Datum arg6)
 {
-	FunctionCallInfoData fcinfo;
+	STACK_FCINFO_FOR_ARGS(fcinfo, 6);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, NULL, 6, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, NULL, 6, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.arg[5] = arg6;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
-	fcinfo.argnull[5] = false;
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
+	fcinfo->args[1].datum = arg2;
+	fcinfo->args[1].isnull = false;
+	fcinfo->args[2].datum = arg3;
+	fcinfo->args[2].isnull = false;
+	fcinfo->args[3].datum = arg4;
+	fcinfo->args[3].isnull = false;
+	fcinfo->args[4].datum = arg5;
+	fcinfo->args[4].isnull = false;
+	fcinfo->args[5].datum = arg6;
+	fcinfo->args[5].isnull = false;
 
-	result = (*func) (&fcinfo);
+	result = (*func) (fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
+	if (fcinfo->isnull)
 		elog(ERROR, "function %p returned NULL", (void *) func);
 
 	return result;
@@ -949,30 +948,30 @@ DirectFunctionCall7Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
 						Datum arg3, Datum arg4, Datum arg5,
 						Datum arg6, Datum arg7)
 {
-	FunctionCallInfoData fcinfo;
+	STACK_FCINFO_FOR_ARGS(fcinfo, 7);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, NULL, 7, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, NULL, 7, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.arg[5] = arg6;
-	fcinfo.arg[6] = arg7;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
-	fcinfo.argnull[5] = false;
-	fcinfo.argnull[6] = false;
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
+	fcinfo->args[1].datum = arg2;
+	fcinfo->args[1].isnull = false;
+	fcinfo->args[2].datum = arg3;
+	fcinfo->args[2].isnull = false;
+	fcinfo->args[3].datum = arg4;
+	fcinfo->args[3].isnull = false;
+	fcinfo->args[4].datum = arg5;
+	fcinfo->args[4].isnull = false;
+	fcinfo->args[5].datum = arg6;
+	fcinfo->args[5].isnull = false;
+	fcinfo->args[6].datum = arg7;
+	fcinfo->args[6].isnull = false;
 
-	result = (*func) (&fcinfo);
+	result = (*func) (fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
+	if (fcinfo->isnull)
 		elog(ERROR, "function %p returned NULL", (void *) func);
 
 	return result;
@@ -983,32 +982,32 @@ DirectFunctionCall8Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
 						Datum arg3, Datum arg4, Datum arg5,
 						Datum arg6, Datum arg7, Datum arg8)
 {
-	FunctionCallInfoData fcinfo;
+	STACK_FCINFO_FOR_ARGS(fcinfo, 8);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, NULL, 8, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, NULL, 8, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.arg[5] = arg6;
-	fcinfo.arg[6] = arg7;
-	fcinfo.arg[7] = arg8;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
-	fcinfo.argnull[5] = false;
-	fcinfo.argnull[6] = false;
-	fcinfo.argnull[7] = false;
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
+	fcinfo->args[1].datum = arg2;
+	fcinfo->args[1].isnull = false;
+	fcinfo->args[2].datum = arg3;
+	fcinfo->args[2].isnull = false;
+	fcinfo->args[3].datum = arg4;
+	fcinfo->args[3].isnull = false;
+	fcinfo->args[4].datum = arg5;
+	fcinfo->args[4].isnull = false;
+	fcinfo->args[5].datum = arg6;
+	fcinfo->args[5].isnull = false;
+	fcinfo->args[6].datum = arg7;
+	fcinfo->args[6].isnull = false;
+	fcinfo->args[7].datum = arg8;
+	fcinfo->args[7].isnull = false;
 
-	result = (*func) (&fcinfo);
+	result = (*func) (fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
+	if (fcinfo->isnull)
 		elog(ERROR, "function %p returned NULL", (void *) func);
 
 	return result;
@@ -1020,34 +1019,34 @@ DirectFunctionCall9Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
 						Datum arg6, Datum arg7, Datum arg8,
 						Datum arg9)
 {
-	FunctionCallInfoData fcinfo;
+	STACK_FCINFO_FOR_ARGS(fcinfo, 9);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, NULL, 9, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, NULL, 9, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.arg[5] = arg6;
-	fcinfo.arg[6] = arg7;
-	fcinfo.arg[7] = arg8;
-	fcinfo.arg[8] = arg9;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
-	fcinfo.argnull[5] = false;
-	fcinfo.argnull[6] = false;
-	fcinfo.argnull[7] = false;
-	fcinfo.argnull[8] = false;
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
+	fcinfo->args[1].datum = arg2;
+	fcinfo->args[1].isnull = false;
+	fcinfo->args[2].datum = arg3;
+	fcinfo->args[2].isnull = false;
+	fcinfo->args[3].datum = arg4;
+	fcinfo->args[3].isnull = false;
+	fcinfo->args[4].datum = arg5;
+	fcinfo->args[4].isnull = false;
+	fcinfo->args[5].datum = arg6;
+	fcinfo->args[5].isnull = false;
+	fcinfo->args[6].datum = arg7;
+	fcinfo->args[6].isnull = false;
+	fcinfo->args[7].datum = arg8;
+	fcinfo->args[7].isnull = false;
+	fcinfo->args[8].datum = arg9;
+	fcinfo->args[8].isnull = false;
 
-	result = (*func) (&fcinfo);
+	result = (*func) (fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
+	if (fcinfo->isnull)
 		elog(ERROR, "function %p returned NULL", (void *) func);
 
 	return result;
@@ -1065,18 +1064,18 @@ DirectFunctionCall9Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
 Datum
 CallerFInfoFunctionCall1(PGFunction func, FmgrInfo *flinfo, Oid collation, Datum arg1)
 {
-	FunctionCallInfoData fcinfo;
+	STACK_FCINFO_FOR_ARGS(fcinfo, 1);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, flinfo, 1, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, flinfo, 1, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.argnull[0] = false;
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
 
-	result = (*func) (&fcinfo);
+	result = (*func) (fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
+	if (fcinfo->isnull)
 		elog(ERROR, "function %p returned NULL", (void *) func);
 
 	return result;
@@ -1085,25 +1084,47 @@ CallerFInfoFunctionCall1(PGFunction func, FmgrInfo *flinfo, Oid collation, Datum
 Datum
 CallerFInfoFunctionCall2(PGFunction func, FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
 {
-	FunctionCallInfoData fcinfo;
+	STACK_FCINFO_FOR_ARGS(fcinfo, 2);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, flinfo, 2, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, flinfo, 2, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
+	fcinfo->args[1].datum = arg2;
+	fcinfo->args[1].isnull = false;
 
-	result = (*func) (&fcinfo);
+	result = (*func) (fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
+	if (fcinfo->isnull)
 		elog(ERROR, "function %p returned NULL", (void *) func);
 
 	return result;
 }
 
+/*
+ * These are for invocation of a previously-looked-up function with a
+ * directly-computed parameter list.  Note that neither arguments nor result
+ * are allowed to be NULL.
+ */
+Datum
+FunctionCall0Coll(FmgrInfo *flinfo, Oid collation)
+{
+	STACK_FCINFO_FOR_ARGS(fcinfo, 0);
+	Datum		result;
+
+	InitFunctionCallInfoData(*fcinfo, flinfo, 0, collation, NULL, NULL);
+
+	result = FunctionCallInvoke(fcinfo);
+
+	/* Check for null result, since caller is clearly not expecting one */
+	if (fcinfo->isnull)
+		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
+
+	return result;
+}
+
 /*
  * These are for invocation of a previously-looked-up function with a
  * directly-computed parameter list.  Note that neither arguments nor result
@@ -1112,19 +1133,19 @@ CallerFInfoFunctionCall2(PGFunction func, FmgrInfo *flinfo, Oid collation, Datum
 Datum
 FunctionCall1Coll(FmgrInfo *flinfo, Oid collation, Datum arg1)
 {
-	FunctionCallInfoData fcinfo;
+	STACK_FCINFO_FOR_ARGS(fcinfo, 1);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, flinfo, 1, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, flinfo, 1, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.argnull[0] = false;
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
 
-	result = FunctionCallInvoke(&fcinfo);
+	result = FunctionCallInvoke(fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid);
+	if (fcinfo->isnull)
+		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
 
 	return result;
 }
@@ -1132,21 +1153,21 @@ FunctionCall1Coll(FmgrInfo *flinfo, Oid collation, Datum arg1)
 Datum
 FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
 {
-	FunctionCallInfoData fcinfo;
+	STACK_FCINFO_FOR_ARGS(fcinfo, 2);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, flinfo, 2, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, flinfo, 2, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
+	fcinfo->args[1].datum = arg2;
+	fcinfo->args[1].isnull = false;
 
-	result = FunctionCallInvoke(&fcinfo);
+	result = FunctionCallInvoke(fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid);
+	if (fcinfo->isnull)
+		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
 
 	return result;
 }
@@ -1155,23 +1176,23 @@ Datum
 FunctionCall3Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
 				  Datum arg3)
 {
-	FunctionCallInfoData fcinfo;
+	STACK_FCINFO_FOR_ARGS(fcinfo, 3);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, flinfo, 3, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, flinfo, 3, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
+	fcinfo->args[1].datum = arg2;
+	fcinfo->args[1].isnull = false;
+	fcinfo->args[2].datum = arg3;
+	fcinfo->args[2].isnull = false;
 
-	result = FunctionCallInvoke(&fcinfo);
+	result = FunctionCallInvoke(fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid);
+	if (fcinfo->isnull)
+		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
 
 	return result;
 }
@@ -1180,25 +1201,25 @@ Datum
 FunctionCall4Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
 				  Datum arg3, Datum arg4)
 {
-	FunctionCallInfoData fcinfo;
+	STACK_FCINFO_FOR_ARGS(fcinfo, 4);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, flinfo, 4, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, flinfo, 4, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
+	fcinfo->args[1].datum = arg2;
+	fcinfo->args[1].isnull = false;
+	fcinfo->args[2].datum = arg3;
+	fcinfo->args[2].isnull = false;
+	fcinfo->args[3].datum = arg4;
+	fcinfo->args[3].isnull = false;
 
-	result = FunctionCallInvoke(&fcinfo);
+	result = FunctionCallInvoke(fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid);
+	if (fcinfo->isnull)
+		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
 
 	return result;
 }
@@ -1207,27 +1228,27 @@ Datum
 FunctionCall5Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
 				  Datum arg3, Datum arg4, Datum arg5)
 {
-	FunctionCallInfoData fcinfo;
+	STACK_FCINFO_FOR_ARGS(fcinfo, 5);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, flinfo, 5, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, flinfo, 5, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
+	fcinfo->args[1].datum = arg2;
+	fcinfo->args[1].isnull = false;
+	fcinfo->args[2].datum = arg3;
+	fcinfo->args[2].isnull = false;
+	fcinfo->args[3].datum = arg4;
+	fcinfo->args[3].isnull = false;
+	fcinfo->args[4].datum = arg5;
+	fcinfo->args[4].isnull = false;
 
-	result = FunctionCallInvoke(&fcinfo);
+	result = FunctionCallInvoke(fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid);
+	if (fcinfo->isnull)
+		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
 
 	return result;
 }
@@ -1237,29 +1258,29 @@ FunctionCall6Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
 				  Datum arg3, Datum arg4, Datum arg5,
 				  Datum arg6)
 {
-	FunctionCallInfoData fcinfo;
+	STACK_FCINFO_FOR_ARGS(fcinfo, 6);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, flinfo, 6, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, flinfo, 6, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.arg[5] = arg6;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
-	fcinfo.argnull[5] = false;
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
+	fcinfo->args[1].datum = arg2;
+	fcinfo->args[1].isnull = false;
+	fcinfo->args[2].datum = arg3;
+	fcinfo->args[2].isnull = false;
+	fcinfo->args[3].datum = arg4;
+	fcinfo->args[3].isnull = false;
+	fcinfo->args[4].datum = arg5;
+	fcinfo->args[4].isnull = false;
+	fcinfo->args[5].datum = arg6;
+	fcinfo->args[5].isnull = false;
 
-	result = FunctionCallInvoke(&fcinfo);
+	result = FunctionCallInvoke(fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid);
+	if (fcinfo->isnull)
+		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
 
 	return result;
 }
@@ -1269,31 +1290,31 @@ FunctionCall7Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
 				  Datum arg3, Datum arg4, Datum arg5,
 				  Datum arg6, Datum arg7)
 {
-	FunctionCallInfoData fcinfo;
+	STACK_FCINFO_FOR_ARGS(fcinfo, 7);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, flinfo, 7, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, flinfo, 7, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.arg[5] = arg6;
-	fcinfo.arg[6] = arg7;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
-	fcinfo.argnull[5] = false;
-	fcinfo.argnull[6] = false;
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
+	fcinfo->args[1].datum = arg2;
+	fcinfo->args[1].isnull = false;
+	fcinfo->args[2].datum = arg3;
+	fcinfo->args[2].isnull = false;
+	fcinfo->args[3].datum = arg4;
+	fcinfo->args[3].isnull = false;
+	fcinfo->args[4].datum = arg5;
+	fcinfo->args[4].isnull = false;
+	fcinfo->args[5].datum = arg6;
+	fcinfo->args[5].isnull = false;
+	fcinfo->args[6].datum = arg7;
+	fcinfo->args[6].isnull = false;
 
-	result = FunctionCallInvoke(&fcinfo);
+	result = FunctionCallInvoke(fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid);
+	if (fcinfo->isnull)
+		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
 
 	return result;
 }
@@ -1303,33 +1324,33 @@ FunctionCall8Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
 				  Datum arg3, Datum arg4, Datum arg5,
 				  Datum arg6, Datum arg7, Datum arg8)
 {
-	FunctionCallInfoData fcinfo;
+	STACK_FCINFO_FOR_ARGS(fcinfo, 8);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, flinfo, 8, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, flinfo, 8, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.arg[5] = arg6;
-	fcinfo.arg[6] = arg7;
-	fcinfo.arg[7] = arg8;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
-	fcinfo.argnull[5] = false;
-	fcinfo.argnull[6] = false;
-	fcinfo.argnull[7] = false;
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
+	fcinfo->args[1].datum = arg2;
+	fcinfo->args[1].isnull = false;
+	fcinfo->args[2].datum = arg3;
+	fcinfo->args[2].isnull = false;
+	fcinfo->args[3].datum = arg4;
+	fcinfo->args[3].isnull = false;
+	fcinfo->args[4].datum = arg5;
+	fcinfo->args[4].isnull = false;
+	fcinfo->args[5].datum = arg6;
+	fcinfo->args[5].isnull = false;
+	fcinfo->args[6].datum = arg7;
+	fcinfo->args[6].isnull = false;
+	fcinfo->args[7].datum = arg8;
+	fcinfo->args[7].isnull = false;
 
-	result = FunctionCallInvoke(&fcinfo);
+	result = FunctionCallInvoke(fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid);
+	if (fcinfo->isnull)
+		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
 
 	return result;
 }
@@ -1340,35 +1361,35 @@ FunctionCall9Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
 				  Datum arg6, Datum arg7, Datum arg8,
 				  Datum arg9)
 {
-	FunctionCallInfoData fcinfo;
+	STACK_FCINFO_FOR_ARGS(fcinfo, 9);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, flinfo, 9, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, flinfo, 9, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.arg[5] = arg6;
-	fcinfo.arg[6] = arg7;
-	fcinfo.arg[7] = arg8;
-	fcinfo.arg[8] = arg9;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
-	fcinfo.argnull[5] = false;
-	fcinfo.argnull[6] = false;
-	fcinfo.argnull[7] = false;
-	fcinfo.argnull[8] = false;
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
+	fcinfo->args[1].datum = arg2;
+	fcinfo->args[1].isnull = false;
+	fcinfo->args[2].datum = arg3;
+	fcinfo->args[2].isnull = false;
+	fcinfo->args[3].datum = arg4;
+	fcinfo->args[3].isnull = false;
+	fcinfo->args[4].datum = arg5;
+	fcinfo->args[4].isnull = false;
+	fcinfo->args[5].datum = arg6;
+	fcinfo->args[5].isnull = false;
+	fcinfo->args[6].datum = arg7;
+	fcinfo->args[6].isnull = false;
+	fcinfo->args[7].datum = arg8;
+	fcinfo->args[7].isnull = false;
+	fcinfo->args[8].datum = arg9;
+	fcinfo->args[8].isnull = false;
 
-	result = FunctionCallInvoke(&fcinfo);
+	result = FunctionCallInvoke(fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid);
+	if (fcinfo->isnull)
+		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
 
 	return result;
 }
@@ -1385,68 +1406,30 @@ Datum
 OidFunctionCall0Coll(Oid functionId, Oid collation)
 {
 	FmgrInfo	flinfo;
-	FunctionCallInfoData fcinfo;
-	Datum		result;
 
 	fmgr_info(functionId, &flinfo);
 
-	InitFunctionCallInfoData(fcinfo, &flinfo, 0, collation, NULL, NULL);
-
-	result = FunctionCallInvoke(&fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
-	return result;
+	return FunctionCall0Coll(&flinfo, collation);
 }
 
 Datum
 OidFunctionCall1Coll(Oid functionId, Oid collation, Datum arg1)
 {
 	FmgrInfo	flinfo;
-	FunctionCallInfoData fcinfo;
-	Datum		result;
 
 	fmgr_info(functionId, &flinfo);
 
-	InitFunctionCallInfoData(fcinfo, &flinfo, 1, collation, NULL, NULL);
-
-	fcinfo.arg[0] = arg1;
-	fcinfo.argnull[0] = false;
-
-	result = FunctionCallInvoke(&fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
-	return result;
+	return FunctionCall1Coll(&flinfo, collation, arg1);
 }
 
 Datum
 OidFunctionCall2Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2)
 {
 	FmgrInfo	flinfo;
-	FunctionCallInfoData fcinfo;
-	Datum		result;
 
 	fmgr_info(functionId, &flinfo);
 
-	InitFunctionCallInfoData(fcinfo, &flinfo, 2, collation, NULL, NULL);
-
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-
-	result = FunctionCallInvoke(&fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
-	return result;
+	return FunctionCall2Coll(&flinfo, collation, arg1, arg2);
 }
 
 Datum
@@ -1454,27 +1437,10 @@ OidFunctionCall3Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
 					 Datum arg3)
 {
 	FmgrInfo	flinfo;
-	FunctionCallInfoData fcinfo;
-	Datum		result;
 
 	fmgr_info(functionId, &flinfo);
 
-	InitFunctionCallInfoData(fcinfo, &flinfo, 3, collation, NULL, NULL);
-
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-
-	result = FunctionCallInvoke(&fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
-	return result;
+	return FunctionCall3Coll(&flinfo, collation, arg1, arg2, arg3);
 }
 
 Datum
@@ -1482,29 +1448,10 @@ OidFunctionCall4Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
 					 Datum arg3, Datum arg4)
 {
 	FmgrInfo	flinfo;
-	FunctionCallInfoData fcinfo;
-	Datum		result;
 
 	fmgr_info(functionId, &flinfo);
 
-	InitFunctionCallInfoData(fcinfo, &flinfo, 4, collation, NULL, NULL);
-
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-
-	result = FunctionCallInvoke(&fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
-	return result;
+	return FunctionCall4Coll(&flinfo, collation, arg1, arg2, arg3, arg4);
 }
 
 Datum
@@ -1512,31 +1459,10 @@ OidFunctionCall5Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
 					 Datum arg3, Datum arg4, Datum arg5)
 {
 	FmgrInfo	flinfo;
-	FunctionCallInfoData fcinfo;
-	Datum		result;
 
 	fmgr_info(functionId, &flinfo);
 
-	InitFunctionCallInfoData(fcinfo, &flinfo, 5, collation, NULL, NULL);
-
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
-
-	result = FunctionCallInvoke(&fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
-	return result;
+	return FunctionCall5Coll(&flinfo, collation, arg1, arg2, arg3, arg4, arg5);
 }
 
 Datum
@@ -1545,33 +1471,11 @@ OidFunctionCall6Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
 					 Datum arg6)
 {
 	FmgrInfo	flinfo;
-	FunctionCallInfoData fcinfo;
-	Datum		result;
 
 	fmgr_info(functionId, &flinfo);
 
-	InitFunctionCallInfoData(fcinfo, &flinfo, 6, collation, NULL, NULL);
-
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.arg[5] = arg6;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
-	fcinfo.argnull[5] = false;
-
-	result = FunctionCallInvoke(&fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
-	return result;
+	return FunctionCall6Coll(&flinfo, collation, arg1, arg2, arg3, arg4, arg5,
+							 arg6);
 }
 
 Datum
@@ -1580,35 +1484,11 @@ OidFunctionCall7Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
 					 Datum arg6, Datum arg7)
 {
 	FmgrInfo	flinfo;
-	FunctionCallInfoData fcinfo;
-	Datum		result;
 
 	fmgr_info(functionId, &flinfo);
 
-	InitFunctionCallInfoData(fcinfo, &flinfo, 7, collation, NULL, NULL);
-
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.arg[5] = arg6;
-	fcinfo.arg[6] = arg7;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
-	fcinfo.argnull[5] = false;
-	fcinfo.argnull[6] = false;
-
-	result = FunctionCallInvoke(&fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
-	return result;
+	return FunctionCall7Coll(&flinfo, collation, arg1, arg2, arg3, arg4, arg5,
+							 arg6, arg7);
 }
 
 Datum
@@ -1617,37 +1497,11 @@ OidFunctionCall8Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
 					 Datum arg6, Datum arg7, Datum arg8)
 {
 	FmgrInfo	flinfo;
-	FunctionCallInfoData fcinfo;
-	Datum		result;
 
 	fmgr_info(functionId, &flinfo);
 
-	InitFunctionCallInfoData(fcinfo, &flinfo, 8, collation, NULL, NULL);
-
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.arg[5] = arg6;
-	fcinfo.arg[6] = arg7;
-	fcinfo.arg[7] = arg8;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
-	fcinfo.argnull[5] = false;
-	fcinfo.argnull[6] = false;
-	fcinfo.argnull[7] = false;
-
-	result = FunctionCallInvoke(&fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
-	return result;
+	return FunctionCall8Coll(&flinfo, collation, arg1, arg2, arg3, arg4, arg5,
+							 arg6, arg7, arg8);
 }
 
 Datum
@@ -1657,39 +1511,11 @@ OidFunctionCall9Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
 					 Datum arg9)
 {
 	FmgrInfo	flinfo;
-	FunctionCallInfoData fcinfo;
-	Datum		result;
 
 	fmgr_info(functionId, &flinfo);
 
-	InitFunctionCallInfoData(fcinfo, &flinfo, 9, collation, NULL, NULL);
-
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.arg[5] = arg6;
-	fcinfo.arg[6] = arg7;
-	fcinfo.arg[7] = arg8;
-	fcinfo.arg[8] = arg9;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
-	fcinfo.argnull[5] = false;
-	fcinfo.argnull[6] = false;
-	fcinfo.argnull[7] = false;
-	fcinfo.argnull[8] = false;
-
-	result = FunctionCallInvoke(&fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
-	return result;
+	return FunctionCall9Coll(&flinfo, collation, arg1, arg2, arg3, arg4, arg5,
+							 arg6, arg7, arg8, arg9);
 }
 
 
@@ -1708,35 +1534,35 @@ OidFunctionCall9Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
 Datum
 InputFunctionCall(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod)
 {
-	FunctionCallInfoData fcinfo;
+	STACK_FCINFO_FOR_ARGS(fcinfo, 3);
 	Datum		result;
 
 	if (str == NULL && flinfo->fn_strict)
 		return (Datum) 0;		/* just return null result */
 
-	InitFunctionCallInfoData(fcinfo, flinfo, 3, InvalidOid, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, flinfo, 3, InvalidOid, 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;
+	fcinfo->args[0].datum = CStringGetDatum(str);
+	fcinfo->args[0].isnull = false;
+	fcinfo->args[1].datum = ObjectIdGetDatum(typioparam);
+	fcinfo->args[1].isnull = false;
+	fcinfo->args[2].datum = Int32GetDatum(typmod);
+	fcinfo->args[2].isnull = false;
 
-	result = FunctionCallInvoke(&fcinfo);
+	result = FunctionCallInvoke(fcinfo);
 
 	/* Should get null result if and only if str is NULL */
 	if (str == NULL)
 	{
-		if (!fcinfo.isnull)
+		if (!fcinfo->isnull)
 			elog(ERROR, "input function %u returned non-NULL",
-				 fcinfo.flinfo->fn_oid);
+				 flinfo->fn_oid);
 	}
 	else
 	{
-		if (fcinfo.isnull)
+		if (fcinfo->isnull)
 			elog(ERROR, "input function %u returned NULL",
-				 fcinfo.flinfo->fn_oid);
+				 flinfo->fn_oid);
 	}
 
 	return result;
@@ -1767,35 +1593,35 @@ Datum
 ReceiveFunctionCall(FmgrInfo *flinfo, StringInfo buf,
 					Oid typioparam, int32 typmod)
 {
-	FunctionCallInfoData fcinfo;
+	STACK_FCINFO_FOR_ARGS(fcinfo, 3);
 	Datum		result;
 
 	if (buf == NULL && flinfo->fn_strict)
 		return (Datum) 0;		/* just return null result */
 
-	InitFunctionCallInfoData(fcinfo, flinfo, 3, InvalidOid, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, flinfo, 3, InvalidOid, 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;
+	fcinfo->args[0].datum = PointerGetDatum(buf);
+	fcinfo->args[0].isnull = false;
+	fcinfo->args[1].datum = ObjectIdGetDatum(typioparam);
+	fcinfo->args[1].isnull = false;
+	fcinfo->args[2].datum = Int32GetDatum(typmod);
+	fcinfo->args[2].isnull = false;
 
-	result = FunctionCallInvoke(&fcinfo);
+	result = FunctionCallInvoke(fcinfo);
 
 	/* Should get null result if and only if buf is NULL */
 	if (buf == NULL)
 	{
-		if (!fcinfo.isnull)
+		if (!fcinfo->isnull)
 			elog(ERROR, "receive function %u returned non-NULL",
-				 fcinfo.flinfo->fn_oid);
+				 flinfo->fn_oid);
 	}
 	else
 	{
-		if (fcinfo.isnull)
+		if (fcinfo->isnull)
 			elog(ERROR, "receive function %u returned NULL",
-				 fcinfo.flinfo->fn_oid);
+				 flinfo->fn_oid);
 	}
 
 	return result;
diff --git a/src/backend/utils/sort/sortsupport.c b/src/backend/utils/sort/sortsupport.c
index 8c09fe4a430..31aa4100d67 100644
--- a/src/backend/utils/sort/sortsupport.c
+++ b/src/backend/utils/sort/sortsupport.c
@@ -26,10 +26,11 @@
 /* Info needed to use an old-style comparison function as a sort comparator */
 typedef struct
 {
-	FunctionCallInfoData fcinfo;	/* reusable callinfo structure */
 	FmgrInfo	flinfo;			/* lookup data for comparison function */
+	FunctionCallInfoData fcinfo;	/* reusable callinfo structure */
 } SortShimExtra;
 
+#define SizeForSortShimExtra(nargs) (offsetof(SortShimExtra, fcinfo) + SizeForFunctionCallInfoData(nargs))
 
 /*
  * Shim function for calling an old-style comparator
@@ -44,8 +45,8 @@ comparison_shim(Datum x, Datum y, SortSupport ssup)
 	SortShimExtra *extra = (SortShimExtra *) ssup->ssup_extra;
 	Datum		result;
 
-	extra->fcinfo.arg[0] = x;
-	extra->fcinfo.arg[1] = y;
+	extra->fcinfo.args[0].datum = x;
+	extra->fcinfo.args[1].datum = y;
 
 	/* just for paranoia's sake, we reset isnull each time */
 	extra->fcinfo.isnull = false;
@@ -69,7 +70,7 @@ PrepareSortSupportComparisonShim(Oid cmpFunc, SortSupport ssup)
 	SortShimExtra *extra;
 
 	extra = (SortShimExtra *) MemoryContextAlloc(ssup->ssup_cxt,
-												 sizeof(SortShimExtra));
+												 SizeForSortShimExtra(2));
 
 	/* Lookup the comparison function */
 	fmgr_info_cxt(cmpFunc, &extra->flinfo, ssup->ssup_cxt);
@@ -77,8 +78,8 @@ PrepareSortSupportComparisonShim(Oid cmpFunc, SortSupport ssup)
 	/* We can initialize the callinfo just once and re-use it */
 	InitFunctionCallInfoData(extra->fcinfo, &extra->flinfo, 2,
 							 ssup->ssup_collation, NULL, NULL);
-	extra->fcinfo.argnull[0] = false;
-	extra->fcinfo.argnull[1] = false;
+	extra->fcinfo.args[0].isnull = false;
+	extra->fcinfo.args[1].isnull = false;
 
 	ssup->ssup_extra = extra;
 	ssup->comparator = comparison_shim;
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index 02af68f2c25..8233a465189 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -223,7 +223,8 @@ typedef enum ExprEvalOp
 	/* aggregation related nodes */
 	EEOP_AGG_STRICT_DESERIALIZE,
 	EEOP_AGG_DESERIALIZE,
-	EEOP_AGG_STRICT_INPUT_CHECK,
+	EEOP_AGG_STRICT_INPUT_CHECK_ARGS,
+	EEOP_AGG_STRICT_INPUT_CHECK_NULLS,
 	EEOP_AGG_INIT_TRANS,
 	EEOP_AGG_STRICT_TRANS_CHECK,
 	EEOP_AGG_PLAIN_TRANS_BYVAL,
@@ -599,7 +600,8 @@ typedef struct ExprEvalStep
 		/* for EEOP_AGG_STRICT_INPUT_CHECK */
 		struct
 		{
-			bool	   *nulls;
+			NullableDatum *args;
+			bool *nulls;
 			int			nargs;
 			int			jumpnull;
 		}			agg_strict_input_check;
diff --git a/src/include/executor/nodeAgg.h b/src/include/executor/nodeAgg.h
index 8fb8c8fe802..51aec33fd8d 100644
--- a/src/include/executor/nodeAgg.h
+++ b/src/include/executor/nodeAgg.h
@@ -158,12 +158,12 @@ typedef struct AggStatePerTransData
 	 * re-initializing the unchanging fields; which isn't much, but it seems
 	 * worth the extra space consumption.
 	 */
-	FunctionCallInfoData transfn_fcinfo;
+	FunctionCallInfo transfn_fcinfo;
 
 	/* Likewise for serialization and deserialization functions */
-	FunctionCallInfoData serialfn_fcinfo;
+	FunctionCallInfo serialfn_fcinfo;
 
-	FunctionCallInfoData deserialfn_fcinfo;
+	FunctionCallInfo deserialfn_fcinfo;
 }			AggStatePerTransData;
 
 /*
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index 101f513ba67..ee1dbd5fcbb 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -83,12 +83,28 @@ typedef struct FunctionCallInfoData
 #define FIELDNO_FUNCTIONCALLINFODATA_ISNULL 4
 	bool		isnull;			/* function must set true if result is NULL */
 	short		nargs;			/* # arguments actually passed */
-#define FIELDNO_FUNCTIONCALLINFODATA_ARG 6
-	Datum		arg[FUNC_MAX_ARGS]; /* Arguments passed to function */
-#define FIELDNO_FUNCTIONCALLINFODATA_ARGNULL 7
-	bool		argnull[FUNC_MAX_ARGS]; /* T if arg[i] is actually NULL */
+#define FIELDNO_FUNCTIONCALLINFODATA_ARGS 6
+
+	NullableDatum args[FLEXIBLE_ARRAY_MEMBER];
+
 } FunctionCallInfoData;
 
+#define SizeForFunctionCallInfoData(nargs) (offsetof(FunctionCallInfoData, args) + sizeof(NullableDatum) * (nargs))
+
+/*
+ * This macro ensures that `name` points to a stack-allocated
+ * FunctionCallInfoData struct with sufficient space for `nargs` arguments.
+ */
+#define STACK_FCINFO_FOR_ARGS(name, nargs) \
+	union \
+	{ \
+		FunctionCallInfoData fcinfo; \
+		/* ensure enough space for nargs args is available */ \
+		char *fcinfo_data[SizeForFunctionCallInfoData(nargs)]; \
+	} name##data; \
+	FunctionCallInfo name = &name##data.fcinfo
+
+
 /*
  * This routine fills a FmgrInfo struct, given the OID
  * of the function to be called.
@@ -141,7 +157,6 @@ extern void fmgr_symbol(Oid functionId, char **mod, char **fn);
  */
 #define FunctionCallInvoke(fcinfo)	((* (fcinfo)->flinfo->fn_addr) (fcinfo))
 
-
 /*-------------------------------------------------------------------------
  *		Support macros to ease writing fmgr-compatible functions
  *
@@ -176,7 +191,7 @@ extern void fmgr_symbol(Oid functionId, char **mod, char **fn);
  * If function is not marked "proisstrict" in pg_proc, it must check for
  * null arguments using this macro.  Do not try to GETARG a null argument!
  */
-#define PG_ARGISNULL(n)  (fcinfo->argnull[n])
+#define PG_ARGISNULL(n)  (fcinfo->args[n].isnull)
 
 /*
  * Support for fetching detoasted copies of toastable datatypes (all of
@@ -235,7 +250,7 @@ extern struct varlena *pg_detoast_datum_packed(struct varlena *datum);
 
 /* Macros for fetching arguments of standard types */
 
-#define PG_GETARG_DATUM(n)	 (fcinfo->arg[n])
+#define PG_GETARG_DATUM(n)	 (fcinfo->args[n].datum)
 #define PG_GETARG_INT32(n)	 DatumGetInt32(PG_GETARG_DATUM(n))
 #define PG_GETARG_UINT32(n)  DatumGetUInt32(PG_GETARG_DATUM(n))
 #define PG_GETARG_INT16(n)	 DatumGetInt16(PG_GETARG_DATUM(n))
@@ -514,6 +529,7 @@ extern Datum CallerFInfoFunctionCall2(PGFunction func, FmgrInfo *flinfo,
  * directly-computed parameter list.  Note that neither arguments nor result
  * are allowed to be NULL.
  */
+extern Datum FunctionCall0Coll(FmgrInfo *flinfo, Oid collation);
 extern Datum FunctionCall1Coll(FmgrInfo *flinfo, Oid collation,
 				  Datum arg1);
 extern Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation,
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 834708944bf..e04573f11ae 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -828,7 +828,7 @@ typedef struct SetExprState
 	 * (by InitFunctionCallInfoData) if func.fn_oid is valid.  It also saves
 	 * argument values between calls, when setArgsValid is true.
 	 */
-	FunctionCallInfoData fcinfo_data;
+	FunctionCallInfo fcinfo;
 } SetExprState;
 
 /* ----------------
diff --git a/src/include/postgres.h b/src/include/postgres.h
index b596fcb513e..b3ff25dc6e2 100644
--- a/src/include/postgres.h
+++ b/src/include/postgres.h
@@ -366,6 +366,13 @@ typedef struct
 
 typedef uintptr_t Datum;
 
+typedef struct NullableDatum
+{
+	Datum datum;
+	bool isnull;
+	/* FIXME: space for flags? */
+} NullableDatum;
+
 #define SIZEOF_DATUM SIZEOF_VOID_P
 
 /*
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index 4cfc5062531..3d4350a16c9 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -1877,7 +1877,7 @@ Datum
 plperl_inline_handler(PG_FUNCTION_ARGS)
 {
 	InlineCodeBlock *codeblock = (InlineCodeBlock *) PG_GETARG_POINTER(0);
-	FunctionCallInfoData fake_fcinfo;
+	STACK_FCINFO_FOR_ARGS(fake_fcinfo, 0);
 	FmgrInfo	flinfo;
 	plperl_proc_desc desc;
 	plperl_call_data *volatile save_call_data = current_call_data;
@@ -1899,10 +1899,10 @@ plperl_inline_handler(PG_FUNCTION_ARGS)
 	 * plperl_call_perl_func().  In particular note that this sets things up
 	 * with no arguments passed, and a result type of VOID.
 	 */
-	MemSet(&fake_fcinfo, 0, sizeof(fake_fcinfo));
+	MemSet(&fake_fcinfodata, 0, sizeof(fake_fcinfodata));
 	MemSet(&flinfo, 0, sizeof(flinfo));
 	MemSet(&desc, 0, sizeof(desc));
-	fake_fcinfo.flinfo = &flinfo;
+	fake_fcinfo->flinfo = &flinfo;
 	flinfo.fn_oid = InvalidOid;
 	flinfo.fn_mcxt = CurrentMemoryContext;
 
@@ -1920,7 +1920,7 @@ plperl_inline_handler(PG_FUNCTION_ARGS)
 	desc.nargs = 0;
 	desc.reference = NULL;
 
-	this_call_data.fcinfo = &fake_fcinfo;
+	this_call_data.fcinfo = fake_fcinfo;
 	this_call_data.prodesc = &desc;
 	/* we do not bother with refcounting the fake prodesc */
 
@@ -1940,7 +1940,7 @@ plperl_inline_handler(PG_FUNCTION_ARGS)
 		if (!desc.reference)	/* can this happen? */
 			elog(ERROR, "could not create internal procedure for anonymous code block");
 
-		perlret = plperl_call_perl_func(&desc, &fake_fcinfo);
+		perlret = plperl_call_perl_func(&desc, fake_fcinfo);
 
 		SvREFCNT_dec_current(perlret);
 
@@ -2194,11 +2194,11 @@ plperl_call_perl_func(plperl_proc_desc *desc, FunctionCallInfo fcinfo)
 
 	for (i = 0; i < desc->nargs; i++)
 	{
-		if (fcinfo->argnull[i])
+		if (fcinfo->args[i].isnull)
 			PUSHs(&PL_sv_undef);
 		else if (desc->arg_is_rowtype[i])
 		{
-			SV		   *sv = plperl_hash_from_datum(fcinfo->arg[i]);
+			SV		   *sv = plperl_hash_from_datum(fcinfo->args[i].datum);
 
 			PUSHs(sv_2mortal(sv));
 		}
@@ -2208,15 +2208,15 @@ plperl_call_perl_func(plperl_proc_desc *desc, FunctionCallInfo fcinfo)
 			Oid			funcid;
 
 			if (OidIsValid(desc->arg_arraytype[i]))
-				sv = plperl_ref_from_pg_array(fcinfo->arg[i], desc->arg_arraytype[i]);
+				sv = plperl_ref_from_pg_array(fcinfo->args[i].datum, desc->arg_arraytype[i]);
 			else if ((funcid = get_transform_fromsql(argtypes[i], current_call_data->prodesc->lang_oid, current_call_data->prodesc->trftypes)))
-				sv = (SV *) DatumGetPointer(OidFunctionCall1(funcid, fcinfo->arg[i]));
+				sv = (SV *) DatumGetPointer(OidFunctionCall1(funcid, fcinfo->args[i].datum));
 			else
 			{
 				char	   *tmp;
 
 				tmp = OutputFunctionCall(&(desc->arg_out_func[i]),
-										 fcinfo->arg[i]);
+										 fcinfo->args[i].datum);
 				sv = cstr2sv(tmp);
 				pfree(tmp);
 			}
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 45526383f25..665118cfe82 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -489,8 +489,8 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo,
 					PLpgSQL_var *var = (PLpgSQL_var *) estate.datums[n];
 
 					assign_simple_var(&estate, var,
-									  fcinfo->arg[i],
-									  fcinfo->argnull[i],
+									  fcinfo->args[i].datum,
+									  fcinfo->args[i].isnull,
 									  false);
 
 					/*
@@ -541,12 +541,12 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo,
 				{
 					PLpgSQL_rec *rec = (PLpgSQL_rec *) estate.datums[n];
 
-					if (!fcinfo->argnull[i])
+					if (!fcinfo->args[i].isnull)
 					{
 						/* Assign row value from composite datum */
 						exec_move_row_from_datum(&estate,
 												 (PLpgSQL_variable *) rec,
-												 fcinfo->arg[i]);
+												 fcinfo->args[i].datum);
 					}
 					else
 					{
diff --git a/src/pl/plpython/plpy_exec.c b/src/pl/plpython/plpy_exec.c
index 47ed95dcc60..d1a8c395ffd 100644
--- a/src/pl/plpython/plpy_exec.c
+++ b/src/pl/plpython/plpy_exec.c
@@ -436,10 +436,10 @@ PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure *proc)
 		{
 			PLyDatumToOb *arginfo = &proc->args[i];
 
-			if (fcinfo->argnull[i])
+			if (fcinfo->args[i].isnull)
 				arg = NULL;
 			else
-				arg = PLy_input_convert(arginfo, fcinfo->arg[i]);
+				arg = PLy_input_convert(arginfo, fcinfo->args[i].datum);
 
 			if (arg == NULL)
 			{
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index e2fa43b890d..3ee2a7fd4a2 100644
--- a/src/pl/tcl/pltcl.c
+++ b/src/pl/tcl/pltcl.c
@@ -601,7 +601,8 @@ call_pltcl_start_proc(Oid prolang, bool pltrusted)
 	Form_pg_proc procStruct;
 	AclResult	aclresult;
 	FmgrInfo	finfo;
-	FunctionCallInfoData fcinfo;
+	STACK_FCINFO_FOR_ARGS(fcinfo, 3);
+
 	PgStat_FunctionCallUsage fcusage;
 
 	/* select appropriate GUC */
@@ -662,11 +663,11 @@ call_pltcl_start_proc(Oid prolang, bool pltrusted)
 	 */
 	InvokeFunctionExecuteHook(procOid);
 	fmgr_info(procOid, &finfo);
-	InitFunctionCallInfoData(fcinfo, &finfo,
+	InitFunctionCallInfoData(*fcinfo, &finfo,
 							 0,
 							 InvalidOid, NULL, NULL);
-	pgstat_init_function_usage(&fcinfo, &fcusage);
-	(void) FunctionCallInvoke(&fcinfo);
+	pgstat_init_function_usage(fcinfo, &fcusage);
+	(void) FunctionCallInvoke(fcinfo);
 	pgstat_end_function_usage(&fcusage, true);
 
 	/* Pop the error context stack */
@@ -873,7 +874,7 @@ pltcl_func_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
 				/**************************************************
 				 * For tuple values, add a list for 'array set ...'
 				 **************************************************/
-				if (fcinfo->argnull[i])
+				if (fcinfo->args[i].isnull)
 					Tcl_ListObjAppendElement(NULL, tcl_cmd, Tcl_NewObj());
 				else
 				{
@@ -884,7 +885,7 @@ pltcl_func_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
 					HeapTupleData tmptup;
 					Tcl_Obj    *list_tmp;
 
-					td = DatumGetHeapTupleHeader(fcinfo->arg[i]);
+					td = DatumGetHeapTupleHeader(fcinfo->args[i].datum);
 					/* Extract rowtype info and find a tupdesc */
 					tupType = HeapTupleHeaderGetTypeId(td);
 					tupTypmod = HeapTupleHeaderGetTypMod(td);
@@ -905,14 +906,14 @@ pltcl_func_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
 				 * Single values are added as string element
 				 * of their external representation
 				 **************************************************/
-				if (fcinfo->argnull[i])
+				if (fcinfo->args[i].isnull)
 					Tcl_ListObjAppendElement(NULL, tcl_cmd, Tcl_NewObj());
 				else
 				{
 					char	   *tmp;
 
 					tmp = OutputFunctionCall(&prodesc->arg_out_func[i],
-											 fcinfo->arg[i]);
+											 fcinfo->args[i].datum);
 					UTF_BEGIN;
 					Tcl_ListObjAppendElement(NULL, tcl_cmd,
 											 Tcl_NewStringObj(UTF_E2U(tmp), -1));
-- 
2.18.0.rc2.dirty

v2-0002-Replace-fmgr.-ch-duplication-with-macro-magic.patchtext/x-diff; charset=us-asciiDownload
From ad354a3d1a83b2d8824d9c0502c04b8e18892d82 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Fri, 5 Oct 2018 11:36:10 -0700
Subject: [PATCH v2 2/2] Replace fmgr.[ch] duplication with macro magic.

Author:
Reviewed-By:
Discussion: https://postgr.es/m/
Backpatch:
---
 src/backend/utils/fmgr/fmgr.c | 737 +---------------------------------
 src/include/fmgr.h            | 327 ++++++++-------
 2 files changed, 176 insertions(+), 888 deletions(-)

diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index 86d5cef8108..f7c877feb8e 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -782,741 +782,6 @@ fmgr_security_definer(PG_FUNCTION_ARGS)
  *		Support routines for callers of fmgr-compatible functions
  *-------------------------------------------------------------------------
  */
-/*
- * These are for invocation of a specifically named function with a
- * directly-computed parameter list.  Note that neither arguments nor result
- * are allowed to be NULL.  Also, the function cannot be one that needs to
- * look at FmgrInfo, since there won't be any.
- */
-Datum
-DirectFunctionCall1Coll(PGFunction func, Oid collation, Datum arg1)
-{
-	STACK_FCINFO_FOR_ARGS(fcinfo, 1);
-	Datum		result;
-
-	InitFunctionCallInfoData(*fcinfo, NULL, 1, collation, NULL, NULL);
-
-	fcinfo->args[0].datum = arg1;
-	fcinfo->args[0].isnull = false;
-
-	result = (*func) (fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo->isnull)
-		elog(ERROR, "function %p returned NULL", (void *) func);
-
-	return result;
-}
-
-Datum
-DirectFunctionCall2Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2)
-{
-	STACK_FCINFO_FOR_ARGS(fcinfo, 2);
-	Datum		result;
-
-	InitFunctionCallInfoData(*fcinfo, NULL, 2, collation, NULL, NULL);
-
-	fcinfo->args[0].datum = arg1;
-	fcinfo->args[0].isnull = false;
-	fcinfo->args[1].datum = arg2;
-	fcinfo->args[1].isnull = false;
-
-	result = (*func) (fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo->isnull)
-		elog(ERROR, "function %p returned NULL", (void *) func);
-
-	return result;
-}
-
-Datum
-DirectFunctionCall3Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
-						Datum arg3)
-{
-	STACK_FCINFO_FOR_ARGS(fcinfo, 3);
-	Datum		result;
-
-	InitFunctionCallInfoData(*fcinfo, NULL, 3, collation, NULL, NULL);
-
-	fcinfo->args[0].datum = arg1;
-	fcinfo->args[0].isnull = false;
-	fcinfo->args[1].datum = arg2;
-	fcinfo->args[1].isnull = false;
-	fcinfo->args[2].datum = arg3;
-	fcinfo->args[2].isnull = false;
-
-	result = (*func) (fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo->isnull)
-		elog(ERROR, "function %p returned NULL", (void *) func);
-
-	return result;
-}
-
-Datum
-DirectFunctionCall4Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
-						Datum arg3, Datum arg4)
-{
-	STACK_FCINFO_FOR_ARGS(fcinfo, 4);
-	Datum		result;
-
-	InitFunctionCallInfoData(*fcinfo, NULL, 4, collation, NULL, NULL);
-
-	fcinfo->args[0].datum = arg1;
-	fcinfo->args[0].isnull = false;
-	fcinfo->args[1].datum = arg2;
-	fcinfo->args[1].isnull = false;
-	fcinfo->args[2].datum = arg3;
-	fcinfo->args[2].isnull = false;
-	fcinfo->args[3].datum = arg4;
-	fcinfo->args[3].isnull = false;
-
-	result = (*func) (fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo->isnull)
-		elog(ERROR, "function %p returned NULL", (void *) func);
-
-	return result;
-}
-
-Datum
-DirectFunctionCall5Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
-						Datum arg3, Datum arg4, Datum arg5)
-{
-	STACK_FCINFO_FOR_ARGS(fcinfo, 5);
-	Datum		result;
-
-	InitFunctionCallInfoData(*fcinfo, NULL, 5, collation, NULL, NULL);
-
-	fcinfo->args[0].datum = arg1;
-	fcinfo->args[0].isnull = false;
-	fcinfo->args[1].datum = arg2;
-	fcinfo->args[1].isnull = false;
-	fcinfo->args[2].datum = arg3;
-	fcinfo->args[2].isnull = false;
-	fcinfo->args[3].datum = arg4;
-	fcinfo->args[3].isnull = false;
-	fcinfo->args[4].datum = arg5;
-	fcinfo->args[4].isnull = false;
-
-	result = (*func) (fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo->isnull)
-		elog(ERROR, "function %p returned NULL", (void *) func);
-
-	return result;
-}
-
-Datum
-DirectFunctionCall6Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
-						Datum arg3, Datum arg4, Datum arg5,
-						Datum arg6)
-{
-	STACK_FCINFO_FOR_ARGS(fcinfo, 6);
-	Datum		result;
-
-	InitFunctionCallInfoData(*fcinfo, NULL, 6, collation, NULL, NULL);
-
-	fcinfo->args[0].datum = arg1;
-	fcinfo->args[0].isnull = false;
-	fcinfo->args[1].datum = arg2;
-	fcinfo->args[1].isnull = false;
-	fcinfo->args[2].datum = arg3;
-	fcinfo->args[2].isnull = false;
-	fcinfo->args[3].datum = arg4;
-	fcinfo->args[3].isnull = false;
-	fcinfo->args[4].datum = arg5;
-	fcinfo->args[4].isnull = false;
-	fcinfo->args[5].datum = arg6;
-	fcinfo->args[5].isnull = false;
-
-	result = (*func) (fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo->isnull)
-		elog(ERROR, "function %p returned NULL", (void *) func);
-
-	return result;
-}
-
-Datum
-DirectFunctionCall7Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
-						Datum arg3, Datum arg4, Datum arg5,
-						Datum arg6, Datum arg7)
-{
-	STACK_FCINFO_FOR_ARGS(fcinfo, 7);
-	Datum		result;
-
-	InitFunctionCallInfoData(*fcinfo, NULL, 7, collation, NULL, NULL);
-
-	fcinfo->args[0].datum = arg1;
-	fcinfo->args[0].isnull = false;
-	fcinfo->args[1].datum = arg2;
-	fcinfo->args[1].isnull = false;
-	fcinfo->args[2].datum = arg3;
-	fcinfo->args[2].isnull = false;
-	fcinfo->args[3].datum = arg4;
-	fcinfo->args[3].isnull = false;
-	fcinfo->args[4].datum = arg5;
-	fcinfo->args[4].isnull = false;
-	fcinfo->args[5].datum = arg6;
-	fcinfo->args[5].isnull = false;
-	fcinfo->args[6].datum = arg7;
-	fcinfo->args[6].isnull = false;
-
-	result = (*func) (fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo->isnull)
-		elog(ERROR, "function %p returned NULL", (void *) func);
-
-	return result;
-}
-
-Datum
-DirectFunctionCall8Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
-						Datum arg3, Datum arg4, Datum arg5,
-						Datum arg6, Datum arg7, Datum arg8)
-{
-	STACK_FCINFO_FOR_ARGS(fcinfo, 8);
-	Datum		result;
-
-	InitFunctionCallInfoData(*fcinfo, NULL, 8, collation, NULL, NULL);
-
-	fcinfo->args[0].datum = arg1;
-	fcinfo->args[0].isnull = false;
-	fcinfo->args[1].datum = arg2;
-	fcinfo->args[1].isnull = false;
-	fcinfo->args[2].datum = arg3;
-	fcinfo->args[2].isnull = false;
-	fcinfo->args[3].datum = arg4;
-	fcinfo->args[3].isnull = false;
-	fcinfo->args[4].datum = arg5;
-	fcinfo->args[4].isnull = false;
-	fcinfo->args[5].datum = arg6;
-	fcinfo->args[5].isnull = false;
-	fcinfo->args[6].datum = arg7;
-	fcinfo->args[6].isnull = false;
-	fcinfo->args[7].datum = arg8;
-	fcinfo->args[7].isnull = false;
-
-	result = (*func) (fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo->isnull)
-		elog(ERROR, "function %p returned NULL", (void *) func);
-
-	return result;
-}
-
-Datum
-DirectFunctionCall9Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
-						Datum arg3, Datum arg4, Datum arg5,
-						Datum arg6, Datum arg7, Datum arg8,
-						Datum arg9)
-{
-	STACK_FCINFO_FOR_ARGS(fcinfo, 9);
-	Datum		result;
-
-	InitFunctionCallInfoData(*fcinfo, NULL, 9, collation, NULL, NULL);
-
-	fcinfo->args[0].datum = arg1;
-	fcinfo->args[0].isnull = false;
-	fcinfo->args[1].datum = arg2;
-	fcinfo->args[1].isnull = false;
-	fcinfo->args[2].datum = arg3;
-	fcinfo->args[2].isnull = false;
-	fcinfo->args[3].datum = arg4;
-	fcinfo->args[3].isnull = false;
-	fcinfo->args[4].datum = arg5;
-	fcinfo->args[4].isnull = false;
-	fcinfo->args[5].datum = arg6;
-	fcinfo->args[5].isnull = false;
-	fcinfo->args[6].datum = arg7;
-	fcinfo->args[6].isnull = false;
-	fcinfo->args[7].datum = arg8;
-	fcinfo->args[7].isnull = false;
-	fcinfo->args[8].datum = arg9;
-	fcinfo->args[8].isnull = false;
-
-	result = (*func) (fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo->isnull)
-		elog(ERROR, "function %p returned NULL", (void *) func);
-
-	return result;
-}
-
-/*
- * These functions work like the DirectFunctionCall functions except that
- * they use the flinfo parameter to initialise the fcinfo for the call.
- * It's recommended that the callee only use the fn_extra and fn_mcxt
- * fields, as other fields will typically describe the calling function
- * not the callee.  Conversely, the calling function should not have
- * used fn_extra, unless its use is known to be compatible with the callee's.
- */
-
-Datum
-CallerFInfoFunctionCall1(PGFunction func, FmgrInfo *flinfo, Oid collation, Datum arg1)
-{
-	STACK_FCINFO_FOR_ARGS(fcinfo, 1);
-	Datum		result;
-
-	InitFunctionCallInfoData(*fcinfo, flinfo, 1, collation, NULL, NULL);
-
-	fcinfo->args[0].datum = arg1;
-	fcinfo->args[0].isnull = false;
-
-	result = (*func) (fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo->isnull)
-		elog(ERROR, "function %p returned NULL", (void *) func);
-
-	return result;
-}
-
-Datum
-CallerFInfoFunctionCall2(PGFunction func, FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
-{
-	STACK_FCINFO_FOR_ARGS(fcinfo, 2);
-	Datum		result;
-
-	InitFunctionCallInfoData(*fcinfo, flinfo, 2, collation, NULL, NULL);
-
-	fcinfo->args[0].datum = arg1;
-	fcinfo->args[0].isnull = false;
-	fcinfo->args[1].datum = arg2;
-	fcinfo->args[1].isnull = false;
-
-	result = (*func) (fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo->isnull)
-		elog(ERROR, "function %p returned NULL", (void *) func);
-
-	return result;
-}
-
-/*
- * These are for invocation of a previously-looked-up function with a
- * directly-computed parameter list.  Note that neither arguments nor result
- * are allowed to be NULL.
- */
-Datum
-FunctionCall0Coll(FmgrInfo *flinfo, Oid collation)
-{
-	STACK_FCINFO_FOR_ARGS(fcinfo, 0);
-	Datum		result;
-
-	InitFunctionCallInfoData(*fcinfo, flinfo, 0, collation, NULL, NULL);
-
-	result = FunctionCallInvoke(fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo->isnull)
-		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
-
-	return result;
-}
-
-/*
- * These are for invocation of a previously-looked-up function with a
- * directly-computed parameter list.  Note that neither arguments nor result
- * are allowed to be NULL.
- */
-Datum
-FunctionCall1Coll(FmgrInfo *flinfo, Oid collation, Datum arg1)
-{
-	STACK_FCINFO_FOR_ARGS(fcinfo, 1);
-	Datum		result;
-
-	InitFunctionCallInfoData(*fcinfo, flinfo, 1, collation, NULL, NULL);
-
-	fcinfo->args[0].datum = arg1;
-	fcinfo->args[0].isnull = false;
-
-	result = FunctionCallInvoke(fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo->isnull)
-		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
-
-	return result;
-}
-
-Datum
-FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
-{
-	STACK_FCINFO_FOR_ARGS(fcinfo, 2);
-	Datum		result;
-
-	InitFunctionCallInfoData(*fcinfo, flinfo, 2, collation, NULL, NULL);
-
-	fcinfo->args[0].datum = arg1;
-	fcinfo->args[0].isnull = false;
-	fcinfo->args[1].datum = arg2;
-	fcinfo->args[1].isnull = false;
-
-	result = FunctionCallInvoke(fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo->isnull)
-		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
-
-	return result;
-}
-
-Datum
-FunctionCall3Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
-				  Datum arg3)
-{
-	STACK_FCINFO_FOR_ARGS(fcinfo, 3);
-	Datum		result;
-
-	InitFunctionCallInfoData(*fcinfo, flinfo, 3, collation, NULL, NULL);
-
-	fcinfo->args[0].datum = arg1;
-	fcinfo->args[0].isnull = false;
-	fcinfo->args[1].datum = arg2;
-	fcinfo->args[1].isnull = false;
-	fcinfo->args[2].datum = arg3;
-	fcinfo->args[2].isnull = false;
-
-	result = FunctionCallInvoke(fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo->isnull)
-		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
-
-	return result;
-}
-
-Datum
-FunctionCall4Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
-				  Datum arg3, Datum arg4)
-{
-	STACK_FCINFO_FOR_ARGS(fcinfo, 4);
-	Datum		result;
-
-	InitFunctionCallInfoData(*fcinfo, flinfo, 4, collation, NULL, NULL);
-
-	fcinfo->args[0].datum = arg1;
-	fcinfo->args[0].isnull = false;
-	fcinfo->args[1].datum = arg2;
-	fcinfo->args[1].isnull = false;
-	fcinfo->args[2].datum = arg3;
-	fcinfo->args[2].isnull = false;
-	fcinfo->args[3].datum = arg4;
-	fcinfo->args[3].isnull = false;
-
-	result = FunctionCallInvoke(fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo->isnull)
-		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
-
-	return result;
-}
-
-Datum
-FunctionCall5Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
-				  Datum arg3, Datum arg4, Datum arg5)
-{
-	STACK_FCINFO_FOR_ARGS(fcinfo, 5);
-	Datum		result;
-
-	InitFunctionCallInfoData(*fcinfo, flinfo, 5, collation, NULL, NULL);
-
-	fcinfo->args[0].datum = arg1;
-	fcinfo->args[0].isnull = false;
-	fcinfo->args[1].datum = arg2;
-	fcinfo->args[1].isnull = false;
-	fcinfo->args[2].datum = arg3;
-	fcinfo->args[2].isnull = false;
-	fcinfo->args[3].datum = arg4;
-	fcinfo->args[3].isnull = false;
-	fcinfo->args[4].datum = arg5;
-	fcinfo->args[4].isnull = false;
-
-	result = FunctionCallInvoke(fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo->isnull)
-		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
-
-	return result;
-}
-
-Datum
-FunctionCall6Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
-				  Datum arg3, Datum arg4, Datum arg5,
-				  Datum arg6)
-{
-	STACK_FCINFO_FOR_ARGS(fcinfo, 6);
-	Datum		result;
-
-	InitFunctionCallInfoData(*fcinfo, flinfo, 6, collation, NULL, NULL);
-
-	fcinfo->args[0].datum = arg1;
-	fcinfo->args[0].isnull = false;
-	fcinfo->args[1].datum = arg2;
-	fcinfo->args[1].isnull = false;
-	fcinfo->args[2].datum = arg3;
-	fcinfo->args[2].isnull = false;
-	fcinfo->args[3].datum = arg4;
-	fcinfo->args[3].isnull = false;
-	fcinfo->args[4].datum = arg5;
-	fcinfo->args[4].isnull = false;
-	fcinfo->args[5].datum = arg6;
-	fcinfo->args[5].isnull = false;
-
-	result = FunctionCallInvoke(fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo->isnull)
-		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
-
-	return result;
-}
-
-Datum
-FunctionCall7Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
-				  Datum arg3, Datum arg4, Datum arg5,
-				  Datum arg6, Datum arg7)
-{
-	STACK_FCINFO_FOR_ARGS(fcinfo, 7);
-	Datum		result;
-
-	InitFunctionCallInfoData(*fcinfo, flinfo, 7, collation, NULL, NULL);
-
-	fcinfo->args[0].datum = arg1;
-	fcinfo->args[0].isnull = false;
-	fcinfo->args[1].datum = arg2;
-	fcinfo->args[1].isnull = false;
-	fcinfo->args[2].datum = arg3;
-	fcinfo->args[2].isnull = false;
-	fcinfo->args[3].datum = arg4;
-	fcinfo->args[3].isnull = false;
-	fcinfo->args[4].datum = arg5;
-	fcinfo->args[4].isnull = false;
-	fcinfo->args[5].datum = arg6;
-	fcinfo->args[5].isnull = false;
-	fcinfo->args[6].datum = arg7;
-	fcinfo->args[6].isnull = false;
-
-	result = FunctionCallInvoke(fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo->isnull)
-		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
-
-	return result;
-}
-
-Datum
-FunctionCall8Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
-				  Datum arg3, Datum arg4, Datum arg5,
-				  Datum arg6, Datum arg7, Datum arg8)
-{
-	STACK_FCINFO_FOR_ARGS(fcinfo, 8);
-	Datum		result;
-
-	InitFunctionCallInfoData(*fcinfo, flinfo, 8, collation, NULL, NULL);
-
-	fcinfo->args[0].datum = arg1;
-	fcinfo->args[0].isnull = false;
-	fcinfo->args[1].datum = arg2;
-	fcinfo->args[1].isnull = false;
-	fcinfo->args[2].datum = arg3;
-	fcinfo->args[2].isnull = false;
-	fcinfo->args[3].datum = arg4;
-	fcinfo->args[3].isnull = false;
-	fcinfo->args[4].datum = arg5;
-	fcinfo->args[4].isnull = false;
-	fcinfo->args[5].datum = arg6;
-	fcinfo->args[5].isnull = false;
-	fcinfo->args[6].datum = arg7;
-	fcinfo->args[6].isnull = false;
-	fcinfo->args[7].datum = arg8;
-	fcinfo->args[7].isnull = false;
-
-	result = FunctionCallInvoke(fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo->isnull)
-		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
-
-	return result;
-}
-
-Datum
-FunctionCall9Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
-				  Datum arg3, Datum arg4, Datum arg5,
-				  Datum arg6, Datum arg7, Datum arg8,
-				  Datum arg9)
-{
-	STACK_FCINFO_FOR_ARGS(fcinfo, 9);
-	Datum		result;
-
-	InitFunctionCallInfoData(*fcinfo, flinfo, 9, collation, NULL, NULL);
-
-	fcinfo->args[0].datum = arg1;
-	fcinfo->args[0].isnull = false;
-	fcinfo->args[1].datum = arg2;
-	fcinfo->args[1].isnull = false;
-	fcinfo->args[2].datum = arg3;
-	fcinfo->args[2].isnull = false;
-	fcinfo->args[3].datum = arg4;
-	fcinfo->args[3].isnull = false;
-	fcinfo->args[4].datum = arg5;
-	fcinfo->args[4].isnull = false;
-	fcinfo->args[5].datum = arg6;
-	fcinfo->args[5].isnull = false;
-	fcinfo->args[6].datum = arg7;
-	fcinfo->args[6].isnull = false;
-	fcinfo->args[7].datum = arg8;
-	fcinfo->args[7].isnull = false;
-	fcinfo->args[8].datum = arg9;
-	fcinfo->args[8].isnull = false;
-
-	result = FunctionCallInvoke(fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo->isnull)
-		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
-
-	return result;
-}
-
-
-/*
- * These are for invocation of a function identified by OID with a
- * directly-computed parameter list.  Note that neither arguments nor result
- * are allowed to be NULL.  These are essentially fmgr_info() followed
- * by FunctionCallN().  If the same function is to be invoked repeatedly,
- * do the fmgr_info() once and then use FunctionCallN().
- */
-Datum
-OidFunctionCall0Coll(Oid functionId, Oid collation)
-{
-	FmgrInfo	flinfo;
-
-	fmgr_info(functionId, &flinfo);
-
-	return FunctionCall0Coll(&flinfo, collation);
-}
-
-Datum
-OidFunctionCall1Coll(Oid functionId, Oid collation, Datum arg1)
-{
-	FmgrInfo	flinfo;
-
-	fmgr_info(functionId, &flinfo);
-
-	return FunctionCall1Coll(&flinfo, collation, arg1);
-}
-
-Datum
-OidFunctionCall2Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2)
-{
-	FmgrInfo	flinfo;
-
-	fmgr_info(functionId, &flinfo);
-
-	return FunctionCall2Coll(&flinfo, collation, arg1, arg2);
-}
-
-Datum
-OidFunctionCall3Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
-					 Datum arg3)
-{
-	FmgrInfo	flinfo;
-
-	fmgr_info(functionId, &flinfo);
-
-	return FunctionCall3Coll(&flinfo, collation, arg1, arg2, arg3);
-}
-
-Datum
-OidFunctionCall4Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
-					 Datum arg3, Datum arg4)
-{
-	FmgrInfo	flinfo;
-
-	fmgr_info(functionId, &flinfo);
-
-	return FunctionCall4Coll(&flinfo, collation, arg1, arg2, arg3, arg4);
-}
-
-Datum
-OidFunctionCall5Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
-					 Datum arg3, Datum arg4, Datum arg5)
-{
-	FmgrInfo	flinfo;
-
-	fmgr_info(functionId, &flinfo);
-
-	return FunctionCall5Coll(&flinfo, collation, arg1, arg2, arg3, arg4, arg5);
-}
-
-Datum
-OidFunctionCall6Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
-					 Datum arg3, Datum arg4, Datum arg5,
-					 Datum arg6)
-{
-	FmgrInfo	flinfo;
-
-	fmgr_info(functionId, &flinfo);
-
-	return FunctionCall6Coll(&flinfo, collation, arg1, arg2, arg3, arg4, arg5,
-							 arg6);
-}
-
-Datum
-OidFunctionCall7Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
-					 Datum arg3, Datum arg4, Datum arg5,
-					 Datum arg6, Datum arg7)
-{
-	FmgrInfo	flinfo;
-
-	fmgr_info(functionId, &flinfo);
-
-	return FunctionCall7Coll(&flinfo, collation, arg1, arg2, arg3, arg4, arg5,
-							 arg6, arg7);
-}
-
-Datum
-OidFunctionCall8Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
-					 Datum arg3, Datum arg4, Datum arg5,
-					 Datum arg6, Datum arg7, Datum arg8)
-{
-	FmgrInfo	flinfo;
-
-	fmgr_info(functionId, &flinfo);
-
-	return FunctionCall8Coll(&flinfo, collation, arg1, arg2, arg3, arg4, arg5,
-							 arg6, arg7, arg8);
-}
-
-Datum
-OidFunctionCall9Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
-					 Datum arg3, Datum arg4, Datum arg5,
-					 Datum arg6, Datum arg7, Datum arg8,
-					 Datum arg9)
-{
-	FmgrInfo	flinfo;
-
-	fmgr_info(functionId, &flinfo);
-
-	return FunctionCall9Coll(&flinfo, collation, arg1, arg2, arg3, arg4, arg5,
-							 arg6, arg7, arg8, arg9);
-}
 
 
 /*
@@ -1578,7 +843,7 @@ InputFunctionCall(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod)
 char *
 OutputFunctionCall(FmgrInfo *flinfo, Datum val)
 {
-	return DatumGetCString(FunctionCall1(flinfo, val));
+   return DatumGetCString(FunctionCall1(flinfo, val));
 }
 
 /*
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index ee1dbd5fcbb..b522f5b4633 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -480,37 +480,99 @@ extern int no_such_variable
 /* These are for invocation of a specifically named function with a
  * directly-computed parameter list.  Note that neither arguments nor result
  * are allowed to be NULL.
+ *
+ * Call like val = DirectFunctionCallColl(func, collation, a, b, .., y)
  */
-extern Datum DirectFunctionCall1Coll(PGFunction func, Oid collation,
-						Datum arg1);
-extern Datum DirectFunctionCall2Coll(PGFunction func, Oid collation,
-						Datum arg1, Datum arg2);
-extern Datum DirectFunctionCall3Coll(PGFunction func, Oid collation,
-						Datum arg1, Datum arg2,
-						Datum arg3);
-extern Datum DirectFunctionCall4Coll(PGFunction func, Oid collation,
-						Datum arg1, Datum arg2,
-						Datum arg3, Datum arg4);
-extern Datum DirectFunctionCall5Coll(PGFunction func, Oid collation,
-						Datum arg1, Datum arg2,
-						Datum arg3, Datum arg4, Datum arg5);
-extern Datum DirectFunctionCall6Coll(PGFunction func, Oid collation,
-						Datum arg1, Datum arg2,
-						Datum arg3, Datum arg4, Datum arg5,
-						Datum arg6);
-extern Datum DirectFunctionCall7Coll(PGFunction func, Oid collation,
-						Datum arg1, Datum arg2,
-						Datum arg3, Datum arg4, Datum arg5,
-						Datum arg6, Datum arg7);
-extern Datum DirectFunctionCall8Coll(PGFunction func, Oid collation,
-						Datum arg1, Datum arg2,
-						Datum arg3, Datum arg4, Datum arg5,
-						Datum arg6, Datum arg7, Datum arg8);
-extern Datum DirectFunctionCall9Coll(PGFunction func, Oid collation,
-						Datum arg1, Datum arg2,
-						Datum arg3, Datum arg4, Datum arg5,
-						Datum arg6, Datum arg7, Datum arg8,
-						Datum arg9);
+
+#define DirectFunctionCallColl(func, collation, ...) \
+	DirectFunctionCallCollForN(func, NULL, collation, VA_ARGS_NARGS(__VA_ARGS__), __VA_ARGS__)
+
+#define DirectFunctionCallCollForN(func, flinfo, collation, nargs_, ...) \
+	DirectFunctionCallExec(func, \
+						   MemoryForFunctionCallN(flinfo, nargs_, collation, NULL, NULL), \
+						   nargs_, \
+						   (Datum[]){__VA_ARGS__})
+
+#define MemoryForFunctionCallN(Flinfo, Nargs, Collation, Context, Resultinfo) \
+	(&(\
+		union { \
+			struct FunctionCallInfoData fcinfo; \
+			char *fcinfo_data[SizeForFunctionCallInfoData(Nargs)]; \
+		} \
+		){.fcinfo = {.flinfo = Flinfo, .context = Context, .nargs = Nargs, .resultinfo = Resultinfo, .fncollation = Collation} \
+	}.fcinfo)
+
+static inline Datum
+DirectFunctionCallExec(PGFunction func, FunctionCallInfo fcinfo, int nargs, Datum *args)
+{
+	Datum		result;
+
+	for (int argno = 0; argno < nargs; argno++)
+	{
+		fcinfo->args[argno].datum = args[argno];
+		Assert(!fcinfo->args[argno].isnull);
+	}
+
+	result = (*func) (fcinfo);
+
+	/* Check for null result, since caller is clearly not expecting one */
+	Assert(!fcinfo->isnull);
+
+	return result;
+}
+
+static inline Datum
+FunctionCallExec(FunctionCallInfo fcinfo, int nargs, Datum *args)
+{
+	Datum		result;
+
+	for (int argno = 0; argno < nargs; argno++)
+	{
+		fcinfo->args[argno].datum = args[argno];
+		Assert(!fcinfo->args[argno].isnull);
+	}
+
+	result = FunctionCallInvoke(fcinfo);
+
+	/* Check for null result, since caller is clearly not expecting one */
+	Assert(!fcinfo->isnull);
+
+	return result;
+}
+
+static inline Datum
+OidFunctionCallExec(Oid functionId, FunctionCallInfo fcinfo, int nargs, Datum *args)
+{
+	Datum		result;
+	FmgrInfo	flinfo;
+
+	fmgr_info(functionId, &flinfo);
+	fcinfo->flinfo = &flinfo;
+
+	for (int argno = 0; argno < nargs; argno++)
+	{
+		fcinfo->args[argno].datum = args[argno];
+		Assert(!fcinfo->args[argno].isnull);
+	}
+
+	result = FunctionCallInvoke(fcinfo);
+
+	/* Check for null result, since caller is clearly not expecting one */
+	Assert(!fcinfo->isnull);
+
+	return result;
+}
+
+/* for backward compatibility */
+#define DirectFunctionCall1Coll DirectFunctionCallColl
+#define DirectFunctionCall2Coll DirectFunctionCallColl
+#define DirectFunctionCall3Coll DirectFunctionCallColl
+#define DirectFunctionCall4Coll DirectFunctionCallColl
+#define DirectFunctionCall5Coll DirectFunctionCallColl
+#define DirectFunctionCall6Coll DirectFunctionCallColl
+#define DirectFunctionCall7Coll DirectFunctionCallColl
+#define DirectFunctionCall8Coll DirectFunctionCallColl
+#define DirectFunctionCall9Coll DirectFunctionCallColl
 
 /*
  * These functions work like the DirectFunctionCall functions except that
@@ -520,46 +582,37 @@ extern Datum DirectFunctionCall9Coll(PGFunction func, Oid collation,
  * not the callee.  Conversely, the calling function should not have
  * used fn_extra, unless its use is known to be compatible with the callee's.
  */
-extern Datum CallerFInfoFunctionCall1(PGFunction func, FmgrInfo *flinfo,
-						 Oid collation, Datum arg1);
-extern Datum CallerFInfoFunctionCall2(PGFunction func, FmgrInfo *flinfo,
-						 Oid collation, Datum arg1, Datum arg2);
+#define CallerFInfoFunctionCall(func, flinfo, collation, ...) \
+	DirectFunctionCallCollForN(func, flinfo, collation, VA_ARGS_NARGS(__VA_ARGS__), __VA_ARGS__)
+
+/* for backward compatibility */
+#define CallerFInfoFunctionCall1 CallerFInfoFunctionCall
+#define CallerFInfoFunctionCall2 CallerFInfoFunctionCall
+
 
 /* These are for invocation of a previously-looked-up function with a
  * directly-computed parameter list.  Note that neither arguments nor result
  * are allowed to be NULL.
  */
-extern Datum FunctionCall0Coll(FmgrInfo *flinfo, Oid collation);
-extern Datum FunctionCall1Coll(FmgrInfo *flinfo, Oid collation,
-				  Datum arg1);
-extern Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation,
-				  Datum arg1, Datum arg2);
-extern Datum FunctionCall3Coll(FmgrInfo *flinfo, Oid collation,
-				  Datum arg1, Datum arg2,
-				  Datum arg3);
-extern Datum FunctionCall4Coll(FmgrInfo *flinfo, Oid collation,
-				  Datum arg1, Datum arg2,
-				  Datum arg3, Datum arg4);
-extern Datum FunctionCall5Coll(FmgrInfo *flinfo, Oid collation,
-				  Datum arg1, Datum arg2,
-				  Datum arg3, Datum arg4, Datum arg5);
-extern Datum FunctionCall6Coll(FmgrInfo *flinfo, Oid collation,
-				  Datum arg1, Datum arg2,
-				  Datum arg3, Datum arg4, Datum arg5,
-				  Datum arg6);
-extern Datum FunctionCall7Coll(FmgrInfo *flinfo, Oid collation,
-				  Datum arg1, Datum arg2,
-				  Datum arg3, Datum arg4, Datum arg5,
-				  Datum arg6, Datum arg7);
-extern Datum FunctionCall8Coll(FmgrInfo *flinfo, Oid collation,
-				  Datum arg1, Datum arg2,
-				  Datum arg3, Datum arg4, Datum arg5,
-				  Datum arg6, Datum arg7, Datum arg8);
-extern Datum FunctionCall9Coll(FmgrInfo *flinfo, Oid collation,
-				  Datum arg1, Datum arg2,
-				  Datum arg3, Datum arg4, Datum arg5,
-				  Datum arg6, Datum arg7, Datum arg8,
-				  Datum arg9);
+#define FunctionCallColl(flinfo, collation, ...) \
+	FunctionCallCollForN(flinfo, collation, VA_ARGS_NARGS(__VA_ARGS__), __VA_ARGS__)
+
+#define FunctionCallCollForN(flinfo, collation, nargs_, ...) \
+	FunctionCallExec(MemoryForFunctionCallN(flinfo, nargs_, collation, NULL, NULL), \
+					 nargs_, \
+					 (Datum[]){__VA_ARGS__})
+
+/* for backward compatibility */
+#define FunctionCall0Coll FunctionCallColl
+#define FunctionCall1Coll FunctionCallColl
+#define FunctionCall2Coll FunctionCallColl
+#define FunctionCall3Coll FunctionCallColl
+#define FunctionCall4Coll FunctionCallColl
+#define FunctionCall5Coll FunctionCallColl
+#define FunctionCall6Coll FunctionCallColl
+#define FunctionCall7Coll FunctionCallColl
+#define FunctionCall8Coll FunctionCallColl
+#define FunctionCall9Coll FunctionCallColl
 
 /* These are for invocation of a function identified by OID with a
  * directly-computed parameter list.  Note that neither arguments nor result
@@ -567,98 +620,68 @@ extern Datum FunctionCall9Coll(FmgrInfo *flinfo, Oid collation,
  * FunctionCallN().  If the same function is to be invoked repeatedly, do the
  * fmgr_info() once and then use FunctionCallN().
  */
-extern Datum OidFunctionCall0Coll(Oid functionId, Oid collation);
-extern Datum OidFunctionCall1Coll(Oid functionId, Oid collation,
-					 Datum arg1);
-extern Datum OidFunctionCall2Coll(Oid functionId, Oid collation,
-					 Datum arg1, Datum arg2);
-extern Datum OidFunctionCall3Coll(Oid functionId, Oid collation,
-					 Datum arg1, Datum arg2,
-					 Datum arg3);
-extern Datum OidFunctionCall4Coll(Oid functionId, Oid collation,
-					 Datum arg1, Datum arg2,
-					 Datum arg3, Datum arg4);
-extern Datum OidFunctionCall5Coll(Oid functionId, Oid collation,
-					 Datum arg1, Datum arg2,
-					 Datum arg3, Datum arg4, Datum arg5);
-extern Datum OidFunctionCall6Coll(Oid functionId, Oid collation,
-					 Datum arg1, Datum arg2,
-					 Datum arg3, Datum arg4, Datum arg5,
-					 Datum arg6);
-extern Datum OidFunctionCall7Coll(Oid functionId, Oid collation,
-					 Datum arg1, Datum arg2,
-					 Datum arg3, Datum arg4, Datum arg5,
-					 Datum arg6, Datum arg7);
-extern Datum OidFunctionCall8Coll(Oid functionId, Oid collation,
-					 Datum arg1, Datum arg2,
-					 Datum arg3, Datum arg4, Datum arg5,
-					 Datum arg6, Datum arg7, Datum arg8);
-extern Datum OidFunctionCall9Coll(Oid functionId, Oid collation,
-					 Datum arg1, Datum arg2,
-					 Datum arg3, Datum arg4, Datum arg5,
-					 Datum arg6, Datum arg7, Datum arg8,
-					 Datum arg9);
+
+#define OidFunctionCallColl(oid, collation, ...) \
+	OidFunctionCallCollForN(oid, collation, VA_ARGS_NARGS(__VA_ARGS__), __VA_ARGS__)
+
+#define OidFunctionCallCollForN(oid, collation, nargs_, ...) \
+	OidFunctionCallExec(oid, MemoryForFunctionCallN(NULL, nargs_, collation, NULL, NULL), \
+					 nargs_, \
+					 (Datum[]){__VA_ARGS__})
+#define OidFunctionCall0Coll OidFunctionCallColl
+#define OidFunctionCall1Coll OidFunctionCallColl
+#define OidFunctionCall2Coll OidFunctionCallColl
+#define OidFunctionCall3Coll OidFunctionCallColl
+#define OidFunctionCall4Coll OidFunctionCallColl
+#define OidFunctionCall5Coll OidFunctionCallColl
+#define OidFunctionCall6Coll OidFunctionCallColl
+#define OidFunctionCall7Coll OidFunctionCallColl
+#define OidFunctionCall8Coll OidFunctionCallColl
+#define OidFunctionCall9Coll OidFunctionCallColl
 
 /* These macros allow the collation argument to be omitted (with a default of
  * InvalidOid, ie, no collation).  They exist mostly for backwards
  * compatibility of source code.
  */
-#define DirectFunctionCall1(func, arg1) \
-	DirectFunctionCall1Coll(func, InvalidOid, arg1)
-#define DirectFunctionCall2(func, arg1, arg2) \
-	DirectFunctionCall2Coll(func, InvalidOid, arg1, arg2)
-#define DirectFunctionCall3(func, arg1, arg2, arg3) \
-	DirectFunctionCall3Coll(func, InvalidOid, arg1, arg2, arg3)
-#define DirectFunctionCall4(func, arg1, arg2, arg3, arg4) \
-	DirectFunctionCall4Coll(func, InvalidOid, arg1, arg2, arg3, arg4)
-#define DirectFunctionCall5(func, arg1, arg2, arg3, arg4, arg5) \
-	DirectFunctionCall5Coll(func, InvalidOid, arg1, arg2, arg3, arg4, arg5)
-#define DirectFunctionCall6(func, arg1, arg2, arg3, arg4, arg5, arg6) \
-	DirectFunctionCall6Coll(func, InvalidOid, arg1, arg2, arg3, arg4, arg5, arg6)
-#define DirectFunctionCall7(func, arg1, arg2, arg3, arg4, arg5, arg6, arg7) \
-	DirectFunctionCall7Coll(func, InvalidOid, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
-#define DirectFunctionCall8(func, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) \
-	DirectFunctionCall8Coll(func, InvalidOid, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)
-#define DirectFunctionCall9(func, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) \
-	DirectFunctionCall9Coll(func, InvalidOid, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)
-#define FunctionCall1(flinfo, arg1) \
-	FunctionCall1Coll(flinfo, InvalidOid, arg1)
-#define FunctionCall2(flinfo, arg1, arg2) \
-	FunctionCall2Coll(flinfo, InvalidOid, arg1, arg2)
-#define FunctionCall3(flinfo, arg1, arg2, arg3) \
-	FunctionCall3Coll(flinfo, InvalidOid, arg1, arg2, arg3)
-#define FunctionCall4(flinfo, arg1, arg2, arg3, arg4) \
-	FunctionCall4Coll(flinfo, InvalidOid, arg1, arg2, arg3, arg4)
-#define FunctionCall5(flinfo, arg1, arg2, arg3, arg4, arg5) \
-	FunctionCall5Coll(flinfo, InvalidOid, arg1, arg2, arg3, arg4, arg5)
-#define FunctionCall6(flinfo, arg1, arg2, arg3, arg4, arg5, arg6) \
-	FunctionCall6Coll(flinfo, InvalidOid, arg1, arg2, arg3, arg4, arg5, arg6)
-#define FunctionCall7(flinfo, arg1, arg2, arg3, arg4, arg5, arg6, arg7) \
-	FunctionCall7Coll(flinfo, InvalidOid, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
-#define FunctionCall8(flinfo, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) \
-	FunctionCall8Coll(flinfo, InvalidOid, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)
-#define FunctionCall9(flinfo, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) \
-	FunctionCall9Coll(flinfo, InvalidOid, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)
-#define OidFunctionCall0(functionId) \
-	OidFunctionCall0Coll(functionId, InvalidOid)
-#define OidFunctionCall1(functionId, arg1) \
-	OidFunctionCall1Coll(functionId, InvalidOid, arg1)
-#define OidFunctionCall2(functionId, arg1, arg2) \
-	OidFunctionCall2Coll(functionId, InvalidOid, arg1, arg2)
-#define OidFunctionCall3(functionId, arg1, arg2, arg3) \
-	OidFunctionCall3Coll(functionId, InvalidOid, arg1, arg2, arg3)
-#define OidFunctionCall4(functionId, arg1, arg2, arg3, arg4) \
-	OidFunctionCall4Coll(functionId, InvalidOid, arg1, arg2, arg3, arg4)
-#define OidFunctionCall5(functionId, arg1, arg2, arg3, arg4, arg5) \
-	OidFunctionCall5Coll(functionId, InvalidOid, arg1, arg2, arg3, arg4, arg5)
-#define OidFunctionCall6(functionId, arg1, arg2, arg3, arg4, arg5, arg6) \
-	OidFunctionCall6Coll(functionId, InvalidOid, arg1, arg2, arg3, arg4, arg5, arg6)
-#define OidFunctionCall7(functionId, arg1, arg2, arg3, arg4, arg5, arg6, arg7) \
-	OidFunctionCall7Coll(functionId, InvalidOid, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
-#define OidFunctionCall8(functionId, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) \
-	OidFunctionCall8Coll(functionId, InvalidOid, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)
-#define OidFunctionCall9(functionId, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) \
-	OidFunctionCall9Coll(functionId, InvalidOid, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)
+
+#define DirectFunctionCall(func, ...) \
+	DirectFunctionCallColl(func, InvalidOid, __VA_ARGS__)
+#define FunctionCall(finfo, ...) \
+	FunctionCallColl(finfo, InvalidOid, __VA_ARGS__)
+#define OidFunctionCall(oid, ...) \
+	OidFunctionCallColl(oid, InvalidOid, __VA_ARGS__)
+
+#define DirectFunctionCall1 DirectFunctionCall
+#define DirectFunctionCall2 DirectFunctionCall
+#define DirectFunctionCall3 DirectFunctionCall
+#define DirectFunctionCall4 DirectFunctionCall
+#define DirectFunctionCall5 DirectFunctionCall
+#define DirectFunctionCall6 DirectFunctionCall
+#define DirectFunctionCall7 DirectFunctionCall
+#define DirectFunctionCall8 DirectFunctionCall
+#define DirectFunctionCall9 DirectFunctionCall
+
+#define FunctionCall1 FunctionCall
+#define FunctionCall2 FunctionCall
+#define FunctionCall3 FunctionCall
+#define FunctionCall4 FunctionCall
+#define FunctionCall5 FunctionCall
+#define FunctionCall6 FunctionCall
+#define FunctionCall7 FunctionCall
+#define FunctionCall8 FunctionCall
+#define FunctionCall9 FunctionCall
+
+#define OidFunctionCall0 OidFunctionCall
+#define OidFunctionCall1 OidFunctionCall
+#define OidFunctionCall2 OidFunctionCall
+#define OidFunctionCall3 OidFunctionCall
+#define OidFunctionCall4 OidFunctionCall
+#define OidFunctionCall5 OidFunctionCall
+#define OidFunctionCall6 OidFunctionCall
+#define OidFunctionCall7 OidFunctionCall
+#define OidFunctionCall8 OidFunctionCall
+#define OidFunctionCall9 OidFunctionCall
+
 
 
 /* Special cases for convenient invocation of datatype I/O functions. */
-- 
2.18.0.rc2.dirty

#8Andres Freund
andres@anarazel.de
In reply to: Andres Freund (#7)
Re: Variable-length FunctionCallInfoData

Hi,

On 2018-10-09 12:18:02 -0700, Andres Freund wrote:

Here's an updated version of the patch. Besides a rebase the biggest
change is that I've wrapped:

On 2018-06-05 10:29:52 -0700, Andres Freund wrote:

There's some added uglyness, which I hope we can polish a bit
further. Right now we allocate a good number of FunctionCallInfoData
struct on the stack - which doesn't quite work afterwards anymore. So
the stack allocations, for the majoroity cases where the argument number
is known, currently looks like:

union {
FunctionCallInfoData fcinfo;
char *fcinfo_data[SizeForFunctionCallInfoData(0)];
} fcinfodata;
FunctionCallInfo fcinfo = &fcinfodata.fcinfo;

that's not pretty, but also not that bad.

into a macro STACK_FCINFO_FOR_ARGS(varname, numargs). That makes the
code look much nicer.

I think we should go for this. If there's some agreement on that I'll
perform a bit more polishing.

I think it'd probably good to add accessors for value/nullness in
arguments that hide the difference between <v12 and v12, for the sake of
extension authors. Would probably mostly make sense if we backpatched
those for compatibility.

Also attached is a second patch that avoids all the duplication in
fmgr.[ch]. While the savings are nice, I'm a bit doubtful that the
amount of magic here is reasonable. Any opinions?

Any comments? Otherwise I plan to press forward with this soon-ish.

Greetings,

Andres Freund

#9Andrew Gierth
andrew@tao11.riddles.org.uk
In reply to: Andres Freund (#8)
Re: Variable-length FunctionCallInfoData

"Andres" == Andres Freund <andres@anarazel.de> writes:

I think it'd probably good to add accessors for value/nullness in
arguments that hide the difference between <v12 and v12, for the
sake of extension authors. Would probably mostly make sense if we
backpatched those for compatibility.

Speaking as an affected extension author: don't backpatch them.

The extension code has to cope with being compiled against a minor
release that doesn't have the backpatch, so the extension author has to
do their own workaround anyway. Having additional conditions based on
the minor release is just more pain.

--
Andrew (irc:RhodiumToad)

#10Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andrew Gierth (#9)
Re: Variable-length FunctionCallInfoData

Andrew Gierth <andrew@tao11.riddles.org.uk> writes:

"Andres" == Andres Freund <andres@anarazel.de> writes:

I think it'd probably good to add accessors for value/nullness in
arguments that hide the difference between <v12 and v12, for the
sake of extension authors. Would probably mostly make sense if we
backpatched those for compatibility.

Speaking as an affected extension author: don't backpatch them.

Yeah, I agree.

Looking at the patch, it seems like a real pain that substituting
"STACK_FCINFO_FOR_ARGS(fcinfo, ...)" for "FunctionCallInfoData fcinfo"
has the effect of converting "fcinfo" into a pointer. Is there a way
to define the macro so that that doesn't happen, and the ensuing
minor-but-invasive code changes aren't needed? (It'd be easy in C++,
but not quite seeing how to do it in C, at least not without defining
an additional macro for "fcinfo" that we'd then need to #undef at the
end of the function.)

With or without that, I'm pretty sure you wanted the pad member to be
char fcinfo_data[SizeForFunctionCallInfoData(nargs)]; \
not
char *fcinfo_data[SizeForFunctionCallInfoData(nargs)]; \

I also wonder if we should rename the type FunctionCallInfoData,
perhaps to FunctionCallInfo_Data, so as to intentionally break
code that hasn't been converted. On the other hand, that might
introduce too much useless code churn --- not sure how many live
references to that struct type will remain in place.

One more naming thought: would "LOCAL_FCINFO(...)" be a better
name for that macro? I don't think FOR_ARGS is adding much in
any case.

Why does struct agg_strict_input_check now have *both*
NullableDatum and "bool *nulls"? If that's not a typo,
it needs to be documented what the fields are for.

Please try to avoid random changes of vertical whitespace, eg in
the first hunk in pltcl.c. pgindent isn't very good about
cleaning that up.

I do not think the 0002 patch is a good idea. It's going to add
cycles to function calls, and it's not buying anything I'd call
important.

regards, tom lane

#11Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#10)
Re: Variable-length FunctionCallInfoData

Hi,

On 2018-12-15 10:45:21 -0500, Tom Lane wrote:

Andrew Gierth <andrew@tao11.riddles.org.uk> writes:

"Andres" == Andres Freund <andres@anarazel.de> writes:

I think it'd probably good to add accessors for value/nullness in
arguments that hide the difference between <v12 and v12, for the
sake of extension authors. Would probably mostly make sense if we
backpatched those for compatibility.

Speaking as an affected extension author: don't backpatch them.

Yeah, I agree.

Ok, convinced.

Looking at the patch, it seems like a real pain that substituting
"STACK_FCINFO_FOR_ARGS(fcinfo, ...)" for "FunctionCallInfoData fcinfo"
has the effect of converting "fcinfo" into a pointer. Is there a way
to define the macro so that that doesn't happen, and the ensuing
minor-but-invasive code changes aren't needed? (It'd be easy in C++,
but not quite seeing how to do it in C, at least not without defining
an additional macro for "fcinfo" that we'd then need to #undef at the
end of the function.)

It'd be nice, but I wasn't able to come up with anything either. While
I'd like that, I'm unfortunately doubtful others will agree to move to
c++ for this :P.

I also wonder if we should rename the type FunctionCallInfoData,
perhaps to FunctionCallInfo_Data, so as to intentionally break
code that hasn't been converted. On the other hand, that might
introduce too much useless code churn --- not sure how many live
references to that struct type will remain in place.

Probably doable, there ought not to be many FunctionCallInfoData
references afterwards.

One more naming thought: would "LOCAL_FCINFO(...)" be a better
name for that macro? I don't think FOR_ARGS is adding much in
any case.

Hm, that works.

Why does struct agg_strict_input_check now have *both*
NullableDatum and "bool *nulls"? If that's not a typo,
it needs to be documented what the fields are for.

I'll check whether it can be simplified.

I do not think the 0002 patch is a good idea. It's going to add
cycles to function calls, and it's not buying anything I'd call
important.

Yea, same conclusion I came to. I dislike those verbose copies of
functions a lot, but the approach I could find out to resolve that, seem
like a cure worse than the disease.

Greetings,

Andres Freund

#12Andres Freund
andres@anarazel.de
In reply to: Andres Freund (#11)
1 attachment(s)
Re: Variable-length FunctionCallInfoData

Hi,

On 2018-12-15 11:44:30 -0800, Andres Freund wrote:

On 2018-12-15 10:45:21 -0500, Tom Lane wrote:

I also wonder if we should rename the type FunctionCallInfoData,
perhaps to FunctionCallInfo_Data, so as to intentionally break
code that hasn't been converted. On the other hand, that might
introduce too much useless code churn --- not sure how many live
references to that struct type will remain in place.

Probably doable, there ought not to be many FunctionCallInfoData
references afterwards.

I did not like FunctionCallInfo_Data, the _ grated
me. FunctionCallInfoBaseData isn't great, but seems better?

With or without that, I'm pretty sure you wanted the pad member to be
char fcinfo_data[SizeForFunctionCallInfoData(nargs)]; \
not
char *fcinfo_data[SizeForFunctionCallInfoData(nargs)]; \

Indeed. And that hid a bug or two, where not enough space for
arguments was allocated. I changed a few on-stack infos to be of
FUNC_MAX_ARGS length, because they're not known at compile time. Seems
better than unnecesarily introducing a dynamic allocation, and they're
not that hot locations.

One more naming thought: would "LOCAL_FCINFO(...)" be a better
name for that macro? I don't think FOR_ARGS is adding much in
any case.

Hm, that works.

Done.

Why does struct agg_strict_input_check now have *both*
NullableDatum and "bool *nulls"? If that's not a typo,
it needs to be documented what the fields are for.

I'll check whether it can be simplified.

It can't trivially: For tuplesort cases the null check points into
TupleTableSlot's isnull array, but for other aggs it points into
FunctionCallInfoData->args. If we change TupleTableSlots to use
NullableDatum as well - probably a good idea for efficiency reasons -
we could change that, but that's a separate reasonably large sided
patch. Added a comment to that end.

Updated patch attached. Besides the above changes, there's a fair bit
of comment changes, and I've implemented the necessary JIT changes.

Greetings,

Andres Freund

Attachments:

v3-0001-Change-function-call-information-to-be-variable-l.patchtext/x-diff; charset=us-asciiDownload
From 6d19c7882908610177359aa3ceea24b4ab0d1527 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Fri, 25 Jan 2019 12:32:30 -0800
Subject: [PATCH v3] Change function call information to be variable length.

Before this change FunctionCallInfoData, the struct arguments etc for
V1 function calls are stored in, always had space for
FUNC_MAX_ARGS/100 arguments, storing datums and their nullness in two
arrays.  For nearly every function call 100 arguments is far more than
needed, therefore wasting memory. Arg and argnull being two separate
arrays also guarantees that to access a single argument, two
cachelines have to be touched.

Change the layout so there's a single variable-length array with pairs
of value / isnull. That drastically reduces memory consumption for
most function calls (on x86-64 a two argument function now uses
64bytes, previously 936 bytes), and makes it very likely that argument
value and its nullness are on the same cacheline.

Arguments are stored in a new NullableDatum struct, which, due to
padding, needs more memory per argument than before. But as usually
far fewer arguments are stored, and individual arguments are cheaper
to access, that's still a clear win.  It's likely that there's other
places where conversion to NullableDatum arrays would make sense,
e.g. TupleTableSlots, but that's for another commit.

Because the function call information is now variable-length
allocations have to take the number of arguments into account. For
heap allocations that can be done with SizeForFunctionCallInfoData(),
for on-stack allocations there's a new LOCAL_FCINFO(name, nargs) macro
that helps to allocate an appropriately sized and aligned variable.

Some places with stack allocation function call information don't know
the number of arguments at compile time, and currently variably sized
stack allocations aren't allowed in postgres. Therefore allow for
FUNC_MAX_ARGS space in these cases. They're not that common, so for
now that seems acceptable.

Because of the need to allocate FunctionCallInfo of the appropriate
size, older extensions may need to update their code. To avoid subtle
breakages, the FunctionCallInfoData struct has been renamed to
FunctionCallInfoBaseData. Most code only references FunctionCallInfo,
so that shouldn't cause much collateral damage.

Author: Andres Freund
Reviewed-By: Tom Lane
Discussion: https://postgr.es/m/20180605172952.x34m5uz6ju6enaem@alap3.anarazel.de
---
 contrib/hstore/hstore_op.c            |   2 +-
 doc/src/sgml/plhandler.sgml           |   8 +-
 src/backend/commands/event_trigger.c  |   8 +-
 src/backend/commands/functioncmds.c   |  15 +-
 src/backend/commands/tablecmds.c      |   8 +-
 src/backend/commands/trigger.c        |  12 +-
 src/backend/executor/README           |   6 +-
 src/backend/executor/execExpr.c       |  83 +--
 src/backend/executor/execExprInterp.c | 129 ++--
 src/backend/executor/execSRF.c        |  45 +-
 src/backend/executor/functions.c      |   4 +-
 src/backend/executor/nodeAgg.c        |  83 +--
 src/backend/executor/nodeWindowAgg.c  |  64 +-
 src/backend/jit/llvm/llvmjit.c        |   2 +
 src/backend/jit/llvm/llvmjit_expr.c   | 146 ++---
 src/backend/jit/llvm/llvmjit_types.c  |   3 +-
 src/backend/postmaster/pgstat.c       |   2 +-
 src/backend/tcop/fastpath.c           |  42 +-
 src/backend/utils/adt/arrayfuncs.c    | 123 ++--
 src/backend/utils/adt/int.c           |  20 +-
 src/backend/utils/adt/oid.c           |  20 +-
 src/backend/utils/adt/rowtypes.c      |  32 +-
 src/backend/utils/fmgr/README         |  23 +-
 src/backend/utils/fmgr/fmgr.c         | 846 ++++++++++----------------
 src/backend/utils/sort/sortsupport.c  |  15 +-
 src/include/executor/execExpr.h       |  17 +-
 src/include/executor/nodeAgg.h        |   6 +-
 src/include/fmgr.h                    |  60 +-
 src/include/jit/llvmjit.h             |   1 +
 src/include/jit/llvmjit_emit.h        |  56 ++
 src/include/nodes/execnodes.h         |   2 +-
 src/include/pgstat.h                  |   2 +-
 src/include/postgres.h                |  15 +
 src/pl/plperl/plperl.c                |  20 +-
 src/pl/plpgsql/src/pl_exec.c          |   8 +-
 src/pl/plpgsql/src/pl_handler.c       |  20 +-
 src/pl/plpython/plpy_exec.c           |   4 +-
 src/pl/plpython/plpy_main.c           |   8 +-
 src/pl/tcl/pltcl.c                    |  17 +-
 src/tools/pgindent/typedefs.list      |   2 +-
 40 files changed, 942 insertions(+), 1037 deletions(-)

diff --git a/contrib/hstore/hstore_op.c b/contrib/hstore/hstore_op.c
index a915215fb6f..b852fb5a8ac 100644
--- a/contrib/hstore/hstore_op.c
+++ b/contrib/hstore/hstore_op.c
@@ -854,7 +854,7 @@ hstore_to_matrix(PG_FUNCTION_ARGS)
 
 static void
 setup_firstcall(FuncCallContext *funcctx, HStore *hs,
-				FunctionCallInfoData *fcinfo)
+				FunctionCallInfo fcinfo)
 {
 	MemoryContext oldcontext;
 	HStore	   *st;
diff --git a/doc/src/sgml/plhandler.sgml b/doc/src/sgml/plhandler.sgml
index 73cd7d13875..14ad15aab8f 100644
--- a/doc/src/sgml/plhandler.sgml
+++ b/doc/src/sgml/plhandler.sgml
@@ -34,15 +34,15 @@
    <para>
     The call handler is called in the same way as any other function:
     It receives a pointer to a
-    <structname>FunctionCallInfoData</structname> <type>struct</type> containing
+    <structname>FunctionCallInfoBaseData</structname> <type>struct</type> containing
     argument values and information about the called function, and it
     is expected to return a <type>Datum</type> result (and possibly
     set the <structfield>isnull</structfield> field of the
-    <structname>FunctionCallInfoData</structname> structure, if it wishes
+    <structname>FunctionCallInfoBaseData</structname> structure, if it wishes
     to return an SQL null result).  The difference between a call
     handler and an ordinary callee function is that the
     <structfield>flinfo-&gt;fn_oid</structfield> field of the
-    <structname>FunctionCallInfoData</structname> structure will contain
+    <structname>FunctionCallInfoBaseData</structname> structure will contain
     the OID of the actual function to be called, not of the call
     handler itself.  The call handler must use this field to determine
     which function to execute.  Also, the passed argument list has
@@ -87,7 +87,7 @@
    <para>
     When a procedural-language function is invoked as a trigger, no arguments
     are passed in the usual way, but the
-    <structname>FunctionCallInfoData</structname>'s
+    <structname>FunctionCallInfoBaseData</structname>'s
     <structfield>context</structfield> field points at a
     <structname>TriggerData</structname> structure, rather than being <symbol>NULL</symbol>
     as it is in a plain function call.  A language handler should
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 8adc2cadaf4..7103ed91374 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -1057,7 +1057,7 @@ EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata)
 	{
 		Oid			fnoid = lfirst_oid(lc);
 		FmgrInfo	flinfo;
-		FunctionCallInfoData fcinfo;
+		LOCAL_FCINFO(fcinfo, 0);
 		PgStat_FunctionCallUsage fcusage;
 
 		elog(DEBUG1, "EventTriggerInvoke %u", fnoid);
@@ -1077,10 +1077,10 @@ EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata)
 		fmgr_info(fnoid, &flinfo);
 
 		/* Call the function, passing no arguments but setting a context. */
-		InitFunctionCallInfoData(fcinfo, &flinfo, 0,
+		InitFunctionCallInfoData(*fcinfo, &flinfo, 0,
 								 InvalidOid, (Node *) trigdata, NULL);
-		pgstat_init_function_usage(&fcinfo, &fcusage);
-		FunctionCallInvoke(&fcinfo);
+		pgstat_init_function_usage(fcinfo, &fcusage);
+		FunctionCallInvoke(fcinfo);
 		pgstat_end_function_usage(&fcusage, true);
 
 		/* Reclaim memory. */
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index eae2b09830b..25dd1adfa0b 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -2222,7 +2222,7 @@ ExecuteCallStmt(CallStmt *stmt, ParamListInfo params, bool atomic, DestReceiver
 	int			i;
 	AclResult	aclresult;
 	FmgrInfo	flinfo;
-	FunctionCallInfoData fcinfo;
+	LOCAL_FCINFO(fcinfo, FUNC_MAX_ARGS);
 	CallContext *callcontext;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2297,7 +2297,8 @@ ExecuteCallStmt(CallStmt *stmt, ParamListInfo params, bool atomic, DestReceiver
 	InvokeFunctionExecuteHook(fexpr->funcid);
 	fmgr_info(fexpr->funcid, &flinfo);
 	fmgr_info_set_expr((Node *) fexpr, &flinfo);
-	InitFunctionCallInfoData(fcinfo, &flinfo, nargs, fexpr->inputcollid, (Node *) callcontext, NULL);
+	InitFunctionCallInfoData(*fcinfo, &flinfo, nargs, fexpr->inputcollid,
+							 (Node *) callcontext, NULL);
 
 	/*
 	 * Evaluate procedure arguments inside a suitable execution context.  Note
@@ -2318,14 +2319,14 @@ ExecuteCallStmt(CallStmt *stmt, ParamListInfo params, bool atomic, DestReceiver
 
 		val = ExecEvalExprSwitchContext(exprstate, econtext, &isnull);
 
-		fcinfo.arg[i] = val;
-		fcinfo.argnull[i] = isnull;
+		fcinfo->args[i].datum = val;
+		fcinfo->args[i].isnull = isnull;
 
 		i++;
 	}
 
-	pgstat_init_function_usage(&fcinfo, &fcusage);
-	retval = FunctionCallInvoke(&fcinfo);
+	pgstat_init_function_usage(fcinfo, &fcusage);
+	retval = FunctionCallInvoke(fcinfo);
 	pgstat_end_function_usage(&fcusage, true);
 
 	if (fexpr->funcresulttype == VOIDOID)
@@ -2346,7 +2347,7 @@ ExecuteCallStmt(CallStmt *stmt, ParamListInfo params, bool atomic, DestReceiver
 		TupOutputState *tstate;
 		TupleTableSlot *slot;
 
-		if (fcinfo.isnull)
+		if (fcinfo->isnull)
 			elog(ERROR, "procedure returned null record");
 
 		td = DatumGetHeapTupleHeader(retval);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index e010586dd67..1d7fd334375 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8870,7 +8870,7 @@ validateForeignKeyConstraint(char *conname,
 
 	while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
 	{
-		FunctionCallInfoData fcinfo;
+		LOCAL_FCINFO(fcinfo, 0);
 		TriggerData trigdata;
 
 		/*
@@ -8878,7 +8878,7 @@ validateForeignKeyConstraint(char *conname,
 		 *
 		 * No parameters are passed, but we do set a context
 		 */
-		MemSet(&fcinfo, 0, sizeof(fcinfo));
+		MemSet(&fcinfodata, 0, sizeof(fcinfodata));
 
 		/*
 		 * We assume RI_FKey_check_ins won't look at flinfo...
@@ -8892,9 +8892,9 @@ validateForeignKeyConstraint(char *conname,
 		trigdata.tg_trigtuplebuf = scan->rs_cbuf;
 		trigdata.tg_newtuplebuf = InvalidBuffer;
 
-		fcinfo.context = (Node *) &trigdata;
+		fcinfo->context = (Node *) &trigdata;
 
-		RI_FKey_check_ins(&fcinfo);
+		RI_FKey_check_ins(fcinfo);
 	}
 
 	heap_endscan(scan);
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 7ffaeaffc67..499030c4455 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2357,7 +2357,7 @@ ExecCallTriggerFunc(TriggerData *trigdata,
 					Instrumentation *instr,
 					MemoryContext per_tuple_context)
 {
-	FunctionCallInfoData fcinfo;
+	LOCAL_FCINFO(fcinfo, 0);
 	PgStat_FunctionCallUsage fcusage;
 	Datum		result;
 	MemoryContext oldContext;
@@ -2402,15 +2402,15 @@ ExecCallTriggerFunc(TriggerData *trigdata,
 	/*
 	 * Call the function, passing no arguments but setting a context.
 	 */
-	InitFunctionCallInfoData(fcinfo, finfo, 0,
+	InitFunctionCallInfoData(*fcinfo, finfo, 0,
 							 InvalidOid, (Node *) trigdata, NULL);
 
-	pgstat_init_function_usage(&fcinfo, &fcusage);
+	pgstat_init_function_usage(fcinfo, &fcusage);
 
 	MyTriggerDepth++;
 	PG_TRY();
 	{
-		result = FunctionCallInvoke(&fcinfo);
+		result = FunctionCallInvoke(fcinfo);
 	}
 	PG_CATCH();
 	{
@@ -2428,11 +2428,11 @@ ExecCallTriggerFunc(TriggerData *trigdata,
 	 * Trigger protocol allows function to return a null pointer, but NOT to
 	 * set the isnull result flag.
 	 */
-	if (fcinfo.isnull)
+	if (fcinfo->isnull)
 		ereport(ERROR,
 				(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
 				 errmsg("trigger function %u returned null value",
-						fcinfo.flinfo->fn_oid)));
+						fcinfo->flinfo->fn_oid)));
 
 	/*
 	 * If doing EXPLAIN ANALYZE, stop charging time to this trigger, and count
diff --git a/src/backend/executor/README b/src/backend/executor/README
index ddbd62b4dba..05f197bc75b 100644
--- a/src/backend/executor/README
+++ b/src/backend/executor/README
@@ -124,7 +124,7 @@ For example, "a + b" (one OpExpr, with two Var expressions) would be
 represented as two steps to fetch the Var values, and one step for the
 evaluation of the function underlying the + operator.  The steps for the
 Vars would have their resvalue/resnull pointing directly to the appropriate
-arg[] and argnull[] array elements in the FunctionCallInfoData struct that
+args[].value .isnull elements in the FunctionCallInfoBaseData struct that
 is used by the function evaluation step, thus avoiding extra work to copy
 the result values around.
 
@@ -145,7 +145,7 @@ sub-expressions.
 Each ExecInitExprRec() call has to specify where that subexpression's
 results are to be stored (via the resv/resnull parameters).  This allows
 the above scenario of evaluating a (sub-)expression directly into
-fcinfo->arg/argnull, but also requires some care: target Datum/isnull
+fcinfo->args[].value/isnull, but also requires some care: target Datum/isnull
 variables may not be shared with another ExecInitExprRec() unless the
 results are only needed by steps executing before further usages of those
 target Datum/isnull variables.  Due to the non-recursiveness of the
@@ -158,7 +158,7 @@ not enough space.  Because of that it is *not* allowed to point directly
 into any of the steps during expression initialization.  Therefore, the
 resv/resnull for a subexpression usually point to some storage that is
 palloc'd separately from the steps array.  For instance, the
-FunctionCallInfoData for a function call step is separately allocated
+FunctionCallInfoBaseData for a function call step is separately allocated
 rather than being part of the ExprEvalStep array.  The overall result
 of a complete expression is typically returned into the resvalue/resnull
 fields of the ExprState node itself.
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index 4047d24b03e..563d7c160ef 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -966,7 +966,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 
 				/* Set up the primary fmgr lookup information */
 				finfo = palloc0(sizeof(FmgrInfo));
-				fcinfo = palloc0(sizeof(FunctionCallInfoData));
+				fcinfo = palloc0(SizeForFunctionCallInfo(2));
 				fmgr_info(opexpr->opfuncid, finfo);
 				fmgr_info_set_expr((Node *) node, finfo);
 				InitFunctionCallInfoData(*fcinfo, finfo, 2,
@@ -974,7 +974,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 
 				/* Evaluate scalar directly into left function argument */
 				ExecInitExprRec(scalararg, state,
-								&fcinfo->arg[0], &fcinfo->argnull[0]);
+								&fcinfo->args[0].datum, &fcinfo->args[0].isnull);
 
 				/*
 				 * Evaluate array argument into our return value.  There's no
@@ -1263,7 +1263,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 
 				/* lookup the source type's output function */
 				scratch.d.iocoerce.finfo_out = palloc0(sizeof(FmgrInfo));
-				scratch.d.iocoerce.fcinfo_data_out = palloc0(sizeof(FunctionCallInfoData));
+				scratch.d.iocoerce.fcinfo_data_out = palloc0(SizeForFunctionCallInfo(1));
 
 				getTypeOutputInfo(exprType((Node *) iocoerce->arg),
 								  &iofunc, &typisvarlena);
@@ -1275,7 +1275,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 
 				/* lookup the result type's input function */
 				scratch.d.iocoerce.finfo_in = palloc0(sizeof(FmgrInfo));
-				scratch.d.iocoerce.fcinfo_data_in = palloc0(sizeof(FunctionCallInfoData));
+				scratch.d.iocoerce.fcinfo_data_in = palloc0(SizeForFunctionCallInfo(3));
 
 				getTypeInputInfo(iocoerce->resulttype,
 								 &iofunc, &typioparam);
@@ -1290,10 +1290,10 @@ ExecInitExprRec(Expr *node, ExprState *state,
 				 * function, since they're constants.
 				 */
 				fcinfo_in = scratch.d.iocoerce.fcinfo_data_in;
-				fcinfo_in->arg[1] = ObjectIdGetDatum(typioparam);
-				fcinfo_in->argnull[1] = false;
-				fcinfo_in->arg[2] = Int32GetDatum(-1);
-				fcinfo_in->argnull[2] = false;
+				fcinfo_in->args[1].datum = ObjectIdGetDatum(typioparam);
+				fcinfo_in->args[1].isnull = false;
+				fcinfo_in->args[2].datum = Int32GetDatum(-1);
+				fcinfo_in->args[2].isnull = false;
 
 				ExprEvalPushStep(state, &scratch);
 				break;
@@ -1735,7 +1735,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 
 					/* Set up the primary fmgr lookup information */
 					finfo = palloc0(sizeof(FmgrInfo));
-					fcinfo = palloc0(sizeof(FunctionCallInfoData));
+					fcinfo = palloc0(SizeForFunctionCallInfo(2));
 					fmgr_info(proc, finfo);
 					fmgr_info_set_expr((Node *) node, finfo);
 					InitFunctionCallInfoData(*fcinfo, finfo, 2,
@@ -1750,9 +1750,9 @@ ExecInitExprRec(Expr *node, ExprState *state,
 
 					/* evaluate left and right args directly into fcinfo */
 					ExecInitExprRec(left_expr, state,
-									&fcinfo->arg[0], &fcinfo->argnull[0]);
+									&fcinfo->args[0].datum, &fcinfo->args[0].isnull);
 					ExecInitExprRec(right_expr, state,
-									&fcinfo->arg[1], &fcinfo->argnull[1]);
+									&fcinfo->args[1].datum, &fcinfo->args[1].isnull);
 
 					scratch.opcode = EEOP_ROWCOMPARE_STEP;
 					scratch.d.rowcompare_step.finfo = finfo;
@@ -1878,7 +1878,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 
 				/* Perform function lookup */
 				finfo = palloc0(sizeof(FmgrInfo));
-				fcinfo = palloc0(sizeof(FunctionCallInfoData));
+				fcinfo = palloc0(SizeForFunctionCallInfo(2));
 				fmgr_info(typentry->cmp_proc, finfo);
 				fmgr_info_set_expr((Node *) node, finfo);
 				InitFunctionCallInfoData(*fcinfo, finfo, 2,
@@ -2187,7 +2187,7 @@ ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid,
 
 	/* Allocate function lookup data and parameter workspace for this call */
 	scratch->d.func.finfo = palloc0(sizeof(FmgrInfo));
-	scratch->d.func.fcinfo_data = palloc0(sizeof(FunctionCallInfoData));
+	scratch->d.func.fcinfo_data = palloc0(SizeForFunctionCallInfo(nargs));
 	flinfo = scratch->d.func.finfo;
 	fcinfo = scratch->d.func.fcinfo_data;
 
@@ -2226,13 +2226,14 @@ ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid,
 			 */
 			Const	   *con = (Const *) arg;
 
-			fcinfo->arg[argno] = con->constvalue;
-			fcinfo->argnull[argno] = con->constisnull;
+			fcinfo->args[argno].datum = con->constvalue;
+			fcinfo->args[argno].isnull = con->constisnull;
 		}
 		else
 		{
 			ExecInitExprRec(arg, state,
-							&fcinfo->arg[argno], &fcinfo->argnull[argno]);
+							&fcinfo->args[argno].datum,
+							&fcinfo->args[argno].isnull);
 		}
 		argno++;
 	}
@@ -2961,10 +2962,11 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
 		AggStatePerTrans pertrans = &aggstate->pertrans[transno];
 		int			argno;
 		int			setno;
-		FunctionCallInfo trans_fcinfo = &pertrans->transfn_fcinfo;
+		FunctionCallInfo trans_fcinfo = pertrans->transfn_fcinfo;
 		ListCell   *arg;
 		ListCell   *bail;
 		List	   *adjust_bailout = NIL;
+		NullableDatum *strictargs = NULL;
 		bool	   *strictnulls = NULL;
 
 		/*
@@ -3002,7 +3004,7 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
 			Assert(pertrans->numSortCols == 0);
 			Assert(list_length(pertrans->aggref->args) == 1);
 
-			strictnulls = trans_fcinfo->argnull + 1;
+			strictargs = trans_fcinfo->args + 1;
 			source_tle = (TargetEntry *) linitial(pertrans->aggref->args);
 
 			/*
@@ -3016,21 +3018,21 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
 				 * value
 				 */
 				ExecInitExprRec(source_tle->expr, state,
-								&trans_fcinfo->arg[argno + 1],
-								&trans_fcinfo->argnull[argno + 1]);
+								&trans_fcinfo->args[argno + 1].datum,
+								&trans_fcinfo->args[argno + 1].isnull);
 			}
 			else
 			{
-				FunctionCallInfo ds_fcinfo = &pertrans->deserialfn_fcinfo;
+				FunctionCallInfo ds_fcinfo = pertrans->deserialfn_fcinfo;
 
 				/* evaluate argument */
 				ExecInitExprRec(source_tle->expr, state,
-								&ds_fcinfo->arg[0],
-								&ds_fcinfo->argnull[0]);
+								&ds_fcinfo->args[0].datum,
+								&ds_fcinfo->args[0].isnull);
 
 				/* Dummy second argument for type-safety reasons */
-				ds_fcinfo->arg[1] = PointerGetDatum(NULL);
-				ds_fcinfo->argnull[1] = false;
+				ds_fcinfo->args[1].datum = PointerGetDatum(NULL);
+				ds_fcinfo->args[1].isnull = false;
 
 				/*
 				 * Don't call a strict deserialization function with NULL
@@ -3044,8 +3046,8 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
 				scratch.d.agg_deserialize.aggstate = aggstate;
 				scratch.d.agg_deserialize.fcinfo_data = ds_fcinfo;
 				scratch.d.agg_deserialize.jumpnull = -1;	/* adjust later */
-				scratch.resvalue = &trans_fcinfo->arg[argno + 1];
-				scratch.resnull = &trans_fcinfo->argnull[argno + 1];
+				scratch.resvalue = &trans_fcinfo->args[argno + 1].datum;
+				scratch.resnull = &trans_fcinfo->args[argno + 1].isnull;
 
 				ExprEvalPushStep(state, &scratch);
 				adjust_bailout = lappend_int(adjust_bailout,
@@ -3062,7 +3064,7 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
 			/*
 			 * Normal transition function without ORDER BY / DISTINCT.
 			 */
-			strictnulls = trans_fcinfo->argnull + 1;
+			strictargs = trans_fcinfo->args + 1;
 
 			foreach(arg, pertrans->aggref->args)
 			{
@@ -3073,8 +3075,8 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
 				 * value
 				 */
 				ExecInitExprRec(source_tle->expr, state,
-								&trans_fcinfo->arg[argno + 1],
-								&trans_fcinfo->argnull[argno + 1]);
+								&trans_fcinfo->args[argno + 1].datum,
+								&trans_fcinfo->args[argno + 1].isnull);
 				argno++;
 			}
 		}
@@ -3122,8 +3124,12 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
 		 */
 		if (trans_fcinfo->flinfo->fn_strict && pertrans->numTransInputs > 0)
 		{
-			scratch.opcode = EEOP_AGG_STRICT_INPUT_CHECK;
+			if (strictnulls)
+				scratch.opcode = EEOP_AGG_STRICT_INPUT_CHECK_NULLS;
+			else
+				scratch.opcode = EEOP_AGG_STRICT_INPUT_CHECK_ARGS;
 			scratch.d.agg_strict_input_check.nulls = strictnulls;
+			scratch.d.agg_strict_input_check.args = strictargs;
 			scratch.d.agg_strict_input_check.jumpnull = -1; /* adjust later */
 			scratch.d.agg_strict_input_check.nargs = pertrans->numTransInputs;
 			ExprEvalPushStep(state, &scratch);
@@ -3177,7 +3183,8 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
 				Assert(as->d.jump.jumpdone == -1);
 				as->d.jump.jumpdone = state->steps_len;
 			}
-			else if (as->opcode == EEOP_AGG_STRICT_INPUT_CHECK)
+			else if (as->opcode == EEOP_AGG_STRICT_INPUT_CHECK_ARGS ||
+					 as->opcode == EEOP_AGG_STRICT_INPUT_CHECK_NULLS)
 			{
 				Assert(as->d.agg_strict_input_check.jumpnull == -1);
 				as->d.agg_strict_input_check.jumpnull = state->steps_len;
@@ -3312,7 +3319,7 @@ ExecBuildAggTransCall(ExprState *state, AggState *aggstate,
  */
 ExprState *
 ExecBuildGroupingEqual(TupleDesc ldesc, TupleDesc rdesc,
-					   const TupleTableSlotOps *lops, const TupleTableSlotOps *rops,
+					   const TupleTableSlotOps * lops, const TupleTableSlotOps * rops,
 					   int numCols,
 					   const AttrNumber *keyColIdx,
 					   const Oid *eqfunctions,
@@ -3389,7 +3396,7 @@ ExecBuildGroupingEqual(TupleDesc ldesc, TupleDesc rdesc,
 
 		/* Set up the primary fmgr lookup information */
 		finfo = palloc0(sizeof(FmgrInfo));
-		fcinfo = palloc0(sizeof(FunctionCallInfoData));
+		fcinfo = palloc0(SizeForFunctionCallInfo(2));
 		fmgr_info(foid, finfo);
 		fmgr_info_set_expr(NULL, finfo);
 		InitFunctionCallInfoData(*fcinfo, finfo, 2,
@@ -3399,16 +3406,16 @@ ExecBuildGroupingEqual(TupleDesc ldesc, TupleDesc rdesc,
 		scratch.opcode = EEOP_INNER_VAR;
 		scratch.d.var.attnum = attno - 1;
 		scratch.d.var.vartype = latt->atttypid;
-		scratch.resvalue = &fcinfo->arg[0];
-		scratch.resnull = &fcinfo->argnull[0];
+		scratch.resvalue = &fcinfo->args[0].datum;
+		scratch.resnull = &fcinfo->args[0].isnull;
 		ExprEvalPushStep(state, &scratch);
 
 		/* right arg */
 		scratch.opcode = EEOP_OUTER_VAR;
 		scratch.d.var.attnum = attno - 1;
 		scratch.d.var.vartype = ratt->atttypid;
-		scratch.resvalue = &fcinfo->arg[1];
-		scratch.resnull = &fcinfo->argnull[1];
+		scratch.resvalue = &fcinfo->args[1].datum;
+		scratch.resnull = &fcinfo->args[1].isnull;
 		ExprEvalPushStep(state, &scratch);
 
 		/* evaluate distinctness */
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 5a7206f5882..481d17dc44e 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -387,7 +387,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 		&&CASE_EEOP_ALTERNATIVE_SUBPLAN,
 		&&CASE_EEOP_AGG_STRICT_DESERIALIZE,
 		&&CASE_EEOP_AGG_DESERIALIZE,
-		&&CASE_EEOP_AGG_STRICT_INPUT_CHECK,
+		&&CASE_EEOP_AGG_STRICT_INPUT_CHECK_ARGS,
+		&&CASE_EEOP_AGG_STRICT_INPUT_CHECK_NULLS,
 		&&CASE_EEOP_AGG_INIT_TRANS,
 		&&CASE_EEOP_AGG_STRICT_TRANS_CHECK,
 		&&CASE_EEOP_AGG_PLAIN_TRANS_BYVAL,
@@ -631,14 +632,14 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 		EEO_CASE(EEOP_FUNCEXPR_STRICT)
 		{
 			FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
-			bool	   *argnull = fcinfo->argnull;
+			NullableDatum *args = fcinfo->args;
 			int			argno;
 			Datum		d;
 
 			/* strict function, so check for NULL args */
 			for (argno = 0; argno < op->d.func.nargs; argno++)
 			{
-				if (argnull[argno])
+				if (args[argno].isnull)
 				{
 					*op->resnull = true;
 					goto strictfail;
@@ -1054,8 +1055,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 				FunctionCallInfo fcinfo_out;
 
 				fcinfo_out = op->d.iocoerce.fcinfo_data_out;
-				fcinfo_out->arg[0] = *op->resvalue;
-				fcinfo_out->argnull[0] = false;
+				fcinfo_out->args[0].datum = *op->resvalue;
+				fcinfo_out->args[0].isnull = false;
 
 				fcinfo_out->isnull = false;
 				str = DatumGetCString(FunctionCallInvoke(fcinfo_out));
@@ -1071,8 +1072,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 				Datum		d;
 
 				fcinfo_in = op->d.iocoerce.fcinfo_data_in;
-				fcinfo_in->arg[0] = PointerGetDatum(str);
-				fcinfo_in->argnull[0] = *op->resnull;
+				fcinfo_in->args[0].datum = PointerGetDatum(str);
+				fcinfo_in->args[0].isnull = *op->resnull;
 				/* second and third arguments are already set up */
 
 				fcinfo_in->isnull = false;
@@ -1099,7 +1100,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 		{
 			/*
 			 * IS DISTINCT FROM must evaluate arguments (already done into
-			 * fcinfo->arg/argnull) to determine whether they are NULL; if
+			 * fcinfo->args) to determine whether they are NULL; if
 			 * either is NULL then the result is determined.  If neither is
 			 * NULL, then proceed to evaluate the comparison function, which
 			 * is just the type's standard equality operator.  We need not
@@ -1109,13 +1110,13 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 			FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
 
 			/* check function arguments for NULLness */
-			if (fcinfo->argnull[0] && fcinfo->argnull[1])
+			if (fcinfo->args[0].isnull && fcinfo->args[1].isnull)
 			{
 				/* Both NULL? Then is not distinct... */
 				*op->resvalue = BoolGetDatum(false);
 				*op->resnull = false;
 			}
-			else if (fcinfo->argnull[0] || fcinfo->argnull[1])
+			else if (fcinfo->args[0].isnull || fcinfo->args[1].isnull)
 			{
 				/* Only one is NULL? Then is distinct... */
 				*op->resvalue = BoolGetDatum(true);
@@ -1141,12 +1142,12 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 		{
 			FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
 
-			if (fcinfo->argnull[0] && fcinfo->argnull[1])
+			if (fcinfo->args[0].isnull && fcinfo->args[1].isnull)
 			{
 				*op->resvalue = BoolGetDatum(true);
 				*op->resnull = false;
 			}
-			else if (fcinfo->argnull[0] || fcinfo->argnull[1])
+			else if (fcinfo->args[0].isnull || fcinfo->args[1].isnull)
 			{
 				*op->resvalue = BoolGetDatum(false);
 				*op->resnull = false;
@@ -1167,12 +1168,12 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 		EEO_CASE(EEOP_NULLIF)
 		{
 			/*
-			 * The arguments are already evaluated into fcinfo->arg/argnull.
+			 * The arguments are already evaluated into fcinfo->args.
 			 */
 			FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
 
 			/* if either argument is NULL they can't be equal */
-			if (!fcinfo->argnull[0] && !fcinfo->argnull[1])
+			if (!fcinfo->args[0].isnull && !fcinfo->args[1].isnull)
 			{
 				Datum		result;
 
@@ -1190,8 +1191,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 			}
 
 			/* Arguments aren't equal, so return the first one */
-			*op->resvalue = fcinfo->arg[0];
-			*op->resnull = fcinfo->argnull[0];
+			*op->resvalue = fcinfo->args[0].datum;
+			*op->resnull = fcinfo->args[0].isnull;
 
 			EEO_NEXT();
 		}
@@ -1257,7 +1258,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 
 			/* force NULL result if strict fn and NULL input */
 			if (op->d.rowcompare_step.finfo->fn_strict &&
-				(fcinfo->argnull[0] || fcinfo->argnull[1]))
+				(fcinfo->args[0].isnull || fcinfo->args[1].isnull))
 			{
 				*op->resnull = true;
 				EEO_JUMP(op->d.rowcompare_step.jumpnull);
@@ -1496,10 +1497,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 		/* evaluate a strict aggregate deserialization function */
 		EEO_CASE(EEOP_AGG_STRICT_DESERIALIZE)
 		{
-			bool	   *argnull = op->d.agg_deserialize.fcinfo_data->argnull;
-
 			/* Don't call a strict deserialization function with NULL input */
-			if (argnull[0])
+			if (op->d.agg_deserialize.fcinfo_data->args[0].isnull)
 				EEO_JUMP(op->d.agg_deserialize.jumpnull);
 
 			/* fallthrough */
@@ -1529,7 +1528,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 		 * Check that a strict aggregate transition / combination function's
 		 * input is not NULL.
 		 */
-		EEO_CASE(EEOP_AGG_STRICT_INPUT_CHECK)
+		EEO_CASE(EEOP_AGG_STRICT_INPUT_CHECK_NULLS)
 		{
 			int			argno;
 			bool	   *nulls = op->d.agg_strict_input_check.nulls;
@@ -1543,6 +1542,20 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 			EEO_NEXT();
 		}
 
+		EEO_CASE(EEOP_AGG_STRICT_INPUT_CHECK_ARGS)
+		{
+			int			argno;
+			NullableDatum *args = op->d.agg_strict_input_check.args;
+			int			nargs = op->d.agg_strict_input_check.nargs;
+
+			for (argno = 0; argno < nargs; argno++)
+			{
+				if (args[argno].isnull)
+					EEO_JUMP(op->d.agg_strict_input_check.jumpnull);
+			}
+			EEO_NEXT();
+		}
+
 		/*
 		 * Initialize an aggregate's first value if necessary.
 		 */
@@ -1613,7 +1626,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 
 			Assert(pertrans->transtypeByVal);
 
-			fcinfo = &pertrans->transfn_fcinfo;
+			fcinfo = pertrans->transfn_fcinfo;
 
 			/* cf. select_current_set() */
 			aggstate->curaggcontext = op->d.agg_trans.aggcontext;
@@ -1625,8 +1638,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 			/* invoke transition function in per-tuple context */
 			oldContext = MemoryContextSwitchTo(aggstate->tmpcontext->ecxt_per_tuple_memory);
 
-			fcinfo->arg[0] = pergroup->transValue;
-			fcinfo->argnull[0] = pergroup->transValueIsNull;
+			fcinfo->args[0].datum = pergroup->transValue;
+			fcinfo->args[0].isnull = pergroup->transValueIsNull;
 			fcinfo->isnull = false; /* just in case transfn doesn't set it */
 
 			newVal = FunctionCallInvoke(fcinfo);
@@ -1664,7 +1677,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 
 			Assert(!pertrans->transtypeByVal);
 
-			fcinfo = &pertrans->transfn_fcinfo;
+			fcinfo = pertrans->transfn_fcinfo;
 
 			/* cf. select_current_set() */
 			aggstate->curaggcontext = op->d.agg_trans.aggcontext;
@@ -1676,8 +1689,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 			/* invoke transition function in per-tuple context */
 			oldContext = MemoryContextSwitchTo(aggstate->tmpcontext->ecxt_per_tuple_memory);
 
-			fcinfo->arg[0] = pergroup->transValue;
-			fcinfo->argnull[0] = pergroup->transValueIsNull;
+			fcinfo->args[0].datum = pergroup->transValue;
+			fcinfo->args[0].isnull = pergroup->transValueIsNull;
 			fcinfo->isnull = false; /* just in case transfn doesn't set it */
 
 			newVal = FunctionCallInvoke(fcinfo);
@@ -2075,7 +2088,7 @@ ExecJustApplyFuncToCase(ExprState *state, ExprContext *econtext, bool *isnull)
 {
 	ExprEvalStep *op = &state->steps[0];
 	FunctionCallInfo fcinfo;
-	bool	   *argnull;
+	NullableDatum *args;
 	int			argno;
 	Datum		d;
 
@@ -2089,12 +2102,12 @@ ExecJustApplyFuncToCase(ExprState *state, ExprContext *econtext, bool *isnull)
 	op++;
 
 	fcinfo = op->d.func.fcinfo_data;
-	argnull = fcinfo->argnull;
+	args = fcinfo->args;
 
 	/* strict function, so check for NULL args */
 	for (argno = 0; argno < op->d.func.nargs; argno++)
 	{
-		if (argnull[argno])
+		if (args[argno].isnull)
 		{
 			*isnull = true;
 			return (Datum) 0;
@@ -2220,14 +2233,14 @@ ExecEvalFuncExprStrictFusage(ExprState *state, ExprEvalStep *op,
 
 	FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
 	PgStat_FunctionCallUsage fcusage;
-	bool	   *argnull = fcinfo->argnull;
+	NullableDatum *args = fcinfo->args;
 	int			argno;
 	Datum		d;
 
 	/* strict function, so check for NULL args */
 	for (argno = 0; argno < op->d.func.nargs; argno++)
 	{
-		if (argnull[argno])
+		if (args[argno].isnull)
 		{
 			*op->resnull = true;
 			return;
@@ -2318,7 +2331,7 @@ void
 ExecEvalSQLValueFunction(ExprState *state, ExprEvalStep *op)
 {
 	SQLValueFunction *svf = op->d.sqlvaluefunction.svf;
-	FunctionCallInfoData fcinfo;
+	LOCAL_FCINFO(fcinfo, 0);
 
 	*op->resnull = false;
 
@@ -2350,24 +2363,24 @@ ExecEvalSQLValueFunction(ExprState *state, ExprEvalStep *op)
 		case SVFOP_CURRENT_ROLE:
 		case SVFOP_CURRENT_USER:
 		case SVFOP_USER:
-			InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
-			*op->resvalue = current_user(&fcinfo);
-			*op->resnull = fcinfo.isnull;
+			InitFunctionCallInfoData(*fcinfo, NULL, 0, InvalidOid, NULL, NULL);
+			*op->resvalue = current_user(fcinfo);
+			*op->resnull = fcinfo->isnull;
 			break;
 		case SVFOP_SESSION_USER:
-			InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
-			*op->resvalue = session_user(&fcinfo);
-			*op->resnull = fcinfo.isnull;
+			InitFunctionCallInfoData(*fcinfo, NULL, 0, InvalidOid, NULL, NULL);
+			*op->resvalue = session_user(fcinfo);
+			*op->resnull = fcinfo->isnull;
 			break;
 		case SVFOP_CURRENT_CATALOG:
-			InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
-			*op->resvalue = current_database(&fcinfo);
-			*op->resnull = fcinfo.isnull;
+			InitFunctionCallInfoData(*fcinfo, NULL, 0, InvalidOid, NULL, NULL);
+			*op->resvalue = current_database(fcinfo);
+			*op->resnull = fcinfo->isnull;
 			break;
 		case SVFOP_CURRENT_SCHEMA:
-			InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
-			*op->resvalue = current_schema(&fcinfo);
-			*op->resnull = fcinfo.isnull;
+			InitFunctionCallInfoData(*fcinfo, NULL, 0, InvalidOid, NULL, NULL);
+			*op->resvalue = current_schema(fcinfo);
+			*op->resnull = fcinfo->isnull;
 			break;
 	}
 }
@@ -2800,8 +2813,8 @@ ExecEvalMinMax(ExprState *state, ExprEvalStep *op)
 	int			off;
 
 	/* set at initialization */
-	Assert(fcinfo->argnull[0] == false);
-	Assert(fcinfo->argnull[1] == false);
+	Assert(fcinfo->args[0].isnull == false);
+	Assert(fcinfo->args[1].isnull == false);
 
 	/* default to null result */
 	*op->resnull = true;
@@ -2823,8 +2836,8 @@ ExecEvalMinMax(ExprState *state, ExprEvalStep *op)
 			int			cmpresult;
 
 			/* apply comparison function */
-			fcinfo->arg[0] = *op->resvalue;
-			fcinfo->arg[1] = values[off];
+			fcinfo->args[0].datum = *op->resvalue;
+			fcinfo->args[1].datum = values[off];
 
 			fcinfo->isnull = false;
 			cmpresult = DatumGetInt32(FunctionCallInvoke(fcinfo));
@@ -3324,7 +3337,7 @@ ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext
  * Evaluate "scalar op ANY/ALL (array)".
  *
  * Source array is in our result area, scalar arg is already evaluated into
- * fcinfo->arg[0]/argnull[0].
+ * fcinfo->args[0].
  *
  * The operator always yields boolean, and we combine the results across all
  * array elements using OR and AND (for ANY and ALL respectively).  Of course
@@ -3376,7 +3389,7 @@ ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op)
 	 * If the scalar is NULL, and the function is strict, return NULL; no
 	 * point in iterating the loop.
 	 */
-	if (fcinfo->argnull[0] && strictfunc)
+	if (fcinfo->args[0].isnull && strictfunc)
 	{
 		*op->resnull = true;
 		return;
@@ -3416,20 +3429,20 @@ ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op)
 		/* Get array element, checking for NULL */
 		if (bitmap && (*bitmap & bitmask) == 0)
 		{
-			fcinfo->arg[1] = (Datum) 0;
-			fcinfo->argnull[1] = true;
+			fcinfo->args[1].datum = (Datum) 0;
+			fcinfo->args[1].isnull = true;
 		}
 		else
 		{
 			elt = fetch_att(s, typbyval, typlen);
 			s = att_addlength_pointer(s, typlen, s);
 			s = (char *) att_align_nominal(s, typalign);
-			fcinfo->arg[1] = elt;
-			fcinfo->argnull[1] = false;
+			fcinfo->args[1].datum = elt;
+			fcinfo->args[1].isnull = false;
 		}
 
 		/* Call comparison function */
-		if (fcinfo->argnull[1] && strictfunc)
+		if (fcinfo->args[1].isnull && strictfunc)
 		{
 			fcinfo->isnull = true;
 			thisresult = (Datum) 0;
@@ -4044,7 +4057,7 @@ ExecEvalSysVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext,
 void
 ExecAggInitGroup(AggState *aggstate, AggStatePerTrans pertrans, AggStatePerGroup pergroup)
 {
-	FunctionCallInfo fcinfo = &pertrans->transfn_fcinfo;
+	FunctionCallInfo fcinfo = pertrans->transfn_fcinfo;
 	MemoryContext oldContext;
 
 	/*
@@ -4055,7 +4068,7 @@ ExecAggInitGroup(AggState *aggstate, AggStatePerTrans pertrans, AggStatePerGroup
 	 */
 	oldContext = MemoryContextSwitchTo(
 									   aggstate->curaggcontext->ecxt_per_tuple_memory);
-	pergroup->transValue = datumCopy(fcinfo->arg[1],
+	pergroup->transValue = datumCopy(fcinfo->args[1].datum,
 									 pertrans->transtypeByVal,
 									 pertrans->transtypeLen);
 	pergroup->transValueIsNull = false;
diff --git a/src/backend/executor/execSRF.c b/src/backend/executor/execSRF.c
index 0c086c52904..44e102a5a20 100644
--- a/src/backend/executor/execSRF.c
+++ b/src/backend/executor/execSRF.c
@@ -109,7 +109,7 @@ ExecMakeTableFunctionResult(SetExprState *setexpr,
 	Oid			funcrettype;
 	bool		returnsTuple;
 	bool		returnsSet = false;
-	FunctionCallInfoData fcinfo;
+	FunctionCallInfo fcinfo;
 	PgStat_FunctionCallUsage fcusage;
 	ReturnSetInfo rsinfo;
 	HeapTupleData tmptup;
@@ -141,6 +141,8 @@ ExecMakeTableFunctionResult(SetExprState *setexpr,
 	rsinfo.setResult = NULL;
 	rsinfo.setDesc = NULL;
 
+	fcinfo = palloc(SizeForFunctionCallInfo(list_length(setexpr->args)));
+
 	/*
 	 * Normally the passed expression tree will be a SetExprState, since the
 	 * grammar only allows a function call at the top level of a table
@@ -157,9 +159,9 @@ ExecMakeTableFunctionResult(SetExprState *setexpr,
 		 * This path is similar to ExecMakeFunctionResultSet.
 		 */
 		returnsSet = setexpr->funcReturnsSet;
-		InitFunctionCallInfoData(fcinfo, &(setexpr->func),
+		InitFunctionCallInfoData(*fcinfo, &(setexpr->func),
 								 list_length(setexpr->args),
-								 setexpr->fcinfo_data.fncollation,
+								 setexpr->fcinfo->fncollation,
 								 NULL, (Node *) &rsinfo);
 
 		/*
@@ -174,7 +176,7 @@ ExecMakeTableFunctionResult(SetExprState *setexpr,
 		 */
 		MemoryContextReset(argContext);
 		oldcontext = MemoryContextSwitchTo(argContext);
-		ExecEvalFuncArgs(&fcinfo, setexpr->args, econtext);
+		ExecEvalFuncArgs(fcinfo, setexpr->args, econtext);
 		MemoryContextSwitchTo(oldcontext);
 
 		/*
@@ -186,9 +188,9 @@ ExecMakeTableFunctionResult(SetExprState *setexpr,
 		{
 			int			i;
 
-			for (i = 0; i < fcinfo.nargs; i++)
+			for (i = 0; i < fcinfo->nargs; i++)
 			{
-				if (fcinfo.argnull[i])
+				if (fcinfo->args[i].isnull)
 					goto no_function_result;
 			}
 		}
@@ -196,7 +198,7 @@ ExecMakeTableFunctionResult(SetExprState *setexpr,
 	else
 	{
 		/* Treat setexpr as a generic expression */
-		InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
+		InitFunctionCallInfoData(*fcinfo, NULL, 0, InvalidOid, NULL, NULL);
 	}
 
 	/*
@@ -224,11 +226,11 @@ ExecMakeTableFunctionResult(SetExprState *setexpr,
 		/* Call the function or expression one time */
 		if (!setexpr->elidedFuncState)
 		{
-			pgstat_init_function_usage(&fcinfo, &fcusage);
+			pgstat_init_function_usage(fcinfo, &fcusage);
 
-			fcinfo.isnull = false;
+			fcinfo->isnull = false;
 			rsinfo.isDone = ExprSingleResult;
-			result = FunctionCallInvoke(&fcinfo);
+			result = FunctionCallInvoke(fcinfo);
 
 			pgstat_end_function_usage(&fcusage,
 									  rsinfo.isDone != ExprMultipleResult);
@@ -236,7 +238,7 @@ ExecMakeTableFunctionResult(SetExprState *setexpr,
 		else
 		{
 			result =
-				ExecEvalExpr(setexpr->elidedFuncState, econtext, &fcinfo.isnull);
+				ExecEvalExpr(setexpr->elidedFuncState, econtext, &fcinfo->isnull);
 			rsinfo.isDone = ExprSingleResult;
 		}
 
@@ -277,7 +279,7 @@ ExecMakeTableFunctionResult(SetExprState *setexpr,
 			 */
 			if (returnsTuple)
 			{
-				if (!fcinfo.isnull)
+				if (!fcinfo->isnull)
 				{
 					HeapTupleHeader td = DatumGetHeapTupleHeader(result);
 
@@ -338,7 +340,7 @@ ExecMakeTableFunctionResult(SetExprState *setexpr,
 			else
 			{
 				/* Scalar-type case: just store the function result */
-				tuplestore_putvalues(tupstore, tupdesc, &result, &fcinfo.isnull);
+				tuplestore_putvalues(tupstore, tupdesc, &result, &fcinfo->isnull);
 			}
 
 			/*
@@ -547,7 +549,7 @@ restart:
 	 * rows from this SRF have been returned, otherwise ValuePerCall SRFs
 	 * would reference freed memory after the first returned row.
 	 */
-	fcinfo = &fcache->fcinfo_data;
+	fcinfo = fcache->fcinfo;
 	arguments = fcache->args;
 	if (!fcache->setArgsValid)
 	{
@@ -587,7 +589,7 @@ restart:
 	{
 		for (i = 0; i < fcinfo->nargs; i++)
 		{
-			if (fcinfo->argnull[i])
+			if (fcinfo->args[i].isnull)
 			{
 				callit = false;
 				break;
@@ -678,6 +680,7 @@ init_sexpr(Oid foid, Oid input_collation, Expr *node,
 		   MemoryContext sexprCxt, bool allowSRF, bool needDescForSRF)
 {
 	AclResult	aclresult;
+	size_t		numargs = list_length(sexpr->args);
 
 	/* Check permission to call function */
 	aclresult = pg_proc_aclcheck(foid, GetUserId(), ACL_EXECUTE);
@@ -704,8 +707,10 @@ init_sexpr(Oid foid, Oid input_collation, Expr *node,
 	fmgr_info_set_expr((Node *) sexpr->expr, &(sexpr->func));
 
 	/* Initialize the function call parameter struct as well */
-	InitFunctionCallInfoData(sexpr->fcinfo_data, &(sexpr->func),
-							 list_length(sexpr->args),
+	sexpr->fcinfo =
+		(FunctionCallInfo) palloc(SizeForFunctionCallInfo(numargs));
+	InitFunctionCallInfoData(*sexpr->fcinfo, &(sexpr->func),
+							 numargs,
 							 input_collation, NULL, NULL);
 
 	/* If function returns set, check if that's allowed by caller */
@@ -820,9 +825,9 @@ ExecEvalFuncArgs(FunctionCallInfo fcinfo,
 	{
 		ExprState  *argstate = (ExprState *) lfirst(arg);
 
-		fcinfo->arg[i] = ExecEvalExpr(argstate,
-									  econtext,
-									  &fcinfo->argnull[i]);
+		fcinfo->args[i].datum = ExecEvalExpr(argstate,
+											 econtext,
+											 &fcinfo->args[i].isnull);
 		i++;
 	}
 
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index de41588105e..637d826af4d 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -933,8 +933,8 @@ postquel_sub_params(SQLFunctionCachePtr fcache,
 		{
 			ParamExternData *prm = &paramLI->params[i];
 
-			prm->value = fcinfo->arg[i];
-			prm->isnull = fcinfo->argnull[i];
+			prm->value = fcinfo->args[i].datum;
+			prm->isnull = fcinfo->args[i].isnull;
 			prm->pflags = 0;
 			prm->ptype = fcache->pinfo->argtypes[i];
 		}
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index 508c9195743..f6176b14aa1 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -553,7 +553,7 @@ advance_transition_function(AggState *aggstate,
 							AggStatePerTrans pertrans,
 							AggStatePerGroup pergroupstate)
 {
-	FunctionCallInfo fcinfo = &pertrans->transfn_fcinfo;
+	FunctionCallInfo fcinfo = pertrans->transfn_fcinfo;
 	MemoryContext oldContext;
 	Datum		newVal;
 
@@ -568,7 +568,7 @@ advance_transition_function(AggState *aggstate,
 
 		for (i = 1; i <= numTransInputs; i++)
 		{
-			if (fcinfo->argnull[i])
+			if (fcinfo->args[i].isnull)
 				return;
 		}
 		if (pergroupstate->noTransValue)
@@ -584,7 +584,7 @@ advance_transition_function(AggState *aggstate,
 			 */
 			oldContext = MemoryContextSwitchTo(
 											   aggstate->curaggcontext->ecxt_per_tuple_memory);
-			pergroupstate->transValue = datumCopy(fcinfo->arg[1],
+			pergroupstate->transValue = datumCopy(fcinfo->args[1].datum,
 												  pertrans->transtypeByVal,
 												  pertrans->transtypeLen);
 			pergroupstate->transValueIsNull = false;
@@ -613,8 +613,8 @@ advance_transition_function(AggState *aggstate,
 	/*
 	 * OK to call the transition function
 	 */
-	fcinfo->arg[0] = pergroupstate->transValue;
-	fcinfo->argnull[0] = pergroupstate->transValueIsNull;
+	fcinfo->args[0].datum = pergroupstate->transValue;
+	fcinfo->args[0].isnull = pergroupstate->transValueIsNull;
 	fcinfo->isnull = false;		/* just in case transfn doesn't set it */
 
 	newVal = FunctionCallInvoke(fcinfo);
@@ -717,7 +717,7 @@ process_ordered_aggregate_single(AggState *aggstate,
 	bool		isDistinct = (pertrans->numDistinctCols > 0);
 	Datum		newAbbrevVal = (Datum) 0;
 	Datum		oldAbbrevVal = (Datum) 0;
-	FunctionCallInfo fcinfo = &pertrans->transfn_fcinfo;
+	FunctionCallInfo fcinfo = pertrans->transfn_fcinfo;
 	Datum	   *newVal;
 	bool	   *isNull;
 
@@ -726,8 +726,8 @@ process_ordered_aggregate_single(AggState *aggstate,
 	tuplesort_performsort(pertrans->sortstates[aggstate->current_set]);
 
 	/* Load the column into argument 1 (arg 0 will be transition value) */
-	newVal = fcinfo->arg + 1;
-	isNull = fcinfo->argnull + 1;
+	newVal = &fcinfo->args[1].datum;
+	isNull = &fcinfo->args[1].isnull;
 
 	/*
 	 * Note: if input type is pass-by-ref, the datums returned by the sort are
@@ -803,7 +803,7 @@ process_ordered_aggregate_multi(AggState *aggstate,
 								AggStatePerGroup pergroupstate)
 {
 	ExprContext *tmpcontext = aggstate->tmpcontext;
-	FunctionCallInfo fcinfo = &pertrans->transfn_fcinfo;
+	FunctionCallInfo fcinfo = pertrans->transfn_fcinfo;
 	TupleTableSlot *slot1 = pertrans->sortslot;
 	TupleTableSlot *slot2 = pertrans->uniqslot;
 	int			numTransInputs = pertrans->numTransInputs;
@@ -843,8 +843,8 @@ process_ordered_aggregate_multi(AggState *aggstate,
 			/* Start from 1, since the 0th arg will be the transition value */
 			for (i = 0; i < numTransInputs; i++)
 			{
-				fcinfo->arg[i + 1] = slot1->tts_values[i];
-				fcinfo->argnull[i + 1] = slot1->tts_isnull[i];
+				fcinfo->args[i + 1].datum = slot1->tts_values[i];
+				fcinfo->args[i + 1].isnull = slot1->tts_isnull[i];
 			}
 
 			advance_transition_function(aggstate, pertrans, pergroupstate);
@@ -897,7 +897,7 @@ finalize_aggregate(AggState *aggstate,
 				   AggStatePerGroup pergroupstate,
 				   Datum *resultVal, bool *resultIsNull)
 {
-	FunctionCallInfoData fcinfo;
+	LOCAL_FCINFO(fcinfo, FUNC_MAX_ARGS);
 	bool		anynull = false;
 	MemoryContext oldContext;
 	int			i;
@@ -917,10 +917,10 @@ finalize_aggregate(AggState *aggstate,
 	{
 		ExprState  *expr = (ExprState *) lfirst(lc);
 
-		fcinfo.arg[i] = ExecEvalExpr(expr,
-									 aggstate->ss.ps.ps_ExprContext,
-									 &fcinfo.argnull[i]);
-		anynull |= fcinfo.argnull[i];
+		fcinfo->args[i].datum = ExecEvalExpr(expr,
+											 aggstate->ss.ps.ps_ExprContext,
+											 &fcinfo->args[i].isnull);
+		anynull |= fcinfo->args[i].isnull;
 		i++;
 	}
 
@@ -934,27 +934,28 @@ finalize_aggregate(AggState *aggstate,
 		/* set up aggstate->curperagg for AggGetAggref() */
 		aggstate->curperagg = peragg;
 
-		InitFunctionCallInfoData(fcinfo, &peragg->finalfn,
+		InitFunctionCallInfoData(*fcinfo, &peragg->finalfn,
 								 numFinalArgs,
 								 pertrans->aggCollation,
 								 (void *) aggstate, NULL);
 
 		/* Fill in the transition state value */
-		fcinfo.arg[0] = MakeExpandedObjectReadOnly(pergroupstate->transValue,
-												   pergroupstate->transValueIsNull,
-												   pertrans->transtypeLen);
-		fcinfo.argnull[0] = pergroupstate->transValueIsNull;
+		fcinfo->args[0].datum =
+			MakeExpandedObjectReadOnly(pergroupstate->transValue,
+									   pergroupstate->transValueIsNull,
+									   pertrans->transtypeLen);
+		fcinfo->args[0].isnull = pergroupstate->transValueIsNull;
 		anynull |= pergroupstate->transValueIsNull;
 
 		/* Fill any remaining argument positions with nulls */
 		for (; i < numFinalArgs; i++)
 		{
-			fcinfo.arg[i] = (Datum) 0;
-			fcinfo.argnull[i] = true;
+			fcinfo->args[i].datum = (Datum) 0;
+			fcinfo->args[i].isnull = true;
 			anynull = true;
 		}
 
-		if (fcinfo.flinfo->fn_strict && anynull)
+		if (fcinfo->flinfo->fn_strict && anynull)
 		{
 			/* don't call a strict function with NULL inputs */
 			*resultVal = (Datum) 0;
@@ -962,8 +963,8 @@ finalize_aggregate(AggState *aggstate,
 		}
 		else
 		{
-			*resultVal = FunctionCallInvoke(&fcinfo);
-			*resultIsNull = fcinfo.isnull;
+			*resultVal = FunctionCallInvoke(fcinfo);
+			*resultIsNull = fcinfo->isnull;
 		}
 		aggstate->curperagg = NULL;
 	}
@@ -1018,12 +1019,13 @@ finalize_partialaggregate(AggState *aggstate,
 		}
 		else
 		{
-			FunctionCallInfo fcinfo = &pertrans->serialfn_fcinfo;
+			FunctionCallInfo fcinfo = pertrans->serialfn_fcinfo;
 
-			fcinfo->arg[0] = MakeExpandedObjectReadOnly(pergroupstate->transValue,
-														pergroupstate->transValueIsNull,
-														pertrans->transtypeLen);
-			fcinfo->argnull[0] = pergroupstate->transValueIsNull;
+			fcinfo->args[0].datum =
+				MakeExpandedObjectReadOnly(pergroupstate->transValue,
+										   pergroupstate->transValueIsNull,
+										   pertrans->transtypeLen);
+			fcinfo->args[0].isnull = pergroupstate->transValueIsNull;
 
 			*resultVal = FunctionCallInvoke(fcinfo);
 			*resultIsNull = fcinfo->isnull;
@@ -2927,7 +2929,9 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
 		fmgr_info(aggtransfn, &pertrans->transfn);
 		fmgr_info_set_expr((Node *) combinefnexpr, &pertrans->transfn);
 
-		InitFunctionCallInfoData(pertrans->transfn_fcinfo,
+		pertrans->transfn_fcinfo =
+			(FunctionCallInfo) palloc(SizeForFunctionCallInfo(2));
+		InitFunctionCallInfoData(*pertrans->transfn_fcinfo,
 								 &pertrans->transfn,
 								 2,
 								 pertrans->aggCollation,
@@ -2947,6 +2951,7 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
 	else
 	{
 		Expr	   *transfnexpr;
+		size_t		numInputs = pertrans->numTransInputs + 1;
 
 		/*
 		 * Set up infrastructure for calling the transfn.  Note that invtrans
@@ -2965,9 +2970,11 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
 		fmgr_info(aggtransfn, &pertrans->transfn);
 		fmgr_info_set_expr((Node *) transfnexpr, &pertrans->transfn);
 
-		InitFunctionCallInfoData(pertrans->transfn_fcinfo,
+		pertrans->transfn_fcinfo =
+			(FunctionCallInfo) palloc(SizeForFunctionCallInfo(numInputs));
+		InitFunctionCallInfoData(*pertrans->transfn_fcinfo,
 								 &pertrans->transfn,
-								 pertrans->numTransInputs + 1,
+								 numInputs,
 								 pertrans->aggCollation,
 								 (void *) aggstate, NULL);
 
@@ -3003,7 +3010,9 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
 		fmgr_info(aggserialfn, &pertrans->serialfn);
 		fmgr_info_set_expr((Node *) serialfnexpr, &pertrans->serialfn);
 
-		InitFunctionCallInfoData(pertrans->serialfn_fcinfo,
+		pertrans->serialfn_fcinfo =
+			(FunctionCallInfo) palloc(SizeForFunctionCallInfo(1));
+		InitFunctionCallInfoData(*pertrans->serialfn_fcinfo,
 								 &pertrans->serialfn,
 								 1,
 								 InvalidOid,
@@ -3017,7 +3026,9 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
 		fmgr_info(aggdeserialfn, &pertrans->deserialfn);
 		fmgr_info_set_expr((Node *) deserialfnexpr, &pertrans->deserialfn);
 
-		InitFunctionCallInfoData(pertrans->deserialfn_fcinfo,
+		pertrans->deserialfn_fcinfo =
+			(FunctionCallInfo) palloc(SizeForFunctionCallInfo(2));
+		InitFunctionCallInfoData(*pertrans->deserialfn_fcinfo,
 								 &pertrans->deserialfn,
 								 2,
 								 InvalidOid,
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index 7ae56074ca8..f0c1c7a487a 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -243,8 +243,7 @@ advance_windowaggregate(WindowAggState *winstate,
 {
 	WindowFuncExprState *wfuncstate = perfuncstate->wfuncstate;
 	int			numArguments = perfuncstate->numArguments;
-	FunctionCallInfoData fcinfodata;
-	FunctionCallInfo fcinfo = &fcinfodata;
+	LOCAL_FCINFO(fcinfo, FUNC_MAX_ARGS);
 	Datum		newVal;
 	ListCell   *arg;
 	int			i;
@@ -273,8 +272,8 @@ advance_windowaggregate(WindowAggState *winstate,
 	{
 		ExprState  *argstate = (ExprState *) lfirst(arg);
 
-		fcinfo->arg[i] = ExecEvalExpr(argstate, econtext,
-									  &fcinfo->argnull[i]);
+		fcinfo->args[i].datum = ExecEvalExpr(argstate, econtext,
+											 &fcinfo->args[i].isnull);
 		i++;
 	}
 
@@ -287,7 +286,7 @@ advance_windowaggregate(WindowAggState *winstate,
 		 */
 		for (i = 1; i <= numArguments; i++)
 		{
-			if (fcinfo->argnull[i])
+			if (fcinfo->args[i].isnull)
 			{
 				MemoryContextSwitchTo(oldContext);
 				return;
@@ -306,7 +305,7 @@ advance_windowaggregate(WindowAggState *winstate,
 		if (peraggstate->transValueCount == 0 && peraggstate->transValueIsNull)
 		{
 			MemoryContextSwitchTo(peraggstate->aggcontext);
-			peraggstate->transValue = datumCopy(fcinfo->arg[1],
+			peraggstate->transValue = datumCopy(fcinfo->args[1].datum,
 												peraggstate->transtypeByVal,
 												peraggstate->transtypeLen);
 			peraggstate->transValueIsNull = false;
@@ -339,8 +338,8 @@ advance_windowaggregate(WindowAggState *winstate,
 							 numArguments + 1,
 							 perfuncstate->winCollation,
 							 (void *) winstate, NULL);
-	fcinfo->arg[0] = peraggstate->transValue;
-	fcinfo->argnull[0] = peraggstate->transValueIsNull;
+	fcinfo->args[0].datum = peraggstate->transValue;
+	fcinfo->args[0].isnull = peraggstate->transValueIsNull;
 	winstate->curaggcontext = peraggstate->aggcontext;
 	newVal = FunctionCallInvoke(fcinfo);
 	winstate->curaggcontext = NULL;
@@ -420,8 +419,7 @@ advance_windowaggregate_base(WindowAggState *winstate,
 {
 	WindowFuncExprState *wfuncstate = perfuncstate->wfuncstate;
 	int			numArguments = perfuncstate->numArguments;
-	FunctionCallInfoData fcinfodata;
-	FunctionCallInfo fcinfo = &fcinfodata;
+	LOCAL_FCINFO(fcinfo, FUNC_MAX_ARGS);
 	Datum		newVal;
 	ListCell   *arg;
 	int			i;
@@ -450,8 +448,8 @@ advance_windowaggregate_base(WindowAggState *winstate,
 	{
 		ExprState  *argstate = (ExprState *) lfirst(arg);
 
-		fcinfo->arg[i] = ExecEvalExpr(argstate, econtext,
-									  &fcinfo->argnull[i]);
+		fcinfo->args[i].datum = ExecEvalExpr(argstate, econtext,
+											 &fcinfo->args[i].isnull);
 		i++;
 	}
 
@@ -464,7 +462,7 @@ advance_windowaggregate_base(WindowAggState *winstate,
 		 */
 		for (i = 1; i <= numArguments; i++)
 		{
-			if (fcinfo->argnull[i])
+			if (fcinfo->args[i].isnull)
 			{
 				MemoryContextSwitchTo(oldContext);
 				return true;
@@ -510,8 +508,8 @@ advance_windowaggregate_base(WindowAggState *winstate,
 							 numArguments + 1,
 							 perfuncstate->winCollation,
 							 (void *) winstate, NULL);
-	fcinfo->arg[0] = peraggstate->transValue;
-	fcinfo->argnull[0] = peraggstate->transValueIsNull;
+	fcinfo->args[0].datum = peraggstate->transValue;
+	fcinfo->args[0].isnull = peraggstate->transValueIsNull;
 	winstate->curaggcontext = peraggstate->aggcontext;
 	newVal = FunctionCallInvoke(fcinfo);
 	winstate->curaggcontext = NULL;
@@ -592,29 +590,30 @@ finalize_windowaggregate(WindowAggState *winstate,
 	if (OidIsValid(peraggstate->finalfn_oid))
 	{
 		int			numFinalArgs = peraggstate->numFinalArgs;
-		FunctionCallInfoData fcinfo;
+		LOCAL_FCINFO(fcinfo, FUNC_MAX_ARGS);
 		bool		anynull;
 		int			i;
 
-		InitFunctionCallInfoData(fcinfo, &(peraggstate->finalfn),
+		InitFunctionCallInfoData(fcinfodata.fcinfo, &(peraggstate->finalfn),
 								 numFinalArgs,
 								 perfuncstate->winCollation,
 								 (void *) winstate, NULL);
-		fcinfo.arg[0] = MakeExpandedObjectReadOnly(peraggstate->transValue,
-												   peraggstate->transValueIsNull,
-												   peraggstate->transtypeLen);
-		fcinfo.argnull[0] = peraggstate->transValueIsNull;
+		fcinfo->args[0].datum =
+			MakeExpandedObjectReadOnly(peraggstate->transValue,
+									   peraggstate->transValueIsNull,
+									   peraggstate->transtypeLen);
+		fcinfo->args[0].isnull = peraggstate->transValueIsNull;
 		anynull = peraggstate->transValueIsNull;
 
 		/* Fill any remaining argument positions with nulls */
 		for (i = 1; i < numFinalArgs; i++)
 		{
-			fcinfo.arg[i] = (Datum) 0;
-			fcinfo.argnull[i] = true;
+			fcinfo->args[i].datum = (Datum) 0;
+			fcinfo->args[i].isnull = true;
 			anynull = true;
 		}
 
-		if (fcinfo.flinfo->fn_strict && anynull)
+		if (fcinfo->flinfo->fn_strict && anynull)
 		{
 			/* don't call a strict function with NULL inputs */
 			*result = (Datum) 0;
@@ -623,9 +622,9 @@ finalize_windowaggregate(WindowAggState *winstate,
 		else
 		{
 			winstate->curaggcontext = peraggstate->aggcontext;
-			*result = FunctionCallInvoke(&fcinfo);
+			*result = FunctionCallInvoke(fcinfo);
 			winstate->curaggcontext = NULL;
-			*isnull = fcinfo.isnull;
+			*isnull = fcinfo->isnull;
 		}
 	}
 	else
@@ -1032,7 +1031,7 @@ static void
 eval_windowfunction(WindowAggState *winstate, WindowStatePerFunc perfuncstate,
 					Datum *result, bool *isnull)
 {
-	FunctionCallInfoData fcinfo;
+	LOCAL_FCINFO(fcinfo, FUNC_MAX_ARGS);
 	MemoryContext oldContext;
 
 	oldContext = MemoryContextSwitchTo(winstate->ss.ps.ps_ExprContext->ecxt_per_tuple_memory);
@@ -1043,24 +1042,25 @@ eval_windowfunction(WindowAggState *winstate, WindowStatePerFunc perfuncstate,
 	 * implementations to support varying numbers of arguments.  The real info
 	 * goes through the WindowObject, which is passed via fcinfo->context.
 	 */
-	InitFunctionCallInfoData(fcinfo, &(perfuncstate->flinfo),
+	InitFunctionCallInfoData(*fcinfo, &(perfuncstate->flinfo),
 							 perfuncstate->numArguments,
 							 perfuncstate->winCollation,
 							 (void *) perfuncstate->winobj, NULL);
 	/* Just in case, make all the regular argument slots be null */
-	memset(fcinfo.argnull, true, perfuncstate->numArguments);
+	for (int argno = 0; argno < perfuncstate->numArguments; argno++)
+		fcinfo->args[argno].isnull = true;
 	/* Window functions don't have a current aggregate context, either */
 	winstate->curaggcontext = NULL;
 
-	*result = FunctionCallInvoke(&fcinfo);
-	*isnull = fcinfo.isnull;
+	*result = FunctionCallInvoke(fcinfo);
+	*isnull = fcinfo->isnull;
 
 	/*
 	 * Make sure pass-by-ref data is allocated in the appropriate context. (We
 	 * need this in case the function returns a pointer into some short-lived
 	 * tuple, as is entirely possible.)
 	 */
-	if (!perfuncstate->resulttypeByVal && !fcinfo.isnull &&
+	if (!perfuncstate->resulttypeByVal && !fcinfo->isnull &&
 		!MemoryContextContains(CurrentMemoryContext,
 							   DatumGetPointer(*result)))
 		*result = datumCopy(*result,
diff --git a/src/backend/jit/llvm/llvmjit.c b/src/backend/jit/llvm/llvmjit.c
index 6b9aaf49c68..c4d38a64a44 100644
--- a/src/backend/jit/llvm/llvmjit.c
+++ b/src/backend/jit/llvm/llvmjit.c
@@ -53,6 +53,7 @@ LLVMTypeRef TypeSizeT;
 LLVMTypeRef TypeParamBool;
 LLVMTypeRef TypeStorageBool;
 LLVMTypeRef TypePGFunction;
+LLVMTypeRef StructNullableDatum;
 LLVMTypeRef StructHeapTupleFieldsField3;
 LLVMTypeRef StructHeapTupleFields;
 LLVMTypeRef StructHeapTupleHeaderData;
@@ -807,6 +808,7 @@ llvm_create_types(void)
 	TypeParamBool = load_return_type(mod, "FunctionReturningBool");
 	TypeStorageBool = load_type(mod, "TypeStorageBool");
 	TypePGFunction = load_type(mod, "TypePGFunction");
+	StructNullableDatum = load_type(mod, "StructNullableDatum");
 	StructExprContext = load_type(mod, "StructExprContext");
 	StructExprEvalStep = load_type(mod, "StructExprEvalStep");
 	StructExprState = load_type(mod, "StructExprState");
diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c
index 70b55dd202e..351f6ec7e1a 100644
--- a/src/backend/jit/llvm/llvmjit_expr.c
+++ b/src/backend/jit/llvm/llvmjit_expr.c
@@ -570,7 +570,6 @@ llvm_compile_expr(ExprState *state)
 					LLVMBasicBlockRef b_nonull;
 					int			argno;
 					LLVMValueRef v_fcinfo;
-					LLVMValueRef v_argnullp;
 					LLVMBasicBlockRef *b_checkargnulls;
 
 					/*
@@ -587,12 +586,6 @@ llvm_compile_expr(ExprState *state)
 					v_fcinfo =
 						l_ptr_const(fcinfo, l_ptr(StructFunctionCallInfoData));
 
-					v_argnullp =
-						LLVMBuildStructGEP(b,
-										   v_fcinfo,
-										   FIELDNO_FUNCTIONCALLINFODATA_ARGNULL,
-										   "v_argnullp");
-
 					/*
 					 * set resnull to true, if the function is actually
 					 * called, it'll be reset
@@ -624,8 +617,7 @@ llvm_compile_expr(ExprState *state)
 							b_argnotnull = b_checkargnulls[argno + 1];
 
 						/* and finally load & check NULLness of arg */
-						v_argisnull = l_load_struct_gep(b, v_argnullp,
-														argno, "");
+						v_argisnull = l_funcnull(b, v_fcinfo, argno);
 						LLVMBuildCondBr(b,
 										LLVMBuildICmp(b, LLVMIntEQ,
 													  v_argisnull,
@@ -1276,10 +1268,6 @@ llvm_compile_expr(ExprState *state)
 					LLVMValueRef v_fn_addr_out,
 								v_fn_addr_in;
 					LLVMValueRef v_fcinfo_in_isnullp;
-					LLVMValueRef v_in_argp,
-								v_out_argp;
-					LLVMValueRef v_in_argnullp,
-								v_out_argnullp;
 					LLVMValueRef v_retval;
 					LLVMValueRef v_resvalue;
 					LLVMValueRef v_resnull;
@@ -1313,22 +1301,6 @@ llvm_compile_expr(ExprState *state)
 						LLVMBuildStructGEP(b, v_fcinfo_in,
 										   FIELDNO_FUNCTIONCALLINFODATA_ISNULL,
 										   "v_fcinfo_in_isnull");
-					v_out_argnullp =
-						LLVMBuildStructGEP(b, v_fcinfo_out,
-										   FIELDNO_FUNCTIONCALLINFODATA_ARGNULL,
-										   "v_fcinfo_out_argnullp");
-					v_in_argnullp =
-						LLVMBuildStructGEP(b, v_fcinfo_in,
-										   FIELDNO_FUNCTIONCALLINFODATA_ARGNULL,
-										   "v_fcinfo_in_argnullp");
-					v_out_argp =
-						LLVMBuildStructGEP(b, v_fcinfo_out,
-										   FIELDNO_FUNCTIONCALLINFODATA_ARG,
-										   "v_fcinfo_out_argp");
-					v_in_argp =
-						LLVMBuildStructGEP(b, v_fcinfo_in,
-										   FIELDNO_FUNCTIONCALLINFODATA_ARG,
-										   "v_fcinfo_in_argp");
 
 					/* output functions are not called on nulls */
 					v_resnull = LLVMBuildLoad(b, v_resnullp, "");
@@ -1348,11 +1320,10 @@ llvm_compile_expr(ExprState *state)
 					/* set arg[0] */
 					LLVMBuildStore(b,
 								   v_resvalue,
-								   LLVMBuildStructGEP(b, v_out_argp, 0, ""));
+								   l_funcvaluep(b, v_fcinfo_out, 0));
 					LLVMBuildStore(b,
 								   l_sbool_const(0),
-								   LLVMBuildStructGEP(b, v_out_argnullp,
-													  0, ""));
+								   l_funcnullp(b, v_fcinfo_out, 0));
 					/* and call output function (can never return NULL) */
 					v_output = LLVMBuildCall(b, v_fn_addr_out, &v_fcinfo_out,
 											 1, "funccall_coerce_out");
@@ -1399,9 +1370,9 @@ llvm_compile_expr(ExprState *state)
 					/* set arguments */
 					/* arg0: output */
 					LLVMBuildStore(b, v_output,
-								   LLVMBuildStructGEP(b, v_in_argp, 0, ""));
+								   l_funcvaluep(b, v_fcinfo_in, 0));
 					LLVMBuildStore(b, v_resnull,
-								   LLVMBuildStructGEP(b, v_in_argnullp, 0, ""));
+								   l_funcnullp(b, v_fcinfo_in, 0));
 
 					/* arg1: ioparam: preset in execExpr.c */
 					/* arg2: typmod: preset in execExpr.c  */
@@ -1426,7 +1397,6 @@ llvm_compile_expr(ExprState *state)
 					LLVMValueRef v_fcinfo;
 					LLVMValueRef v_fcinfo_isnull;
 
-					LLVMValueRef v_argnullp;
 					LLVMValueRef v_argnull0,
 								v_argisnull0;
 					LLVMValueRef v_argnull1,
@@ -1449,18 +1419,11 @@ llvm_compile_expr(ExprState *state)
 
 					v_fcinfo = l_ptr_const(fcinfo, l_ptr(StructFunctionCallInfoData));
 
-					v_argnullp =
-						LLVMBuildStructGEP(b,
-										   v_fcinfo,
-										   FIELDNO_FUNCTIONCALLINFODATA_ARGNULL,
-										   "v_argnullp");
-
-					/* load argnull[0|1] for both arguments */
-					v_argnull0 = l_load_struct_gep(b, v_argnullp, 0, "");
+					/* load args[0|1].isnull for both arguments */
+					v_argnull0 = l_funcnull(b, v_fcinfo, 0);
 					v_argisnull0 = LLVMBuildICmp(b, LLVMIntEQ, v_argnull0,
 												 l_sbool_const(1), "");
-
-					v_argnull1 = l_load_struct_gep(b, v_argnullp, 1, "");
+					v_argnull1 = l_funcnull(b, v_fcinfo, 1);
 					v_argisnull1 = LLVMBuildICmp(b, LLVMIntEQ, v_argnull1,
 												 l_sbool_const(1), "");
 
@@ -1532,11 +1495,9 @@ llvm_compile_expr(ExprState *state)
 
 					LLVMValueRef v_fcinfo;
 					LLVMValueRef v_fcinfo_isnull;
-					LLVMValueRef v_argnullp;
 					LLVMValueRef v_argnull0;
 					LLVMValueRef v_argnull1;
 					LLVMValueRef v_anyargisnull;
-					LLVMValueRef v_argp;
 					LLVMValueRef v_arg0;
 					LLVMBasicBlockRef b_hasnull;
 					LLVMBasicBlockRef b_nonull;
@@ -1553,21 +1514,9 @@ llvm_compile_expr(ExprState *state)
 
 					v_fcinfo = l_ptr_const(fcinfo, l_ptr(StructFunctionCallInfoData));
 
-					v_argnullp =
-						LLVMBuildStructGEP(b,
-										   v_fcinfo,
-										   FIELDNO_FUNCTIONCALLINFODATA_ARGNULL,
-										   "v_argnullp");
-
-					v_argp =
-						LLVMBuildStructGEP(b,
-										   v_fcinfo,
-										   FIELDNO_FUNCTIONCALLINFODATA_ARG,
-										   "v_argp");
-
 					/* if either argument is NULL they can't be equal */
-					v_argnull0 = l_load_struct_gep(b, v_argnullp, 0, "");
-					v_argnull1 = l_load_struct_gep(b, v_argnullp, 1, "");
+					v_argnull0 = l_funcnull(b, v_fcinfo, 0);
+					v_argnull1 = l_funcnull(b, v_fcinfo, 1);
 
 					v_anyargisnull =
 						LLVMBuildOr(b,
@@ -1581,7 +1530,7 @@ llvm_compile_expr(ExprState *state)
 
 					/* one (or both) of the arguments are null, return arg[0] */
 					LLVMPositionBuilderAtEnd(b, b_hasnull);
-					v_arg0 = l_load_struct_gep(b, v_argp, 0, "");
+					v_arg0 = l_funcvalue(b, v_fcinfo, 0);
 					LLVMBuildStore(b, v_argnull0, v_resnullp);
 					LLVMBuildStore(b, v_arg0, v_resvaluep);
 					LLVMBuildBr(b, opblocks[i + 1]);
@@ -1680,7 +1629,6 @@ llvm_compile_expr(ExprState *state)
 					if (op->d.rowcompare_step.finfo->fn_strict)
 					{
 						LLVMValueRef v_fcinfo;
-						LLVMValueRef v_argnullp;
 						LLVMValueRef v_argnull0;
 						LLVMValueRef v_argnull1;
 						LLVMValueRef v_anyargisnull;
@@ -1688,13 +1636,8 @@ llvm_compile_expr(ExprState *state)
 						v_fcinfo = l_ptr_const(fcinfo,
 											   l_ptr(StructFunctionCallInfoData));
 
-						v_argnullp =
-							LLVMBuildStructGEP(b, v_fcinfo,
-											   FIELDNO_FUNCTIONCALLINFODATA_ARGNULL,
-											   "v_argnullp");
-
-						v_argnull0 = l_load_struct_gep(b, v_argnullp, 0, "");
-						v_argnull1 = l_load_struct_gep(b, v_argnullp, 1, "");
+						v_argnull0 = l_funcnull(b, v_fcinfo, 0);
+						v_argnull1 = l_funcnull(b, v_fcinfo, 1);
 
 						v_anyargisnull =
 							LLVMBuildOr(b,
@@ -2021,7 +1964,6 @@ llvm_compile_expr(ExprState *state)
 				{
 					FunctionCallInfo fcinfo = op->d.agg_deserialize.fcinfo_data;
 					LLVMValueRef v_fcinfo;
-					LLVMValueRef v_argnullp;
 					LLVMValueRef v_argnull0;
 					LLVMBasicBlockRef b_deserialize;
 
@@ -2030,14 +1972,7 @@ llvm_compile_expr(ExprState *state)
 
 					v_fcinfo = l_ptr_const(fcinfo,
 										   l_ptr(StructFunctionCallInfoData));
-
-					v_argnullp =
-						LLVMBuildStructGEP(b,
-										   v_fcinfo,
-										   FIELDNO_FUNCTIONCALLINFODATA_ARGNULL,
-										   "v_argnullp");
-					v_argnull0 =
-						l_load_struct_gep(b, v_argnullp, 0, "v_argnull0");
+					v_argnull0 = l_funcnull(b, v_fcinfo, 0);
 
 					LLVMBuildCondBr(b,
 									LLVMBuildICmp(b,
@@ -2079,20 +2014,24 @@ llvm_compile_expr(ExprState *state)
 					break;
 				}
 
-			case EEOP_AGG_STRICT_INPUT_CHECK:
+			case EEOP_AGG_STRICT_INPUT_CHECK_NULLS:
+			case EEOP_AGG_STRICT_INPUT_CHECK_ARGS:
 				{
 					int			nargs = op->d.agg_strict_input_check.nargs;
+					NullableDatum *args = op->d.agg_strict_input_check.args;
 					bool	   *nulls = op->d.agg_strict_input_check.nulls;
 					int			jumpnull;
 					int			argno;
 
-					LLVMValueRef v_nullp;
+					LLVMValueRef v_argsp;
+					LLVMValueRef v_nullsp;
 					LLVMBasicBlockRef *b_checknulls;
 
 					Assert(nargs > 0);
 
 					jumpnull = op->d.agg_strict_input_check.jumpnull;
-					v_nullp = l_ptr_const(nulls, l_ptr(TypeStorageBool));
+					v_argsp = l_ptr_const(args, l_ptr(StructNullableDatum));
+					v_nullsp = l_ptr_const(nulls, l_ptr(TypeStorageBool));
 
 					/* create blocks for checking args */
 					b_checknulls = palloc(sizeof(LLVMBasicBlockRef *) * nargs);
@@ -2120,7 +2059,18 @@ llvm_compile_expr(ExprState *state)
 						else
 							b_argnotnull = b_checknulls[argno + 1];
 
-						v_argisnull = l_load_gep1(b, v_nullp, v_argno, "");
+						if (opcode == EEOP_AGG_STRICT_INPUT_CHECK_NULLS)
+							v_argisnull = l_load_gep1(b, v_nullsp, v_argno, "");
+						else
+						{
+							LLVMValueRef v_argn;
+
+							v_argn = LLVMBuildGEP(b, v_argsp, &v_argno, 1, "");
+							v_argisnull =
+								l_load_struct_gep(b, v_argn,
+												  FIELDNO_NULLABLE_DATUM_ISNULL,
+												  "");
+						}
 
 						LLVMBuildCondBr(b,
 										LLVMBuildICmp(b,
@@ -2291,8 +2241,6 @@ llvm_compile_expr(ExprState *state)
 					LLVMValueRef v_aggstatep;
 					LLVMValueRef v_fcinfo;
 					LLVMValueRef v_fcinfo_isnull;
-					LLVMValueRef v_argp,
-								v_argnullp;
 
 					LLVMValueRef v_transvaluep;
 					LLVMValueRef v_transnullp;
@@ -2319,7 +2267,7 @@ llvm_compile_expr(ExprState *state)
 					aggstate = op->d.agg_trans.aggstate;
 					pertrans = op->d.agg_trans.pertrans;
 
-					fcinfo = &pertrans->transfn_fcinfo;
+					fcinfo = pertrans->transfn_fcinfo;
 
 					v_aggstatep = l_ptr_const(aggstate,
 											  l_ptr(StructAggState));
@@ -2344,18 +2292,6 @@ llvm_compile_expr(ExprState *state)
 
 					v_fcinfo = l_ptr_const(fcinfo,
 										   l_ptr(StructFunctionCallInfoData));
-
-					v_argnullp =
-						LLVMBuildStructGEP(b,
-										   v_fcinfo,
-										   FIELDNO_FUNCTIONCALLINFODATA_ARGNULL,
-										   "v_argnullp");
-					v_argp =
-						LLVMBuildStructGEP(b,
-										   v_fcinfo,
-										   FIELDNO_FUNCTIONCALLINFODATA_ARG,
-										   "v_argp");
-
 					v_aggcontext = l_ptr_const(op->d.agg_trans.aggcontext,
 											   l_ptr(StructExprContext));
 
@@ -2387,7 +2323,7 @@ llvm_compile_expr(ExprState *state)
 									l_ptr(StructMemoryContextData));
 					v_oldcontext = l_mcxt_switch(mod, b, v_tmpcontext);
 
-					/* store transvalue in fcinfo->arg/argnull[0] */
+					/* store transvalue in fcinfo->args[0] */
 					v_transvaluep =
 						LLVMBuildStructGEP(b, v_pergroupp,
 										   FIELDNO_AGGSTATEPERGROUPDATA_TRANSVALUE,
@@ -2399,10 +2335,10 @@ llvm_compile_expr(ExprState *state)
 					LLVMBuildStore(b,
 								   LLVMBuildLoad(b, v_transvaluep,
 												 "transvalue"),
-								   LLVMBuildStructGEP(b, v_argp, 0, ""));
+								   l_funcvaluep(b, v_fcinfo, 0));
 					LLVMBuildStore(b,
 								   LLVMBuildLoad(b, v_transnullp, "transnull"),
-								   LLVMBuildStructGEP(b, v_argnullp, 0, ""));
+								   l_funcnullp(b, v_fcinfo, 0));
 
 					/* and invoke transition function */
 					v_retval = BuildV1Call(context, b, mod, fcinfo,
@@ -2589,12 +2525,8 @@ BuildV1Call(LLVMJitContext *context, LLVMBuilderRef b,
 		LLVMValueRef v_lifetime = create_LifetimeEnd(mod);
 		LLVMValueRef params[2];
 
-		params[0] = l_int64_const(sizeof(fcinfo->arg));
-		params[1] = l_ptr_const(fcinfo->arg, l_ptr(LLVMInt8Type()));
-		LLVMBuildCall(b, v_lifetime, params, lengthof(params), "");
-
-		params[0] = l_int64_const(sizeof(fcinfo->argnull));
-		params[1] = l_ptr_const(fcinfo->argnull, l_ptr(LLVMInt8Type()));
+		params[0] = l_int64_const(sizeof(NullableDatum) * fcinfo->nargs);
+		params[1] = l_ptr_const(fcinfo->args, l_ptr(LLVMInt8Type()));
 		LLVMBuildCall(b, v_lifetime, params, lengthof(params), "");
 
 		params[0] = l_int64_const(sizeof(fcinfo->isnull));
diff --git a/src/backend/jit/llvm/llvmjit_types.c b/src/backend/jit/llvm/llvmjit_types.c
index 79949829721..c814bc7b3ee 100644
--- a/src/backend/jit/llvm/llvmjit_types.c
+++ b/src/backend/jit/llvm/llvmjit_types.c
@@ -49,13 +49,14 @@ PGFunction	TypePGFunction;
 size_t		TypeSizeT;
 bool		TypeStorageBool;
 
+NullableDatum StructNullableDatum;
 AggState	StructAggState;
 AggStatePerGroupData StructAggStatePerGroupData;
 AggStatePerTransData StructAggStatePerTransData;
 ExprContext StructExprContext;
 ExprEvalStep StructExprEvalStep;
 ExprState	StructExprState;
-FunctionCallInfoData StructFunctionCallInfoData;
+FunctionCallInfoBaseData StructFunctionCallInfoData;
 HeapTupleData StructHeapTupleData;
 MemoryContextData StructMemoryContextData;
 TupleTableSlot StructTupleTableSlot;
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 1d3268b4abe..3b9e86f7702 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -1581,7 +1581,7 @@ pgstat_send_inquiry(TimestampTz clock_time, TimestampTz cutoff_time, Oid databas
  * Called by the executor before invoking a function.
  */
 void
-pgstat_init_function_usage(FunctionCallInfoData *fcinfo,
+pgstat_init_function_usage(FunctionCallInfo fcinfo,
 						   PgStat_FunctionCallUsage *fcu)
 {
 	PgStat_BackendFunctionEntry *htabent;
diff --git a/src/backend/tcop/fastpath.c b/src/backend/tcop/fastpath.c
index 4a243b0777a..341c3a1f7f1 100644
--- a/src/backend/tcop/fastpath.c
+++ b/src/backend/tcop/fastpath.c
@@ -260,7 +260,7 @@ HandleFunctionRequest(StringInfo msgBuf)
 {
 	Oid			fid;
 	AclResult	aclresult;
-	FunctionCallInfoData fcinfo;
+	LOCAL_FCINFO(fcinfo, FUNC_MAX_ARGS);
 	int16		rformat;
 	Datum		retval;
 	struct fp_info my_fp;
@@ -332,12 +332,12 @@ HandleFunctionRequest(StringInfo msgBuf)
 	 * functions can't be called this way.  Perhaps we should pass
 	 * DEFAULT_COLLATION_OID, instead?
 	 */
-	InitFunctionCallInfoData(fcinfo, &fip->flinfo, 0, InvalidOid, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, &fip->flinfo, 0, InvalidOid, NULL, NULL);
 
 	if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
-		rformat = parse_fcall_arguments(msgBuf, fip, &fcinfo);
+		rformat = parse_fcall_arguments(msgBuf, fip, fcinfo);
 	else
-		rformat = parse_fcall_arguments_20(msgBuf, fip, &fcinfo);
+		rformat = parse_fcall_arguments_20(msgBuf, fip, fcinfo);
 
 	/* Verify we reached the end of the message where expected. */
 	pq_getmsgend(msgBuf);
@@ -350,9 +350,9 @@ HandleFunctionRequest(StringInfo msgBuf)
 	{
 		int			i;
 
-		for (i = 0; i < fcinfo.nargs; i++)
+		for (i = 0; i < fcinfo->nargs; i++)
 		{
-			if (fcinfo.argnull[i])
+			if (fcinfo->args[i].isnull)
 			{
 				callit = false;
 				break;
@@ -363,18 +363,18 @@ HandleFunctionRequest(StringInfo msgBuf)
 	if (callit)
 	{
 		/* Okay, do it ... */
-		retval = FunctionCallInvoke(&fcinfo);
+		retval = FunctionCallInvoke(fcinfo);
 	}
 	else
 	{
-		fcinfo.isnull = true;
+		fcinfo->isnull = true;
 		retval = (Datum) 0;
 	}
 
 	/* ensure we do at least one CHECK_FOR_INTERRUPTS per function call */
 	CHECK_FOR_INTERRUPTS();
 
-	SendFunctionResult(retval, fcinfo.isnull, fip->rettype, rformat);
+	SendFunctionResult(retval, fcinfo->isnull, fip->rettype, rformat);
 
 	/* We no longer need the snapshot */
 	PopActiveSnapshot();
@@ -450,11 +450,11 @@ parse_fcall_arguments(StringInfo msgBuf, struct fp_info *fip,
 		argsize = pq_getmsgint(msgBuf, 4);
 		if (argsize == -1)
 		{
-			fcinfo->argnull[i] = true;
+			fcinfo->args[i].isnull = true;
 		}
 		else
 		{
-			fcinfo->argnull[i] = false;
+			fcinfo->args[i].isnull = false;
 			if (argsize < 0)
 				ereport(ERROR,
 						(errcode(ERRCODE_PROTOCOL_VIOLATION),
@@ -494,8 +494,8 @@ parse_fcall_arguments(StringInfo msgBuf, struct fp_info *fip,
 			else
 				pstring = pg_client_to_server(abuf.data, argsize);
 
-			fcinfo->arg[i] = OidInputFunctionCall(typinput, pstring,
-												  typioparam, -1);
+			fcinfo->args[i].datum = OidInputFunctionCall(typinput, pstring,
+														 typioparam, -1);
 			/* Free result of encoding conversion, if any */
 			if (pstring && pstring != abuf.data)
 				pfree(pstring);
@@ -514,8 +514,8 @@ parse_fcall_arguments(StringInfo msgBuf, struct fp_info *fip,
 			else
 				bufptr = &abuf;
 
-			fcinfo->arg[i] = OidReceiveFunctionCall(typreceive, bufptr,
-													typioparam, -1);
+			fcinfo->args[i].datum = OidReceiveFunctionCall(typreceive, bufptr,
+														   typioparam, -1);
 
 			/* Trouble if it didn't eat the whole buffer */
 			if (argsize != -1 && abuf.cursor != abuf.len)
@@ -579,12 +579,12 @@ parse_fcall_arguments_20(StringInfo msgBuf, struct fp_info *fip,
 		argsize = pq_getmsgint(msgBuf, 4);
 		if (argsize == -1)
 		{
-			fcinfo->argnull[i] = true;
-			fcinfo->arg[i] = OidReceiveFunctionCall(typreceive, NULL,
-													typioparam, -1);
+			fcinfo->args[i].isnull = true;
+			fcinfo->args[i].datum = OidReceiveFunctionCall(typreceive, NULL,
+														   typioparam, -1);
 			continue;
 		}
-		fcinfo->argnull[i] = false;
+		fcinfo->args[i].isnull = false;
 		if (argsize < 0)
 			ereport(ERROR,
 					(errcode(ERRCODE_PROTOCOL_VIOLATION),
@@ -597,8 +597,8 @@ parse_fcall_arguments_20(StringInfo msgBuf, struct fp_info *fip,
 							   pq_getmsgbytes(msgBuf, argsize),
 							   argsize);
 
-		fcinfo->arg[i] = OidReceiveFunctionCall(typreceive, &abuf,
-												typioparam, -1);
+		fcinfo->args[i].datum = OidReceiveFunctionCall(typreceive, &abuf,
+													   typioparam, -1);
 
 		/* Trouble if it didn't eat the whole buffer */
 		if (abuf.cursor != abuf.len)
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index e457d81f23c..59084d2f6b0 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -3590,7 +3590,7 @@ array_eq(PG_FUNCTION_ARGS)
 	array_iter	it1;
 	array_iter	it2;
 	int			i;
-	FunctionCallInfoData locfcinfo;
+	LOCAL_FCINFO(locfcinfo, 2);
 
 	if (element_type != AARR_ELEMTYPE(array2))
 		ereport(ERROR,
@@ -3630,7 +3630,7 @@ array_eq(PG_FUNCTION_ARGS)
 		/*
 		 * apply the operator to each pair of array elements.
 		 */
-		InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
+		InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
 								 collation, NULL, NULL);
 
 		/* Loop over source data */
@@ -3666,12 +3666,12 @@ array_eq(PG_FUNCTION_ARGS)
 			/*
 			 * Apply the operator to the element pair
 			 */
-			locfcinfo.arg[0] = elt1;
-			locfcinfo.arg[1] = elt2;
-			locfcinfo.argnull[0] = false;
-			locfcinfo.argnull[1] = false;
-			locfcinfo.isnull = false;
-			oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
+			locfcinfo->args[0].datum = elt1;
+			locfcinfo->args[0].isnull = false;
+			locfcinfo->args[1].datum = elt2;
+			locfcinfo->args[1].isnull = false;
+			locfcinfo->isnull = false;
+			oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
 			if (!oprresult)
 			{
 				result = false;
@@ -3761,7 +3761,7 @@ array_cmp(FunctionCallInfo fcinfo)
 	array_iter	it1;
 	array_iter	it2;
 	int			i;
-	FunctionCallInfoData locfcinfo;
+	LOCAL_FCINFO(locfcinfo, 2);
 
 	if (element_type != AARR_ELEMTYPE(array2))
 		ereport(ERROR,
@@ -3794,7 +3794,7 @@ array_cmp(FunctionCallInfo fcinfo)
 	/*
 	 * apply the operator to each pair of array elements.
 	 */
-	InitFunctionCallInfoData(locfcinfo, &typentry->cmp_proc_finfo, 2,
+	InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
 							 collation, NULL, NULL);
 
 	/* Loop over source data */
@@ -3833,12 +3833,12 @@ array_cmp(FunctionCallInfo fcinfo)
 		}
 
 		/* Compare the pair of elements */
-		locfcinfo.arg[0] = elt1;
-		locfcinfo.arg[1] = elt2;
-		locfcinfo.argnull[0] = false;
-		locfcinfo.argnull[1] = false;
-		locfcinfo.isnull = false;
-		cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
+		locfcinfo->args[0].datum = elt1;
+		locfcinfo->args[0].isnull = false;
+		locfcinfo->args[1].datum = elt2;
+		locfcinfo->args[1].isnull = false;
+		locfcinfo->isnull = false;
+		cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
 
 		if (cmpresult == 0)
 			continue;			/* equal */
@@ -3925,7 +3925,7 @@ hash_array(PG_FUNCTION_ARGS)
 	char		typalign;
 	int			i;
 	array_iter	iter;
-	FunctionCallInfoData locfcinfo;
+	LOCAL_FCINFO(locfcinfo, 1);
 
 	/*
 	 * We arrange to look up the hash function only once per series of calls,
@@ -3953,7 +3953,7 @@ hash_array(PG_FUNCTION_ARGS)
 	/*
 	 * apply the hash function to each array element.
 	 */
-	InitFunctionCallInfoData(locfcinfo, &typentry->hash_proc_finfo, 1,
+	InitFunctionCallInfoData(*locfcinfo, &typentry->hash_proc_finfo, 1,
 							 InvalidOid, NULL, NULL);
 
 	/* Loop over source data */
@@ -3977,10 +3977,10 @@ hash_array(PG_FUNCTION_ARGS)
 		else
 		{
 			/* Apply the hash function */
-			locfcinfo.arg[0] = elt;
-			locfcinfo.argnull[0] = false;
-			locfcinfo.isnull = false;
-			elthash = DatumGetUInt32(FunctionCallInvoke(&locfcinfo));
+			locfcinfo->args[0].datum = elt;
+			locfcinfo->args[0].isnull = false;
+			locfcinfo->isnull = false;
+			elthash = DatumGetUInt32(FunctionCallInvoke(locfcinfo));
 		}
 
 		/*
@@ -4023,7 +4023,7 @@ hash_array_extended(PG_FUNCTION_ARGS)
 	char		typalign;
 	int			i;
 	array_iter	iter;
-	FunctionCallInfoData locfcinfo;
+	LOCAL_FCINFO(locfcinfo, 2);
 
 	typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
 	if (typentry == NULL ||
@@ -4042,7 +4042,7 @@ hash_array_extended(PG_FUNCTION_ARGS)
 	typbyval = typentry->typbyval;
 	typalign = typentry->typalign;
 
-	InitFunctionCallInfoData(locfcinfo, &typentry->hash_extended_proc_finfo, 2,
+	InitFunctionCallInfoData(*locfcinfo, &typentry->hash_extended_proc_finfo, 2,
 							 InvalidOid, NULL, NULL);
 
 	/* Loop over source data */
@@ -4065,12 +4065,11 @@ hash_array_extended(PG_FUNCTION_ARGS)
 		else
 		{
 			/* Apply the hash function */
-			locfcinfo.arg[0] = elt;
-			locfcinfo.arg[1] = Int64GetDatum(seed);
-			locfcinfo.argnull[0] = false;
-			locfcinfo.argnull[1] = false;
-			locfcinfo.isnull = false;
-			elthash = DatumGetUInt64(FunctionCallInvoke(&locfcinfo));
+			locfcinfo->args[0].datum = elt;
+			locfcinfo->args[0].isnull = false;
+			locfcinfo->args[1].datum = Int64GetDatum(seed);
+			locfcinfo->args[1].isnull = false;
+			elthash = DatumGetUInt64(FunctionCallInvoke(locfcinfo));
 		}
 
 		result = (result << 5) - result + elthash;
@@ -4113,7 +4112,7 @@ array_contain_compare(AnyArrayType *array1, AnyArrayType *array2, Oid collation,
 	int			i;
 	int			j;
 	array_iter	it1;
-	FunctionCallInfoData locfcinfo;
+	LOCAL_FCINFO(locfcinfo, 2);
 
 	if (element_type != AARR_ELEMTYPE(array2))
 		ereport(ERROR,
@@ -4164,7 +4163,7 @@ array_contain_compare(AnyArrayType *array1, AnyArrayType *array2, Oid collation,
 	/*
 	 * Apply the comparison operator to each pair of array elements.
 	 */
-	InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
+	InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
 							 collation, NULL, NULL);
 
 	/* Loop over source data */
@@ -4206,12 +4205,12 @@ array_contain_compare(AnyArrayType *array1, AnyArrayType *array2, Oid collation,
 			/*
 			 * Apply the operator to the element pair
 			 */
-			locfcinfo.arg[0] = elt1;
-			locfcinfo.arg[1] = elt2;
-			locfcinfo.argnull[0] = false;
-			locfcinfo.argnull[1] = false;
-			locfcinfo.isnull = false;
-			oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
+			locfcinfo->args[0].datum = elt1;
+			locfcinfo->args[0].isnull = false;
+			locfcinfo->args[1].datum = elt2;
+			locfcinfo->args[1].isnull = false;
+			locfcinfo->isnull = false;
+			oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
 			if (oprresult)
 				break;
 		}
@@ -6062,7 +6061,7 @@ array_replace_internal(ArrayType *array,
 	int			bitmask;
 	bool		changed = false;
 	TypeCacheEntry *typentry;
-	FunctionCallInfoData locfcinfo;
+	LOCAL_FCINFO(locfcinfo, 2);
 
 	element_type = ARR_ELEMTYPE(array);
 	ndim = ARR_NDIM(array);
@@ -6117,7 +6116,7 @@ array_replace_internal(ArrayType *array,
 	}
 
 	/* Prepare to apply the comparison operator */
-	InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
+	InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
 							 collation, NULL, NULL);
 
 	/* Allocate temporary arrays for new values */
@@ -6175,12 +6174,12 @@ array_replace_internal(ArrayType *array,
 				/*
 				 * Apply the operator to the element pair
 				 */
-				locfcinfo.arg[0] = elt;
-				locfcinfo.arg[1] = search;
-				locfcinfo.argnull[0] = false;
-				locfcinfo.argnull[1] = false;
-				locfcinfo.isnull = false;
-				oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
+				locfcinfo->args[0].datum = elt;
+				locfcinfo->args[0].isnull = false;
+				locfcinfo->args[1].datum = search;
+				locfcinfo->args[1].isnull = false;
+				locfcinfo->isnull = false;
+				oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
 				if (!oprresult)
 				{
 					/* no match, keep element */
@@ -6460,7 +6459,8 @@ width_bucket_array_fixed(Datum operand,
 	char	   *thresholds_data;
 	int			typlen = typentry->typlen;
 	bool		typbyval = typentry->typbyval;
-	FunctionCallInfoData locfcinfo;
+	LOCAL_FCINFO(locfcinfo, 2);
+
 	int			left;
 	int			right;
 
@@ -6470,7 +6470,7 @@ width_bucket_array_fixed(Datum operand,
 	 */
 	thresholds_data = (char *) ARR_DATA_PTR(thresholds);
 
-	InitFunctionCallInfoData(locfcinfo, &typentry->cmp_proc_finfo, 2,
+	InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
 							 collation, NULL, NULL);
 
 	/* Find the bucket */
@@ -6484,13 +6484,13 @@ width_bucket_array_fixed(Datum operand,
 
 		ptr = thresholds_data + mid * typlen;
 
-		locfcinfo.arg[0] = operand;
-		locfcinfo.arg[1] = fetch_att(ptr, typbyval, typlen);
-		locfcinfo.argnull[0] = false;
-		locfcinfo.argnull[1] = false;
-		locfcinfo.isnull = false;
+		locfcinfo->args[0].datum = operand;
+		locfcinfo->args[0].isnull = false;
+		locfcinfo->args[1].datum = fetch_att(ptr, typbyval, typlen);
+		locfcinfo->args[1].isnull = false;
+		locfcinfo->isnull = false;
 
-		cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
+		cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
 
 		if (cmpresult < 0)
 			right = mid;
@@ -6514,13 +6514,13 @@ width_bucket_array_variable(Datum operand,
 	int			typlen = typentry->typlen;
 	bool		typbyval = typentry->typbyval;
 	char		typalign = typentry->typalign;
-	FunctionCallInfoData locfcinfo;
+	LOCAL_FCINFO(locfcinfo, 2);
 	int			left;
 	int			right;
 
 	thresholds_data = (char *) ARR_DATA_PTR(thresholds);
 
-	InitFunctionCallInfoData(locfcinfo, &typentry->cmp_proc_finfo, 2,
+	InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
 							 collation, NULL, NULL);
 
 	/* Find the bucket */
@@ -6541,13 +6541,12 @@ width_bucket_array_variable(Datum operand,
 			ptr = (char *) att_align_nominal(ptr, typalign);
 		}
 
-		locfcinfo.arg[0] = operand;
-		locfcinfo.arg[1] = fetch_att(ptr, typbyval, typlen);
-		locfcinfo.argnull[0] = false;
-		locfcinfo.argnull[1] = false;
-		locfcinfo.isnull = false;
+		locfcinfo->args[0].datum = operand;
+		locfcinfo->args[0].isnull = false;
+		locfcinfo->args[1].datum = fetch_att(ptr, typbyval, typlen);
+		locfcinfo->args[1].isnull = false;
 
-		cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
+		cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
 
 		if (cmpresult < 0)
 			right = mid;
diff --git a/src/backend/utils/adt/int.c b/src/backend/utils/adt/int.c
index fd82a83c47c..5c16e715987 100644
--- a/src/backend/utils/adt/int.c
+++ b/src/backend/utils/adt/int.c
@@ -202,7 +202,7 @@ Datum
 int2vectorrecv(PG_FUNCTION_ARGS)
 {
 	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
-	FunctionCallInfoData locfcinfo;
+	LOCAL_FCINFO(locfcinfo, 3);
 	int2vector *result;
 
 	/*
@@ -211,19 +211,19 @@ int2vectorrecv(PG_FUNCTION_ARGS)
 	 * fcinfo->flinfo->fn_extra.  So we need to pass it our own flinfo
 	 * parameter.
 	 */
-	InitFunctionCallInfoData(locfcinfo, fcinfo->flinfo, 3,
+	InitFunctionCallInfoData(*locfcinfo, fcinfo->flinfo, 3,
 							 InvalidOid, NULL, NULL);
 
-	locfcinfo.arg[0] = PointerGetDatum(buf);
-	locfcinfo.arg[1] = ObjectIdGetDatum(INT2OID);
-	locfcinfo.arg[2] = Int32GetDatum(-1);
-	locfcinfo.argnull[0] = false;
-	locfcinfo.argnull[1] = false;
-	locfcinfo.argnull[2] = false;
+	locfcinfo->args[0].datum = PointerGetDatum(buf);
+	locfcinfo->args[0].isnull = false;
+	locfcinfo->args[1].datum = ObjectIdGetDatum(INT2OID);
+	locfcinfo->args[1].isnull = false;
+	locfcinfo->args[2].datum = Int32GetDatum(-1);
+	locfcinfo->args[2].isnull = false;
 
-	result = (int2vector *) DatumGetPointer(array_recv(&locfcinfo));
+	result = (int2vector *) DatumGetPointer(array_recv(locfcinfo));
 
-	Assert(!locfcinfo.isnull);
+	Assert(!locfcinfo->isnull);
 
 	/* sanity checks: int2vector must be 1-D, 0-based, no nulls */
 	if (ARR_NDIM(result) != 1 ||
diff --git a/src/backend/utils/adt/oid.c b/src/backend/utils/adt/oid.c
index eb21b078f40..79a7f402864 100644
--- a/src/backend/utils/adt/oid.c
+++ b/src/backend/utils/adt/oid.c
@@ -257,7 +257,7 @@ Datum
 oidvectorrecv(PG_FUNCTION_ARGS)
 {
 	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
-	FunctionCallInfoData locfcinfo;
+	LOCAL_FCINFO(locfcinfo, 3);
 	oidvector  *result;
 
 	/*
@@ -266,19 +266,19 @@ oidvectorrecv(PG_FUNCTION_ARGS)
 	 * fcinfo->flinfo->fn_extra.  So we need to pass it our own flinfo
 	 * parameter.
 	 */
-	InitFunctionCallInfoData(locfcinfo, fcinfo->flinfo, 3,
+	InitFunctionCallInfoData(*locfcinfo, fcinfo->flinfo, 3,
 							 InvalidOid, NULL, NULL);
 
-	locfcinfo.arg[0] = PointerGetDatum(buf);
-	locfcinfo.arg[1] = ObjectIdGetDatum(OIDOID);
-	locfcinfo.arg[2] = Int32GetDatum(-1);
-	locfcinfo.argnull[0] = false;
-	locfcinfo.argnull[1] = false;
-	locfcinfo.argnull[2] = false;
+	locfcinfo->args[0].datum = PointerGetDatum(buf);
+	locfcinfo->args[0].isnull = false;
+	locfcinfo->args[1].datum = ObjectIdGetDatum(OIDOID);
+	locfcinfo->args[1].isnull = false;
+	locfcinfo->args[2].datum = Int32GetDatum(-1);
+	locfcinfo->args[2].isnull = false;
 
-	result = (oidvector *) DatumGetPointer(array_recv(&locfcinfo));
+	result = (oidvector *) DatumGetPointer(array_recv(locfcinfo));
 
-	Assert(!locfcinfo.isnull);
+	Assert(!locfcinfo->isnull);
 
 	/* sanity checks: oidvector must be 1-D, 0-based, no nulls */
 	if (ARR_NDIM(result) != 1 ||
diff --git a/src/backend/utils/adt/rowtypes.c b/src/backend/utils/adt/rowtypes.c
index 0467f97dc3f..06a08fd1b3b 100644
--- a/src/backend/utils/adt/rowtypes.c
+++ b/src/backend/utils/adt/rowtypes.c
@@ -942,7 +942,7 @@ record_cmp(FunctionCallInfo fcinfo)
 		 */
 		if (!nulls1[i1] || !nulls2[i2])
 		{
-			FunctionCallInfoData locfcinfo;
+			LOCAL_FCINFO(locfcinfo, 2);
 			int32		cmpresult;
 
 			if (nulls1[i1])
@@ -959,14 +959,14 @@ record_cmp(FunctionCallInfo fcinfo)
 			}
 
 			/* Compare the pair of elements */
-			InitFunctionCallInfoData(locfcinfo, &typentry->cmp_proc_finfo, 2,
+			InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
 									 collation, NULL, NULL);
-			locfcinfo.arg[0] = values1[i1];
-			locfcinfo.arg[1] = values2[i2];
-			locfcinfo.argnull[0] = false;
-			locfcinfo.argnull[1] = false;
-			locfcinfo.isnull = false;
-			cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
+			locfcinfo->args[0].datum = values1[i1];
+			locfcinfo->args[0].isnull = false;
+			locfcinfo->args[1].datum = values2[i2];
+			locfcinfo->args[1].isnull = false;
+			locfcinfo->isnull = false;
+			cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
 
 			if (cmpresult < 0)
 			{
@@ -1123,7 +1123,7 @@ record_eq(PG_FUNCTION_ARGS)
 		Form_pg_attribute att2;
 		TypeCacheEntry *typentry;
 		Oid			collation;
-		FunctionCallInfoData locfcinfo;
+		LOCAL_FCINFO(locfcinfo, 2);
 		bool		oprresult;
 
 		/*
@@ -1193,14 +1193,14 @@ record_eq(PG_FUNCTION_ARGS)
 			}
 
 			/* Compare the pair of elements */
-			InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
+			InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
 									 collation, NULL, NULL);
-			locfcinfo.arg[0] = values1[i1];
-			locfcinfo.arg[1] = values2[i2];
-			locfcinfo.argnull[0] = false;
-			locfcinfo.argnull[1] = false;
-			locfcinfo.isnull = false;
-			oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
+			locfcinfo->args[0].datum = values1[i1];
+			locfcinfo->args[0].isnull = false;
+			locfcinfo->args[1].datum = values2[i2];
+			locfcinfo->args[1].isnull = false;
+			locfcinfo->isnull = false;
+			oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
 			if (!oprresult)
 			{
 				result = false;
diff --git a/src/backend/utils/fmgr/README b/src/backend/utils/fmgr/README
index 5a2331ff159..0cb1e6583c3 100644
--- a/src/backend/utils/fmgr/README
+++ b/src/backend/utils/fmgr/README
@@ -53,7 +53,7 @@ point to the expression parse tree for the function call; this can be used
 to extract parse-time knowledge about the actual arguments.  Note that this
 field really is information about the arguments rather than information
 about the function, but it's proven to be more convenient to keep it in
-FmgrInfo than in FunctionCallInfoData where it might more logically go.
+FmgrInfo than in FunctionCallInfoBaseData where it might more logically go.
 
 
 During a call of a function, the following data structure is created
@@ -67,10 +67,9 @@ typedef struct
     Oid         fncollation;    /* collation for function to use */
     bool        isnull;         /* function must set true if result is NULL */
     short       nargs;          /* # arguments actually passed */
-    Datum       arg[FUNC_MAX_ARGS];  /* Arguments passed to function */
-    bool        argnull[FUNC_MAX_ARGS];  /* T if arg[i] is actually NULL */
-} FunctionCallInfoData;
-typedef FunctionCallInfoData* FunctionCallInfo;
+    NullableDatum args[];	/* Arguments passed to function */
+} FunctionCallInfoBaseData;
+typedef FunctionCallInfoBaseData* FunctionCallInfo;
 
 flinfo points to the lookup info used to make the call.  Ordinary functions
 will probably ignore this field, but function class handlers will need it
@@ -97,24 +96,24 @@ when there are no inputs of collatable types or they don't share a common
 collation.  This is effectively a hidden additional argument, which
 collation-sensitive functions can use to determine their behavior.
 
-nargs, arg[], and argnull[] hold the arguments being passed to the function.
+nargs and args[] hold the arguments being passed to the function.
 Notice that all the arguments passed to a function (as well as its result
 value) will now uniformly be of type Datum.  As discussed below, callers
 and callees should apply the standard Datum-to-and-from-whatever macros
 to convert to the actual argument types of a particular function.  The
-value in arg[i] is unspecified when argnull[i] is true.
+value in args[i].value is unspecified when args[i].isnull is true.
 
 It is generally the responsibility of the caller to ensure that the
 number of arguments passed matches what the callee is expecting; except
 for callees that take a variable number of arguments, the callee will
-typically ignore the nargs field and just grab values from arg[].
+typically ignore the nargs field and just grab values from args[].
 
 The isnull field will be initialized to "false" before the call.  On
 return from the function, isnull is the null flag for the function result:
 if it is true the function's result is NULL, regardless of the actual
 function return value.  Note that simple "strict" functions can ignore
-both isnull and argnull[], since they won't even get called when there
-are any TRUE values in argnull[].
+both isnull and args[i].isnull, since they won't even get called when there
+are any TRUE values in args[].isnull.
 
 FunctionCallInfo replaces FmgrValues plus a bunch of ad-hoc parameter
 conventions, global variables (fmgr_pl_finfo and CurrentTriggerData at
@@ -157,7 +156,7 @@ again, it might come in handy to have this macro in place.
 
 A nonstrict function is responsible for checking whether each individual
 argument is null or not, which it can do with PG_ARGISNULL(n) (which is
-just "fcinfo->argnull[n]").  It should avoid trying to fetch the value
+just "fcinfo->args[n].isnull").  It should avoid trying to fetch the value
 of any argument that is null.
 
 Both strict and nonstrict functions can return NULL, if needed, with
@@ -171,7 +170,7 @@ Argument values are ordinarily fetched using code like
 For float4, float8, and int8, the PG_GETARG macros will hide whether the
 types are pass-by-value or pass-by-reference.  For example, if float8 is
 pass-by-reference then PG_GETARG_FLOAT8 expands to
-	(* (float8 *) DatumGetPointer(fcinfo->arg[number]))
+	(* (float8 *) DatumGetPointer(fcinfo->args[number].value))
 and would typically be called like this:
 	float8  arg = PG_GETARG_FLOAT8(0);
 For what are now historical reasons, the float-related typedefs and macros
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index 506eeef01c3..5581912be53 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -792,18 +792,18 @@ fmgr_security_definer(PG_FUNCTION_ARGS)
 Datum
 DirectFunctionCall1Coll(PGFunction func, Oid collation, Datum arg1)
 {
-	FunctionCallInfoData fcinfo;
+	LOCAL_FCINFO(fcinfo, 1);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, NULL, 1, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, NULL, 1, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.argnull[0] = false;
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
 
-	result = (*func) (&fcinfo);
+	result = (*func) (fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
+	if (fcinfo->isnull)
 		elog(ERROR, "function %p returned NULL", (void *) func);
 
 	return result;
@@ -812,20 +812,20 @@ DirectFunctionCall1Coll(PGFunction func, Oid collation, Datum arg1)
 Datum
 DirectFunctionCall2Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2)
 {
-	FunctionCallInfoData fcinfo;
+	LOCAL_FCINFO(fcinfo, 2);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, NULL, 2, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, NULL, 2, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
+	fcinfo->args[1].datum = arg2;
+	fcinfo->args[1].isnull = false;
 
-	result = (*func) (&fcinfo);
+	result = (*func) (fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
+	if (fcinfo->isnull)
 		elog(ERROR, "function %p returned NULL", (void *) func);
 
 	return result;
@@ -835,22 +835,22 @@ Datum
 DirectFunctionCall3Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
 						Datum arg3)
 {
-	FunctionCallInfoData fcinfo;
+	LOCAL_FCINFO(fcinfo, 3);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, NULL, 3, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, NULL, 3, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
+	fcinfo->args[1].datum = arg2;
+	fcinfo->args[1].isnull = false;
+	fcinfo->args[2].datum = arg3;
+	fcinfo->args[2].isnull = false;
 
-	result = (*func) (&fcinfo);
+	result = (*func) (fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
+	if (fcinfo->isnull)
 		elog(ERROR, "function %p returned NULL", (void *) func);
 
 	return result;
@@ -860,24 +860,24 @@ Datum
 DirectFunctionCall4Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
 						Datum arg3, Datum arg4)
 {
-	FunctionCallInfoData fcinfo;
+	LOCAL_FCINFO(fcinfo, 4);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, NULL, 4, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, NULL, 4, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
+	fcinfo->args[1].datum = arg2;
+	fcinfo->args[1].isnull = false;
+	fcinfo->args[2].datum = arg3;
+	fcinfo->args[2].isnull = false;
+	fcinfo->args[3].datum = arg4;
+	fcinfo->args[3].isnull = false;
 
-	result = (*func) (&fcinfo);
+	result = (*func) (fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
+	if (fcinfo->isnull)
 		elog(ERROR, "function %p returned NULL", (void *) func);
 
 	return result;
@@ -887,26 +887,26 @@ Datum
 DirectFunctionCall5Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
 						Datum arg3, Datum arg4, Datum arg5)
 {
-	FunctionCallInfoData fcinfo;
+	LOCAL_FCINFO(fcinfo, 5);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, NULL, 5, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, NULL, 5, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
+	fcinfo->args[1].datum = arg2;
+	fcinfo->args[1].isnull = false;
+	fcinfo->args[2].datum = arg3;
+	fcinfo->args[2].isnull = false;
+	fcinfo->args[3].datum = arg4;
+	fcinfo->args[3].isnull = false;
+	fcinfo->args[4].datum = arg5;
+	fcinfo->args[4].isnull = false;
 
-	result = (*func) (&fcinfo);
+	result = (*func) (fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
+	if (fcinfo->isnull)
 		elog(ERROR, "function %p returned NULL", (void *) func);
 
 	return result;
@@ -917,28 +917,28 @@ DirectFunctionCall6Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
 						Datum arg3, Datum arg4, Datum arg5,
 						Datum arg6)
 {
-	FunctionCallInfoData fcinfo;
+	LOCAL_FCINFO(fcinfo, 6);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, NULL, 6, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, NULL, 6, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.arg[5] = arg6;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
-	fcinfo.argnull[5] = false;
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
+	fcinfo->args[1].datum = arg2;
+	fcinfo->args[1].isnull = false;
+	fcinfo->args[2].datum = arg3;
+	fcinfo->args[2].isnull = false;
+	fcinfo->args[3].datum = arg4;
+	fcinfo->args[3].isnull = false;
+	fcinfo->args[4].datum = arg5;
+	fcinfo->args[4].isnull = false;
+	fcinfo->args[5].datum = arg6;
+	fcinfo->args[5].isnull = false;
 
-	result = (*func) (&fcinfo);
+	result = (*func) (fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
+	if (fcinfo->isnull)
 		elog(ERROR, "function %p returned NULL", (void *) func);
 
 	return result;
@@ -949,30 +949,30 @@ DirectFunctionCall7Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
 						Datum arg3, Datum arg4, Datum arg5,
 						Datum arg6, Datum arg7)
 {
-	FunctionCallInfoData fcinfo;
+	LOCAL_FCINFO(fcinfo, 7);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, NULL, 7, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, NULL, 7, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.arg[5] = arg6;
-	fcinfo.arg[6] = arg7;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
-	fcinfo.argnull[5] = false;
-	fcinfo.argnull[6] = false;
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
+	fcinfo->args[1].datum = arg2;
+	fcinfo->args[1].isnull = false;
+	fcinfo->args[2].datum = arg3;
+	fcinfo->args[2].isnull = false;
+	fcinfo->args[3].datum = arg4;
+	fcinfo->args[3].isnull = false;
+	fcinfo->args[4].datum = arg5;
+	fcinfo->args[4].isnull = false;
+	fcinfo->args[5].datum = arg6;
+	fcinfo->args[5].isnull = false;
+	fcinfo->args[6].datum = arg7;
+	fcinfo->args[6].isnull = false;
 
-	result = (*func) (&fcinfo);
+	result = (*func) (fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
+	if (fcinfo->isnull)
 		elog(ERROR, "function %p returned NULL", (void *) func);
 
 	return result;
@@ -983,32 +983,32 @@ DirectFunctionCall8Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
 						Datum arg3, Datum arg4, Datum arg5,
 						Datum arg6, Datum arg7, Datum arg8)
 {
-	FunctionCallInfoData fcinfo;
+	LOCAL_FCINFO(fcinfo, 8);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, NULL, 8, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, NULL, 8, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.arg[5] = arg6;
-	fcinfo.arg[6] = arg7;
-	fcinfo.arg[7] = arg8;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
-	fcinfo.argnull[5] = false;
-	fcinfo.argnull[6] = false;
-	fcinfo.argnull[7] = false;
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
+	fcinfo->args[1].datum = arg2;
+	fcinfo->args[1].isnull = false;
+	fcinfo->args[2].datum = arg3;
+	fcinfo->args[2].isnull = false;
+	fcinfo->args[3].datum = arg4;
+	fcinfo->args[3].isnull = false;
+	fcinfo->args[4].datum = arg5;
+	fcinfo->args[4].isnull = false;
+	fcinfo->args[5].datum = arg6;
+	fcinfo->args[5].isnull = false;
+	fcinfo->args[6].datum = arg7;
+	fcinfo->args[6].isnull = false;
+	fcinfo->args[7].datum = arg8;
+	fcinfo->args[7].isnull = false;
 
-	result = (*func) (&fcinfo);
+	result = (*func) (fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
+	if (fcinfo->isnull)
 		elog(ERROR, "function %p returned NULL", (void *) func);
 
 	return result;
@@ -1020,34 +1020,34 @@ DirectFunctionCall9Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
 						Datum arg6, Datum arg7, Datum arg8,
 						Datum arg9)
 {
-	FunctionCallInfoData fcinfo;
+	LOCAL_FCINFO(fcinfo, 9);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, NULL, 9, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, NULL, 9, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.arg[5] = arg6;
-	fcinfo.arg[6] = arg7;
-	fcinfo.arg[7] = arg8;
-	fcinfo.arg[8] = arg9;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
-	fcinfo.argnull[5] = false;
-	fcinfo.argnull[6] = false;
-	fcinfo.argnull[7] = false;
-	fcinfo.argnull[8] = false;
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
+	fcinfo->args[1].datum = arg2;
+	fcinfo->args[1].isnull = false;
+	fcinfo->args[2].datum = arg3;
+	fcinfo->args[2].isnull = false;
+	fcinfo->args[3].datum = arg4;
+	fcinfo->args[3].isnull = false;
+	fcinfo->args[4].datum = arg5;
+	fcinfo->args[4].isnull = false;
+	fcinfo->args[5].datum = arg6;
+	fcinfo->args[5].isnull = false;
+	fcinfo->args[6].datum = arg7;
+	fcinfo->args[6].isnull = false;
+	fcinfo->args[7].datum = arg8;
+	fcinfo->args[7].isnull = false;
+	fcinfo->args[8].datum = arg9;
+	fcinfo->args[8].isnull = false;
 
-	result = (*func) (&fcinfo);
+	result = (*func) (fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
+	if (fcinfo->isnull)
 		elog(ERROR, "function %p returned NULL", (void *) func);
 
 	return result;
@@ -1065,18 +1065,18 @@ DirectFunctionCall9Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
 Datum
 CallerFInfoFunctionCall1(PGFunction func, FmgrInfo *flinfo, Oid collation, Datum arg1)
 {
-	FunctionCallInfoData fcinfo;
+	LOCAL_FCINFO(fcinfo, 1);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, flinfo, 1, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, flinfo, 1, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.argnull[0] = false;
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
 
-	result = (*func) (&fcinfo);
+	result = (*func) (fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
+	if (fcinfo->isnull)
 		elog(ERROR, "function %p returned NULL", (void *) func);
 
 	return result;
@@ -1085,20 +1085,20 @@ CallerFInfoFunctionCall1(PGFunction func, FmgrInfo *flinfo, Oid collation, Datum
 Datum
 CallerFInfoFunctionCall2(PGFunction func, FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
 {
-	FunctionCallInfoData fcinfo;
+	LOCAL_FCINFO(fcinfo, 2);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, flinfo, 2, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, flinfo, 2, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
+	fcinfo->args[1].datum = arg2;
+	fcinfo->args[1].isnull = false;
 
-	result = (*func) (&fcinfo);
+	result = (*func) (fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
+	if (fcinfo->isnull)
 		elog(ERROR, "function %p returned NULL", (void *) func);
 
 	return result;
@@ -1110,21 +1110,38 @@ CallerFInfoFunctionCall2(PGFunction func, FmgrInfo *flinfo, Oid collation, Datum
  * are allowed to be NULL.
  */
 Datum
-FunctionCall1Coll(FmgrInfo *flinfo, Oid collation, Datum arg1)
+FunctionCall0Coll(FmgrInfo *flinfo, Oid collation)
 {
-	FunctionCallInfoData fcinfo;
+	LOCAL_FCINFO(fcinfo, 0);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, flinfo, 1, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, flinfo, 0, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.argnull[0] = false;
-
-	result = FunctionCallInvoke(&fcinfo);
+	result = FunctionCallInvoke(fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid);
+	if (fcinfo->isnull)
+		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
+
+	return result;
+}
+
+Datum
+FunctionCall1Coll(FmgrInfo *flinfo, Oid collation, Datum arg1)
+{
+	LOCAL_FCINFO(fcinfo, 1);
+	Datum		result;
+
+	InitFunctionCallInfoData(*fcinfo, flinfo, 1, collation, NULL, NULL);
+
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
+
+	result = FunctionCallInvoke(fcinfo);
+
+	/* Check for null result, since caller is clearly not expecting one */
+	if (fcinfo->isnull)
+		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
 
 	return result;
 }
@@ -1132,21 +1149,21 @@ FunctionCall1Coll(FmgrInfo *flinfo, Oid collation, Datum arg1)
 Datum
 FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
 {
-	FunctionCallInfoData fcinfo;
+	LOCAL_FCINFO(fcinfo, 2);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, flinfo, 2, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, flinfo, 2, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
+	fcinfo->args[1].datum = arg2;
+	fcinfo->args[1].isnull = false;
 
-	result = FunctionCallInvoke(&fcinfo);
+	result = FunctionCallInvoke(fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid);
+	if (fcinfo->isnull)
+		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
 
 	return result;
 }
@@ -1155,23 +1172,23 @@ Datum
 FunctionCall3Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
 				  Datum arg3)
 {
-	FunctionCallInfoData fcinfo;
+	LOCAL_FCINFO(fcinfo, 3);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, flinfo, 3, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, flinfo, 3, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
+	fcinfo->args[1].datum = arg2;
+	fcinfo->args[1].isnull = false;
+	fcinfo->args[2].datum = arg3;
+	fcinfo->args[2].isnull = false;
 
-	result = FunctionCallInvoke(&fcinfo);
+	result = FunctionCallInvoke(fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid);
+	if (fcinfo->isnull)
+		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
 
 	return result;
 }
@@ -1180,25 +1197,25 @@ Datum
 FunctionCall4Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
 				  Datum arg3, Datum arg4)
 {
-	FunctionCallInfoData fcinfo;
+	LOCAL_FCINFO(fcinfo, 4);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, flinfo, 4, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, flinfo, 4, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
+	fcinfo->args[1].datum = arg2;
+	fcinfo->args[1].isnull = false;
+	fcinfo->args[2].datum = arg3;
+	fcinfo->args[2].isnull = false;
+	fcinfo->args[3].datum = arg4;
+	fcinfo->args[3].isnull = false;
 
-	result = FunctionCallInvoke(&fcinfo);
+	result = FunctionCallInvoke(fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid);
+	if (fcinfo->isnull)
+		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
 
 	return result;
 }
@@ -1207,27 +1224,27 @@ Datum
 FunctionCall5Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
 				  Datum arg3, Datum arg4, Datum arg5)
 {
-	FunctionCallInfoData fcinfo;
+	LOCAL_FCINFO(fcinfo, 5);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, flinfo, 5, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, flinfo, 5, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
+	fcinfo->args[1].datum = arg2;
+	fcinfo->args[1].isnull = false;
+	fcinfo->args[2].datum = arg3;
+	fcinfo->args[2].isnull = false;
+	fcinfo->args[3].datum = arg4;
+	fcinfo->args[3].isnull = false;
+	fcinfo->args[4].datum = arg5;
+	fcinfo->args[4].isnull = false;
 
-	result = FunctionCallInvoke(&fcinfo);
+	result = FunctionCallInvoke(fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid);
+	if (fcinfo->isnull)
+		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
 
 	return result;
 }
@@ -1237,29 +1254,29 @@ FunctionCall6Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
 				  Datum arg3, Datum arg4, Datum arg5,
 				  Datum arg6)
 {
-	FunctionCallInfoData fcinfo;
+	LOCAL_FCINFO(fcinfo, 6);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, flinfo, 6, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, flinfo, 6, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.arg[5] = arg6;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
-	fcinfo.argnull[5] = false;
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
+	fcinfo->args[1].datum = arg2;
+	fcinfo->args[1].isnull = false;
+	fcinfo->args[2].datum = arg3;
+	fcinfo->args[2].isnull = false;
+	fcinfo->args[3].datum = arg4;
+	fcinfo->args[3].isnull = false;
+	fcinfo->args[4].datum = arg5;
+	fcinfo->args[4].isnull = false;
+	fcinfo->args[5].datum = arg6;
+	fcinfo->args[5].isnull = false;
 
-	result = FunctionCallInvoke(&fcinfo);
+	result = FunctionCallInvoke(fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid);
+	if (fcinfo->isnull)
+		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
 
 	return result;
 }
@@ -1269,31 +1286,31 @@ FunctionCall7Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
 				  Datum arg3, Datum arg4, Datum arg5,
 				  Datum arg6, Datum arg7)
 {
-	FunctionCallInfoData fcinfo;
+	LOCAL_FCINFO(fcinfo, 7);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, flinfo, 7, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, flinfo, 7, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.arg[5] = arg6;
-	fcinfo.arg[6] = arg7;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
-	fcinfo.argnull[5] = false;
-	fcinfo.argnull[6] = false;
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
+	fcinfo->args[1].datum = arg2;
+	fcinfo->args[1].isnull = false;
+	fcinfo->args[2].datum = arg3;
+	fcinfo->args[2].isnull = false;
+	fcinfo->args[3].datum = arg4;
+	fcinfo->args[3].isnull = false;
+	fcinfo->args[4].datum = arg5;
+	fcinfo->args[4].isnull = false;
+	fcinfo->args[5].datum = arg6;
+	fcinfo->args[5].isnull = false;
+	fcinfo->args[6].datum = arg7;
+	fcinfo->args[6].isnull = false;
 
-	result = FunctionCallInvoke(&fcinfo);
+	result = FunctionCallInvoke(fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid);
+	if (fcinfo->isnull)
+		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
 
 	return result;
 }
@@ -1303,33 +1320,33 @@ FunctionCall8Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
 				  Datum arg3, Datum arg4, Datum arg5,
 				  Datum arg6, Datum arg7, Datum arg8)
 {
-	FunctionCallInfoData fcinfo;
+	LOCAL_FCINFO(fcinfo, 8);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, flinfo, 8, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, flinfo, 8, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.arg[5] = arg6;
-	fcinfo.arg[6] = arg7;
-	fcinfo.arg[7] = arg8;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
-	fcinfo.argnull[5] = false;
-	fcinfo.argnull[6] = false;
-	fcinfo.argnull[7] = false;
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
+	fcinfo->args[1].datum = arg2;
+	fcinfo->args[1].isnull = false;
+	fcinfo->args[2].datum = arg3;
+	fcinfo->args[2].isnull = false;
+	fcinfo->args[3].datum = arg4;
+	fcinfo->args[3].isnull = false;
+	fcinfo->args[4].datum = arg5;
+	fcinfo->args[4].isnull = false;
+	fcinfo->args[5].datum = arg6;
+	fcinfo->args[5].isnull = false;
+	fcinfo->args[6].datum = arg7;
+	fcinfo->args[6].isnull = false;
+	fcinfo->args[7].datum = arg8;
+	fcinfo->args[7].isnull = false;
 
-	result = FunctionCallInvoke(&fcinfo);
+	result = FunctionCallInvoke(fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid);
+	if (fcinfo->isnull)
+		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
 
 	return result;
 }
@@ -1340,35 +1357,35 @@ FunctionCall9Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
 				  Datum arg6, Datum arg7, Datum arg8,
 				  Datum arg9)
 {
-	FunctionCallInfoData fcinfo;
+	LOCAL_FCINFO(fcinfo, 9);
 	Datum		result;
 
-	InitFunctionCallInfoData(fcinfo, flinfo, 9, collation, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, flinfo, 9, collation, NULL, NULL);
 
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.arg[5] = arg6;
-	fcinfo.arg[6] = arg7;
-	fcinfo.arg[7] = arg8;
-	fcinfo.arg[8] = arg9;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
-	fcinfo.argnull[5] = false;
-	fcinfo.argnull[6] = false;
-	fcinfo.argnull[7] = false;
-	fcinfo.argnull[8] = false;
+	fcinfo->args[0].datum = arg1;
+	fcinfo->args[0].isnull = false;
+	fcinfo->args[1].datum = arg2;
+	fcinfo->args[1].isnull = false;
+	fcinfo->args[2].datum = arg3;
+	fcinfo->args[2].isnull = false;
+	fcinfo->args[3].datum = arg4;
+	fcinfo->args[3].isnull = false;
+	fcinfo->args[4].datum = arg5;
+	fcinfo->args[4].isnull = false;
+	fcinfo->args[5].datum = arg6;
+	fcinfo->args[5].isnull = false;
+	fcinfo->args[6].datum = arg7;
+	fcinfo->args[6].isnull = false;
+	fcinfo->args[7].datum = arg8;
+	fcinfo->args[7].isnull = false;
+	fcinfo->args[8].datum = arg9;
+	fcinfo->args[8].isnull = false;
 
-	result = FunctionCallInvoke(&fcinfo);
+	result = FunctionCallInvoke(fcinfo);
 
 	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid);
+	if (fcinfo->isnull)
+		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
 
 	return result;
 }
@@ -1385,68 +1402,30 @@ Datum
 OidFunctionCall0Coll(Oid functionId, Oid collation)
 {
 	FmgrInfo	flinfo;
-	FunctionCallInfoData fcinfo;
-	Datum		result;
 
 	fmgr_info(functionId, &flinfo);
 
-	InitFunctionCallInfoData(fcinfo, &flinfo, 0, collation, NULL, NULL);
-
-	result = FunctionCallInvoke(&fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
-	return result;
+	return FunctionCall0Coll(&flinfo, collation);
 }
 
 Datum
 OidFunctionCall1Coll(Oid functionId, Oid collation, Datum arg1)
 {
 	FmgrInfo	flinfo;
-	FunctionCallInfoData fcinfo;
-	Datum		result;
 
 	fmgr_info(functionId, &flinfo);
 
-	InitFunctionCallInfoData(fcinfo, &flinfo, 1, collation, NULL, NULL);
-
-	fcinfo.arg[0] = arg1;
-	fcinfo.argnull[0] = false;
-
-	result = FunctionCallInvoke(&fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
-	return result;
+	return FunctionCall1Coll(&flinfo, collation, arg1);
 }
 
 Datum
 OidFunctionCall2Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2)
 {
 	FmgrInfo	flinfo;
-	FunctionCallInfoData fcinfo;
-	Datum		result;
 
 	fmgr_info(functionId, &flinfo);
 
-	InitFunctionCallInfoData(fcinfo, &flinfo, 2, collation, NULL, NULL);
-
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-
-	result = FunctionCallInvoke(&fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
-	return result;
+	return FunctionCall2Coll(&flinfo, collation, arg1, arg2);
 }
 
 Datum
@@ -1454,27 +1433,10 @@ OidFunctionCall3Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
 					 Datum arg3)
 {
 	FmgrInfo	flinfo;
-	FunctionCallInfoData fcinfo;
-	Datum		result;
 
 	fmgr_info(functionId, &flinfo);
 
-	InitFunctionCallInfoData(fcinfo, &flinfo, 3, collation, NULL, NULL);
-
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-
-	result = FunctionCallInvoke(&fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
-	return result;
+	return FunctionCall3Coll(&flinfo, collation, arg1, arg2, arg3);
 }
 
 Datum
@@ -1482,29 +1444,10 @@ OidFunctionCall4Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
 					 Datum arg3, Datum arg4)
 {
 	FmgrInfo	flinfo;
-	FunctionCallInfoData fcinfo;
-	Datum		result;
 
 	fmgr_info(functionId, &flinfo);
 
-	InitFunctionCallInfoData(fcinfo, &flinfo, 4, collation, NULL, NULL);
-
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-
-	result = FunctionCallInvoke(&fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
-	return result;
+	return FunctionCall4Coll(&flinfo, collation, arg1, arg2, arg3, arg4);
 }
 
 Datum
@@ -1512,31 +1455,10 @@ OidFunctionCall5Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
 					 Datum arg3, Datum arg4, Datum arg5)
 {
 	FmgrInfo	flinfo;
-	FunctionCallInfoData fcinfo;
-	Datum		result;
 
 	fmgr_info(functionId, &flinfo);
 
-	InitFunctionCallInfoData(fcinfo, &flinfo, 5, collation, NULL, NULL);
-
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
-
-	result = FunctionCallInvoke(&fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
-	return result;
+	return FunctionCall5Coll(&flinfo, collation, arg1, arg2, arg3, arg4, arg5);
 }
 
 Datum
@@ -1545,33 +1467,11 @@ OidFunctionCall6Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
 					 Datum arg6)
 {
 	FmgrInfo	flinfo;
-	FunctionCallInfoData fcinfo;
-	Datum		result;
 
 	fmgr_info(functionId, &flinfo);
 
-	InitFunctionCallInfoData(fcinfo, &flinfo, 6, collation, NULL, NULL);
-
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.arg[5] = arg6;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
-	fcinfo.argnull[5] = false;
-
-	result = FunctionCallInvoke(&fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
-	return result;
+	return FunctionCall6Coll(&flinfo, collation, arg1, arg2, arg3, arg4, arg5,
+							 arg6);
 }
 
 Datum
@@ -1580,35 +1480,11 @@ OidFunctionCall7Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
 					 Datum arg6, Datum arg7)
 {
 	FmgrInfo	flinfo;
-	FunctionCallInfoData fcinfo;
-	Datum		result;
 
 	fmgr_info(functionId, &flinfo);
 
-	InitFunctionCallInfoData(fcinfo, &flinfo, 7, collation, NULL, NULL);
-
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.arg[5] = arg6;
-	fcinfo.arg[6] = arg7;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
-	fcinfo.argnull[5] = false;
-	fcinfo.argnull[6] = false;
-
-	result = FunctionCallInvoke(&fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
-	return result;
+	return FunctionCall7Coll(&flinfo, collation, arg1, arg2, arg3, arg4, arg5,
+							 arg6, arg7);
 }
 
 Datum
@@ -1617,37 +1493,11 @@ OidFunctionCall8Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
 					 Datum arg6, Datum arg7, Datum arg8)
 {
 	FmgrInfo	flinfo;
-	FunctionCallInfoData fcinfo;
-	Datum		result;
 
 	fmgr_info(functionId, &flinfo);
 
-	InitFunctionCallInfoData(fcinfo, &flinfo, 8, collation, NULL, NULL);
-
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.arg[5] = arg6;
-	fcinfo.arg[6] = arg7;
-	fcinfo.arg[7] = arg8;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
-	fcinfo.argnull[5] = false;
-	fcinfo.argnull[6] = false;
-	fcinfo.argnull[7] = false;
-
-	result = FunctionCallInvoke(&fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
-	return result;
+	return FunctionCall8Coll(&flinfo, collation, arg1, arg2, arg3, arg4, arg5,
+							 arg6, arg7, arg8);
 }
 
 Datum
@@ -1657,39 +1507,11 @@ OidFunctionCall9Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
 					 Datum arg9)
 {
 	FmgrInfo	flinfo;
-	FunctionCallInfoData fcinfo;
-	Datum		result;
 
 	fmgr_info(functionId, &flinfo);
 
-	InitFunctionCallInfoData(fcinfo, &flinfo, 9, collation, NULL, NULL);
-
-	fcinfo.arg[0] = arg1;
-	fcinfo.arg[1] = arg2;
-	fcinfo.arg[2] = arg3;
-	fcinfo.arg[3] = arg4;
-	fcinfo.arg[4] = arg5;
-	fcinfo.arg[5] = arg6;
-	fcinfo.arg[6] = arg7;
-	fcinfo.arg[7] = arg8;
-	fcinfo.arg[8] = arg9;
-	fcinfo.argnull[0] = false;
-	fcinfo.argnull[1] = false;
-	fcinfo.argnull[2] = false;
-	fcinfo.argnull[3] = false;
-	fcinfo.argnull[4] = false;
-	fcinfo.argnull[5] = false;
-	fcinfo.argnull[6] = false;
-	fcinfo.argnull[7] = false;
-	fcinfo.argnull[8] = false;
-
-	result = FunctionCallInvoke(&fcinfo);
-
-	/* Check for null result, since caller is clearly not expecting one */
-	if (fcinfo.isnull)
-		elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
-	return result;
+	return FunctionCall9Coll(&flinfo, collation, arg1, arg2, arg3, arg4, arg5,
+							 arg6, arg7, arg8, arg9);
 }
 
 
@@ -1708,35 +1530,35 @@ OidFunctionCall9Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
 Datum
 InputFunctionCall(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod)
 {
-	FunctionCallInfoData fcinfo;
+	LOCAL_FCINFO(fcinfo, 3);
 	Datum		result;
 
 	if (str == NULL && flinfo->fn_strict)
 		return (Datum) 0;		/* just return null result */
 
-	InitFunctionCallInfoData(fcinfo, flinfo, 3, InvalidOid, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, flinfo, 3, InvalidOid, 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;
+	fcinfo->args[0].datum = CStringGetDatum(str);
+	fcinfo->args[0].isnull = false;
+	fcinfo->args[1].datum = ObjectIdGetDatum(typioparam);
+	fcinfo->args[1].isnull = false;
+	fcinfo->args[2].datum = Int32GetDatum(typmod);
+	fcinfo->args[2].isnull = false;
 
-	result = FunctionCallInvoke(&fcinfo);
+	result = FunctionCallInvoke(fcinfo);
 
 	/* Should get null result if and only if str is NULL */
 	if (str == NULL)
 	{
-		if (!fcinfo.isnull)
+		if (!fcinfo->isnull)
 			elog(ERROR, "input function %u returned non-NULL",
-				 fcinfo.flinfo->fn_oid);
+				 flinfo->fn_oid);
 	}
 	else
 	{
-		if (fcinfo.isnull)
+		if (fcinfo->isnull)
 			elog(ERROR, "input function %u returned NULL",
-				 fcinfo.flinfo->fn_oid);
+				 flinfo->fn_oid);
 	}
 
 	return result;
@@ -1767,35 +1589,35 @@ Datum
 ReceiveFunctionCall(FmgrInfo *flinfo, StringInfo buf,
 					Oid typioparam, int32 typmod)
 {
-	FunctionCallInfoData fcinfo;
+	LOCAL_FCINFO(fcinfo, 3);
 	Datum		result;
 
 	if (buf == NULL && flinfo->fn_strict)
 		return (Datum) 0;		/* just return null result */
 
-	InitFunctionCallInfoData(fcinfo, flinfo, 3, InvalidOid, NULL, NULL);
+	InitFunctionCallInfoData(*fcinfo, flinfo, 3, InvalidOid, 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;
+	fcinfo->args[0].datum = PointerGetDatum(buf);
+	fcinfo->args[0].isnull = false;
+	fcinfo->args[1].datum = ObjectIdGetDatum(typioparam);
+	fcinfo->args[1].isnull = false;
+	fcinfo->args[2].datum = Int32GetDatum(typmod);
+	fcinfo->args[2].isnull = false;
 
-	result = FunctionCallInvoke(&fcinfo);
+	result = FunctionCallInvoke(fcinfo);
 
 	/* Should get null result if and only if buf is NULL */
 	if (buf == NULL)
 	{
-		if (!fcinfo.isnull)
+		if (!fcinfo->isnull)
 			elog(ERROR, "receive function %u returned non-NULL",
-				 fcinfo.flinfo->fn_oid);
+				 flinfo->fn_oid);
 	}
 	else
 	{
-		if (fcinfo.isnull)
+		if (fcinfo->isnull)
 			elog(ERROR, "receive function %u returned NULL",
-				 fcinfo.flinfo->fn_oid);
+				 flinfo->fn_oid);
 	}
 
 	return result;
diff --git a/src/backend/utils/sort/sortsupport.c b/src/backend/utils/sort/sortsupport.c
index e2100da266a..b3a7aaf7b9e 100644
--- a/src/backend/utils/sort/sortsupport.c
+++ b/src/backend/utils/sort/sortsupport.c
@@ -26,16 +26,17 @@
 /* Info needed to use an old-style comparison function as a sort comparator */
 typedef struct
 {
-	FunctionCallInfoData fcinfo;	/* reusable callinfo structure */
 	FmgrInfo	flinfo;			/* lookup data for comparison function */
+	FunctionCallInfoBaseData fcinfo;	/* reusable callinfo structure */
 } SortShimExtra;
 
+#define SizeForSortShimExtra(nargs) (offsetof(SortShimExtra, fcinfo) + SizeForFunctionCallInfo(nargs))
 
 /*
  * Shim function for calling an old-style comparator
  *
  * This is essentially an inlined version of FunctionCall2Coll(), except
- * we assume that the FunctionCallInfoData was already mostly set up by
+ * we assume that the FunctionCallInfoBaseData was already mostly set up by
  * PrepareSortSupportComparisonShim.
  */
 static int
@@ -44,8 +45,8 @@ comparison_shim(Datum x, Datum y, SortSupport ssup)
 	SortShimExtra *extra = (SortShimExtra *) ssup->ssup_extra;
 	Datum		result;
 
-	extra->fcinfo.arg[0] = x;
-	extra->fcinfo.arg[1] = y;
+	extra->fcinfo.args[0].datum = x;
+	extra->fcinfo.args[1].datum = y;
 
 	/* just for paranoia's sake, we reset isnull each time */
 	extra->fcinfo.isnull = false;
@@ -69,7 +70,7 @@ PrepareSortSupportComparisonShim(Oid cmpFunc, SortSupport ssup)
 	SortShimExtra *extra;
 
 	extra = (SortShimExtra *) MemoryContextAlloc(ssup->ssup_cxt,
-												 sizeof(SortShimExtra));
+												 SizeForSortShimExtra(2));
 
 	/* Lookup the comparison function */
 	fmgr_info_cxt(cmpFunc, &extra->flinfo, ssup->ssup_cxt);
@@ -77,8 +78,8 @@ PrepareSortSupportComparisonShim(Oid cmpFunc, SortSupport ssup)
 	/* We can initialize the callinfo just once and re-use it */
 	InitFunctionCallInfoData(extra->fcinfo, &extra->flinfo, 2,
 							 ssup->ssup_collation, NULL, NULL);
-	extra->fcinfo.argnull[0] = false;
-	extra->fcinfo.argnull[1] = false;
+	extra->fcinfo.args[0].isnull = false;
+	extra->fcinfo.args[1].isnull = false;
 
 	ssup->ssup_extra = extra;
 	ssup->comparator = comparison_shim;
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index b875bbad4fb..2c1697dd766 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -223,7 +223,8 @@ typedef enum ExprEvalOp
 	/* aggregation related nodes */
 	EEOP_AGG_STRICT_DESERIALIZE,
 	EEOP_AGG_DESERIALIZE,
-	EEOP_AGG_STRICT_INPUT_CHECK,
+	EEOP_AGG_STRICT_INPUT_CHECK_ARGS,
+	EEOP_AGG_STRICT_INPUT_CHECK_NULLS,
 	EEOP_AGG_INIT_TRANS,
 	EEOP_AGG_STRICT_TRANS_CHECK,
 	EEOP_AGG_PLAIN_TRANS_BYVAL,
@@ -601,9 +602,21 @@ typedef struct ExprEvalStep
 			int			jumpnull;
 		}			agg_deserialize;
 
-		/* for EEOP_AGG_STRICT_INPUT_CHECK */
+		/* for EEOP_AGG_STRICT_INPUT_CHECK_NULLS / STRICT_INPUT_CHECK_ARGS */
 		struct
 		{
+			/*
+			 * For EEOP_AGG_STRICT_INPUT_CHECK_ARGS args contains pointers to
+			 * the NullableDatums that need to be checked for NULLs.
+			 *
+			 * For EEOP_AGG_STRICT_INPUT_CHECK_NULLS nulls contains pointers
+			 * to booleans that need to be checked for NULLs.
+			 *
+			 * Both cases currently need to exist because sometimes the
+			 * to-be-checked nulls are in TupleTableSlot.isnull array, and
+			 * sometimes in FunctionCallInfoBaseData.args[i].isnull.
+			 */
+			NullableDatum *args;
 			bool	   *nulls;
 			int			nargs;
 			int			jumpnull;
diff --git a/src/include/executor/nodeAgg.h b/src/include/executor/nodeAgg.h
index 3d93439e5fc..30541361301 100644
--- a/src/include/executor/nodeAgg.h
+++ b/src/include/executor/nodeAgg.h
@@ -158,12 +158,12 @@ typedef struct AggStatePerTransData
 	 * re-initializing the unchanging fields; which isn't much, but it seems
 	 * worth the extra space consumption.
 	 */
-	FunctionCallInfoData transfn_fcinfo;
+	FunctionCallInfo transfn_fcinfo;
 
 	/* Likewise for serialization and deserialization functions */
-	FunctionCallInfoData serialfn_fcinfo;
+	FunctionCallInfo serialfn_fcinfo;
 
-	FunctionCallInfoData deserialfn_fcinfo;
+	FunctionCallInfo deserialfn_fcinfo;
 }			AggStatePerTransData;
 
 /*
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index ead17f0e442..b256a757fc1 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -35,7 +35,7 @@ typedef struct StringInfoData *fmStringInfo;
  * signature.)
  */
 
-typedef struct FunctionCallInfoData *FunctionCallInfo;
+typedef struct FunctionCallInfoBaseData *FunctionCallInfo;
 
 typedef Datum (*PGFunction) (FunctionCallInfo fcinfo);
 
@@ -46,8 +46,8 @@ typedef Datum (*PGFunction) (FunctionCallInfo fcinfo);
  * info struct saved for re-use.
  *
  * Note that fn_expr really is parse-time-determined information about the
- * arguments, rather than about the function itself.  But it's convenient
- * to store it here rather than in FunctionCallInfoData, where it might more
+ * arguments, rather than about the function itself.  But it's convenient to
+ * store it here rather than in FunctionCallInfoBaseData, where it might more
  * logically belong.
  *
  * fn_extra is available for use by the called function; all other fields
@@ -73,8 +73,16 @@ typedef struct FmgrInfo
  * fields in whatever resultinfo points to.  It should not change any other
  * fields.  (In particular, scribbling on the argument arrays is a bad idea,
  * since some callers assume they can re-call with the same arguments.)
+ *
+ * Note that enough space for arguments needs to be provided, either by using
+ * SizeForFunctionCallInfo() in dynamic allocations, or by using
+ * LOCAL_FCINFO() for on-stack allocations.
+ *
+ * This struct is named *BaseData, rather than *Data, to break pre v12 code
+ * that allocated FunctionCallInfoData itself, as it'd often silently break
+ * old code due to no space for arguments being provided.
  */
-typedef struct FunctionCallInfoData
+typedef struct FunctionCallInfoBaseData
 {
 	FmgrInfo   *flinfo;			/* ptr to lookup info used for this call */
 	fmNodePtr	context;		/* pass info about context of call */
@@ -83,11 +91,31 @@ typedef struct FunctionCallInfoData
 #define FIELDNO_FUNCTIONCALLINFODATA_ISNULL 4
 	bool		isnull;			/* function must set true if result is NULL */
 	short		nargs;			/* # arguments actually passed */
-#define FIELDNO_FUNCTIONCALLINFODATA_ARG 6
-	Datum		arg[FUNC_MAX_ARGS]; /* Arguments passed to function */
-#define FIELDNO_FUNCTIONCALLINFODATA_ARGNULL 7
-	bool		argnull[FUNC_MAX_ARGS]; /* T if arg[i] is actually NULL */
-} FunctionCallInfoData;
+#define FIELDNO_FUNCTIONCALLINFODATA_ARGS 6
+	NullableDatum args[FLEXIBLE_ARRAY_MEMBER];
+} FunctionCallInfoBaseData;
+
+/*
+ * Space needed for for a FunctionCallInfoBaseData struct with sufficient space
+ * for `nargs` arguments.
+ */
+#define SizeForFunctionCallInfo(nargs) \
+	(offsetof(FunctionCallInfoBaseData, args) + \
+	 sizeof(NullableDatum) * (nargs))
+
+/*
+ * This macro ensures that `name` points to a stack-allocated
+ * FunctionCallInfoBaseData struct with sufficient space for `nargs` arguments.
+ */
+#define LOCAL_FCINFO(name, nargs) \
+	union \
+	{ \
+		FunctionCallInfoBaseData fcinfo; \
+		/* ensure enough space for nargs args is available */ \
+		char fcinfo_data[SizeForFunctionCallInfo(nargs)]; \
+	} name##data; \
+	FunctionCallInfo name = &name##data.fcinfo
+
 
 /*
  * This routine fills a FmgrInfo struct, given the OID
@@ -116,11 +144,8 @@ extern void fmgr_info_copy(FmgrInfo *dstinfo, FmgrInfo *srcinfo,
 extern void fmgr_symbol(Oid functionId, char **mod, char **fn);
 
 /*
- * This macro initializes all the fields of a FunctionCallInfoData except
- * for the arg[] and argnull[] arrays.  Performance testing has shown that
- * the fastest way to set up argnull[] for small numbers of arguments is to
- * explicitly set each required element to false, so we don't try to zero
- * out the argnull[] array in the macro.
+ * This macro initializes all the fields of a FunctionCallInfoBaseData except
+ * for the args[] array.
  */
 #define InitFunctionCallInfoData(Fcinfo, Flinfo, Nargs, Collation, Context, Resultinfo) \
 	do { \
@@ -133,7 +158,7 @@ extern void fmgr_symbol(Oid functionId, char **mod, char **fn);
 	} while (0)
 
 /*
- * This macro invokes a function given a filled-in FunctionCallInfoData
+ * This macro invokes a function given a filled-in FunctionCallInfoBaseData
  * struct.  The macro result is the returned Datum --- but note that
  * caller must still check fcinfo->isnull!	Also, if function is strict,
  * it is caller's responsibility to verify that no null arguments are present
@@ -176,7 +201,7 @@ extern void fmgr_symbol(Oid functionId, char **mod, char **fn);
  * If function is not marked "proisstrict" in pg_proc, it must check for
  * null arguments using this macro.  Do not try to GETARG a null argument!
  */
-#define PG_ARGISNULL(n)  (fcinfo->argnull[n])
+#define PG_ARGISNULL(n)  (fcinfo->args[n].isnull)
 
 /*
  * Support for fetching detoasted copies of toastable datatypes (all of
@@ -235,7 +260,7 @@ extern struct varlena *pg_detoast_datum_packed(struct varlena *datum);
 
 /* Macros for fetching arguments of standard types */
 
-#define PG_GETARG_DATUM(n)	 (fcinfo->arg[n])
+#define PG_GETARG_DATUM(n)	 (fcinfo->args[n].datum)
 #define PG_GETARG_INT32(n)	 DatumGetInt32(PG_GETARG_DATUM(n))
 #define PG_GETARG_UINT32(n)  DatumGetUInt32(PG_GETARG_DATUM(n))
 #define PG_GETARG_INT16(n)	 DatumGetInt16(PG_GETARG_DATUM(n))
@@ -514,6 +539,7 @@ extern Datum CallerFInfoFunctionCall2(PGFunction func, FmgrInfo *flinfo,
  * directly-computed parameter list.  Note that neither arguments nor result
  * are allowed to be NULL.
  */
+extern Datum FunctionCall0Coll(FmgrInfo *flinfo, Oid collation);
 extern Datum FunctionCall1Coll(FmgrInfo *flinfo, Oid collation,
 				  Datum arg1);
 extern Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation,
diff --git a/src/include/jit/llvmjit.h b/src/include/jit/llvmjit.h
index 2545abeddd1..6af5fe74388 100644
--- a/src/include/jit/llvmjit.h
+++ b/src/include/jit/llvmjit.h
@@ -62,6 +62,7 @@ extern LLVMTypeRef TypePGFunction;
 extern LLVMTypeRef TypeSizeT;
 extern LLVMTypeRef TypeStorageBool;
 
+extern LLVMTypeRef StructNullableDatum;
 extern LLVMTypeRef StructTupleDescData;
 extern LLVMTypeRef StructHeapTupleData;
 extern LLVMTypeRef StructTupleTableSlot;
diff --git a/src/include/jit/llvmjit_emit.h b/src/include/jit/llvmjit_emit.h
index 48a5c0f86cd..b8d0d629d96 100644
--- a/src/include/jit/llvmjit_emit.h
+++ b/src/include/jit/llvmjit_emit.h
@@ -208,4 +208,60 @@ l_mcxt_switch(LLVMModuleRef mod, LLVMBuilderRef b, LLVMValueRef nc)
 
 	return ret;
 }
+
+/*
+ * Return pointer to the the argno'th argument nullness.
+ */
+static inline LLVMValueRef
+l_funcnullp(LLVMBuilderRef b, LLVMValueRef v_fcinfo, size_t argno)
+{
+	LLVMValueRef v_args;
+	LLVMValueRef v_argn;
+
+	v_args = LLVMBuildStructGEP(b,
+								v_fcinfo,
+								FIELDNO_FUNCTIONCALLINFODATA_ARGS,
+								"v_argsp");
+	v_argn = LLVMBuildStructGEP(b, v_args, argno, "");
+
+	return LLVMBuildStructGEP(b, v_argn, FIELDNO_NULLABLE_DATUM_ISNULL, "");
+}
+
+/*
+ * Return pointer to the the argno'th argument datum.
+ */
+static inline LLVMValueRef
+l_funcvaluep(LLVMBuilderRef b, LLVMValueRef v_fcinfo, size_t argno)
+{
+	LLVMValueRef v_args;
+	LLVMValueRef v_argn;
+
+	v_args = LLVMBuildStructGEP(b,
+								v_fcinfo,
+								FIELDNO_FUNCTIONCALLINFODATA_ARGS,
+								"v_argsp");
+	v_argn = LLVMBuildStructGEP(b, v_args, argno, "");
+
+	return LLVMBuildStructGEP(b, v_argn, FIELDNO_NULLABLE_DATUM_DATUM, "");
+}
+
+/*
+ * Return argno'th argument nullness.
+ */
+static inline LLVMValueRef
+l_funcnull(LLVMBuilderRef b, LLVMValueRef v_fcinfo, size_t argno)
+{
+	return LLVMBuildLoad(b, l_funcnullp(b, v_fcinfo, argno), "");
+}
+
+/*
+ * Return argno'th argument datum.
+ */
+static inline LLVMValueRef
+l_funcvalue(LLVMBuilderRef b, LLVMValueRef v_fcinfo, size_t argno)
+{
+	return LLVMBuildLoad(b, l_funcvaluep(b, v_fcinfo, argno), "");
+}
+
+
 #endif
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 64e8ef37407..3b789ee7cf3 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -826,7 +826,7 @@ typedef struct SetExprState
 	 * (by InitFunctionCallInfoData) if func.fn_oid is valid.  It also saves
 	 * argument values between calls, when setArgsValid is true.
 	 */
-	FunctionCallInfoData fcinfo_data;
+	FunctionCallInfo fcinfo;
 } SetExprState;
 
 /* ----------------
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 313ca5f3c34..0ce79489dad 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -1321,7 +1321,7 @@ extern void pgstat_count_heap_delete(Relation rel);
 extern void pgstat_count_truncate(Relation rel);
 extern void pgstat_update_heap_dead_tuples(Relation rel, int delta);
 
-extern void pgstat_init_function_usage(FunctionCallInfoData *fcinfo,
+extern void pgstat_init_function_usage(FunctionCallInfo fcinfo,
 						   PgStat_FunctionCallUsage *fcu);
 extern void pgstat_end_function_usage(PgStat_FunctionCallUsage *fcu,
 						  bool finalize);
diff --git a/src/include/postgres.h b/src/include/postgres.h
index 446bb44b711..1f9e24bf51a 100644
--- a/src/include/postgres.h
+++ b/src/include/postgres.h
@@ -366,6 +366,21 @@ typedef struct
 
 typedef uintptr_t Datum;
 
+/*
+ * A NullableDatum is used in places where both a Datum and its nullness needs
+ * to be stored. This can be more efficient than storing datums and nullness
+ * in separate arrays, due to better spatial locality, even if more space may
+ * be wasted due to padding.
+ */
+typedef struct NullableDatum
+{
+#define FIELDNO_NULLABLE_DATUM_DATUM 0
+	Datum		datum;
+#define FIELDNO_NULLABLE_DATUM_ISNULL 1
+	bool		isnull;
+	/* due to alignment padding this could be used for flags for free */
+} NullableDatum;
+
 #define SIZEOF_DATUM SIZEOF_VOID_P
 
 /*
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index fe54b209035..d514958ea02 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -1877,7 +1877,7 @@ Datum
 plperl_inline_handler(PG_FUNCTION_ARGS)
 {
 	InlineCodeBlock *codeblock = (InlineCodeBlock *) PG_GETARG_POINTER(0);
-	FunctionCallInfoData fake_fcinfo;
+	LOCAL_FCINFO(fake_fcinfo, 0);
 	FmgrInfo	flinfo;
 	plperl_proc_desc desc;
 	plperl_call_data *volatile save_call_data = current_call_data;
@@ -1899,10 +1899,10 @@ plperl_inline_handler(PG_FUNCTION_ARGS)
 	 * plperl_call_perl_func().  In particular note that this sets things up
 	 * with no arguments passed, and a result type of VOID.
 	 */
-	MemSet(&fake_fcinfo, 0, sizeof(fake_fcinfo));
+	MemSet(fake_fcinfo, 0, SizeForFunctionCallInfo(0));
 	MemSet(&flinfo, 0, sizeof(flinfo));
 	MemSet(&desc, 0, sizeof(desc));
-	fake_fcinfo.flinfo = &flinfo;
+	fake_fcinfo->flinfo = &flinfo;
 	flinfo.fn_oid = InvalidOid;
 	flinfo.fn_mcxt = CurrentMemoryContext;
 
@@ -1920,7 +1920,7 @@ plperl_inline_handler(PG_FUNCTION_ARGS)
 	desc.nargs = 0;
 	desc.reference = NULL;
 
-	this_call_data.fcinfo = &fake_fcinfo;
+	this_call_data.fcinfo = fake_fcinfo;
 	this_call_data.prodesc = &desc;
 	/* we do not bother with refcounting the fake prodesc */
 
@@ -1940,7 +1940,7 @@ plperl_inline_handler(PG_FUNCTION_ARGS)
 		if (!desc.reference)	/* can this happen? */
 			elog(ERROR, "could not create internal procedure for anonymous code block");
 
-		perlret = plperl_call_perl_func(&desc, &fake_fcinfo);
+		perlret = plperl_call_perl_func(&desc, fake_fcinfo);
 
 		SvREFCNT_dec_current(perlret);
 
@@ -2194,11 +2194,11 @@ plperl_call_perl_func(plperl_proc_desc *desc, FunctionCallInfo fcinfo)
 
 	for (i = 0; i < desc->nargs; i++)
 	{
-		if (fcinfo->argnull[i])
+		if (fcinfo->args[i].isnull)
 			PUSHs(&PL_sv_undef);
 		else if (desc->arg_is_rowtype[i])
 		{
-			SV		   *sv = plperl_hash_from_datum(fcinfo->arg[i]);
+			SV		   *sv = plperl_hash_from_datum(fcinfo->args[i].datum);
 
 			PUSHs(sv_2mortal(sv));
 		}
@@ -2208,15 +2208,15 @@ plperl_call_perl_func(plperl_proc_desc *desc, FunctionCallInfo fcinfo)
 			Oid			funcid;
 
 			if (OidIsValid(desc->arg_arraytype[i]))
-				sv = plperl_ref_from_pg_array(fcinfo->arg[i], desc->arg_arraytype[i]);
+				sv = plperl_ref_from_pg_array(fcinfo->args[i].datum, desc->arg_arraytype[i]);
 			else if ((funcid = get_transform_fromsql(argtypes[i], current_call_data->prodesc->lang_oid, current_call_data->prodesc->trftypes)))
-				sv = (SV *) DatumGetPointer(OidFunctionCall1(funcid, fcinfo->arg[i]));
+				sv = (SV *) DatumGetPointer(OidFunctionCall1(funcid, fcinfo->args[i].datum));
 			else
 			{
 				char	   *tmp;
 
 				tmp = OutputFunctionCall(&(desc->arg_out_func[i]),
-										 fcinfo->arg[i]);
+										 fcinfo->args[i].datum);
 				sv = cstr2sv(tmp);
 				pfree(tmp);
 			}
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 5c6dbe4c5fa..156b3132d77 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -491,8 +491,8 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo,
 					PLpgSQL_var *var = (PLpgSQL_var *) estate.datums[n];
 
 					assign_simple_var(&estate, var,
-									  fcinfo->arg[i],
-									  fcinfo->argnull[i],
+									  fcinfo->args[i].datum,
+									  fcinfo->args[i].isnull,
 									  false);
 
 					/*
@@ -543,12 +543,12 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo,
 				{
 					PLpgSQL_rec *rec = (PLpgSQL_rec *) estate.datums[n];
 
-					if (!fcinfo->argnull[i])
+					if (!fcinfo->args[i].isnull)
 					{
 						/* Assign row value from composite datum */
 						exec_move_row_from_datum(&estate,
 												 (PLpgSQL_variable *) rec,
-												 fcinfo->arg[i]);
+												 fcinfo->args[i].datum);
 					}
 					else
 					{
diff --git a/src/pl/plpgsql/src/pl_handler.c b/src/pl/plpgsql/src/pl_handler.c
index 2a9af06cccc..77c118b95a9 100644
--- a/src/pl/plpgsql/src/pl_handler.c
+++ b/src/pl/plpgsql/src/pl_handler.c
@@ -301,7 +301,7 @@ plpgsql_inline_handler(PG_FUNCTION_ARGS)
 {
 	InlineCodeBlock *codeblock = castNode(InlineCodeBlock, DatumGetPointer(PG_GETARG_DATUM(0)));
 	PLpgSQL_function *func;
-	FunctionCallInfoData fake_fcinfo;
+	LOCAL_FCINFO(fake_fcinfo, 0);
 	FmgrInfo	flinfo;
 	EState	   *simple_eval_estate;
 	Datum		retval;
@@ -324,9 +324,9 @@ plpgsql_inline_handler(PG_FUNCTION_ARGS)
 	 * plpgsql_exec_function().  In particular note that this sets things up
 	 * with no arguments passed.
 	 */
-	MemSet(&fake_fcinfo, 0, sizeof(fake_fcinfo));
+	MemSet(fake_fcinfo, 0, SizeForFunctionCallInfo(0));
 	MemSet(&flinfo, 0, sizeof(flinfo));
-	fake_fcinfo.flinfo = &flinfo;
+	fake_fcinfo->flinfo = &flinfo;
 	flinfo.fn_oid = InvalidOid;
 	flinfo.fn_mcxt = CurrentMemoryContext;
 
@@ -336,7 +336,7 @@ plpgsql_inline_handler(PG_FUNCTION_ARGS)
 	/* And run the function */
 	PG_TRY();
 	{
-		retval = plpgsql_exec_function(func, &fake_fcinfo, simple_eval_estate, codeblock->atomic);
+		retval = plpgsql_exec_function(func, fake_fcinfo, simple_eval_estate, codeblock->atomic);
 	}
 	PG_CATCH();
 	{
@@ -466,7 +466,7 @@ plpgsql_validator(PG_FUNCTION_ARGS)
 	/* Postpone body checks if !check_function_bodies */
 	if (check_function_bodies)
 	{
-		FunctionCallInfoData fake_fcinfo;
+		LOCAL_FCINFO(fake_fcinfo, 0);
 		FmgrInfo	flinfo;
 		int			rc;
 		TriggerData trigdata;
@@ -482,26 +482,26 @@ plpgsql_validator(PG_FUNCTION_ARGS)
 		 * Set up a fake fcinfo with just enough info to satisfy
 		 * plpgsql_compile().
 		 */
-		MemSet(&fake_fcinfo, 0, sizeof(fake_fcinfo));
+		MemSet(fake_fcinfo, 0, SizeForFunctionCallInfo(0));
 		MemSet(&flinfo, 0, sizeof(flinfo));
-		fake_fcinfo.flinfo = &flinfo;
+		fake_fcinfo->flinfo = &flinfo;
 		flinfo.fn_oid = funcoid;
 		flinfo.fn_mcxt = CurrentMemoryContext;
 		if (is_dml_trigger)
 		{
 			MemSet(&trigdata, 0, sizeof(trigdata));
 			trigdata.type = T_TriggerData;
-			fake_fcinfo.context = (Node *) &trigdata;
+			fake_fcinfo->context = (Node *) &trigdata;
 		}
 		else if (is_event_trigger)
 		{
 			MemSet(&etrigdata, 0, sizeof(etrigdata));
 			etrigdata.type = T_EventTriggerData;
-			fake_fcinfo.context = (Node *) &etrigdata;
+			fake_fcinfo->context = (Node *) &etrigdata;
 		}
 
 		/* Test-compile the function */
-		plpgsql_compile(&fake_fcinfo, true);
+		plpgsql_compile(fake_fcinfo, true);
 
 		/*
 		 * Disconnect from SPI manager
diff --git a/src/pl/plpython/plpy_exec.c b/src/pl/plpython/plpy_exec.c
index 47ed95dcc60..d1a8c395ffd 100644
--- a/src/pl/plpython/plpy_exec.c
+++ b/src/pl/plpython/plpy_exec.c
@@ -436,10 +436,10 @@ PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure *proc)
 		{
 			PLyDatumToOb *arginfo = &proc->args[i];
 
-			if (fcinfo->argnull[i])
+			if (fcinfo->args[i].isnull)
 				arg = NULL;
 			else
-				arg = PLy_input_convert(arginfo, fcinfo->arg[i]);
+				arg = PLy_input_convert(arginfo, fcinfo->args[i].datum);
 
 			if (arg == NULL)
 			{
diff --git a/src/pl/plpython/plpy_main.c b/src/pl/plpython/plpy_main.c
index 6a66eba1762..2e831ebe523 100644
--- a/src/pl/plpython/plpy_main.c
+++ b/src/pl/plpython/plpy_main.c
@@ -300,7 +300,7 @@ Datum
 plpython_inline_handler(PG_FUNCTION_ARGS)
 {
 	InlineCodeBlock *codeblock = (InlineCodeBlock *) DatumGetPointer(PG_GETARG_DATUM(0));
-	FunctionCallInfoData fake_fcinfo;
+	LOCAL_FCINFO(fake_fcinfo, 0);
 	FmgrInfo	flinfo;
 	PLyProcedure proc;
 	PLyExecutionContext *exec_ctx;
@@ -312,9 +312,9 @@ plpython_inline_handler(PG_FUNCTION_ARGS)
 	if (SPI_connect_ext(codeblock->atomic ? 0 : SPI_OPT_NONATOMIC) != SPI_OK_CONNECT)
 		elog(ERROR, "SPI_connect failed");
 
-	MemSet(&fake_fcinfo, 0, sizeof(fake_fcinfo));
+	MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
 	MemSet(&flinfo, 0, sizeof(flinfo));
-	fake_fcinfo.flinfo = &flinfo;
+	fake_fcinfo->flinfo = &flinfo;
 	flinfo.fn_oid = InvalidOid;
 	flinfo.fn_mcxt = CurrentMemoryContext;
 
@@ -352,7 +352,7 @@ plpython_inline_handler(PG_FUNCTION_ARGS)
 
 		PLy_procedure_compile(&proc, codeblock->source_text);
 		exec_ctx->curr_proc = &proc;
-		PLy_exec_function(&fake_fcinfo, &proc);
+		PLy_exec_function(fake_fcinfo, &proc);
 	}
 	PG_CATCH();
 	{
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index 3b1454f8335..65399366424 100644
--- a/src/pl/tcl/pltcl.c
+++ b/src/pl/tcl/pltcl.c
@@ -597,7 +597,8 @@ call_pltcl_start_proc(Oid prolang, bool pltrusted)
 	Form_pg_proc procStruct;
 	AclResult	aclresult;
 	FmgrInfo	finfo;
-	FunctionCallInfoData fcinfo;
+	LOCAL_FCINFO(fcinfo, 0);
+
 	PgStat_FunctionCallUsage fcusage;
 
 	/* select appropriate GUC */
@@ -658,11 +659,11 @@ call_pltcl_start_proc(Oid prolang, bool pltrusted)
 	 */
 	InvokeFunctionExecuteHook(procOid);
 	fmgr_info(procOid, &finfo);
-	InitFunctionCallInfoData(fcinfo, &finfo,
+	InitFunctionCallInfoData(*fcinfo, &finfo,
 							 0,
 							 InvalidOid, NULL, NULL);
-	pgstat_init_function_usage(&fcinfo, &fcusage);
-	(void) FunctionCallInvoke(&fcinfo);
+	pgstat_init_function_usage(fcinfo, &fcusage);
+	(void) FunctionCallInvoke(fcinfo);
 	pgstat_end_function_usage(&fcusage, true);
 
 	/* Pop the error context stack */
@@ -869,7 +870,7 @@ pltcl_func_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
 				/**************************************************
 				 * For tuple values, add a list for 'array set ...'
 				 **************************************************/
-				if (fcinfo->argnull[i])
+				if (fcinfo->args[i].isnull)
 					Tcl_ListObjAppendElement(NULL, tcl_cmd, Tcl_NewObj());
 				else
 				{
@@ -880,7 +881,7 @@ pltcl_func_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
 					HeapTupleData tmptup;
 					Tcl_Obj    *list_tmp;
 
-					td = DatumGetHeapTupleHeader(fcinfo->arg[i]);
+					td = DatumGetHeapTupleHeader(fcinfo->args[i].datum);
 					/* Extract rowtype info and find a tupdesc */
 					tupType = HeapTupleHeaderGetTypeId(td);
 					tupTypmod = HeapTupleHeaderGetTypMod(td);
@@ -901,14 +902,14 @@ pltcl_func_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
 				 * Single values are added as string element
 				 * of their external representation
 				 **************************************************/
-				if (fcinfo->argnull[i])
+				if (fcinfo->args[i].isnull)
 					Tcl_ListObjAppendElement(NULL, tcl_cmd, Tcl_NewObj());
 				else
 				{
 					char	   *tmp;
 
 					tmp = OutputFunctionCall(&prodesc->arg_out_func[i],
-											 fcinfo->arg[i]);
+											 fcinfo->args[i].datum);
 					UTF_BEGIN;
 					Tcl_ListObjAppendElement(NULL, tcl_cmd,
 											 Tcl_NewStringObj(UTF_E2U(tmp), -1));
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 84640c47a7d..a9a85d8c3b7 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -804,7 +804,7 @@ FuncExpr
 FuncInfo
 Function
 FunctionCallInfo
-FunctionCallInfoData
+FunctionCallInfoBaseData
 FunctionParameter
 FunctionParameterMode
 FunctionScan
-- 
2.18.0.rc2.dirty

#13Andres Freund
andres@anarazel.de
In reply to: Andres Freund (#12)
Re: Variable-length FunctionCallInfoData

Hi,

On 2019-01-25 12:51:02 -0800, Andres Freund wrote:

Updated patch attached. Besides the above changes, there's a fair bit
of comment changes, and I've implemented the necessary JIT changes.

I pushed a further polished version of this.

The buildfarm largely seems to have had no problem with it, but handfish
failed:
https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=handfish&amp;dt=2019-01-26%2022%3A57%3A19
but I have no idea what the error is, nor whether it's related to this
failure, because for reasons I do not understand, the stage message is
effectively empty. Going to wait for another run before investing
resources to debug...

Regards,

Andres

#14Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#13)
Re: Variable-length FunctionCallInfoData

Andres Freund <andres@anarazel.de> writes:

The buildfarm largely seems to have had no problem with it, but handfish
failed:
https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=handfish&amp;dt=2019-01-26%2022%3A57%3A19
but I have no idea what the error is, nor whether it's related to this
failure, because for reasons I do not understand, the stage message is
effectively empty.

Yeah, we've been seeing random failures with empty stage logs on multiple
machines. I suspect something weird with the buildfarm client script,
but no idea what.

regards, tom lane

#15Andrew Dunstan
andrew.dunstan@2ndquadrant.com
In reply to: Tom Lane (#14)
Re: Variable-length FunctionCallInfoData

On 1/26/19 9:46 PM, Tom Lane wrote:

Andres Freund <andres@anarazel.de> writes:

The buildfarm largely seems to have had no problem with it, but handfish
failed:
https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=handfish&amp;dt=2019-01-26%2022%3A57%3A19
but I have no idea what the error is, nor whether it's related to this
failure, because for reasons I do not understand, the stage message is
effectively empty.

Yeah, we've been seeing random failures with empty stage logs on multiple
machines. I suspect something weird with the buildfarm client script,
but no idea what.

It's happening on a handful of animals (see below) , and at that
intermittently. Some of the animals are leading or bleeding edge
distros. Without more info I'm inclined to suspect environmental factors.

cheers

andrew

pgbfprod=> select sysname, branch, snapshot, log_stage from
public.build_status_log where snapshot > now() - interval '90 days' and
length(log_text) = 0 order by 1,3,2;
  sysname   |    branch     |      snapshot       |           
log_stage           
------------+---------------+---------------------+---------------------------------
 caiman     | REL_11_STABLE | 2018-10-30 05:00:06 | configure.log
 caiman     | REL_10_STABLE | 2018-11-05 01:00:18 | check-pg_upgrade.log
 caiman     | REL_11_STABLE | 2018-11-05 01:05:16 | make.log
 caiman     | REL9_4_STABLE | 2018-11-06 02:57:28 | make.log
 caiman     | REL9_5_STABLE | 2018-11-06 03:00:08 | configure.log
 caiman     | REL_10_STABLE | 2018-11-06 03:12:50 | configure.log
 caiman     | HEAD          | 2018-11-06 03:25:58 | make.log
 caiman     | HEAD          | 2018-11-06 21:55:13 | make.log
 caiman     | HEAD          | 2018-11-09 06:15:27 | make.log
 caiman     | HEAD          | 2018-11-14 08:53:13 |
contrib-install-check-C.log
 caiman     | HEAD          | 2018-11-17 19:58:06 | make-install.log
 caiman     | REL9_6_STABLE | 2018-11-26 02:00:23 | make.log
 caiman     | REL_11_STABLE | 2018-11-26 02:12:53 | make.log
 caiman     | HEAD          | 2018-12-05 13:11:32 | configure.log
 caiman     | REL_11_STABLE | 2018-12-16 20:48:41 | make.log
 caiman     | REL9_5_STABLE | 2018-12-17 01:58:40 | check.log
 caiman     | REL9_6_STABLE | 2018-12-17 02:00:07 | make.log
 caiman     | REL_11_STABLE | 2018-12-17 02:10:13 | make.log
 caiman     | REL_10_STABLE | 2018-12-18 18:00:14 | configure.log
 caiman     | HEAD          | 2018-12-20 22:00:31 | make-contrib.log
 caiman     | REL_11_STABLE | 2018-12-24 11:54:44 | lastcomand.log
 caiman     | HEAD          | 2018-12-24 12:00:13 | configure.log
 caiman     | HEAD          | 2019-01-02 19:00:21 | configure.log
 caiman     | REL_11_STABLE | 2019-01-20 00:54:29 | make.log
 caiman     | REL_11_STABLE | 2019-01-22 06:00:25 | configure.log
 caiman     | REL9_6_STABLE | 2019-01-26 03:50:26 | make.log
 caiman     | HEAD          | 2019-01-26 04:14:53 | make.log
 eelpout    | HEAD          | 2018-11-26 00:35:13 | make.log
 eelpout    | HEAD          | 2018-11-26 00:35:13 | configure.log
 eelpout    | HEAD          | 2018-11-26 00:35:13 | make-contrib.log
 handfish   | REL_11_STABLE | 2018-10-31 00:46:56 | make.log
 handfish   | HEAD          | 2018-10-31 01:00:33 | configure.log
 handfish   | REL_11_STABLE | 2018-11-02 22:25:25 | configure.log
 handfish   | REL_11_STABLE | 2018-11-05 01:05:18 | make.log
 handfish   | REL_11_STABLE | 2018-11-06 03:58:58 | make.log
 handfish   | HEAD          | 2018-11-06 21:51:39 | make.log
 handfish   | REL_11_STABLE | 2018-11-07 01:03:16 | make.log
 handfish   | REL9_5_STABLE | 2018-11-09 05:59:24 | make.log
 handfish   | REL_11_STABLE | 2018-11-09 06:24:31 | make.log
 handfish   | REL_11_STABLE | 2018-11-12 01:00:15 | make.log
 handfish   | HEAD          | 2018-11-26 02:13:04 | make.log
 handfish   | REL_11_STABLE | 2018-12-05 13:11:12 | configure.log
 handfish   | REL_11_STABLE | 2018-12-16 20:45:08 | make.log
 handfish   | REL9_4_STABLE | 2018-12-17 01:55:31 | initdb-C.log
 handfish   | REL_10_STABLE | 2018-12-17 02:09:59 | make.log
 handfish   | HEAD          | 2018-12-18 18:25:55 | make.log
 handfish   | HEAD          | 2018-12-24 12:04:56 | make.log
 handfish   | REL9_6_STABLE | 2019-01-01 16:57:14 | ecpg-check.log
 handfish   | REL_10_STABLE | 2019-01-01 17:05:17 | configure.log
 handfish   | HEAD          | 2019-01-26 05:11:49 | make.log
 handfish   | HEAD          | 2019-01-26 22:57:19 |
testmodules-install-check-C.log
 queensnake | REL9_6_STABLE | 2018-11-26 01:57:27 | check.log
 queensnake | REL_10_STABLE | 2018-11-26 02:00:14 | configure.log
 queensnake | REL_11_STABLE | 2018-11-30 04:57:15 | check-pg_upgrade.log
 queensnake | REL_10_STABLE | 2018-12-05 12:57:28 | make-install.log
 queensnake | REL_11_STABLE | 2018-12-16 20:44:19 | make-contrib.log
 queensnake | REL9_5_STABLE | 2018-12-17 01:57:36 | check-pg_upgrade.log
 queensnake | HEAD          | 2018-12-20 22:09:20 | configure.log
 queensnake | HEAD          | 2019-01-20 00:23:49 | make-contrib.log
 queensnake | HEAD          | 2019-01-26 04:39:01 | make.log
 serinus    | HEAD          | 2018-11-10 23:28:34 | make.log
(61 rows)

--
Andrew Dunstan https://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#16Greg Stark
stark@mit.edu
In reply to: Andres Freund (#1)
Re: Variable-length FunctionCallInfoData

I assume you already considered and rejected having a fixed size null
bitmap followed by a variable size array of datums. That seems like it
would be denser and work better with cpu cache.

I guess the reason you prefer the struct is because it can be used
elsewhere on its own?

#17Andres Freund
andres@anarazel.de
In reply to: Greg Stark (#16)
Re: Variable-length FunctionCallInfoData

Hi,

On 2019-01-27 08:03:17 +0100, Greg Stark wrote:

I assume you already considered and rejected having a fixed size null
bitmap followed by a variable size array of datums. That seems like it
would be denser and work better with cpu cache.

It'd be more expensive to access individually (offset calculation +
masks, ~5 insn, not fully pipelineable), it'd not guarantee that the
null bit and datum are on the same cacheline, you could not pass the
null-bit to various functions accepting a bool*, you could not pass the
new equivalent NullableDatums to other functions (like both the past and
current solution allow).

Greetings,

Andres Freund

#18Andrew Dunstan
andrew.dunstan@2ndquadrant.com
In reply to: Andrew Dunstan (#15)
Re: Variable-length FunctionCallInfoData

On 1/26/19 11:20 PM, Andrew Dunstan wrote:

On 1/26/19 9:46 PM, Tom Lane wrote:

Andres Freund <andres@anarazel.de> writes:

The buildfarm largely seems to have had no problem with it, but handfish
failed:
https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=handfish&amp;dt=2019-01-26%2022%3A57%3A19
but I have no idea what the error is, nor whether it's related to this
failure, because for reasons I do not understand, the stage message is
effectively empty.

Yeah, we've been seeing random failures with empty stage logs on multiple
machines. I suspect something weird with the buildfarm client script,
but no idea what.

It's happening on a handful of animals (see below) , and at that
intermittently. Some of the animals are leading or bleeding edge
distros. Without more info I'm inclined to suspect environmental factors.

I think I have discovered the issue - small logic bug on my part :-(
Luckily it won't affect most users, especially if they are using
run_branches.pl.

Fixed in
<https://github.com/PGBuildFarm/client-code/commit/d4dd30e3b43981b51de8f5b572f131c759737813&gt;
There will be a new release this week which will contain the fix.

cheers

andrew

--
Andrew Dunstan https://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services