maintained for each cache entry. A cache entry will not be freed until the matching ReleaseSysCache call has been executed. This eliminates worries about cache entries getting dropped while still in use. See my posting to pg-hackers of even date for more info.
210 lines
5.7 KiB
C
210 lines
5.7 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.34 2000/11/16 22:30:31 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();
|
|
|
|
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);
|
|
|
|
heap_update(procrel, &newtup->t_self, newtup, NULL);
|
|
|
|
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);
|
|
}
|