/*-------------------------------------------------------------------------
 *
 * sets.c
 *	  Functions for sets, which are defined by queries.
 *	  Example:	 a set is defined as being the result of the query
 *			retrieve (X.all)
 *
 * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  $Header: /home/postgres/cvs_root/pgsql-r-v7.2/src/backend/utils/adt/sets.c,v 1.1.1.1 2002/02/07 17:53:32 bpalmer Exp $
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "access/heapam.h"
#include "catalog/catname.h"
#include "catalog/indexing.h"
#include "catalog/pg_proc.h"
#include "executor/executor.h"
#include "utils/fcache.h"
#include "utils/sets.h"
#include "utils/syscache.h"


/*
 *	  SetDefine		   - converts query string defining set to an oid
 *
 *	  We create an SQL function having the given querystring as its body.
 *	  The name of the function is then changed to use the OID of its tuple
 *	  in pg_proc.
 */
Oid
SetDefine(char *querystr, char *typename)
{
	Oid			setoid;
	char	   *procname = GENERICSETNAME;
	char	   *fileName = "-";
	char		realprocname[NAMEDATALEN];
	HeapTuple	tup,
				newtup = NULL;
	Form_pg_proc proc;
	Relation	procrel;
	int			i;
	Datum		replValue[Natts_pg_proc];
	char		replNull[Natts_pg_proc];
	char		repl[Natts_pg_proc];

	setoid = ProcedureCreate(procname,	/* changed below, after oid known */
							 false,		/* don't replace */
							 true,		/* returnsSet */
							 typename,	/* returnTypeName */
							 "sql",		/* languageName */
							 querystr,	/* sourceCode */
							 fileName,	/* fileName */
							 true,		/* trusted */
							 false,		/* canCache (assume unsafe) */
							 false,		/* isStrict (irrelevant, no args) */
							 100,		/* byte_pct */
							 0, /* perbyte_cpu */
							 0, /* percall_cpu */
							 100,		/* outin_ratio */
							 NIL);		/* argList */

	/*
	 * Since we're still inside this command of the transaction, we can't
	 * see the results of the procedure definition unless we pretend we've
	 * started the next command.  (Postgres's solution to the Halloween
	 * problem is to not allow you to see the results of your command
	 * until you start the next command.)
	 */
	CommandCounterIncrement();

	procrel = heap_openr(ProcedureRelationName, RowExclusiveLock);

	tup = SearchSysCache(PROCOID,
						 ObjectIdGetDatum(setoid),
						 0, 0, 0);
	if (!HeapTupleIsValid(tup))
		elog(ERROR, "SetDefine: unable to define set %s", querystr);

	/*
	 * We can tell whether the set was already defined by checking the
	 * name.   If it's GENERICSETNAME, the set is new.  If it's "set<some
	 * oid>" it's already defined.
	 */
	proc = (Form_pg_proc) GETSTRUCT(tup);
	if (strcmp(procname, NameStr(proc->proname)) == 0)
	{
		/* make the real proc name */
		sprintf(realprocname, "set%u", setoid);

		/* set up the attributes to be modified or kept the same */
		repl[0] = 'r';
		for (i = 1; i < Natts_pg_proc; i++)
			repl[i] = ' ';
		replValue[0] = (Datum) realprocname;
		for (i = 1; i < Natts_pg_proc; i++)
			replValue[i] = (Datum) 0;
		for (i = 0; i < Natts_pg_proc; i++)
			replNull[i] = ' ';

		/* change the pg_proc tuple */
		newtup = heap_modifytuple(tup,
								  procrel,
								  replValue,
								  replNull,
								  repl);

		simple_heap_update(procrel, &newtup->t_self, newtup);

		setoid = newtup->t_data->t_oid;

		if (RelationGetForm(procrel)->relhasindex)
		{
			Relation	idescs[Num_pg_proc_indices];

			CatalogOpenIndices(Num_pg_proc_indices, Name_pg_proc_indices, idescs);
			CatalogIndexInsert(idescs, Num_pg_proc_indices, procrel, newtup);
			CatalogCloseIndices(Num_pg_proc_indices, idescs);
		}
		heap_freetuple(newtup);
	}

	ReleaseSysCache(tup);

	heap_close(procrel, RowExclusiveLock);

	return setoid;
}

/*
 * This function executes set evaluation.  The parser sets up a set reference
 * as a call to this function with the OID of the set to evaluate as argument.
 *
 * We build a new fcache for execution of the set's function and run the
 * function until it says "no mas".  The fn_extra field of the call's
 * FmgrInfo record is a handy place to hold onto the fcache.  (Since this
 * is a built-in function, there is no competing use of fn_extra.)
 */
Datum
seteval(PG_FUNCTION_ARGS)
{
	Oid			funcoid = PG_GETARG_OID(0);
	FunctionCachePtr fcache;
	Datum		result;
	bool		isNull;
	ExprDoneCond isDone;

	/*
	 * If this is the first call, we need to set up the fcache for the
	 * target set's function.
	 */
	fcache = (FunctionCachePtr) fcinfo->flinfo->fn_extra;
	if (fcache == NULL)
	{
		fcache = init_fcache(funcoid, 0, fcinfo->flinfo->fn_mcxt);
		fcinfo->flinfo->fn_extra = (void *) fcache;
	}

	/*
	 * Evaluate the function.  NOTE: we need no econtext because there are
	 * no arguments to evaluate.
	 */

	/* ExecMakeFunctionResult assumes these are initialized at call: */
	isNull = false;
	isDone = ExprSingleResult;

	result = ExecMakeFunctionResult(fcache,
									NIL,
									NULL,		/* no econtext, see above */
									&isNull,
									&isDone);

	/*
	 * If we're done with the results of this set function, get rid of its
	 * func cache so that we will start from the top next time. (Can you
	 * say "memory leak"?  This feature is a crock anyway...)
	 */
	if (isDone != ExprMultipleResult)
	{
		pfree(fcache);
		fcinfo->flinfo->fn_extra = NULL;
	}

	/*
	 * Return isNull/isDone status.
	 */
	fcinfo->isnull = isNull;

	if (isDone != ExprSingleResult)
	{
		ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;

		if (rsi && IsA(rsi, ReturnSetInfo))
			rsi->isDone = isDone;
		else
			elog(ERROR, "Set-valued function called in context that cannot accept a set");
	}

	PG_RETURN_DATUM(result);
}
