postgresql/src/backend/utils/adt/sets.c

214 lines
5.8 KiB
C

/*-------------------------------------------------------------------------
*
* 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-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/sets.c,v 1.33 2000/08/24 03:29:06 tgl 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"
extern CommandDest whereToSendOutput; /* defined in tcop/postgres.c */
/*
* 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 */
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 */
whereToSendOutput);
/*
* 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();
tup = SearchSysCacheTuple(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 */
procrel = heap_openr(ProcedureRelationName, RowExclusiveLock);
tup = SearchSysCacheTuple(PROCOID,
ObjectIdGetDatum(setoid),
0, 0, 0);
if (HeapTupleIsValid(tup))
{
newtup = heap_modifytuple(tup,
procrel,
replValue,
replNull,
repl);
heap_update(procrel, &tup->t_self, newtup, NULL);
setoid = newtup->t_data->t_oid;
}
else
elog(ERROR, "SetDefine: could not find new set oid tuple");
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_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);
}