/*-------------------------------------------------------------------------
 *
 * function_utils.c
 *	  Utilities regarding calls to PG functions
 *
 * Copyright (c) Citus Data, Inc.
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "catalog/namespace.h"
#include "executor/executor.h"
#include "utils/builtins.h"
#include "utils/regproc.h"

#include "distributed/function_utils.h"
#include "distributed/version_compat.h"


/*
 * FunctionOid searches for a function that has the given name and the given
 * number of arguments, and returns the corresponding function's oid. The
 * function reports error if the target function is not found, or it found more
 * matching instances.
 */
Oid
FunctionOid(const char *schemaName, const char *functionName, int argumentCount)
{
	return FunctionOidExtended(schemaName, functionName, argumentCount, false);
}


/*
 * FunctionOidExtended searches for a given function identified by schema,
 * functionName, and argumentCount. It reports error if the function is not
 * found or there are more than one match. If the missingOK parameter is set
 * and there are no matches, then the function returns InvalidOid.
 */
Oid
FunctionOidExtended(const char *schemaName, const char *functionName, int argumentCount,
					bool missingOK)
{
	char *qualifiedFunctionName = quote_qualified_identifier(schemaName, functionName);
	List *qualifiedFunctionNameList = stringToQualifiedNameList_compat(
		qualifiedFunctionName);
	List *argumentList = NIL;
	const bool findVariadics = false;
	const bool findDefaults = false;

	FuncCandidateList functionList = FuncnameGetCandidates(
		qualifiedFunctionNameList,
		argumentCount,
		argumentList,
		findVariadics,
		findDefaults,
		false,
		true);

	if (functionList == NULL)
	{
		if (missingOK)
		{
			return InvalidOid;
		}

		ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION),
						errmsg("function \"%s\" does not exist", functionName)));
	}
	else if (functionList->next != NULL)
	{
		ereport(ERROR, (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
						errmsg("more than one function named \"%s\"", functionName)));
	}

	/* get function oid from function list's head */
	Oid functionOid = functionList->oid;

	return functionOid;
}


/*
 * FunctionCallGetTupleStore1 calls the given set-returning PGFunction with the given
 * argument and returns the ResultSetInfo filled by the called function.
 */
ReturnSetInfo *
FunctionCallGetTupleStore1(PGFunction function, Oid functionId, Datum argument)
{
	LOCAL_FCINFO(fcinfo, 1);
	FmgrInfo flinfo;
	ReturnSetInfo *rsinfo = makeNode(ReturnSetInfo);
	EState *estate = CreateExecutorState();
	rsinfo->econtext = GetPerTupleExprContext(estate);
	rsinfo->allowedModes = SFRM_Materialize;

	fmgr_info(functionId, &flinfo);
	InitFunctionCallInfoData(*fcinfo, &flinfo, 1, InvalidOid, NULL, (Node *) rsinfo);

	fcSetArg(fcinfo, 0, argument);

	(*function)(fcinfo);

	return rsinfo;
}
