SQL-language functions are now callable in ordinary fmgr contexts ...
authorTom Lane <[email protected]>
Thu, 24 Aug 2000 03:29:15 +0000 (03:29 +0000)
committerTom Lane <[email protected]>
Thu, 24 Aug 2000 03:29:15 +0000 (03:29 +0000)
for example, an SQL function can be used in a functional index.  (I make
no promises about speed, but it'll work ;-).)  Clean up and simplify
handling of functions returning sets.

35 files changed:
src/backend/executor/execFlatten.c
src/backend/executor/execQual.c
src/backend/executor/execScan.c
src/backend/executor/functions.c
src/backend/executor/nodeAgg.c
src/backend/executor/nodeGroup.c
src/backend/executor/nodeHash.c
src/backend/executor/nodeHashjoin.c
src/backend/executor/nodeIndexscan.c
src/backend/executor/nodeMergejoin.c
src/backend/executor/nodeNestloop.c
src/backend/executor/nodeResult.c
src/backend/executor/nodeSubplan.c
src/backend/executor/nodeTidscan.c
src/backend/optimizer/path/indxpath.c
src/backend/optimizer/util/clauses.c
src/backend/parser/parse_func.c
src/backend/parser/parse_node.c
src/backend/utils/adt/sets.c
src/backend/utils/cache/fcache.c
src/backend/utils/fmgr/fmgr.c
src/include/catalog/pg_proc.h
src/include/commands/defrem.h
src/include/executor/execFlatten.h
src/include/executor/executor.h
src/include/executor/functions.h
src/include/fmgr.h
src/include/nodes/execnodes.h
src/include/nodes/nodes.h
src/include/nodes/primnodes.h
src/include/utils/builtins.h
src/include/utils/fcache.h
src/include/utils/fcache2.h [deleted file]
src/include/utils/sets.h
src/pl/plpgsql/src/pl_exec.c

index bb45e63a8a59d28a205cfab2cee08127c9667929..e94a43f3cf4f75067373f0f06a6b31bc4f36b05a 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/Attic/execFlatten.c,v 1.12 2000/01/26 05:56:21 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/Attic/execFlatten.c,v 1.13 2000/08/24 03:29:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -36,11 +36,12 @@ static bool FjoinBumpOuterNodes(TargetEntry *tlist, ExprContext *econtext,
 
 #endif
 
+
 Datum
 ExecEvalIter(Iter *iterNode,
                         ExprContext *econtext,
-                        bool *resultIsNull,
-                        bool *iterIsDone)
+                        bool *isNull,
+                        ExprDoneCond *isDone)
 {
        Node       *expression;
 
@@ -52,14 +53,14 @@ ExecEvalIter(Iter *iterNode,
         * only worrying about postquel functions, c functions will come
         * later.
         */
-       return ExecEvalExpr(expression, econtext, resultIsNull, iterIsDone);
+       return ExecEvalExpr(expression, econtext, isNull, isDone);
 }
 
 void
 ExecEvalFjoin(TargetEntry *tlist,
                          ExprContext *econtext,
                          bool *isNullVect,
-                         bool *fj_isDone)
+                         ExprDoneCond *fj_isDone)
 {
 
 #ifdef SETS_FIXED
@@ -72,7 +73,7 @@ ExecEvalFjoin(TargetEntry *tlist,
        BoolPtr         alwaysDone = fjNode->fj_alwaysDone;
 
        if (fj_isDone)
-               *fj_isDone = false;
+               *fj_isDone = ExprMultipleResult;
 
        /*
         * For the next tuple produced by the plan, we need to re-initialize
index 83117d836ebef83d7d5f9fb3233a40fb09c32708..3929c8782a9a6351ae5f520ff730f0db979832a3 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.78 2000/08/21 20:55:30 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.79 2000/08/24 03:29:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
 #include "access/heapam.h"
-#include "catalog/pg_language.h"
 #include "executor/execFlatten.h"
 #include "executor/execdebug.h"
 #include "executor/functions.h"
 #include "executor/nodeSubplan.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
-#include "utils/fmgroids.h"
-#include "utils/fcache2.h"
+#include "utils/fcache.h"
 
 
 /* static function decls */
-static Datum ExecEvalAggref(Aggref *aggref, ExprContext *econtext, bool *isNull);
+static Datum ExecEvalAggref(Aggref *aggref, ExprContext *econtext,
+                                                       bool *isNull);
 static Datum ExecEvalArrayRef(ArrayRef *arrayRef, ExprContext *econtext,
-                                bool *isNull, bool *isDone);
+                                                         bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull);
 static Datum ExecEvalOper(Expr *opClause, ExprContext *econtext,
-                        bool *isNull);
+                                                 bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalFunc(Expr *funcClause, ExprContext *econtext,
-                        bool *isNull, bool *isDone);
-static void ExecEvalFuncArgs(FunctionCachePtr fcache, ExprContext *econtext,
-                                                        List *argList, FunctionCallInfo fcinfo,
-                                                        bool *argIsDone);
+                                                 bool *isNull, ExprDoneCond *isDone);
+static ExprDoneCond ExecEvalFuncArgs(FunctionCachePtr fcache,
+                                                                        List *argList,
+                                                                        ExprContext *econtext);
 static Datum ExecEvalNot(Expr *notclause, ExprContext *econtext, bool *isNull);
 static Datum ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull);
 static Datum ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull);
-static Datum ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull);
-static Datum ExecMakeFunctionResult(Node *node, List *arguments,
-                                          ExprContext *econtext, bool *isNull, bool *isDone);
+static Datum ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext,
+                                                 bool *isNull, ExprDoneCond *isDone);
+
 
 /*----------
  *       ExecEvalArrayRef
@@ -93,7 +93,7 @@ static Datum
 ExecEvalArrayRef(ArrayRef *arrayRef,
                                 ExprContext *econtext,
                                 bool *isNull,
-                                bool *isDone)
+                                ExprDoneCond *isDone)
 {
        ArrayType  *array_source;
        ArrayType  *resultArray;
@@ -104,9 +104,6 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
        IntArray        upper,
                                lower;
        int                *lIndex;
-       bool            dummy;
-
-       *isNull = false;
 
        if (arrayRef->refexpr != NULL)
        {
@@ -146,7 +143,7 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
                upper.indx[i++] = DatumGetInt32(ExecEvalExpr((Node *) lfirst(elt),
                                                                                                         econtext,
                                                                                                         isNull,
-                                                                                                        &dummy));
+                                                                                                        NULL));
                /* If any index expr yields NULL, result is NULL or source array */
                if (*isNull)
                {
@@ -168,7 +165,7 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
                        lower.indx[j++] = DatumGetInt32(ExecEvalExpr((Node *) lfirst(elt),
                                                                                                                 econtext,
                                                                                                                 isNull,
-                                                                                                                &dummy));
+                                                                                                                NULL));
                        /* If any index expr yields NULL, result is NULL or source array */
                        if (*isNull)
                        {
@@ -191,7 +188,7 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
                Datum           sourceData = ExecEvalExpr(arrayRef->refassgnexpr,
                                                                                          econtext,
                                                                                          isNull,
-                                                                                         &dummy);
+                                                                                         NULL);
                /*
                 * For now, can't cope with inserting NULL into an array,
                 * so make it a no-op per discussion above...
@@ -588,162 +585,109 @@ GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull)
        return (char *) retval;
 }
 
-
-static void
+/*
+ * Evaluate arguments for a function.
+ */
+static ExprDoneCond
 ExecEvalFuncArgs(FunctionCachePtr fcache,
-                                ExprContext *econtext,
                                 List *argList,
-                                FunctionCallInfo fcinfo,
-                                bool *argIsDone)
+                                ExprContext *econtext)
 {
+       ExprDoneCond argIsDone;
        int                     i;
        List       *arg;
 
+       argIsDone = ExprSingleResult; /* default assumption */
+
        i = 0;
        foreach(arg, argList)
        {
+               ExprDoneCond    thisArgIsDone;
 
-               /*
-                * evaluate the expression, in general functions cannot take sets
-                * as arguments but we make an exception in the case of nested dot
-                * expressions.  We have to watch out for this case here.
-                */
-               fcinfo->arg[i] = ExecEvalExpr((Node *) lfirst(arg),
-                                                                         econtext,
-                                                                         &fcinfo->argnull[i],
-                                                                         argIsDone);
+               fcache->fcinfo.arg[i] = ExecEvalExpr((Node *) lfirst(arg),
+                                                                                        econtext,
+                                                                                        &fcache->fcinfo.argnull[i],
+                                                                                        &thisArgIsDone);
 
-               if (!(*argIsDone))
+               if (thisArgIsDone != ExprSingleResult)
                {
-                       if (i != 0)
-                               elog(ERROR, "functions can only take sets in their first argument");
-                       fcache->setArg = fcinfo->arg[0];
+                       /*
+                        * We allow only one argument to have a set value; we'd need
+                        * much more complexity to keep track of multiple set arguments
+                        * (cf. ExecTargetList) and it doesn't seem worth it.
+                        */
+                       if (argIsDone != ExprSingleResult)
+                               elog(ERROR, "Functions and operators can take only one set argument");
                        fcache->hasSetArg = true;
+                       argIsDone = thisArgIsDone;
                }
                i++;
        }
+
+       return argIsDone;
 }
 
 /*
  *             ExecMakeFunctionResult
+ *
+ * Evaluate the arguments to a function and then the function itself.
+ *
+ * NOTE: econtext is used only for evaluating the argument expressions;
+ * it is not passed to the function itself.
  */
