SQL-language functions are now callable in ordinary fmgr contexts ...
authorTom Lane
Thu, 24 Aug 2000 03:29:15 +0000 (03:29 +0000)
committerTom Lane
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:
-        *
-        * (( expr) ( 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);