]> granicus.if.org Git - postgresql/commitdiff
Fix ExecMakeTableFunctionResult() to work with generic expressions as
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 1 Dec 2002 20:27:32 +0000 (20:27 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 1 Dec 2002 20:27:32 +0000 (20:27 +0000)
well as function calls.  This is needed for cases where the planner has
constant-folded or inlined the original function call.  Possibly we should
back-patch this change into 7.3 branch as well.

src/backend/executor/execQual.c
src/backend/executor/nodeFunctionscan.c
src/include/executor/executor.h

index 90a38f31ee2ee50f3d8ebbbe9974e393707085db..cb31c025df24878c8ae6388e36719f47fe384f31 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.111 2002/11/30 21:25:04 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.112 2002/12/01 20:27:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -40,6 +40,7 @@
 #include "executor/functions.h"
 #include "executor/nodeSubplan.h"
 #include "miscadmin.h"
+#include "parser/parse_expr.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/fcache.h"
@@ -820,80 +821,109 @@ ExecMakeFunctionResult(FunctionCachePtr fcache,
  * object.     (If function returns an empty set, we just return NULL instead.)
  */
 Tuplestorestate *
-ExecMakeTableFunctionResult(Expr *funcexpr,
+ExecMakeTableFunctionResult(Node *funcexpr,
                                                        ExprContext *econtext,
                                                        TupleDesc expectedDesc,
                                                        TupleDesc *returnDesc)
 {
        Tuplestorestate *tupstore = NULL;
        TupleDesc       tupdesc = NULL;
-       Func       *func;
-       List       *argList;
-       FunctionCachePtr fcache;
+       Oid                     funcrettype;
        FunctionCallInfoData fcinfo;
        ReturnSetInfo rsinfo;
-       ExprDoneCond argDone;
        MemoryContext callerContext;
        MemoryContext oldcontext;
        TupleTableSlot *slot;
+       bool            direct_function_call;
        bool            first_time = true;
        bool            returnsTuple = false;
 
-       /* Extract data from function-call expression node */
-       if (!funcexpr || !IsA(funcexpr, Expr) ||funcexpr->opType != FUNC_EXPR)
-               elog(ERROR, "ExecMakeTableFunctionResult: expression is not a function call");
-       func = (Func *) funcexpr->oper;
-       argList = funcexpr->args;
-
        /*
-        * get the fcache from the Func node. If it is NULL, then initialize
-        * it
+        * Normally the passed expression tree will be a FUNC_EXPR, since the
+        * grammar only allows a function call at the top level of a table
+        * function reference.  However, if the function doesn't return set then
+        * the planner might have replaced the function call via constant-folding
+        * or inlining.  So if we see any other kind of expression node, execute
+        * it via the general ExecEvalExpr() code; the only difference is that
+        * we don't get a chance to pass a special ReturnSetInfo to any functions
+        * buried in the expression.
         */
-       fcache = func->func_fcache;
-       if (fcache == NULL)
+       if (funcexpr &&
+               IsA(funcexpr, Expr) &&
+               ((Expr *) funcexpr)->opType == FUNC_EXPR)
        {
-               fcache = init_fcache(func->funcid, length(argList),
-                                                        econtext->ecxt_per_query_memory);
-               func->func_fcache = fcache;
-       }
+               Func       *func;
+               List       *argList;
+               FunctionCachePtr fcache;
+               ExprDoneCond argDone;
 
-       /*
-        * Evaluate the function's argument list.
-        *
-        * Note: ideally, we'd do this in the per-tuple context, but then the
-        * argument values would disappear when we reset the context in the
-        * inner loop.  So do it in caller context.  Perhaps we should make a
-        * separate context just to hold the evaluated arguments?
-        */
-       MemSet(&fcinfo, 0, sizeof(fcinfo));
-       fcinfo.flinfo = &(fcache->func);
-       argDone = ExecEvalFuncArgs(&fcinfo, argList, econtext);
-       /* We don't allow sets in the arguments of the table function */
-       if (argDone != ExprSingleResult)
-               elog(ERROR, "Set-valued function called in context that cannot accept a set");
+               /*
+                * This path is similar to ExecMakeFunctionResult.
+                */
+               direct_function_call = true;
 
-       /*
-        * If function is strict, and there are any NULL arguments, skip
-        * calling the function and return NULL (actually an empty set).
-        */
-       if (fcache->func.fn_strict)
-       {
-               int                     i;
+               funcrettype = ((Expr *) funcexpr)->typeOid;
+               func = (Func *) ((Expr *) funcexpr)->oper;
+               argList = ((Expr *) funcexpr)->args;
 
-               for (i = 0; i < fcinfo.nargs; i++)
+               /*
+                * get the fcache from the Func node. If it is NULL, then initialize
+                * it
+                */
+               fcache = func->func_fcache;
+               if (fcache == NULL)
                {
-                       if (fcinfo.argnull[i])
+                       fcache = init_fcache(func->funcid, length(argList),
+                                                                econtext->ecxt_per_query_memory);
+                       func->func_fcache = fcache;
+               }
+
+               /*
+                * Evaluate the function's argument list.
+                *
+                * Note: ideally, we'd do this in the per-tuple context, but then the
+                * argument values would disappear when we reset the context in the
+                * inner loop.  So do it in caller context.  Perhaps we should make a
+                * separate context just to hold the evaluated arguments?
+                */
+               MemSet(&fcinfo, 0, sizeof(fcinfo));
+               fcinfo.flinfo = &(fcache->func);
+               argDone = ExecEvalFuncArgs(&fcinfo, argList, econtext);
+               /* We don't allow sets in the arguments of the table function */
+               if (argDone != ExprSingleResult)
+                       elog(ERROR, "Set-valued function called in context that cannot accept a set");
+
+               /*
+                * If function is strict, and there are any NULL arguments, skip
+                * calling the function and return NULL (actually an empty set).
+                */
+               if (fcache->func.fn_strict)
+               {
+                       int                     i;
+
+                       for (i = 0; i < fcinfo.nargs; i++)
                        {
-                               *returnDesc = NULL;
-                               return NULL;
+                               if (fcinfo.argnull[i])
+                               {
+                                       *returnDesc = NULL;
+                                       return NULL;
+                               }
                        }
                }
        }
+       else
+       {
+               /* Treat funcexpr as a generic expression */
+               direct_function_call = false;
+               funcrettype = exprType(funcexpr);
+       }
 
        /*
         * Prepare a resultinfo node for communication.  We always do this
         * even if not expecting a set result, so that we can pass
-        * expectedDesc.
+        * expectedDesc.  In the generic-expression case, the expression
+        * doesn't actually get to see the resultinfo, but set it up anyway
+        * because we use some of the fields as our own state variables.
         */
        fcinfo.resultinfo = (Node *) &rsinfo;
        rsinfo.type = T_ReturnSetInfo;
@@ -906,12 +936,13 @@ ExecMakeTableFunctionResult(Expr *funcexpr,
        rsinfo.setDesc = NULL;
 
        /*
-        * Switch to short-lived context for calling the function.
+        * Switch to short-lived context for calling the function or expression.
         */
        callerContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
 
        /*
-        * Loop to handle the ValuePerCall protocol.
+        * Loop to handle the ValuePerCall protocol (which is also the same
+        * behavior needed in the generic ExecEvalExpr path).
         */
        for (;;)
        {
@@ -920,15 +951,23 @@ ExecMakeTableFunctionResult(Expr *funcexpr,
 
                /*
                 * reset per-tuple memory context before each call of the
-                * function. This cleans up any local memory the function may leak
-                * when called.
+                * function or expression. This cleans up any local memory the
+                * function may leak when called.
                 */
                ResetExprContext(econtext);
 
-               /* Call the function one time */
-               fcinfo.isnull = false;
-               rsinfo.isDone = ExprSingleResult;
-               result = FunctionCallInvoke(&fcinfo);
+               /* Call the function or expression one time */
+               if (direct_function_call)
+               {
+                       fcinfo.isnull = false;
+                       rsinfo.isDone = ExprSingleResult;
+                       result = FunctionCallInvoke(&fcinfo);
+               }
+               else
+               {
+                       result = ExecEvalExpr(funcexpr, econtext,
+                                                                 &fcinfo.isnull, &rsinfo.isDone);
+               }
 
                /* Which protocol does function want to use? */
                if (rsinfo.returnMode == SFRM_ValuePerCall)
@@ -949,8 +988,6 @@ ExecMakeTableFunctionResult(Expr *funcexpr,
                         */
                        if (first_time)
                        {
-                               Oid                     funcrettype = funcexpr->typeOid;
-
                                oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
                                if (funcrettype == RECORDOID ||
                                        get_typtype(funcrettype) == 'c')
@@ -960,7 +997,9 @@ ExecMakeTableFunctionResult(Expr *funcexpr,
                                         * TupleTableSlot; use its descriptor
                                         */
                                        slot = (TupleTableSlot *) DatumGetPointer(result);
-                                       if (fcinfo.isnull || !slot || !IsA(slot, TupleTableSlot) ||
+                                       if (fcinfo.isnull ||
+                                               !slot ||
+                                               !IsA(slot, TupleTableSlot) ||
                                                !slot->ttc_tupleDescriptor)
                                                elog(ERROR, "ExecMakeTableFunctionResult: Invalid result from function returning tuple");
                                        tupdesc = CreateTupleDescCopy(slot->ttc_tupleDescriptor);
@@ -993,7 +1032,9 @@ ExecMakeTableFunctionResult(Expr *funcexpr,
                        if (returnsTuple)
                        {
                                slot = (TupleTableSlot *) DatumGetPointer(result);
-                               if (fcinfo.isnull || !slot || !IsA(slot, TupleTableSlot) ||
+                               if (fcinfo.isnull ||
+                                       !slot ||
+                                       !IsA(slot, TupleTableSlot) ||
                                        TupIsNull(slot))
                                        elog(ERROR, "ExecMakeTableFunctionResult: Invalid result from function returning tuple");
                                tuple = slot->val;
index cf8d74a06f2b93125b3af732478e395206350ced..97da70b2b7860fd3f0d890529275ef7cf4f7fb6e 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/nodeFunctionscan.c,v 1.12 2002/09/04 20:31:18 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/nodeFunctionscan.c,v 1.13 2002/12/01 20:27:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -78,7 +78,7 @@ FunctionNext(FunctionScan *node)
                TupleDesc       funcTupdesc;
 
                scanstate->tuplestorestate = tuplestorestate =
-                       ExecMakeTableFunctionResult((Expr *) scanstate->funcexpr,
+                       ExecMakeTableFunctionResult(scanstate->funcexpr,
                                                                                econtext,
                                                                                scanstate->tupdesc,
                                                                                &funcTupdesc);
index 312c56fa85c6717b28291259fdb10d45cbeefb2e..33b83bce6fe29caff2966ee7e80f5f8b7d381967 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: executor.h,v 1.79 2002/11/30 05:21:03 tgl Exp $
+ * $Id: executor.h,v 1.80 2002/12/01 20:27:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -81,7 +81,7 @@ extern Datum ExecMakeFunctionResult(FunctionCachePtr fcache,
                                           ExprContext *econtext,
                                           bool *isNull,
                                           ExprDoneCond *isDone);
-extern Tuplestorestate *ExecMakeTableFunctionResult(Expr *funcexpr,
+extern Tuplestorestate *ExecMakeTableFunctionResult(Node *funcexpr,
                                                        ExprContext *econtext,
                                                        TupleDesc expectedDesc,
                                                        TupleDesc *returnDesc);