diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 9c3f451..dfd5d8c 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -1400,3 +1400,120 @@ TypeGetTupleDesc(Oid typeoid, List *colaliases)
 
 	return tupdesc;
 }
+
+/*
+ * Extract a set of variadic argument values, types and NULL markers. This is
+ * useful for abstracting away whether or not the call has been done
+ * with an explicit VARIADIC array or a normal set of arguments, for a
+ * VARIADIC "any" function. When doing a VARIADIC call, the caller has
+ * provided one argument made of an array of keys, so deconstruct the
+ * array data before using it for the next processing.
+ * If no explicit VARIADIC call is used, just fill in the data based on
+ * all the arguments given by the caller.
+ * This function returns the number of arguments generated. In the event
+ * where the caller provided a NULL array input, return -1.
+ * nfixed is the number of non-variadic arguments to the function - these
+ * will be ignored by this function.
+ * If required by the caller, values of type "unknown" will be converted
+ * to text datums.
+ */
+int
+extract_variadic_args(FunctionCallInfo fcinfo, Datum **args,
+					  Oid **types, bool **nulls, int nfixed,
+					  bool convert_unknown_to_text)
+{
+	bool		variadic = get_fn_expr_variadic(fcinfo->flinfo);
+	Datum	   *args_res;
+	bool	   *nulls_res;
+	Oid		   *types_res;
+	int			nargs, i;
+
+	*args = NULL;
+	*types = NULL;
+	*nulls = NULL;
+
+	if (variadic)
+	{
+		ArrayType  *array_in;
+		Oid			element_type;
+		bool		typbyval;
+		char		typalign;
+		int16		typlen;
+
+		Assert(PG_NARGS() == nfixed + 1);
+
+		if (PG_ARGISNULL(nfixed))
+			return -1;
+
+		array_in = PG_GETARG_ARRAYTYPE_P(nfixed);
+		element_type = ARR_ELEMTYPE(array_in);
+
+		get_typlenbyvalalign(element_type,
+							 &typlen, &typbyval, &typalign);
+		deconstruct_array(array_in, element_type, typlen, typbyval,
+						  typalign, &args_res, &nulls_res,
+						  &nargs);
+
+		/* All the elements of the array have the same type */
+		types_res = (Oid *) palloc0(nargs * sizeof(Oid));
+		for (i = 0; i < nargs; i++)
+			types_res[i] = element_type;
+	}
+	else
+	{
+		nargs = PG_NARGS() - nfixed;
+		Assert (nargs > 0);
+		nulls_res = (bool *) palloc0(nargs * sizeof(bool));
+		args_res = (Datum *) palloc0(nargs * sizeof(Datum));
+		types_res = (Oid *) palloc0(nargs * sizeof(Oid));
+
+		for (i = 0; i < nargs; i++)
+		{
+			nulls_res[i] = PG_ARGISNULL(i + nfixed);
+			types_res[i] = get_fn_expr_argtype(fcinfo->flinfo, i + nfixed);
+
+			if (!OidIsValid(types_res[i]))
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						 errmsg("could not determine data type for argument %d",
+								i + nfixed + 1)));
+
+			/*
+			 * Turn a constant (more or less literal) value that's of unknown
+			 * type into text if required . Unknowns come in as a cstring
+			 * pointer.
+			 */
+			if (convert_unknown_to_text && types_res[i] == UNKNOWNOID &&
+				get_fn_expr_arg_stable(fcinfo->flinfo, i + nfixed))
+			{
+				types_res[i] = TEXTOID;
+
+				/* important for value, key cannot being NULL */
+				if (PG_ARGISNULL(i + nfixed))
+					args_res[i] = (Datum) 0;
+				else
+					args_res[i] =
+						CStringGetTextDatum(PG_GETARG_POINTER(i + nfixed));
+			}
+			else
+			{
+				/* no conversion needed, just take the datum as given */
+				args_res[i] = PG_GETARG_DATUM(i + nfixed);
+			}
+
+			if (!OidIsValid(types_res[i]) ||
+				(convert_unknown_to_text && (types_res[i] == UNKNOWNOID)))
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						 errmsg("could not determine data type for argument %d",
+								i + nfixed + 1)));
+		}
+	}
+
+	/* Fill in results */
+	*args = args_res;
+	*nulls = nulls_res;
+	*types = types_res;
+
+	return nargs;
+}
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 951af2a..0550c07 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -281,6 +281,9 @@ extern TupleTableSlot *TupleDescGetSlot(TupleDesc tupdesc);
 extern FuncCallContext *init_MultiFuncCall(PG_FUNCTION_ARGS);
 extern FuncCallContext *per_MultiFuncCall(PG_FUNCTION_ARGS);
 extern void end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx);
+extern int extract_variadic_args(FunctionCallInfo fcinfo, Datum **args,
+					  Oid **types, bool **nulls, int nfixed,
+								 bool convert_unknown_to_text);
 
 #define SRF_IS_FIRSTCALL() (fcinfo->flinfo->fn_extra == NULL)
 