-static Datum
-ExecMakeFunctionResult(Node *node,
+Datum
+ExecMakeFunctionResult(FunctionCachePtr fcache,
                                           List *arguments,
                                           ExprContext *econtext,
                                           bool *isNull,
-                                          bool *isDone)
+                                          ExprDoneCond *isDone)
 {
-       FunctionCallInfoData    fcinfo;
-       FunctionCachePtr                fcache;
-       bool                                    funcisset;
-       Datum                                   result;
-       bool                                    argDone;
-
-       MemSet(&fcinfo, 0, sizeof(fcinfo));
-
-       /*
-        * This is kind of ugly, Func nodes now have targetlists so that we
-        * know when and what to project out from postquel function results.
-        * ExecMakeFunctionResult becomes a little bit more of a dual personality
-        * as a result.
-        */
-       if (IsA(node, Func))
-       {
-               fcache = ((Func *) node)->func_fcache;
-               funcisset = (((Func *) node)->funcid == F_SETEVAL);
-       }
-       else
-       {
-               fcache = ((Oper *) node)->op_fcache;
-               funcisset = false;
-       }
-
-       fcinfo.flinfo = &fcache->func;
-       fcinfo.nargs = fcache->nargs;
+       Datum                           result;
+       ExprDoneCond            argDone;
+       int                                     i;
 
        /*
         * arguments is a list of expressions to evaluate before passing to
-        * the function manager.  We collect the results of evaluating the
-        * expressions into the FunctionCallInfo struct.  Note we assume that
-        * fcache->nargs is the correct length of the arguments list!
+        * the function manager.  We skip the evaluation if it was already
+        * done in the previous call (ie, we are continuing the evaluation
+        * of a set-valued function).  Otherwise, collect the current argument
+        * values into fcache->fcinfo.
         */
-       if (fcache->nargs > 0)
+       if (fcache->fcinfo.nargs > 0 && !fcache->argsValid)
        {
-               if (fcache->nargs > FUNC_MAX_ARGS)
-                       elog(ERROR, "ExecMakeFunctionResult: too many arguments");
-
-               /*
-                * If the setArg in the fcache is set we have an argument
-                * returning a set of tuples (i.e. a nested dot expression).  We
-                * don't want to evaluate the arguments again until the function
-                * is done. hasSetArg will always be false until we eval the args
-                * for the first time.
-                */
-               if (fcache->hasSetArg && fcache->setArg != (Datum) 0)
-               {
-                       fcinfo.arg[0] = fcache->setArg;
-                       argDone = false;
-               }
-               else
-                       ExecEvalFuncArgs(fcache, econtext, arguments, &fcinfo, &argDone);
-
-               if (fcache->hasSetArg && argDone)
+               argDone = ExecEvalFuncArgs(fcache, arguments, econtext);
+               if (argDone == ExprEndResult)
                {
-                       /* can only get here if input is an empty set. */
+                       /* input is an empty set, so return an empty set. */
                        *isNull = true;
-                       *isDone = true;
+                       if (isDone)
+                               *isDone = ExprEndResult;
+                       else
+                               elog(ERROR, "Set-valued function called in context that cannot accept a set");
                        return (Datum) 0;
                }
        }
 
-       /*
-        * If this function is really a set, we have to diddle with things. If
-        * the function has already been called at least once, then the setArg
-        * field of the fcache holds the OID of this set in pg_proc.  (This is
-        * not quite legit, since the setArg field is really for functions
-        * which take sets of tuples as input - set functions take no inputs
-        * at all.      But it's a nice place to stash this value, for now.)
-        *
-        * If this is the first call of the set's function, then the call to
-        * ExecEvalFuncArgs above just returned the OID of the pg_proc tuple
-        * which defines this set.      So replace the existing funcid in the
-        * funcnode with the set's OID.  Also, we want a new fcache which
-        * points to the right function, so get that, now that we have the
-        * right OID.  Also zero out fcinfo.arg, since the real set doesn't take
-        * any arguments.
-        */
-       if (funcisset)
-       {
-               if (fcache->setArg)
-               {
-                       ((Func *) node)->funcid = DatumGetObjectId(fcache->setArg);
-               }
-               else
-               {
-                       ((Func *) node)->funcid = DatumGetObjectId(fcinfo.arg[0]);
-                       setFcache(node, DatumGetObjectId(fcinfo.arg[0]), NIL, econtext);
-                       fcache = ((Func *) node)->func_fcache;
-                       fcache->setArg = fcinfo.arg[0];
-               }
-               fcinfo.arg[0] = (Datum) 0;
-       }
-
        /*
         * now return the value gotten by calling the function manager,
         * passing the function the evaluated parameter values.
         */
-       if (fcache->language == SQLlanguageId)
+       if (fcache->func.fn_retset || fcache->hasSetArg)
        {
-               /*--------------------
-                * This loop handles the situation where we are iterating through
-                * all results in a nested dot function (whose argument function
-                * returns a set of tuples) and the current function finally
-                * finishes.  We need to get the next argument in the set and start
-                * the function all over again.  We might have to do it more than
-                * once, if the function produces no results for a particular argument.
-                * This is getting unclean.
-                *--------------------
+               /*
+                * We need to return a set result.  Complain if caller not ready
+                * to accept one.
+                */
+               if (isDone == NULL)
+                       elog(ERROR, "Set-valued function called in context that cannot accept a set");
+
+               /*
+                * This loop handles the situation where we have both a set argument
+                * and a set-valued function.  Once we have exhausted the function's
+                * value(s) for a particular argument value, we have to get the next
+                * argument value and start the function over again.  We might have
+                * to do it more than once, if the function produces an empty result
+                * set for a particular input value.
                 */
                for (;;)
                {
@@ -753,13 +697,11 @@ ExecMakeFunctionResult(Node *node,
                         */
                        bool    callit = true;
 
-                       if (fcinfo.flinfo->fn_strict)
+                       if (fcache->func.fn_strict)
                        {
-                               int             i;
-
-                               for (i = 0; i < fcinfo.nargs; i++)
+                               for (i = 0; i < fcache->fcinfo.nargs; i++)
                                {
-                                       if (fcinfo.argnull[i])
+                                       if (fcache->fcinfo.argnull[i])
                                        {
                                                callit = false;
                                                break;
@@ -769,35 +711,55 @@ ExecMakeFunctionResult(Node *node,
 
                        if (callit)
                        {
-                               result = postquel_function(&fcinfo, fcache, isDone);
-                               *isNull = fcinfo.isnull;
+                               fcache->fcinfo.isnull = false;
+                               fcache->rsinfo.isDone = ExprSingleResult;
+                               result = FunctionCallInvoke(&fcache->fcinfo);
+                               *isNull = fcache->fcinfo.isnull;
+                               *isDone = fcache->rsinfo.isDone;
                        }
                        else
                        {
                                result = (Datum) 0;
                                *isNull = true;
-                               *isDone = true;
+                               *isDone = ExprEndResult;
+                       }
+
+                       if (*isDone != ExprEndResult)
+                       {
+                               /*
+                                * Got a result from current argument.  If function itself
+                                * returns set, flag that we want to reuse current argument
+                                * values on next call.
+                                */
+                               if (fcache->func.fn_retset)
+                                       fcache->argsValid = true;
+                               /*
+                                * Make sure we say we are returning a set, even if the
+                                * function itself doesn't return sets.
+                                */
+                               *isDone = ExprMultipleResult;
+                               break;
                        }
 
-                       if (!*isDone)
-                               break;                  /* got a result from current argument */
+                       /* Else, done with this argument */
+                       fcache->argsValid = false;
+
                        if (!fcache->hasSetArg)
                                break;                  /* input not a set, so done */
 
-                       /* OK, get the next argument... */
-                       ExecEvalFuncArgs(fcache, econtext, arguments, &fcinfo, &argDone);
+                       /* Re-eval args to get the next element of the input set */
+                       argDone = ExecEvalFuncArgs(fcache, arguments, econtext);
 
-                       if (argDone)
+                       if (argDone != ExprMultipleResult)
                        {
 
                                /*
-                                * End of arguments, so reset the setArg flag and say
+                                * End of arguments, so reset the hasSetArg flag and say
                                 * "Done"
                                 */
-                               fcache->setArg = (Datum) 0;
                                fcache->hasSetArg = false;
-                               *isDone = true;
                                *isNull = true;
+                               *isDone = ExprEndResult;
                                result = (Datum) 0;
                                break;
                        }
@@ -807,50 +769,29 @@ ExecMakeFunctionResult(Node *node,
                         * new argument.
                         */
                }
-
-               if (funcisset)
-               {
-
-                       /*
-                        * reset the funcid so that next call to this routine will
-                        * still recognize this func as a set. Note that for now we
-                        * assume that the set function in pg_proc must be a Postquel
-                        * function - the funcid is not reset below for C functions.
-                        */
-                       ((Func *) node)->funcid = F_SETEVAL;
-
-                       /*
-                        * If we're done with the results of this function, get rid of
-                        * its func cache.
-                        */
-                       if (*isDone)
-                               ((Func *) node)->func_fcache = NULL;
-               }
        }
        else
        {
-               /* A non-SQL function cannot return a set, at present. */
-               *isDone = true;
-
                /*
+                * Non-set case: much easier.
+                *
                 * If function is strict, and there are any NULL arguments,
                 * skip calling the function and return NULL.
                 */
-               if (fcinfo.flinfo->fn_strict)
+               if (fcache->func.fn_strict)
                {
-                       int             i;
-
-                       for (i = 0; i < fcinfo.nargs; i++)
+                       for (i = 0; i < fcache->fcinfo.nargs; i++)
                        {
-                               if (fcinfo.argnull[i])
+                               if (fcache->fcinfo.argnull[i])
                                {
                                        *isNull = true;
                                        return (Datum) 0;
                                }
                        }
                }
-               result = FunctionCallInvoke(&fcinfo);
-               *isNull = fcinfo.isnull;
+               fcache->fcinfo.isnull = false;
+               result = FunctionCallInvoke(&fcache->fcinfo);
+               *isNull = fcache->fcinfo.isnull;
        }
 
        return result;
@@ -871,12 +812,14 @@ ExecMakeFunctionResult(Node *node,
  * ----------------------------------------------------------------
  */
 static Datum
-ExecEvalOper(Expr *opClause, ExprContext *econtext, bool *isNull)
+ExecEvalOper(Expr *opClause,
+                        ExprContext *econtext,
+                        bool *isNull,
+                        ExprDoneCond *isDone)
 {
        Oper       *op;
        List       *argList;
        FunctionCachePtr fcache;
-       bool            isDone;
 
        /*
         * we extract the oid of the function associated with the op and then
@@ -894,16 +837,13 @@ ExecEvalOper(Expr *opClause, ExprContext *econtext, bool *isNull)
        fcache = op->op_fcache;
        if (fcache == NULL)
        {
-               setFcache((Node *) op, op->opid, argList, econtext);
-               fcache = op->op_fcache;
+               fcache = init_fcache(op->opid, length(argList),
+                                                        econtext->ecxt_per_query_memory);
+               op->op_fcache = fcache;
        }
 
-       /*
-        * call ExecMakeFunctionResult() with a dummy isDone that we ignore.
-        * We don't have operator whose arguments are sets.
-        */
-       return ExecMakeFunctionResult((Node *) op, argList, econtext,
-                                                                 isNull, &isDone);
+       return ExecMakeFunctionResult(fcache, argList, econtext,
+                                                                 isNull, isDone);
 }
 
 /* ----------------------------------------------------------------
@@ -915,7 +855,7 @@ static Datum
 ExecEvalFunc(Expr *funcClause,
                         ExprContext *econtext,
                         bool *isNull,
-                        bool *isDone)
+                        ExprDoneCond *isDone)
 {
        Func       *func;
        List       *argList;
@@ -939,11 +879,12 @@ ExecEvalFunc(Expr *funcClause,
        fcache = func->func_fcache;
        if (fcache == NULL)
        {
-               setFcache((Node *) func, func->funcid, argList, econtext);
-               fcache = func->func_fcache;
+               fcache = init_fcache(func->funcid, length(argList),
+                                                        econtext->ecxt_per_query_memory);
+               func->func_fcache = fcache;
        }
 
-       return ExecMakeFunctionResult((Node *) func, argList, econtext,
+       return ExecMakeFunctionResult(fcache, argList, econtext,
                                                                  isNull, isDone);
 }
 
@@ -968,15 +909,10 @@ ExecEvalNot(Expr *notclause, ExprContext *econtext, bool *isNull)
 {
        Node       *clause;
        Datum           expr_value;
-       bool            isDone;
 
        clause = lfirst(notclause->args);
 
-       /*
-        * We don't iterate over sets in the quals, so pass in an isDone flag,
-        * but ignore it.
-        */
-       expr_value = ExecEvalExpr(clause, econtext, isNull, &isDone);
+       expr_value = ExecEvalExpr(clause, econtext, isNull, NULL);
 
        /*
         * if the expression evaluates to null, then we just cascade the null
@@ -1001,7 +937,6 @@ ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull)
 {
        List       *clauses;
        List       *clause;
-       bool            isDone;
        bool            AnyNull;
        Datum           clause_value;
 
@@ -1024,15 +959,8 @@ ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull)
         */
        foreach(clause, clauses)
        {
-
-               /*
-                * We don't iterate over sets in the quals, so pass in an isDone
-                * flag, but ignore it.
-                */
                clause_value = ExecEvalExpr((Node *) lfirst(clause),
-                                                                       econtext,
-                                                                       isNull,
-                                                                       &isDone);
+                                                                       econtext, isNull, NULL);
 
                /*
                 * if we have a non-null true result, then return it.
@@ -1057,7 +985,6 @@ ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull)
 {
        List       *clauses;
        List       *clause;
-       bool            isDone;
        bool            AnyNull;
        Datum           clause_value;
 
@@ -1074,15 +1001,8 @@ ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull)
         */
        foreach(clause, clauses)
        {
-
-               /*
-                * We don't iterate over sets in the quals, so pass in an isDone
-                * flag, but ignore it.
-                */
                clause_value = ExecEvalExpr((Node *) lfirst(clause),
-                                                                       econtext,
-                                                                       isNull,
-                                                                       &isDone);
+                                                                       econtext, isNull, NULL);
 
                /*
                 * if we have a non-null false result, then return it.
@@ -1108,12 +1028,12 @@ ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull)
  * ----------------------------------------------------------------
  */
 static Datum
-ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext, bool *isNull)
+ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext,
+                        bool *isNull, ExprDoneCond *isDone)
 {
        List       *clauses;
        List       *clause;
        Datum           clause_value;
-       bool            isDone;
 
        clauses = caseExpr->args;
 
@@ -1126,14 +1046,10 @@ ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext, bool *isNull)
        {
                CaseWhen   *wclause = lfirst(clause);
 
-               /*
-                * We don't iterate over sets in the quals, so pass in an isDone
-                * flag, but ignore it.
-                */
                clause_value = ExecEvalExpr(wclause->expr,
                                                                        econtext,
                                                                        isNull,
-                                                                       &isDone);
+                                                                       NULL);
 
                /*
                 * if we have a true test, then we return the result, since the
@@ -1145,7 +1061,7 @@ ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext, bool *isNull)
                        return ExecEvalExpr(wclause->result,
                                                                econtext,
                                                                isNull,
-                                                               &isDone);
+                                                               isDone);
                }
        }
 
@@ -1154,7 +1070,7 @@ ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext, bool *isNull)
                return ExecEvalExpr(caseExpr->defresult,
                                                        econtext,
                                                        isNull,
-                                                       &isDone);
+                                                       isDone);
        }
 
        *isNull = true;
@@ -1171,7 +1087,7 @@ static Datum
 ExecEvalFieldSelect(FieldSelect *fselect,
                                        ExprContext *econtext,
                                        bool *isNull,
-                                       bool *isDone)
+                                       ExprDoneCond *isDone)
 {
        Datum                   result;
        TupleTableSlot *resSlot;
@@ -1179,7 +1095,6 @@ ExecEvalFieldSelect(FieldSelect *fselect,
        result = ExecEvalExpr(fselect->arg, econtext, isNull, isDone);
        if (*isNull)
                return result;
-       /* XXX what about isDone? */
        resSlot = (TupleTableSlot *) DatumGetPointer(result);
        Assert(resSlot != NULL && IsA(resSlot, TupleTableSlot));
        result = heap_getattr(resSlot->val,
@@ -1194,28 +1109,52 @@ ExecEvalFieldSelect(FieldSelect *fselect,
  *
  *             Recursively evaluate a targetlist or qualification expression.
  *
- *             The caller should already have switched into the temporary
- *             memory context econtext->ecxt_per_tuple_memory.  The convenience
- *             entry point ExecEvalExprSwitchContext() is provided for callers
- *             who don't prefer to do the switch in an outer loop.  We do not
- *             do the switch here because it'd be a waste of cycles during
- *             recursive entries to ExecEvalExpr().
+ * Inputs:
+ *             expression: the expression tree to evaluate
+ *             econtext: evaluation context information
+ *
+ * Outputs:
+ *             return value: Datum value of result
+ *             *isNull: set to TRUE if result is NULL (actual return value is
+ *                              meaningless if so); set to FALSE if non-null result
+ *             *isDone: set to indicator of set-result status
+ *
+ * A caller that can only accept a singleton (non-set) result should pass
+ * NULL for isDone; if the expression computes a set result then an elog()
+ * error will be reported.  If the caller does pass an isDone pointer then
+ * *isDone is set to one of these three states:
+ *             ExprSingleResult                singleton result (not a set)
+ *             ExprMultipleResult              return value is one element of a set
+ *             ExprEndResult                   there are no more elements in the set
+ * When ExprMultipleResult is returned, the caller should invoke
+ * ExecEvalExpr() repeatedly until ExprEndResult is returned.  ExprEndResult
+ * is returned after the last real set element.  For convenience isNull will
+ * always be set TRUE when ExprEndResult is returned, but this should not be
+ * taken as indicating a NULL element of the set.  Note that these return
+ * conventions allow us to distinguish among a singleton NULL, a NULL element
+ * of a set, and an empty set.
  *
- *             This routine is an inner loop routine and must be as fast
- *             as possible.
+ * The caller should already have switched into the temporary memory
+ * context econtext->ecxt_per_tuple_memory.  The convenience entry point
+ * ExecEvalExprSwitchContext() is provided for callers who don't prefer to
+ * do the switch in an outer loop.  We do not do the switch here because
+ * it'd be a waste of cycles during recursive entries to ExecEvalExpr().
+ *
+ * This routine is an inner loop routine and must be as fast as possible.
  * ----------------------------------------------------------------
  */
 Datum
 ExecEvalExpr(Node *expression,
                         ExprContext *econtext,
                         bool *isNull,
-                        bool *isDone)
+                        ExprDoneCond *isDone)
 {
        Datum           retDatum;
 
        /* Set default values for result flags: non-null, not a set result */
        *isNull = false;
-       *isDone = true;
+       if (isDone)
+               *isDone = ExprSingleResult;
 
        /* Is this still necessary?  Doubtful... */
        if (expression == NULL)
@@ -1266,7 +1205,8 @@ ExecEvalExpr(Node *expression,
                                switch (expr->opType)
                                {
                                        case OP_EXPR:
-                                               retDatum = ExecEvalOper(expr, econtext, isNull);
+                                               retDatum = ExecEvalOper(expr, econtext,
+                                                                                               isNull, isDone);
                                                break;
                                        case FUNC_EXPR:
                                                retDatum = ExecEvalFunc(expr, econtext,
@@ -1307,7 +1247,10 @@ ExecEvalExpr(Node *expression,
                                                                        isDone);
                        break;
                case T_CaseExpr:
-                       retDatum = ExecEvalCase((CaseExpr *) expression, econtext, isNull);
+                       retDatum = ExecEvalCase((CaseExpr *) expression,
+                                                                       econtext,
+                                                                       isNull,
+                                                                       isDone);
                        break;
 
                default:
@@ -1328,7 +1271,7 @@ Datum
 ExecEvalExprSwitchContext(Node *expression,
                                                  ExprContext *econtext,
                                                  bool *isNull,
-                                                 bool *isDone)
+                                                 ExprDoneCond *isDone)
 {
        Datum           retDatum;
        MemoryContext oldContext;
@@ -1413,13 +1356,8 @@ ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
                Node       *clause = (Node *) lfirst(qlist);
                Datum           expr_value;
                bool            isNull;
-               bool            isDone;
 
-               /*
-                * pass isDone, but ignore it.  We don't iterate over multiple
-                * returns in the qualifications.
-                */
-               expr_value = ExecEvalExpr(clause, econtext, &isNull, &isDone);
+               expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL);
 
                if (isNull)
                {
@@ -1496,6 +1434,11 @@ ExecCleanTargetListLength(List *targetlist)
  *
  *             Evaluates a targetlist with respect to the current
  *             expression context and return a tuple.
+ *
+ * As with ExecEvalExpr, the caller should pass isDone = NULL if not
+ * prepared to deal with sets of result tuples.  Otherwise, a return
+ * of *isDone = ExprMultipleResult signifies a set element, and a return
+ * of *isDone = ExprEndResult signifies end of the set of tuple.
  * ----------------------------------------------------------------
  */
 static HeapTuple
@@ -1504,24 +1447,22 @@ ExecTargetList(List *targetlist,
                           TupleDesc targettype,
                           Datum *values,
                           ExprContext *econtext,
-                          bool *isDone)
+                          ExprDoneCond *isDone)
 {
        MemoryContext oldContext;
-       char            nulls_array[64];
-       bool            fjNullArray[64];
-       bool            itemIsDoneArray[64];
-       char       *null_head;
+#define NPREALLOCDOMAINS 64
+       char            nullsArray[NPREALLOCDOMAINS];
+       bool            fjIsNullArray[NPREALLOCDOMAINS];
+       ExprDoneCond itemIsDoneArray[NPREALLOCDOMAINS];
+       char       *nulls;
        bool       *fjIsNull;
-       bool       *itemIsDone;
+       ExprDoneCond *itemIsDone;
        List       *tl;
        TargetEntry *tle;
-       Node       *expr;
-       Resdom     *resdom;
        AttrNumber      resind;
-       Datum           constvalue;
        HeapTuple       newTuple;
        bool            isNull;
-       bool            haveDoneIters;
+       bool            haveDoneSets;
        static struct tupleDesc NullTupleDesc;          /* we assume this inits to
                                                                                                 * zeroes */
 
@@ -1553,70 +1494,67 @@ ExecTargetList(List *targetlist,
         * we have a really large targetlist.  otherwise we use the stack.
         *
         * We also allocate a bool array that is used to hold fjoin result state,
-        * and another that holds the isDone status for each targetlist item.
+        * and another array that holds the isDone status for each targetlist item.
+        * The isDone status is needed so that we can iterate, generating multiple
+        * tuples, when one or more tlist items return sets.  (We expect the caller
+        * to call us again if we return *isDone = ExprMultipleResult.)
         */
-       if (nodomains > 64)
+       if (nodomains > NPREALLOCDOMAINS)
        {
-               null_head = (char *) palloc(nodomains + 1);
-               fjIsNull = (bool *) palloc(nodomains + 1);
-               itemIsDone = (bool *) palloc(nodomains + 1);
+               nulls = (char *) palloc(nodomains * sizeof(char));
+               fjIsNull = (bool *) palloc(nodomains * sizeof(bool));
+               itemIsDone = (ExprDoneCond *) palloc(nodomains * sizeof(ExprDoneCond));
        }
        else
        {
-               null_head = &nulls_array[0];
-               fjIsNull = &fjNullArray[0];
-               itemIsDone = &itemIsDoneArray[0];
+               nulls = nullsArray;
+               fjIsNull = fjIsNullArray;
+               itemIsDone = itemIsDoneArray;
        }
 
        /*
         * evaluate all the expressions in the target list
         */
 
-       *isDone = true;                         /* until proven otherwise */
-       haveDoneIters = false;          /* any isDone Iter exprs in tlist? */
+       if (isDone)
+               *isDone = ExprSingleResult;     /* until proven otherwise */
+
+       haveDoneSets = false;           /* any exhausted set exprs in tlist? */
 
        foreach(tl, targetlist)
        {
-
-               /*
-                * remember, a target list is a list of lists:
-                *
-                * ((<resdom | fjoin> expr) (<resdom | fjoin> expr) ...)
-                *
-                * tl is a pointer to successive cdr's of the targetlist tle is a
-                * pointer to the target list entry in tl
-                */
                tle = lfirst(tl);
 
                if (tle->resdom != NULL)
                {
-                       expr = tle->expr;
-                       resdom = tle->resdom;
-                       resind = resdom->resno - 1;
-
-                       constvalue = ExecEvalExpr(expr,
-                                                                         econtext,
-                                                                         &isNull,
-                                                                         &itemIsDone[resind]);
+                       resind = tle->resdom->resno - 1;
 
-                       values[resind] = constvalue;
+                       values[resind] = ExecEvalExpr(tle->expr,
+                                                                                 econtext,
+                                                                                 &isNull,
+                                                                                 &itemIsDone[resind]);
+                       nulls[resind] = isNull ? 'n' : ' ';
 
-                       if (!isNull)
-                               null_head[resind] = ' ';
-                       else
-                               null_head[resind] = 'n';
-
-                       if (IsA(expr, Iter))
+                       if (itemIsDone[resind] != ExprSingleResult)
                        {
-                               if (itemIsDone[resind])
-                                       haveDoneIters = true;
+                               /* We have a set-valued expression in the tlist */
+                               if (isDone == NULL)
+                                       elog(ERROR, "Set-valued function called in context that cannot accept a set");
+                               if (itemIsDone[resind] == ExprMultipleResult)
+                               {
+                                       /* we have undone sets in the tlist, set flag */
+                                       *isDone = ExprMultipleResult;
+                               }
                                else
-                                       *isDone = false;        /* we have undone Iters in the
-                                                                                * list */
+                               {
+                                       /* we have done sets in the tlist, set flag for that */
+                                       haveDoneSets = true;
+                               }
                        }
                }
                else
                {
+#ifdef SETS_FIXED
                        int                     curNode;
                        Resdom     *fjRes;
                        List       *fjTlist = (List *) tle->expr;
@@ -1626,9 +1564,12 @@ ExecTargetList(List *targetlist,
 
                        ExecEvalFjoin(tle, econtext, fjIsNull, isDone);
 
-                       /* this is probably wrong: */
-                       if (*isDone)
+                       /* XXX this is wrong, but since fjoin code is completely broken
+                        * anyway, I'm not going to worry about it now --- tgl 8/23/00
+                        */
+                       if (isDone && *isDone == ExprEndResult)
                        {
+                               MemoryContextSwitchTo(oldContext);
                                newTuple = NULL;
                                goto exit;
                        }
@@ -1638,13 +1579,8 @@ ExecTargetList(List *targetlist,
                         */
                        fjRes = (Resdom *) fjNode->fj_innerNode;
                        resind = fjRes->resno - 1;
-                       if (fjIsNull[0])
-                               null_head[resind] = 'n';
-                       else
-                       {
-                               null_head[resind] = ' ';
-                               values[resind] = results[0];
-                       }
+                       values[resind] = results[0];
+                       nulls[resind] = fjIsNull[0] ? 'n' : ' ';
 
                        /*
                         * Get results from all of the outer nodes
@@ -1653,32 +1589,32 @@ ExecTargetList(List *targetlist,
                                 curNode < nNodes;
                                 curNode++, fjTlist = lnext(fjTlist))
                        {
-#ifdef NOT_USED                                        /* what is this?? */
                                Node       *outernode = lfirst(fjTlist);
 
                                fjRes = (Resdom *) outernode->iterexpr;
-#endif
                                resind = fjRes->resno - 1;
-                               if (fjIsNull[curNode])
-                                       null_head[resind] = 'n';
-                               else
-                               {
-                                       null_head[resind] = ' ';
-                                       values[resind] = results[curNode];
-                               }
+                               values[resind] = results[curNode];
+                               nulls[resind] = fjIsNull[curNode] ? 'n' : ' ';
                        }
+#else
+                       elog(ERROR, "ExecTargetList: fjoin nodes not currently supported");
+#endif
                }
        }
 
-       if (haveDoneIters)
+       if (haveDoneSets)
        {
-               if (*isDone)
+               /*
+                * note: can't get here unless we verified isDone != NULL
+                */
+               if (*isDone == ExprSingleResult)
                {
 
                        /*
-                        * all Iters are done, so return a null indicating tlist set
-                        * expansion is complete.
+                        * all sets are done, so report that tlist expansion is complete.
                         */
+                       *isDone = ExprEndResult;
+                       MemoryContextSwitchTo(oldContext);
                        newTuple = NULL;
                        goto exit;
                }
@@ -1686,22 +1622,8 @@ ExecTargetList(List *targetlist,
                {
 
                        /*
-                        * We have some done and some undone Iters.  Restart the done
+                        * We have some done and some undone sets.  Restart the done
                         * ones so that we can deliver a tuple (if possible).
-                        *
-                        * XXX this code is a crock, because it only works for Iters at
-                        * the top level of tlist expressions, and doesn't even work
-                        * right for them: you should get all possible combinations of
-                        * Iter results, but you won't unless the numbers of values
-                        * returned by each are relatively prime.  Should have a
-                        * mechanism more like aggregate functions, where we make a
-                        * list of all Iters contained in the tlist and cycle through
-                        * their values in a methodical fashion.  To do someday; can't
-                        * get excited about fixing a Berkeley feature that's not in
-                        * SQL92.  (The only reason we're doing this much is that we
-                        * have to be sure all the Iters are run to completion, or
-                        * their subplan executors will have unreleased resources,
-                        * e.g. pinned buffers...)
                         */
                        foreach(tl, targetlist)
                        {
@@ -1709,36 +1631,57 @@ ExecTargetList(List *targetlist,
 
                                if (tle->resdom != NULL)
                                {
-                                       expr = tle->expr;
-                                       resdom = tle->resdom;
-                                       resind = resdom->resno - 1;
+                                       resind = tle->resdom->resno - 1;
 
-                                       if (IsA(expr, Iter) &&itemIsDone[resind])
+                                       if (itemIsDone[resind] == ExprEndResult)
                                        {
-                                               constvalue = ExecEvalExpr(expr,
-                                                                                                 econtext,
-                                                                                                 &isNull,
-                                                                                                 &itemIsDone[resind]);
-                                               if (itemIsDone[resind])
+                                               values[resind] = ExecEvalExpr(tle->expr,
+                                                                                                         econtext,
+                                                                                                         &isNull,
+                                                                                                         &itemIsDone[resind]);
+                                               nulls[resind] = isNull ? 'n' : ' ';
+
+                                               if (itemIsDone[resind] == ExprEndResult)
                                                {
 
                                                        /*
-                                                        * Oh dear, this Iter is returning an empty
+                                                        * Oh dear, this item is returning an empty
                                                         * set. Guess we can't make a tuple after all.
                                                         */
-                                                       *isDone = true;
-                                                       newTuple = NULL;
-                                                       goto exit;
+                                                       *isDone = ExprEndResult;
+                                                       break;
                                                }
+                                       }
+                               }
+                       }
+                       /*
+                        * If we cannot make a tuple because some sets are empty,
+                        * we still have to cycle the nonempty sets to completion,
+                        * else resources will not be released from subplans etc.
+                        */
+                       if (*isDone == ExprEndResult)
+                       {
+                               foreach(tl, targetlist)
+                               {
+                                       tle = lfirst(tl);
 
-                                               values[resind] = constvalue;
+                                       if (tle->resdom != NULL)
+                                       {
+                                               resind = tle->resdom->resno - 1;
 
-                                               if (!isNull)
-                                                       null_head[resind] = ' ';
-                                               else
-                                                       null_head[resind] = 'n';
+                                               while (itemIsDone[resind] == ExprMultipleResult)
+                                               {
+                                                       (void) ExecEvalExpr(tle->expr,
+                                                                                               econtext,
+                                                                                               &isNull,
+                                                                                               &itemIsDone[resind]);
+                                               }
                                        }
                                }
+
+                               MemoryContextSwitchTo(oldContext);
+                               newTuple = NULL;
+                               goto exit;
                        }
                }
        }
@@ -1748,30 +1691,27 @@ ExecTargetList(List *targetlist,
         */
        MemoryContextSwitchTo(oldContext);
 
-       newTuple = (HeapTuple) heap_formtuple(targettype, values, null_head);
+       newTuple = (HeapTuple) heap_formtuple(targettype, values, nulls);
 
 exit:
 
        /*
         * free the status arrays if we palloc'd them
         */
-       if (nodomains > 64)
+       if (nodomains > NPREALLOCDOMAINS)
        {
-               pfree(null_head);
+               pfree(nulls);
                pfree(fjIsNull);
                pfree(itemIsDone);
        }
 
-       /* make sure we are in the right context if we did "goto exit" */
-       MemoryContextSwitchTo(oldContext);
-
        return newTuple;
 }
 
 /* ----------------------------------------------------------------
  *             ExecProject
  *
- *             projects a tuple based in projection info and stores
+ *             projects a tuple based on projection info and stores
  *             it in the specified tuple table slot.
  *
  *             Note: someday soon the executor can be extended to eliminate
@@ -1782,7 +1722,7 @@ exit:
  * ----------------------------------------------------------------
  */
 TupleTableSlot *
-ExecProject(ProjectionInfo *projInfo, bool *isDone)
+ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
 {
        TupleTableSlot *slot;
        List       *targetlist;
@@ -1810,7 +1750,7 @@ ExecProject(ProjectionInfo *projInfo, bool *isDone)
        econtext = projInfo->pi_exprContext;
 
        /*
-        * form a new (result) tuple
+        * form a new result tuple (if possible --- result can be NULL)
         */
        newTuple = ExecTargetList(targetlist,
                                                          len,
@@ -1822,9 +1762,8 @@ ExecProject(ProjectionInfo *projInfo, bool *isDone)
        /*
         * store the tuple in the projection slot and return the slot.
         */
-       return (TupleTableSlot *)
-               ExecStoreTuple(newTuple,/* tuple to store */
-                                          slot,        /* slot to store in */
-                                          InvalidBuffer,       /* tuple has no buffer */
-                                          true);
+       return ExecStoreTuple(newTuple,                 /* tuple to store */
+                                                 slot,                         /* slot to store in */
+                                                 InvalidBuffer,        /* tuple has no buffer */
+                                                 true);
 }
index a3f66d20cad24d39cfdf3921c742ce4d26a00279..d000a4cf50ad0f446a014604e4dd541750d04df0 100644 (file)
@@ -12,7 +12,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/execScan.c,v 1.13 2000/07/17 03:04:53 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/execScan.c,v 1.14 2000/08/24 03:29:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -50,11 +50,10 @@ ExecScan(Scan *node,
 {
        CommonScanState *scanstate;
        EState     *estate;
+       ExprContext *econtext;
        List       *qual;
-       bool            isDone;
+       ExprDoneCond isDone;
        TupleTableSlot *resultSlot;
-       ExprContext *econtext;
-       ProjectionInfo *projInfo;
 
        /* ----------------
         *      Fetch data from node
@@ -65,13 +64,6 @@ ExecScan(Scan *node,
        econtext = scanstate->cstate.cs_ExprContext;
        qual = node->plan.qual;
 
-       /* ----------------
-        *      Reset per-tuple memory context to free any expression evaluation
-        *      storage allocated in the previous tuple cycle.
-        * ----------------
-        */
-       ResetExprContext(econtext);
-
        /* ----------------
         *      Check to see if we're still projecting out tuples from a previous
         *      scan tuple (because there is a function-returning-set in the
@@ -80,14 +72,21 @@ ExecScan(Scan *node,
         */
        if (scanstate->cstate.cs_TupFromTlist)
        {
-               projInfo = scanstate->cstate.cs_ProjInfo;
-               resultSlot = ExecProject(projInfo, &isDone);
-               if (!isDone)
+               resultSlot = ExecProject(scanstate->cstate.cs_ProjInfo, &isDone);
+               if (isDone == ExprMultipleResult)
                        return resultSlot;
                /* Done with that source tuple... */
                scanstate->cstate.cs_TupFromTlist = false;
        }
 
+       /* ----------------
+        *      Reset per-tuple memory context to free any expression evaluation
+        *      storage allocated in the previous tuple cycle.  Note this can't
+        *      happen until we're done projecting out tuples from a scan tuple.
+        * ----------------
+        */
+       ResetExprContext(econtext);
+
        /*
         * get a tuple from the access method loop until we obtain a tuple
         * which passes the qualification.
@@ -121,8 +120,6 @@ ExecScan(Scan *node,
 
                /* ----------------
                 *      check that the current tuple satisfies the qual-clause
-                *      if our qualification succeeds then we may
-                *      leave the loop.
                 *
                 * check for non-nil qual here to avoid a function call to
                 * ExecQual() when the qual is nil ... saves only a few cycles,
@@ -130,7 +127,22 @@ ExecScan(Scan *node,
                 * ----------------
                 */
                if (!qual || ExecQual(qual, econtext, false))
-                       break;
+               {
+                       /* ----------------
+                        *      Found a satisfactory scan tuple.
+                        *
+                        *      Form a projection tuple, store it in the result tuple
+                        *      slot and return it --- unless we find we can project no
+                        *      tuples from this scan tuple, in which case continue scan.
+                        * ----------------
+                        */
+                       resultSlot = ExecProject(scanstate->cstate.cs_ProjInfo, &isDone);
+                       if (isDone != ExprEndResult)
+                       {
+                               scanstate->cstate.cs_TupFromTlist = (isDone == ExprMultipleResult);
+                               return resultSlot;
+                       }
+               }
 
                /* ----------------
                 *      Tuple fails qual, so free per-tuple memory and try again.
@@ -138,18 +150,4 @@ ExecScan(Scan *node,
                 */
                ResetExprContext(econtext);
        }
-
-       /* ----------------
-        *      Found a satisfactory scan tuple.
-        *
-        *      Form a projection tuple, store it in the result tuple
-        *      slot and return it.
-        * ----------------
-        */
-       projInfo = scanstate->cstate.cs_ProjInfo;
-
-       resultSlot = ExecProject(projInfo, &isDone);
-       scanstate->cstate.cs_TupFromTlist = !isDone;
-
-       return resultSlot;
 }
index 001feb267ffe02e0b65f14228ae3f8dc902f0430..58fb68a6113b241f9a5867661039817125c5a680 100644 (file)
@@ -8,22 +8,29 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.37 2000/08/08 15:41:22 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.38 2000/08/24 03:29:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
 #include "access/heapam.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
 #include "executor/execdefs.h"
 #include "executor/executor.h"
 #include "executor/functions.h"
 #include "tcop/pquery.h"
 #include "tcop/tcopprot.h"
 #include "tcop/utility.h"
+#include "utils/builtins.h"
 #include "utils/datum.h"
+#include "utils/syscache.h"
 
 
+/*
+ * We have an execution_state record for each query in the function.
+ */
 typedef enum
 {
        F_EXEC_START, F_EXEC_RUN, F_EXEC_DONE
@@ -39,15 +46,40 @@ typedef struct local_es
 
 #define LAST_POSTQUEL_COMMAND(es) ((es)->next == (execution_state *) NULL)
 
+
+/*
+ * An SQLFunctionCache record is built during the first call,
+ * and linked to from the fn_extra field of the FmgrInfo struct.
+ */
+
+typedef struct
+{
+       int                     typlen;                 /* length of the return type */
+       bool            typbyval;               /* true if return type is pass by value */
+       bool            returnsTuple;   /* true if return type is a tuple */
+
+       TupleTableSlot *funcSlot;       /* if one result we need to copy it before
+                                                                * we end execution of the function and
+                                                                * free stuff */
+
+       /* head of linked list of execution_state records */
+       execution_state *func_state;
+} SQLFunctionCache;
+
+typedef SQLFunctionCache *SQLFunctionCachePtr;
+
+
 /* non-export function prototypes */
+static execution_state *init_execution_state(char *src,
+                                                                                        Oid *argOidVect, int nargs);
+static void init_sql_fcache(FmgrInfo *finfo);
 static TupleDesc postquel_start(execution_state *es);
-static execution_state *init_execution_state(FunctionCachePtr fcache);
 static TupleTableSlot *postquel_getnext(execution_state *es);
 static void postquel_end(execution_state *es);
 static void postquel_sub_params(execution_state *es, FunctionCallInfo fcinfo);
 static Datum postquel_execute(execution_state *es,
                                                          FunctionCallInfo fcinfo,
-                                                         FunctionCachePtr fcache);
+                                                         SQLFunctionCachePtr fcache);
 
 
 static Datum
@@ -69,21 +101,19 @@ ProjectAttribute(HeapTuple tup,
 }
 
 static execution_state *
-init_execution_state(FunctionCachePtr fcache)
+init_execution_state(char *src, Oid *argOidVect, int nargs)
 {
        execution_state *newes;
        execution_state *nextes;
        execution_state *preves;
        List       *queryTree_list,
                           *qtl_item;
-       int                     nargs = fcache->nargs;
 
        newes = (execution_state *) palloc(sizeof(execution_state));
        nextes = newes;
        preves = (execution_state *) NULL;
 
-       queryTree_list = pg_parse_and_rewrite(fcache->src,
-                                                                                 fcache->argOidVect, nargs);
+       queryTree_list = pg_parse_and_rewrite(src, argOidVect, nargs);
 
        foreach(qtl_item, queryTree_list)
        {
@@ -138,6 +168,134 @@ init_execution_state(FunctionCachePtr fcache)
        return newes;
 }
 
+
+static void
+init_sql_fcache(FmgrInfo *finfo)
+{
+       Oid                     foid = finfo->fn_oid;
+       HeapTuple       procedureTuple;
+       HeapTuple       typeTuple;
+       Form_pg_proc procedureStruct;
+       Form_pg_type typeStruct;
+       SQLFunctionCachePtr fcache;
+       Oid                *argOidVect;
+       char       *src;
+       int                     nargs;
+       Datum           tmp;
+       bool            isNull;
+
+       /* ----------------
+        *       get the procedure tuple corresponding to the given function Oid
+        *
+        *       NB: use SearchSysCacheTupleCopy to ensure tuple lives long enough
+        * ----------------
+        */
+       procedureTuple = SearchSysCacheTupleCopy(PROCOID,
+                                                                                        ObjectIdGetDatum(foid),
+                                                                                        0, 0, 0);
+
+       if (!HeapTupleIsValid(procedureTuple))
+               elog(ERROR, "init_sql_fcache: Cache lookup failed for procedure %u",
+                        foid);
+
+       procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple);
+
+       /* ----------------
+        *       get the return type from the procedure tuple
+        * ----------------
+        */
+       typeTuple = SearchSysCacheTuple(TYPEOID,
+                                                  ObjectIdGetDatum(procedureStruct->prorettype),
+                                                                       0, 0, 0);
+
+       if (!HeapTupleIsValid(typeTuple))
+               elog(ERROR, "init_sql_fcache: Cache lookup failed for type %u",
+                        procedureStruct->prorettype);
+
+       typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
+
+       fcache = (SQLFunctionCachePtr) palloc(sizeof(SQLFunctionCache));
+       MemSet(fcache, 0, sizeof(SQLFunctionCache));
+
+       /* ----------------
+        *       get the type length and by-value flag from the type tuple
+        * ----------------
+        */
+       fcache->typlen = typeStruct->typlen;
+       if (typeStruct->typrelid == InvalidOid)
+       {
+               /* The return type is not a relation, so just use byval */
+               fcache->typbyval = typeStruct->typbyval;
+               fcache->returnsTuple = false;
+       }
+       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.
+                */
+               fcache->typbyval = false;
+               fcache->returnsTuple = true;
+       }
+
+       /*
+        * 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 (!finfo->fn_retset && !fcache->typbyval)
+       {
+               TupleTableSlot *slot;
+
+               slot = makeNode(TupleTableSlot);
+               slot->val = (HeapTuple) NULL;
+               slot->ttc_shouldFree = true;
+               slot->ttc_descIsNew = true;
+               slot->ttc_tupleDescriptor = (TupleDesc) NULL;
+               slot->ttc_buffer = InvalidBuffer;
+               slot->ttc_whichplan = -1;
+
+               fcache->funcSlot = slot;
+       }
+       else
+               fcache->funcSlot = NULL;
+
+       nargs = procedureStruct->pronargs;
+
+       if (nargs > 0)
+       {
+               argOidVect = (Oid *) palloc(nargs * sizeof(Oid));
+               memcpy(argOidVect,
+                          procedureStruct->proargtypes,
+                          nargs * sizeof(Oid));
+       }
+       else
+       {
+               argOidVect = (Oid *) NULL;
+       }
+
+       tmp = SysCacheGetAttr(PROCOID,
+                                                 procedureTuple,
+                                                 Anum_pg_proc_prosrc,
+                                                 &isNull);
+       if (isNull)
+               elog(ERROR, "init_sql_fcache: null prosrc for procedure %u",
+                        foid);
+       src = DatumGetCString(DirectFunctionCall1(textout, tmp));
+
+       fcache->func_state = init_execution_state(src, argOidVect, nargs);
+
+       pfree(src);
+
+       heap_freetuple(procedureTuple);
+
+       finfo->fn_extra = (void *) fcache;
+}
+
+
 static TupleDesc
 postquel_start(execution_state *es)
 {
@@ -208,7 +366,7 @@ postquel_sub_params(execution_state *es, FunctionCallInfo fcinfo)
 }
 
 static TupleTableSlot *
-copy_function_result(FunctionCachePtr fcache,
+copy_function_result(SQLFunctionCachePtr fcache,
                                         TupleTableSlot *resultSlot)
 {
        TupleTableSlot *funcSlot;
@@ -219,10 +377,10 @@ copy_function_result(FunctionCachePtr fcache,
        Assert(!TupIsNull(resultSlot));
        resultTuple = resultSlot->val;
 
-       funcSlot = (TupleTableSlot *) fcache->funcSlot;
+       funcSlot = fcache->funcSlot;
 
-       if (funcSlot == (TupleTableSlot *) NULL)
-               return resultSlot;
+       if (funcSlot == NULL)
+               return resultSlot;              /* no need to copy result */
 
        /*
         * If first time through, we have to initialize the funcSlot's
@@ -243,7 +401,7 @@ copy_function_result(FunctionCachePtr fcache,
 static Datum
 postquel_execute(execution_state *es,
                                 FunctionCallInfo fcinfo,
-                                FunctionCachePtr fcache)
+                                SQLFunctionCachePtr fcache)
 {
        TupleTableSlot *slot;
        Datum           value;
@@ -319,7 +477,7 @@ postquel_execute(execution_state *es,
                 * If this is a single valued function we have to end the function
                 * execution now.
                 */
-               if (!fcache->returnsSet)
+               if (!fcinfo->flinfo->fn_retset)
                {
                        postquel_end(es);
                        es->status = F_EXEC_DONE;
@@ -338,11 +496,10 @@ postquel_execute(execution_state *es,
 }
 
 Datum
-postquel_function(FunctionCallInfo fcinfo,
-                                 FunctionCachePtr fcache,
-                                 bool *isDone)
+fmgr_sql(PG_FUNCTION_ARGS)
 {
        MemoryContext oldcontext;
+       SQLFunctionCachePtr fcache;
        execution_state *es;
        Datum           result = 0;
        CommandId       savedId;
@@ -352,7 +509,7 @@ postquel_function(FunctionCallInfo fcinfo,
         * parsetrees, plans, etc, will have sufficient lifetime.  The
         * sub-executor is responsible for deleting per-tuple information.
         */
-       oldcontext = MemoryContextSwitchTo(fcache->fcacheCxt);
+       oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
 
        /*
         * Before we start do anything we must save CurrentScanCommandId to
@@ -362,13 +519,21 @@ postquel_function(FunctionCallInfo fcinfo,
        savedId = GetScanCommandId();
        SetScanCommandId(GetCurrentCommandId());
 
-       es = (execution_state *) fcache->func_state;
-       if (es == NULL)
+       /*
+        * Initialize fcache and execution state if first time through.
+        */
+       fcache = (SQLFunctionCachePtr) fcinfo->flinfo->fn_extra;
+       if (fcache == NULL)
        {
-               es = init_execution_state(fcache);
-               fcache->func_state = (char *) es;
+               init_sql_fcache(fcinfo->flinfo);
+               fcache = (SQLFunctionCachePtr) fcinfo->flinfo->fn_extra;
        }
+       es = fcache->func_state;
+       Assert(es);
 
+       /*
+        * Find first unfinished query in function.
+        */
        while (es && es->status == F_EXEC_DONE)
                es = es->next;
 
@@ -401,7 +566,7 @@ postquel_function(FunctionCallInfo fcinfo,
                /*
                 * Reset the execution states to start over again
                 */
-               es = (execution_state *) fcache->func_state;
+               es = fcache->func_state;
                while (es)
                {
                        es->status = F_EXEC_START;
@@ -411,9 +576,21 @@ postquel_function(FunctionCallInfo fcinfo,
                /*
                 * Let caller know we're finished.
                 */
-               *isDone = true;
+               if (fcinfo->flinfo->fn_retset)
+               {
+                       ReturnSetInfo  *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
+
+                       if (rsi && IsA(rsi, ReturnSetInfo))
+                               rsi->isDone = ExprEndResult;
+                       else
+                               elog(ERROR, "Set-valued function called in context that cannot accept a set");
+                       fcinfo->isnull = true;
+                       result = (Datum) 0;
+               }
+
                MemoryContextSwitchTo(oldcontext);
-               return (fcache->returnsSet) ? (Datum) NULL : result;
+
+               return result;
        }
 
        /*
@@ -422,7 +599,18 @@ postquel_function(FunctionCallInfo fcinfo,
         */
        Assert(LAST_POSTQUEL_COMMAND(es));
 
-       *isDone = false;
+       /*
+        * Let caller know we're not finished.
+        */
+       if (fcinfo->flinfo->fn_retset)
+       {
+               ReturnSetInfo  *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
+
+               if (rsi && IsA(rsi, ReturnSetInfo))
+                       rsi->isDone = ExprMultipleResult;
+               else
+                       elog(ERROR, "Set-valued function called in context that cannot accept a set");
+       }
 
        MemoryContextSwitchTo(oldcontext);
 
index 547a946b4ce81bf941dfb5ae41181d8dd591b0c6..fae1a5b0d87feeef87bef2e7937b1028e8ca85db 100644 (file)
@@ -34,7 +34,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.70 2000/07/17 03:04:53 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.71 2000/08/24 03:29:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -451,7 +451,6 @@ ExecAgg(Agg *node)
        TupleTableSlot *resultSlot;
        HeapTuple       inputTuple;
        int                     aggno;
-       bool            isDone;
        bool            isNull;
 
        /* ---------------------
@@ -523,7 +522,7 @@ ExecAgg(Agg *node)
                                Datum           newVal;
 
                                newVal = ExecEvalExpr(aggref->target, econtext,
-                                                                         &isNull, &isDone);
+                                                                         &isNull, NULL);
 
                                if (aggref->aggdistinct)
                                {
@@ -677,8 +676,9 @@ ExecAgg(Agg *node)
                /*
                 * Form a projection tuple using the aggregate results and the
                 * representative input tuple.  Store it in the result tuple slot.
+                * Note we do not support aggregates returning sets ...
                 */
-               resultSlot = ExecProject(projInfo, &isDone);
+               resultSlot = ExecProject(projInfo, NULL);
 
                /*
                 * If the completed tuple does not match the qualifications, it is
index 8a445b53d4197944d0c7bea3eaec850147c79136..8fc319a77f0e67c770b0c5e12e9de473ca230df3 100644 (file)
@@ -15,7 +15,7 @@
  *       locate group boundaries.
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.37 2000/07/12 02:37:03 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.38 2000/08/24 03:29:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -73,7 +73,6 @@ ExecGroupEveryTuple(Group *node)
        TupleTableSlot *outerslot;
        ProjectionInfo *projInfo;
        TupleTableSlot *resultSlot;
-       bool            isDone;
 
        /* ---------------------
         *      get state info from node
@@ -163,7 +162,7 @@ ExecGroupEveryTuple(Group *node)
        projInfo = grpstate->csstate.cstate.cs_ProjInfo;
 
        econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot;
-       resultSlot = ExecProject(projInfo, &isDone);
+       resultSlot = ExecProject(projInfo, NULL);
 
        return resultSlot;
 }
@@ -185,7 +184,6 @@ ExecGroupOneTuple(Group *node)
        TupleTableSlot *outerslot;
        ProjectionInfo *projInfo;
        TupleTableSlot *resultSlot;
-       bool            isDone;
 
        /* ---------------------
         *      get state info from node
@@ -258,7 +256,7 @@ ExecGroupOneTuple(Group *node)
                                   grpstate->csstate.css_ScanTupleSlot,
                                   InvalidBuffer, false);
        econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot;
-       resultSlot = ExecProject(projInfo, &isDone);
+       resultSlot = ExecProject(projInfo, NULL);
 
        /* save outerTuple if we are not done yet */
        if (!grpstate->grp_done)
index 682afdba4afa7e9620034af6501897a868175b44..9cd85195cdcc4fe85ea3de4328a3c0b95b1ca168 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
- *     $Id: nodeHash.c,v 1.51 2000/08/22 04:06:19 tgl Exp $
+ *     $Id: nodeHash.c,v 1.52 2000/08/24 03:29:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -524,7 +524,6 @@ ExecHashGetBucket(HashJoinTable hashtable,
        int                     bucketno;
        Datum           keyval;
        bool            isNull;
-       bool            isDone;
 
        /* ----------------
         *      Get the join attribute value of the tuple
@@ -535,8 +534,7 @@ ExecHashGetBucket(HashJoinTable hashtable,
         */
        ResetExprContext(econtext);
 
-       keyval = ExecEvalExprSwitchContext(hashkey, econtext,
-                                                                          &isNull, &isDone);
+       keyval = ExecEvalExprSwitchContext(hashkey, econtext, &isNull, NULL);
 
        /* ------------------
         *      compute the hash function
index 54af882db12333b3d914b5091deab068467d99ec..4b3b4a825050e4acdaa5a17c1f3c93030a2ec81f 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.32 2000/07/17 03:04:53 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.33 2000/08/24 03:29:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -55,6 +55,7 @@ ExecHashJoin(HashJoin *node)
        TupleTableSlot *inntuple;
        Node       *outerVar;
        ExprContext *econtext;
+       ExprDoneCond isDone;
        HashJoinTable hashtable;
        HeapTuple       curtuple;
        TupleTableSlot *outerTupleSlot;
@@ -83,13 +84,6 @@ ExecHashJoin(HashJoin *node)
        hashtable = hjstate->hj_HashTable;
        econtext = hjstate->jstate.cs_ExprContext;
 
-       /* ----------------
-        *      Reset per-tuple memory context to free any expression evaluation
-        *      storage allocated in the previous tuple cycle.
-        * ----------------
-        */
-       ResetExprContext(econtext);
-
        /* ----------------
         *      Check to see if we're still projecting out tuples from a previous
         *      join tuple (because there is a function-returning-set in the
@@ -99,15 +93,22 @@ ExecHashJoin(HashJoin *node)
        if (hjstate->jstate.cs_TupFromTlist)
        {
                TupleTableSlot *result;
-               bool            isDone;
 
                result = ExecProject(hjstate->jstate.cs_ProjInfo, &isDone);
-               if (!isDone)
+               if (isDone == ExprMultipleResult)
                        return result;
                /* Done with that source tuple... */
                hjstate->jstate.cs_TupFromTlist = false;
        }
 
+       /* ----------------
+        *      Reset per-tuple memory context to free any expression evaluation
+        *      storage allocated in the previous tuple cycle.  Note this can't
+        *      happen until we're done projecting out tuples from a join tuple.
+        * ----------------
+        */
+       ResetExprContext(econtext);
+
        /* ----------------
         *      if this is the first call, build the hash table for inner relation
         * ----------------
@@ -241,15 +242,15 @@ ExecHashJoin(HashJoin *node)
                         */
                        if (ExecQual(qual, econtext, false))
                        {
-                               ProjectionInfo *projInfo;
                                TupleTableSlot *result;
-                               bool            isDone;
 
                                hjstate->jstate.cs_OuterTupleSlot = outerTupleSlot;
-                               projInfo = hjstate->jstate.cs_ProjInfo;
-                               result = ExecProject(projInfo, &isDone);
-                               hjstate->jstate.cs_TupFromTlist = !isDone;
-                               return result;
+                               result = ExecProject(hjstate->jstate.cs_ProjInfo, &isDone);
+                               if (isDone != ExprEndResult)
+                               {
+                                       hjstate->jstate.cs_TupFromTlist = (isDone == ExprMultipleResult);
+                                       return result;
+                               }
                        }
                }
 
index 57770d2405806ce5c06fadfc2bce8a95ccbce3bb..a8b29514b882e9538792f0d56f4488f816215413 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.53 2000/08/13 02:50:03 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.54 2000/08/24 03:29:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -334,7 +334,6 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent)
        Node       *scanexpr;
        Datum           scanvalue;
        bool            isNull;
-       bool            isDone;
 
        estate = node->scan.plan.state;
        indexstate = node->indxstate;
@@ -411,14 +410,10 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent)
                                                (Node *) get_rightop(clause) :
                                                (Node *) get_leftop(clause);
 
-                                       /*
-                                        * pass in isDone but ignore it.  We don't iterate in
-                                        * quals
-                                        */
                                        scanvalue = ExecEvalExprSwitchContext(scanexpr,
                                                                                                                  econtext,
                                                                                                                  &isNull,
-                                                                                                                 &isDone);
+                                                                                                                 NULL);
                                        scan_keys[j].sk_argument = scanvalue;
                                        if (isNull)
                                                scan_keys[j].sk_flags |= SK_ISNULL;
index a3f92b06901392dec85ab99cb15335a3c09cb394..5a2f45028a0348d44ed9e75352eb472e9e96a44d 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.36 2000/07/12 02:37:03 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.37 2000/08/24 03:29:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -226,18 +226,16 @@ MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext)
        {
                Datum           const_value;
                bool            isNull;
-               bool            isDone;
 
                /* ----------------
                 *       first test if our compare clause is satisfied.
                 *       if so then return true.
                 *
                 *       A NULL result is considered false.
-                *       ignore isDone, don't iterate in quals.
                 * ----------------
                 */
                const_value = ExecEvalExpr((Node *) lfirst(clause), econtext,
-                                                                  &isNull, &isDone);
+                                                                  &isNull, NULL);
 
                if (DatumGetBool(const_value) && !isNull)
                {
@@ -254,7 +252,7 @@ MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext)
                const_value = ExecEvalExpr((Node *) lfirst(eqclause),
                                                                   econtext,
                                                                   &isNull,
-                                                                  &isDone);
+                                                                  NULL);
 
                if (! DatumGetBool(const_value) || isNull)
                        break;                          /* return false */
@@ -447,13 +445,6 @@ ExecMergeJoin(MergeJoin *node)
                innerSkipQual = mergestate->mj_OuterSkipQual;
        }
 
-       /* ----------------
-        *      Reset per-tuple memory context to free any expression evaluation
-        *      storage allocated in the previous tuple cycle.
-        * ----------------
-        */
-       ResetExprContext(econtext);
-
        /* ----------------
         *      Check to see if we're still projecting out tuples from a previous
         *      join tuple (because there is a function-returning-set in the
@@ -463,15 +454,23 @@ ExecMergeJoin(MergeJoin *node)
        if (mergestate->jstate.cs_TupFromTlist)
        {
                TupleTableSlot *result;
-               bool            isDone;
+               ExprDoneCond    isDone;
 
                result = ExecProject(mergestate->jstate.cs_ProjInfo, &isDone);
-               if (!isDone)
+               if (isDone == ExprMultipleResult)
                        return result;
                /* Done with that source tuple... */
                mergestate->jstate.cs_TupFromTlist = false;
        }
 
+       /* ----------------
+        *      Reset per-tuple memory context to free any expression evaluation
+        *      storage allocated in the previous tuple cycle.  Note this can't
+        *      happen until we're done projecting out tuples from a join tuple.
+        * ----------------
+        */
+       ResetExprContext(econtext);
+
        /* ----------------
         *      ok, everything is setup.. let's go to work
         * ----------------
@@ -599,17 +598,19 @@ ExecMergeJoin(MergeJoin *node)
                                         *      projection tuple and return the slot containing it.
                                         * ----------------
                                         */
-                                       ProjectionInfo *projInfo;
                                        TupleTableSlot *result;
-                                       bool            isDone;
+                                       ExprDoneCond isDone;
 
                                        MJ_printf("ExecMergeJoin: **** returning tuple ****\n");
 
-                                       projInfo = mergestate->jstate.cs_ProjInfo;
+                                       result = ExecProject(mergestate->jstate.cs_ProjInfo,
+                                                                                &isDone);
 
-                                       result = ExecProject(projInfo, &isDone);
-                                       mergestate->jstate.cs_TupFromTlist = !isDone;
-                                       return result;
+                                       if (isDone != ExprEndResult)
+                                       {
+                                               mergestate->jstate.cs_TupFromTlist = (isDone == ExprMultipleResult);
+                                               return result;
+                                       }
                                }
                                break;
 
index f59c1b0f602d2d518abb15581308da85752a7946..3685232c7e42b15e5b9777e81a573920d461ec5f 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.19 2000/08/13 02:50:03 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.20 2000/08/24 03:29:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -87,13 +87,6 @@ ExecNestLoop(NestLoop *node)
        outerTupleSlot = nlstate->jstate.cs_OuterTupleSlot;
        econtext->ecxt_outertuple = outerTupleSlot;
 
-       /* ----------------
-        *      Reset per-tuple memory context to free any expression evaluation
-        *      storage allocated in the previous tuple cycle.
-        * ----------------
-        */
-       ResetExprContext(econtext);
-
        /* ----------------
         *      Check to see if we're still projecting out tuples from a previous
         *      join tuple (because there is a function-returning-set in the
@@ -103,15 +96,23 @@ ExecNestLoop(NestLoop *node)
        if (nlstate->jstate.cs_TupFromTlist)
        {
                TupleTableSlot *result;
-               bool            isDone;
+               ExprDoneCond    isDone;
 
                result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone);
-               if (!isDone)
+               if (isDone == ExprMultipleResult)
                        return result;
                /* Done with that source tuple... */
                nlstate->jstate.cs_TupFromTlist = false;
        }
 
+       /* ----------------
+        *      Reset per-tuple memory context to free any expression evaluation
+        *      storage allocated in the previous tuple cycle.  Note this can't
+        *      happen until we're done projecting out tuples from a join tuple.
+        * ----------------
+        */
+       ResetExprContext(econtext);
+
        /* ----------------
         *      Ok, everything is setup for the join so now loop until
         *      we return a qualifying join tuple..
@@ -219,16 +220,18 @@ ExecNestLoop(NestLoop *node)
                         *      using ExecProject().
                         * ----------------
                         */
-                       ProjectionInfo *projInfo;
                        TupleTableSlot *result;
-                       bool            isDone;
+                       ExprDoneCond isDone;
 
                        ENL1_printf("qualification succeeded, projecting tuple");
 
-                       projInfo = nlstate->jstate.cs_ProjInfo;
-                       result = ExecProject(projInfo, &isDone);
-                       nlstate->jstate.cs_TupFromTlist = !isDone;
-                       return result;
+                       result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone);
+
+                       if (isDone != ExprEndResult)
+                       {
+                               nlstate->jstate.cs_TupFromTlist = (isDone == ExprMultipleResult);
+                               return result;
+                       }
                }
 
                /* ----------------
index 770cc47ccc4f405b80e6f1083e1b35ac6c1c36f7..e36037de7108f63004728135099024581b025fc2 100644 (file)
@@ -34,7 +34,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/nodeResult.c,v 1.15 2000/07/17 03:04:53 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/nodeResult.c,v 1.16 2000/08/24 03:29:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -67,8 +67,7 @@ ExecResult(Result *node)
        TupleTableSlot *resultSlot;
        Plan       *outerPlan;
        ExprContext *econtext;
-       bool            isDone;
-       ProjectionInfo *projInfo;
+       ExprDoneCond isDone;
 
        /* ----------------
         *      initialize the result node's state
@@ -77,13 +76,6 @@ ExecResult(Result *node)
        resstate = node->resstate;
        econtext = resstate->cstate.cs_ExprContext;
 
-       /* ----------------
-        *      Reset per-tuple memory context to free any expression evaluation
-        *      storage allocated in the previous tuple cycle.
-        * ----------------
-        */
-       ResetExprContext(econtext);
-
        /* ----------------
         *       check constant qualifications like (2 > 1), if not already done
         * ----------------
@@ -111,12 +103,20 @@ ExecResult(Result *node)
        if (resstate->cstate.cs_TupFromTlist)
        {
                resultSlot = ExecProject(resstate->cstate.cs_ProjInfo, &isDone);
-               if (!isDone)
+               if (isDone == ExprMultipleResult)
                        return resultSlot;
                /* Done with that source tuple... */
                resstate->cstate.cs_TupFromTlist = false;
        }
 
+       /* ----------------
+        *      Reset per-tuple memory context to free any expression evaluation
+        *      storage allocated in the previous tuple cycle.  Note this can't
+        *      happen until we're done projecting out tuples from a scan tuple.
+        * ----------------
+        */
+       ResetExprContext(econtext);
+
        /* ----------------
         *      if rs_done is true then it means that we were asked to return
         *      a constant tuple and we already did the last time ExecResult()
@@ -124,7 +124,7 @@ ExecResult(Result *node)
         *      Either way, now we are through.
         * ----------------
         */
-       if (!resstate->rs_done)
+       while (!resstate->rs_done)
        {
                outerPlan = outerPlan(node);
 
@@ -159,13 +159,18 @@ ExecResult(Result *node)
                }
 
                /* ----------------
-                *       form the result tuple using ExecProject(), and return it.
+                *       form the result tuple using ExecProject(), and return it
+                *       --- unless the projection produces an empty set, in which case
+                *       we must loop back to see if there are more outerPlan tuples.
                 * ----------------
                 */
-               projInfo = resstate->cstate.cs_ProjInfo;
-               resultSlot = ExecProject(projInfo, &isDone);
-               resstate->cstate.cs_TupFromTlist = !isDone;
-               return resultSlot;
+               resultSlot = ExecProject(resstate->cstate.cs_ProjInfo, &isDone);
+
+               if (isDone != ExprEndResult)
+               {
+                       resstate->cstate.cs_TupFromTlist = (isDone == ExprMultipleResult);
+                       return resultSlot;
+               }
        }
 
        return NULL;
index 3d331c714f74ef853d3150052499a663ba135bf1..aee6911e5e424d5794d624db3473f9373c393e25 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.26 2000/07/12 02:37:04 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.27 2000/08/24 03:29:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -40,7 +40,6 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull)
        MemoryContext oldcontext;
        TupleTableSlot *slot;
        Datum           result;
-       bool            isDone;
        bool            found = false;  /* TRUE if got at least one subplan tuple */
        List       *lst;
 
@@ -67,9 +66,7 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull)
                        prm->value = ExecEvalExprSwitchContext((Node *) lfirst(pvar),
                                                                                                   econtext,
                                                                                                   &(prm->isnull),
-                                                                                                  &isDone);
-                       if (!isDone)
-                               elog(ERROR, "ExecSubPlan: set values not supported for params");
+                                                                                                  NULL);
                        pvar = lnext(pvar);
                }
                plan->chgParam = nconc(plan->chgParam, listCopy(node->parParam));
@@ -189,9 +186,7 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull)
                         * Now we can eval the combining operator for this column.
                         */
                        expresult = ExecEvalExprSwitchContext((Node *) expr, econtext,
-                                                                                                 &expnull, &isDone);
-                       if (!isDone)
-                               elog(ERROR, "ExecSubPlan: set values not supported for combining operators");
+                                                                                                 &expnull, NULL);
 
                        /*
                         * Combine the result into the row result as appropriate.
index ac6511dcbfb5a0a4688bbc51a06738c7fa5834fe..055d07998b9108837c99ec3543d9b562fbb601a3 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/nodeTidscan.c,v 1.11 2000/08/03 19:19:30 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/nodeTidscan.c,v 1.12 2000/08/24 03:29:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -38,7 +38,6 @@ TidListCreate(List *evalList, ExprContext *econtext, ItemPointer *tidList)
        List       *lst;
        ItemPointer itemptr;
        bool            isNull;
-       bool            isDone;
        int                     numTids = 0;
 
        foreach(lst, evalList)
@@ -47,8 +46,8 @@ TidListCreate(List *evalList, ExprContext *econtext, ItemPointer *tidList)
                        DatumGetPointer(ExecEvalExprSwitchContext(lfirst(lst),
                                                                                                          econtext,
                                                                                                          &isNull,
-                                                                                                         &isDone));
-               if (itemptr && ItemPointerIsValid(itemptr))
+                                                                                                         NULL));
+               if (!isNull && itemptr && ItemPointerIsValid(itemptr))
                {
                        tidList[numTids] = itemptr;
                        numTids++;
index 27ae4056d99124a82d478d4e0b4fa08c48d686b5..05f32d25972d24dd10a4f72dba920807d0b9c704 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.93 2000/08/13 02:50:04 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.94 2000/08/24 03:29:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1380,9 +1380,8 @@ clause_pred_clause_test(Expr *predicate, Node *clause)
                                                          copyObject(clause_const),
                                                          copyObject(pred_const));
 
-#ifndef OMIT_PARTIAL_INDEX
        test_result = ExecEvalExpr((Node *) test_expr, NULL, &isNull, NULL);
-#endif  /* OMIT_PARTIAL_INDEX */
+
        if (isNull)
        {
                elog(DEBUG, "clause_pred_clause_test: null test result");
index 22d93b26e7ba33c0d62f378565f34f1852b66582..cf0b6dd703c76b3a904c4322b47b49b9b11ee5fc 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.72 2000/08/21 17:22:34 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.73 2000/08/24 03:29:05 tgl Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -1447,7 +1447,6 @@ simplify_op_or_func(Expr *expr, List *args)
        bool            has_nonconst_input = false;
        bool            has_null_input = false;
        bool            const_is_null;
-       bool            isDone;
 
        /*
         * Check for constant inputs and especially constant-NULL inputs.
@@ -1566,8 +1565,7 @@ simplify_op_or_func(Expr *expr, List *args)
        econtext = MakeExprContext(NULL, CurrentMemoryContext);
 
        const_val = ExecEvalExprSwitchContext((Node *) newexpr, econtext,
-                                                                                 &const_is_null, &isDone);
-       Assert(isDone);                         /* if this isn't set, we blew it... */
+                                                                                 &const_is_null, NULL);
 
        /* Must copy result out of sub-context used by expression eval */
        const_val = datumCopy(const_val, resultTypByVal, resultTypLen);
index 551e7d2b585510092c12ba8fd244f620c0d7162f..bad9401c609f9118525c006b425477c6a1bf50eb 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.88 2000/08/20 00:44:18 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.89 2000/08/24 03:29:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -560,13 +560,13 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
        {                                                       /* we know all of these fields already */
 
                /*
-                * We create a funcnode with a placeholder function SetEval.
-                * SetEval() never actually gets executed.      When the function
-                * evaluation routines see it, they use the funcid projected out
-                * from the relation as the actual function to call. Example:
-                * retrieve (emp.mgr.name) The plan for this will scan the emp
-                * relation, projecting out the mgr attribute, which is a funcid.
-                * This function is then called (instead of SetEval) and "name" is
+                * We create a funcnode with a placeholder function seteval().
+                * At runtime, seteval() will execute the function identified
+                * by the funcid it receives as parameter.
+                *
+                * Example: retrieve (emp.mgr.name).  The plan for this will scan the
+                * emp relation, projecting out the mgr attribute, which is a funcid.
+                * This function is then called (via seteval()) and "name" is
                 * projected from its result.
                 */
                funcid = F_SETEVAL;
index 2bda101538ce88af27123b4df1649b75e393d97e..5d363ea3e690d085a5545a816d629ee1ae873feb 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.44 2000/08/08 15:42:04 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.45 2000/08/24 03:29:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -34,7 +34,6 @@
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
 
-static void disallow_setop(char *op, Type optype, Node *operand);
 static bool fitsInFloat(Value *value);
 
 
@@ -71,7 +70,6 @@ make_operand(char *opname,
 
        if (tree != NULL)
        {
-               disallow_setop(opname, target_type, tree);
                /* must coerce? */
                if (target_typeId != orig_typeId)
                        result = coerce_type(NULL, tree, orig_typeId, target_typeId, -1);
@@ -96,21 +94,6 @@ make_operand(char *opname,
 }      /* make_operand() */
 
 
-static void
-disallow_setop(char *op, Type optype, Node *operand)
-{
-       if (operand == NULL)
-               return;
-
-       if (nodeTag(operand) == T_Iter)
-       {
-               elog(ERROR, "An operand to the '%s' operator returns a set of %s,"
-                        "\n\tbut '%s' takes single values, not sets.",
-                        op, typeTypeName(optype), op);
-       }
-}
-
-
 /* make_op()
  * Operator construction.
  *
index a0c0aa8cb14e3738e13119f483ff61b2ab7d5f3a..9a5f05134cd45f275b130f8d11ac16cdd5fd9578 100644 (file)
@@ -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);
 }
index 080f70b6ce8fd5d9e4444a42be7a5879558faff0..9186f34d104737af5144649f8eb71b7f42ef4ca2 100644 (file)
 /*-------------------------------------------------------------------------
  *
  * 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);
-}
index 7b8aca3feb4a6887aa400ea9d98b0d35824b71f8..c7fbf251f9d96b6d52d591be6f5c4af852c20edf 100644 (file)
@@ -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
index b50f74fdf4fba9b8d46074f58ad26e02dae2f215..c7e092c7020fec5a75f4518ba29c4a136c698439 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_proc.h,v 1.163 2000/08/23 06:04:44 thomas Exp $
+ * $Id: pg_proc.h,v 1.164 2000/08/24 03:29:08 tgl Exp $
  *
  * NOTES
  *       The script catalog/genbki.sh reads this file and generates .bki
@@ -1266,8 +1266,8 @@ DATA(insert OID = 1036 (  aclremove                  PGUID 12 f t f t 2 f 1034 "1034 1033" 10
 DESCR("remove ACL item");
 DATA(insert OID = 1037 (  aclcontains     PGUID 12 f t f t 2 f 16 "1034 1033" 100 0 0 100  aclcontains - ));
 DESCR("does ACL contain item?");
-DATA(insert OID = 1038 (  seteval                 PGUID 12 f t f t 1 f 23 "26" 100 0 0 100  seteval - ));
-DESCR("");
+DATA(insert OID = 1038 (  seteval                 PGUID 12 f t f t 1 t 23 "26" 100 0 0 100  seteval - ));
+DESCR("internal function supporting PostQuel-style sets");
 DATA(insert OID = 1044 (  bpcharin                PGUID 12 f t t t 3 f 1042 "0 26 23" 100 0 0 100 bpcharin - ));
 DESCR("(internal)");
 DATA(insert OID = 1045 (  bpcharout               PGUID 12 f t t t 1 f 23 "0" 100 0 0 100  bpcharout - ));
index f0a1e381e1ae383a8ea686eb92b0fe15981a0dd1..2a7a4bdcfa82464f297871162df9275581022b1a 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: defrem.h,v 1.19 2000/02/18 09:29:49 inoue Exp $
+ * $Id: defrem.h,v 1.20 2000/08/24 03:29:09 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -44,7 +44,6 @@ extern void CreateFunction(ProcedureStmt *stmt, CommandDest dest);
 extern void DefineOperator(char *name, List *parameters);
 extern void DefineAggregate(char *name, List *parameters);
 extern void DefineType(char *name, List *parameters);
-extern void CreateFunction(ProcedureStmt *stmt, CommandDest dest);
 
 /*
  * prototypes in remove.c
index c7d85e2e6da4282b5dc564efcaefe7a443688dc9..3fa3673b201de37bd695a2d53c60d3ed9dcc54c8 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: execFlatten.h,v 1.11 2000/01/26 05:58:05 momjian Exp $
+ * $Id: execFlatten.h,v 1.12 2000/08/24 03:29:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #define EXECFLATTEN_H
 
 #include "nodes/execnodes.h"
-#include "nodes/relation.h"
+#include "nodes/parsenodes.h"
 
-extern Datum ExecEvalIter(Iter *iterNode, ExprContext *econtext, bool *resultIsNull, bool *iterIsDone);
 
-extern void ExecEvalFjoin(TargetEntry *tlist, ExprContext *econtext, bool *isNullVect, bool *fj_isDone);
+extern Datum ExecEvalIter(Iter *iterNode, ExprContext *econtext,
+                                                 bool *isNull, ExprDoneCond *isDone);
+
+extern void ExecEvalFjoin(TargetEntry *tlist, ExprContext *econtext,
+                                                 bool *isNullVect, ExprDoneCond *fj_isDone);
 
 
 #endif  /* EXECFLATTEN_H */
index ea589e06dd95e9f8f9555aa56a59f8e21c4de3fc..e39a60a6a2478140b6d7db997e766264e2e49f3b 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: executor.h,v 1.48 2000/08/21 20:55:29 tgl Exp $
+ * $Id: executor.h,v 1.49 2000/08/24 03:29:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -78,14 +78,20 @@ extern char *GetAttributeByNum(TupleTableSlot *slot, AttrNumber attrno,
                                  bool *isNull);
 extern char *GetAttributeByName(TupleTableSlot *slot, char *attname,
                                                                bool *isNull);
+extern Datum ExecMakeFunctionResult(FunctionCachePtr fcache,
+                                                                       List *arguments,
+                                                                       ExprContext *econtext,
+                                                                       bool *isNull,
+                                                                       ExprDoneCond *isDone);
 extern Datum ExecEvalExpr(Node *expression, ExprContext *econtext,
-                                                 bool *isNull, bool *isDone);
+                                                 bool *isNull, ExprDoneCond *isDone);
 extern Datum ExecEvalExprSwitchContext(Node *expression, ExprContext *econtext,
-                                                                          bool *isNull, bool *isDone);
+                                                                          bool *isNull, ExprDoneCond *isDone);
 extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull);
 extern int     ExecTargetListLength(List *targetlist);
 extern int     ExecCleanTargetListLength(List *targetlist);
-extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo, bool *isDone);
+extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo,
+                                                                  ExprDoneCond *isDone);
 
 /*
  * prototypes from functions in execScan.c
index 0e55be1980ed24fc6d6a9a1bfbe605ffe45a68f3..649e38e142f2360251012b3566d53549e9eb89ad 100644 (file)
@@ -1,24 +1,21 @@
 /*-------------------------------------------------------------------------
  *
  * functions.h
- *
+ *             Declarations for execution of SQL-language functions.
  *
  *
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: functions.h,v 1.13 2000/08/08 15:42:39 tgl Exp $
+ * $Id: functions.h,v 1.14 2000/08/24 03:29:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef FUNCTIONS_H
 #define FUNCTIONS_H
 
-#include "nodes/parsenodes.h"
-#include "utils/syscache.h"
+#include "fmgr.h"
 
-extern Datum postquel_function(FunctionCallInfo fcinfo,
-                                                          FunctionCachePtr fcache,
-                                                          bool *isDone);
+extern Datum fmgr_sql(PG_FUNCTION_ARGS);
 
 #endif  /* FUNCTIONS_H */
