diff options
Diffstat (limited to 'src/backend/utils')
-rw-r--r-- | src/backend/utils/adt/sets.c | 96 | ||||
-rw-r--r-- | src/backend/utils/cache/fcache.c | 254 | ||||
-rw-r--r-- | src/backend/utils/fmgr/fmgr.c | 22 |
3 files changed, 108 insertions, 264 deletions
diff --git a/src/backend/utils/adt/sets.c b/src/backend/utils/adt/sets.c index a0c0aa8cb14..9a5f05134cd 100644 --- a/src/backend/utils/adt/sets.c +++ b/src/backend/utils/adt/sets.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/sets.c,v 1.32 2000/06/09 01:11:09 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/sets.c,v 1.33 2000/08/24 03:29:06 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -21,6 +21,8 @@ #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" @@ -30,9 +32,9 @@ extern CommandDest whereToSendOutput; /* defined in tcop/postgres.c */ /* * SetDefine - converts query string defining set to an oid * - * The query string is used to store the set as a function in - * pg_proc. The name of the function is then changed to use the - * OID of its tuple in pg_proc. + * 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) @@ -57,11 +59,11 @@ SetDefine(char *querystr, char *typename) querystr, /* sourceCode */ fileName, /* fileName */ true, /* trusted */ - false, /* canCache XXX appropriate? */ - false, /* isStrict XXX appropriate? */ + false, /* canCache (assume unsafe) */ + false, /* isStrict (irrelevant, no args) */ 100, /* byte_pct */ - 0, /* perbyte_cpu */ - 0, /* percall_cpu */ + 0, /* perbyte_cpu */ + 0, /* percall_cpu */ 100, /* outin_ratio */ NIL, /* argList */ whereToSendOutput); @@ -74,11 +76,12 @@ SetDefine(char *querystr, char *typename) * until you start the next command.) */ CommandCounterIncrement(); + tup = SearchSysCacheTuple(PROCOID, ObjectIdGetDatum(setoid), 0, 0, 0); if (!HeapTupleIsValid(tup)) - elog(ERROR, "setin: unable to define set %s", querystr); + elog(ERROR, "SetDefine: unable to define set %s", querystr); /* * We can tell whether the set was already defined by checking the @@ -86,7 +89,7 @@ SetDefine(char *querystr, char *typename) * oid>" it's already defined. */ proc = (Form_pg_proc) GETSTRUCT(tup); - if (!strcmp((char *) procname, (char *) &(proc->proname))) + if (strcmp(procname, NameStr(proc->proname)) == 0) { /* make the real proc name */ sprintf(realprocname, "set%u", setoid); @@ -120,7 +123,7 @@ SetDefine(char *querystr, char *typename) setoid = newtup->t_data->t_oid; } else - elog(ERROR, "setin: could not find new set oid tuple"); + elog(ERROR, "SetDefine: could not find new set oid tuple"); if (RelationGetForm(procrel)->relhasindex) { @@ -132,20 +135,79 @@ SetDefine(char *querystr, char *typename) } heap_close(procrel, RowExclusiveLock); } + return setoid; } -/* This function is a placeholder. The parser uses the OID of this - * function to fill in the :funcid field of a set. This routine is - * never executed. At runtime, the OID of the actual set is substituted - * into the :funcid. +/* + * 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; - elog(ERROR, "seteval called for OID %u", funcoid); + 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_INT32(0); /* keep compiler happy */ + PG_RETURN_DATUM(result); } diff --git a/src/backend/utils/cache/fcache.c b/src/backend/utils/cache/fcache.c index 080f70b6ce8..9186f34d104 100644 --- a/src/backend/utils/cache/fcache.c +++ b/src/backend/utils/cache/fcache.c @@ -1,267 +1,61 @@ /*------------------------------------------------------------------------- * * fcache.c - * Code for the 'function cache' used in Oper and Func nodes.... + * Code for the 'function cache' used in Oper and Func nodes. + * * * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/cache/Attic/fcache.c,v 1.36 2000/08/11 18:35:50 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/cache/Attic/fcache.c,v 1.37 2000/08/24 03:29:07 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" -#include "access/heapam.h" -#include "catalog/pg_language.h" -#include "catalog/pg_proc.h" -#include "catalog/pg_type.h" -#include "parser/parsetree.h" -#include "utils/builtins.h" -#include "utils/fcache2.h" -#include "utils/syscache.h" - -static Oid GetDynamicFuncArgType(Var *arg, ExprContext *econtext); -static FunctionCachePtr init_fcache(Oid foid, - List *argList, - ExprContext *econtext); - -#define FuncArgTypeIsDynamic(arg) \ - (IsA(arg,Var) && ((Var*)arg)->varattno == InvalidAttrNumber) - -static Oid -GetDynamicFuncArgType(Var *arg, ExprContext *econtext) -{ - char *relname; - int rtid; - HeapTuple tup; - - Assert(IsA(arg, Var)); +#include "utils/fcache.h" - rtid = ((Var *) arg)->varno; - relname = (char *) getrelname(rtid, econtext->ecxt_range_table); - - tup = SearchSysCacheTuple(TYPENAME, - PointerGetDatum(relname), - 0, 0, 0); - if (!tup) - elog(ERROR, "Lookup failed on type tuple for class %s", - relname); - - return tup->t_data->t_oid; -} /*----------------------------------------------------------------- * - * Initialize a 'FunctionCache' struct given the PG_PROC oid. + * Build a 'FunctionCache' struct given the PG_PROC oid. * *----------------------------------------------------------------- */ -static FunctionCachePtr -init_fcache(Oid foid, - List *argList, - ExprContext *econtext) +FunctionCachePtr +init_fcache(Oid foid, int nargs, MemoryContext fcacheCxt) { - HeapTuple procedureTuple; - HeapTuple typeTuple; - Form_pg_proc procedureStruct; - Form_pg_type typeStruct; + MemoryContext oldcontext; FunctionCachePtr retval; - int nargs; - Datum tmp; - bool isNull; + + /* Switch to a context long-lived enough for the fcache entry */ + oldcontext = MemoryContextSwitchTo(fcacheCxt); retval = (FunctionCachePtr) palloc(sizeof(FunctionCache)); MemSet(retval, 0, sizeof(FunctionCache)); - retval->fcacheCxt = CurrentMemoryContext; - - /* ---------------- - * get the procedure tuple corresponding to the given functionOid - * - * NB: use SearchSysCacheTupleCopy to ensure tuple lives long enough - * ---------------- - */ - procedureTuple = SearchSysCacheTupleCopy(PROCOID, - ObjectIdGetDatum(foid), - 0, 0, 0); - if (!HeapTupleIsValid(procedureTuple)) - elog(ERROR, "init_fcache: Cache lookup failed for procedure %u", - foid); + /* Set up the primary fmgr lookup information */ + fmgr_info(foid, &(retval->func)); - procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple); + /* Initialize unvarying fields of per-call info block */ + retval->fcinfo.flinfo = &(retval->func); + retval->fcinfo.nargs = nargs; - /* ---------------- - * get the return type from the procedure tuple - * ---------------- - */ - typeTuple = SearchSysCacheTuple(TYPEOID, - ObjectIdGetDatum(procedureStruct->prorettype), - 0, 0, 0); + if (nargs > FUNC_MAX_ARGS) + elog(ERROR, "init_fcache: too many arguments"); - if (!HeapTupleIsValid(typeTuple)) - elog(ERROR, "init_fcache: Cache lookup failed for type %u", - procedureStruct->prorettype); - - typeStruct = (Form_pg_type) GETSTRUCT(typeTuple); - - /* ---------------- - * get the type length and by-value flag from the type tuple - * ---------------- - */ - retval->typlen = typeStruct->typlen; - if (typeStruct->typrelid == InvalidOid) + /* If function returns set, prepare a resultinfo node for communication */ + if (retval->func.fn_retset) { - /* The return type is not a relation, so just use byval */ - retval->typbyval = typeStruct->typbyval; - retval->returnsTuple = false; + retval->fcinfo.resultinfo = (Node *) &(retval->rsinfo); + retval->rsinfo.type = T_ReturnSetInfo; } - else - { - /* - * This is a hack. We assume here that any function returning a - * tuple returns it by reference. This needs to be fixed, since - * actually the mechanism isn't quite like return-by-reference. - */ - retval->typbyval = false; - retval->returnsTuple = true; - } - retval->foid = foid; - retval->language = procedureStruct->prolang; - retval->returnsSet = procedureStruct->proretset; + retval->argsValid = false; retval->hasSetArg = false; - retval->func_state = (char *) NULL; - retval->setArg = (Datum) 0; - - /* - * If we are returning exactly one result then we have to copy tuples - * and by reference results because we have to end the execution - * before we return the results. When you do this everything - * allocated by the executor (i.e. slots and tuples) is freed. - */ - if ((retval->language == SQLlanguageId) && - !retval->returnsSet && - !retval->typbyval) - { - TupleTableSlot *slot; - - slot = makeNode(TupleTableSlot); - slot->ttc_shouldFree = true; - slot->ttc_descIsNew = true; - slot->ttc_tupleDescriptor = (TupleDesc) NULL; - slot->ttc_buffer = InvalidBuffer; - slot->ttc_whichplan = -1; - - retval->funcSlot = (Pointer) slot; - } - else - retval->funcSlot = (Pointer) NULL; - - nargs = procedureStruct->pronargs; - retval->nargs = nargs; - - if (nargs > 0) - { - Oid *argTypes; - - if (retval->language == SQLlanguageId) - { - int i; - List *oneArg; - - retval->argOidVect = (Oid *) palloc(retval->nargs * sizeof(Oid)); - argTypes = procedureStruct->proargtypes; - memmove(retval->argOidVect, - argTypes, - (retval->nargs) * sizeof(Oid)); - for (i = 0; - argList; - i++, argList = lnext(argList)) - { - oneArg = lfirst(argList); - if (FuncArgTypeIsDynamic(oneArg)) - retval->argOidVect[i] = GetDynamicFuncArgType((Var *) oneArg, - econtext); - } - } - else - retval->argOidVect = (Oid *) NULL; - } - else - { - retval->argOidVect = (Oid *) NULL; - } - - if (procedureStruct->prolang == SQLlanguageId) - { - tmp = SysCacheGetAttr(PROCOID, - procedureTuple, - Anum_pg_proc_prosrc, - &isNull); - if (isNull) - elog(ERROR, "init_fcache: null prosrc for procedure %u", - foid); - retval->src = DatumGetCString(DirectFunctionCall1(textout, tmp)); - retval->bin = (char *) NULL; - } - else - { - retval->src = (char *) NULL; - if (procedureStruct->proistrusted) - retval->bin = (char *) NULL; - else - { - tmp = SysCacheGetAttr(PROCOID, - procedureTuple, - Anum_pg_proc_probin, - &isNull); - if (isNull) - elog(ERROR, "init_fcache: null probin for procedure %u", - foid); - retval->bin = DatumGetCString(DirectFunctionCall1(textout, tmp)); - } - } - - if (retval->language != SQLlanguageId) - { - fmgr_info(foid, &(retval->func)); - retval->nargs = retval->func.fn_nargs; - } - else - retval->func.fn_addr = (PGFunction) NULL; - - heap_freetuple(procedureTuple); + MemoryContextSwitchTo(oldcontext); return retval; } - -void -setFcache(Node *node, Oid foid, List *argList, ExprContext *econtext) -{ - MemoryContext oldcontext; - FunctionCachePtr fcache; - - /* Switch to a context long-lived enough for the fcache entry */ - oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); - - fcache = init_fcache(foid, argList, econtext); - - if (IsA(node, Oper)) - { - Oper *onode = (Oper *) node; - onode->op_fcache = fcache; - } - else if (IsA(node, Func)) - { - Func *fnode = (Func *) node; - fnode->func_fcache = fcache; - } - else - elog(ERROR, "init_fcache: node must be Oper or Func!"); - - MemoryContextSwitchTo(oldcontext); -} diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c index 7b8aca3feb4..c7fbf251f9d 100644 --- a/src/backend/utils/fmgr/fmgr.c +++ b/src/backend/utils/fmgr/fmgr.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/fmgr/fmgr.c,v 1.45 2000/07/06 05:48:13 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/fmgr/fmgr.c,v 1.46 2000/08/24 03:29:07 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,6 +17,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_proc.h" +#include "executor/functions.h" #include "utils/builtins.h" #include "utils/fmgrtab.h" #include "utils/syscache.h" @@ -44,7 +45,6 @@ typedef char *((*func_ptr) ()); static Datum fmgr_oldstyle(PG_FUNCTION_ARGS); static Datum fmgr_untrusted(PG_FUNCTION_ARGS); -static Datum fmgr_sql(PG_FUNCTION_ARGS); /* @@ -111,6 +111,7 @@ fmgr_info(Oid functionId, FmgrInfo *finfo) finfo->fn_oid = functionId; finfo->fn_extra = NULL; + finfo->fn_mcxt = CurrentMemoryContext; if ((fbp = fmgr_isbuiltin(functionId)) != NULL) { @@ -119,6 +120,7 @@ fmgr_info(Oid functionId, FmgrInfo *finfo) */ finfo->fn_nargs = fbp->nargs; finfo->fn_strict = fbp->strict; + finfo->fn_retset = false; /* assume no builtins return sets! */ if (fbp->oldstyle) { finfo->fn_addr = fmgr_oldstyle; @@ -142,6 +144,7 @@ fmgr_info(Oid functionId, FmgrInfo *finfo) finfo->fn_nargs = procedureStruct->pronargs; finfo->fn_strict = procedureStruct->proisstrict; + finfo->fn_retset = procedureStruct->proretset; if (!procedureStruct->proistrusted) { @@ -427,21 +430,6 @@ fmgr_untrusted(PG_FUNCTION_ARGS) return 0; /* keep compiler happy */ } -/* - * Handler for SQL-language functions - */ -static Datum -fmgr_sql(PG_FUNCTION_ARGS) -{ - /* - * XXX It'd be really nice to support SQL functions anywhere that - * builtins are supported. What would we have to do? What pitfalls - * are there? - */ - elog(ERROR, "SQL-language function not supported in this context"); - return 0; /* keep compiler happy */ -} - /*------------------------------------------------------------------------- * Support routines for callers of fmgr-compatible functions |