diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
new file mode 100644
index 0000000000000000000000000000000000000000..bd65c3911c756cb5200252f334d88fd75d3948db
--- /dev/null
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -0,0 +1,122 @@
+/*-------------------------------------------------------------------------
+ *
+ * funcapi.c
+ *	  Utility and convenience functions for fmgr functions that return
+ *	  sets and/or composite types.
+ *
+ * Copyright (c) 2002, PostgreSQL Global Development Group
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "funcapi.h"
+#include "catalog/pg_type.h"
+#include "utils/syscache.h"
+
+/*
+ * init_MultiFuncCall
+ * Create an empty FuncCallContext data structure
+ * and do some other basic Multi-function call setup
+ * and error checking
+ */
+FuncCallContext *
+init_MultiFuncCall(PG_FUNCTION_ARGS)
+{
+	FuncCallContext *retval;
+
+	/*
+	 * Bail if we're called in the wrong context
+	 */
+	if (fcinfo->resultinfo == NULL || !IsA(fcinfo->resultinfo, ReturnSetInfo))
+		elog(ERROR, "function called in context that does not accept a set result");
+
+	if (fcinfo->flinfo->fn_extra == NULL)
+	{
+		/*
+		 * First call
+		 */
+		MemoryContext oldcontext;
+
+		/* switch to the appropriate memory context */
+		oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
+
+		/*
+		 * allocate space and zero it
+		 */
+		retval = (FuncCallContext *) palloc(sizeof(FuncCallContext));
+		MemSet(retval, 0, sizeof(FuncCallContext));
+
+		/*
+		 * initialize the elements
+		 */
+		retval->call_cntr = 0;
+		retval->max_calls = 0;
+		retval->slot = NULL;
+		retval->fctx = NULL;
+		retval->attinmeta = NULL;
+		retval->fmctx = fcinfo->flinfo->fn_mcxt;
+
+		/*
+		 * save the pointer for cross-call use
+		 */
+		fcinfo->flinfo->fn_extra = retval;
+
+		/* back to the original memory context */
+		MemoryContextSwitchTo(oldcontext);
+	}
+	else	/* second and subsequent calls */
+	{
+		elog(ERROR, "init_MultiFuncCall may not be called more than once");
+
+		/* never reached, but keep compiler happy */
+		retval = NULL;
+	}
+
+	return retval;
+}
+
+/*
+ * end_MultiFuncCall
+ * Clean up after init_MultiFuncCall
+ */
+void
+end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx)
+{
+	MemoryContext oldcontext;
+
+	/* unbind from fcinfo */
+	fcinfo->flinfo->fn_extra = NULL;
+
+	/*
+	 * Caller is responsible to free up memory for individual
+	 * struct elements other than att_in_funcinfo and elements.
+	 */
+	oldcontext = MemoryContextSwitchTo(funcctx->fmctx);
+
+	if (funcctx->attinmeta != NULL)
+		pfree(funcctx->attinmeta);
+
+	pfree(funcctx);
+
+	MemoryContextSwitchTo(oldcontext);
+}
+
+void
+get_type_metadata(Oid typeid, Oid *attinfuncid, Oid *attelem)
+{
+	HeapTuple		typeTuple;
+	Form_pg_type	typtup;
+
+	typeTuple = SearchSysCache(TYPEOID,
+							   ObjectIdGetDatum(typeid),
+							   0, 0, 0);
+	if (!HeapTupleIsValid(typeTuple))
+		elog(ERROR, "get_type_metadata: Cache lookup of type %u failed", typeid);
+
+	typtup = (Form_pg_type) GETSTRUCT(typeTuple);
+
+	*attinfuncid = typtup->typinput;
+	*attelem = typtup->typelem;
+
+	ReleaseSysCache(typeTuple);
+}