index 274f8f959d86edde2e4ff9e3bf62ce9c4aadbccf..28634262bcc13a964342cb652a23bcfb1058dd93 100644 (file)
@@ -11,7 +11,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: fmgr.h,v 1.9 2000/07/29 03:26:47 tgl Exp $
+ * $Id: fmgr.h,v 1.10 2000/08/24 03:29:11 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -41,7 +41,9 @@ typedef struct
     Oid         fn_oid;     /* OID of function (NOT of handler, if any) */
     short       fn_nargs;   /* 0..FUNC_MAX_ARGS, or -1 if variable arg count */
     bool        fn_strict;  /* function is "strict" (NULL in => NULL out) */
+       bool            fn_retset;      /* function returns a set (over multiple calls) */
     void       *fn_extra;   /* extra space for use by handler */
+       MemoryContext fn_mcxt;  /* memory context to store fn_extra in */
 } FmgrInfo;
 
 /*
index 1ec14f4a9697d35d758ae4b25380b5af9cad950f..9626dbf8b1c0c658122af32860d5ed3118b0c5b9 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: execnodes.h,v 1.47 2000/08/22 04:06:22 tgl Exp $
+ * $Id: execnodes.h,v 1.48 2000/08/24 03:29:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -120,6 +120,31 @@ typedef struct ExprContext
        List       *ecxt_range_table;
 } ExprContext;
 
+/*
+ * Set-result status returned by ExecEvalExpr()
+ */
+typedef enum
+{
+       ExprSingleResult,                       /* expression does not return a set */
+       ExprMultipleResult,                     /* this result is an element of a set */
+       ExprEndResult                           /* there are no more elements in the set */
+} ExprDoneCond;
+
+/*
+ * When calling a function that might return a set (multiple rows),
+ * a node of this type is passed as fcinfo->resultinfo to allow
+ * return status to be passed back.  A function returning set should
+ * raise an error if no such resultinfo is provided.
+ *
+ * XXX this mechanism is a quick hack and probably needs to be redesigned.
+ */
+typedef struct ReturnSetInfo
+{
+       NodeTag         type;
+       ExprDoneCond isDone;
+} ReturnSetInfo;
+
+
 /* ----------------
  *             ProjectionInfo node information
  *
index f05e0a0e1c93b784b9ee1c23bcbfd9890011ff8b..d825c8fe395bcd8f64f4d191277884213608104e 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: nodes.h,v 1.74 2000/08/11 23:46:54 tgl Exp $
+ * $Id: nodes.h,v 1.75 2000/08/24 03:29:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -227,7 +227,9 @@ typedef enum NodeTag
         * TAGS FOR FUNCTION-CALL CONTEXT AND RESULTINFO NODES (cf. fmgr.h)
         *---------------------
         */
