/*------------------------------------------------------------------------- * * 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-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/sets.c,v 1.53 2002/09/02 01:05:06 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" #include "access/heapam.h" #include "catalog/catname.h" #include "catalog/indexing.h" #include "catalog/pg_language.h" #include "catalog/pg_namespace.h" #include "catalog/pg_proc.h" #include "executor/executor.h" #include "utils/fcache.h" #include "utils/fmgroids.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, Oid elemType) { 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 */ PG_CATALOG_NAMESPACE, /* XXX wrong */ false, /* don't replace */ true, /* returnsSet */ elemType, /* returnType */ SQLlanguageId, /* language */ F_FMGR_SQL_VALIDATOR, querystr, /* prosrc */ fileName, /* probin */ false, /* not aggregate */ false, /* security invoker */ false, /* isStrict (irrelevant, no args) */ PROVOLATILE_VOLATILE, /* assume unsafe */ 0, /* parameterCount */ NULL); /* parameterTypes */ /* * 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" it's already defined. */ proc = (Form_pg_proc) GETSTRUCT(tup); if (strcmp(procname, NameStr(proc->proname)) == 0) { /* make the real proc name */ snprintf(realprocname, sizeof(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 = HeapTupleGetOid(newtup); CatalogUpdateIndexes(procrel, newtup); 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); }