-       T_TriggerData = 800                     /* in commands/trigger.h */
+       T_TriggerData = 800,            /* in commands/trigger.h */
+       T_ReturnSetInfo                         /* in nodes/execnodes.h */
+
 } NodeTag;
 
 /*
index 1ad9a3d082ad1bd5ecfbf87771482a910fb7b692..0ef350687dc41a6466dc0192260b875c20424aba 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: primnodes.h,v 1.46 2000/08/08 15:42:59 tgl Exp $
+ * $Id: primnodes.h,v 1.47 2000/08/24 03:29:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include "access/attnum.h"
 #include "nodes/pg_list.h"
-#include "utils/fcache.h"
+
+/* FunctionCache is declared in utils/fcache.h */
+typedef struct FunctionCache *FunctionCachePtr;
+
 
 /* ----------------------------------------------------------------
  *                                             node definitions
index 1e16af6874f5f951ad936fe818dc7a8b2f4cb1b5..f1ebe40b5907e7ad2b32dac1ace9ec50ff41be9c 100644 (file)
@@ -7,13 +7,14 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: builtins.h,v 1.133 2000/08/23 06:04:49 thomas Exp $
+ * $Id: builtins.h,v 1.134 2000/08/24 03:29:14 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef BUILTINS_H
 #define BUILTINS_H
 
+#include "fmgr.h"
 #include "nodes/relation.h"            /* for amcostestimate parameters */
 #include "storage/itemptr.h"
 #include "utils/numeric.h"
index efae7613959a75a75491483dd5fd3c91ef73d102..a30c72836085085e0fb90c2a1fa86331d93f9fcd 100644 (file)
@@ -11,7 +11,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: fcache.h,v 1.13 2000/08/08 15:43:12 tgl Exp $
+ * $Id: fcache.h,v 1.14 2000/08/24 03:29:14 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #define FCACHE_H
 
 #include "fmgr.h"
+#include "nodes/execnodes.h"
 
+/*
+ * A FunctionCache record is built for all functions regardless of language.
+ *
+ * We store the fmgr lookup info to avoid recomputing it on each call.
+ * We also store a prebuilt FunctionCallInfo struct.  When evaluating a
+ * function-returning-set, fcinfo holds the argument values across calls
+ * so that we need not re-evaluate the arguments for each call.  Even for
+ * non-set functions, fcinfo saves a few cycles per call by allowing us to
+ * avoid redundant setup of its fields.
+ */
 
-typedef struct
+typedef struct FunctionCache
 {
-       FmgrInfo        func;                   /* info for fmgr call mechanism */
-       Oid                     foid;                   /* oid of the function in pg_proc */
-       Oid                     language;               /* oid of the language in pg_language */
-       int                     typlen;                 /* length of the return type */
-       bool            typbyval;               /* true if return type is pass by value */
-
-       bool            returnsTuple;   /* true if return type is a tuple */
-       bool            returnsSet;             /* true if func returns a set (multi rows) */
-
-       bool            hasSetArg;              /* true if func is part of a nested dot
-                                                                * expr whose argument is func returning a
-                                                                * set ugh! */
-
-       /* If additional info is added to an existing fcache, be sure to
-        * allocate it in the fcacheCxt.
+       /*
+        * Function manager's lookup info for the target function.
         */
-       MemoryContext fcacheCxt;        /* context the fcache lives in */
-
-       int                     nargs;                  /* actual number of arguments */
-       Oid                *argOidVect;         /* oids of all the argument types */
-
-       char       *src;                        /* source code of the function */
-       char       *bin;                        /* binary object code ?? */
-       char       *func_state;         /* function_state struct for execution */
-
-       Pointer         funcSlot;               /* if one result we need to copy it before
-                                                                * we end execution of the function and
-                                                                * free stuff */
-
-       Datum           setArg;                 /* current argument for nested dot
-                                                                * execution Nested dot expressions mean
-                                                                * we have funcs whose argument is a set
-                                                                * of tuples */
+       FmgrInfo        func;
+       /*
+        * Per-call info for calling the target function.  Unvarying fields
+        * are set up by init_fcache().  Argument values are filled in as needed.
+        */
+       FunctionCallInfoData fcinfo;
+       /*
+        * "Resultinfo" node --- used only if target function returns a set.
+        */
+       ReturnSetInfo rsinfo;
+       /*
+        * argsValid is true when we are evaluating a set-valued function and
+        * we are in the middle of a call series; we want to pass the same
+        * argument values to the function again (and again, until it returns
+        * ExprEndResult).
+        */
+       bool            argsValid;              /* TRUE if fcinfo contains valid arguments */
+       /*
+        * hasSetArg is true if we found a set-valued argument to the function.
+        * This causes the function result to be a set as well.
+        */
+       bool            hasSetArg;              /* some argument returns a set */
 } FunctionCache;
 
-typedef FunctionCache *FunctionCachePtr;
+
+extern FunctionCachePtr init_fcache(Oid foid, int nargs,
+                                                                       MemoryContext fcacheCxt);
 
 #endif  /* FCACHE_H */
diff --git a/src/include/utils/fcache2.h b/src/include/utils/fcache2.h
deleted file mode 100644 (file)
index 984edde..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * fcache2.h
- *
- *
- *
- * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- * $Id: fcache2.h,v 1.10 2000/01/26 05:58:38 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-#ifndef FCACHE2_H
-#define FCACHE2_H
-
-#include "nodes/execnodes.h"
-
-extern void setFcache(Node *node, Oid foid, List *argList, ExprContext *econtext);
-
-#endif  /* FCACHE2_H */
index a7b5d6826e2bc53087ffdea921eb7dae28353bc8..7f284ac3467d3f13bbf2cf07291f86dd7e9dd1e3 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: sets.h,v 1.7 2000/06/09 01:11:15 tgl Exp $
+ * $Id: sets.h,v 1.8 2000/08/24 03:29:14 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "fmgr.h"
 
 
-/* Temporary name of set, before SetDefine changes it. */
-#define GENERICSETNAME "zyxset"
+/* Temporary name of a set function, before SetDefine changes it. */
+#define GENERICSETNAME "ZYX#Set#ZYX"
 
 extern Oid     SetDefine(char *querystr, char *typename);
+
 extern Datum seteval(PG_FUNCTION_ARGS);
 
 #endif  /* SETS_H */
index 0096b3825c63019e0aac39c97e3bb0b0d285a401..6c761c50f0c843e93cc8d4914c2051090b2a3408 100644 (file)
@@ -3,7 +3,7 @@
  *                       procedural language
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.27 2000/08/13 02:50:35 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.28 2000/08/24 03:29:15 tgl Exp $
  *
  *       This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -2200,7 +2200,6 @@ exec_eval_simple_expr(PLpgSQL_execstate * estate,
        int                     fno;
        int                     i;
        bool            isnull;
-       bool            isdone;
        ExprContext *econtext;
        ParamListInfo paramLI;
 
@@ -2274,9 +2273,7 @@ exec_eval_simple_expr(PLpgSQL_execstate * estate,
         * Initialize things
         * ----------
         */
-       *isNull = FALSE;
        *rettype = expr->plan_simple_type;
-       isdone = FALSE;
 
        /* ----------
         * Clear the function cache
@@ -2292,14 +2289,17 @@ exec_eval_simple_expr(PLpgSQL_execstate * estate,
        retval = ExecEvalExprSwitchContext(expr->plan_simple_expr,
                                                                           econtext,
                                                                           isNull,
-                                                                          &isdone);
+                                                                          NULL);
        SPI_pop();
 
        /*
         * Copy the result out of the expression-evaluation memory context,
         * so that we can free the expression context.
         */
-       retval = datumCopy(retval, get_typbyval(*rettype), get_typlen(*rettype));
+       if (! *isNull)
+               retval = datumCopy(retval,
+                                                  get_typbyval(*rettype),
+                                                  get_typlen(*rettype));
 
        FreeExprContext(econtext);