Commit 02f8c9a3 authored by Tom Lane's avatar Tom Lane

Fix ExecMakeTableFunctionResult() to work with generic expressions as

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.
parent 9ee7409e
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 @@ ...@@ -40,6 +40,7 @@
#include "executor/functions.h" #include "executor/functions.h"
#include "executor/nodeSubplan.h" #include "executor/nodeSubplan.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "parser/parse_expr.h"
#include "utils/array.h" #include "utils/array.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/fcache.h" #include "utils/fcache.h"
...@@ -820,30 +821,50 @@ ExecMakeFunctionResult(FunctionCachePtr fcache, ...@@ -820,30 +821,50 @@ ExecMakeFunctionResult(FunctionCachePtr fcache,
* object. (If function returns an empty set, we just return NULL instead.) * object. (If function returns an empty set, we just return NULL instead.)
*/ */
Tuplestorestate * Tuplestorestate *
ExecMakeTableFunctionResult(Expr *funcexpr, ExecMakeTableFunctionResult(Node *funcexpr,
ExprContext *econtext, ExprContext *econtext,
TupleDesc expectedDesc, TupleDesc expectedDesc,
TupleDesc *returnDesc) TupleDesc *returnDesc)
{ {
Tuplestorestate *tupstore = NULL; Tuplestorestate *tupstore = NULL;
TupleDesc tupdesc = NULL; TupleDesc tupdesc = NULL;
Func *func; Oid funcrettype;
List *argList;
FunctionCachePtr fcache;
FunctionCallInfoData fcinfo; FunctionCallInfoData fcinfo;
ReturnSetInfo rsinfo; ReturnSetInfo rsinfo;
ExprDoneCond argDone;
MemoryContext callerContext; MemoryContext callerContext;
MemoryContext oldcontext; MemoryContext oldcontext;
TupleTableSlot *slot; TupleTableSlot *slot;
bool direct_function_call;
bool first_time = true; bool first_time = true;
bool returnsTuple = false; bool returnsTuple = false;
/* Extract data from function-call expression node */ /*
if (!funcexpr || !IsA(funcexpr, Expr) ||funcexpr->opType != FUNC_EXPR) * Normally the passed expression tree will be a FUNC_EXPR, since the
elog(ERROR, "ExecMakeTableFunctionResult: expression is not a function call"); * grammar only allows a function call at the top level of a table
func = (Func *) funcexpr->oper; * function reference. However, if the function doesn't return set then
argList = funcexpr->args; * 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.
*/
if (funcexpr &&
IsA(funcexpr, Expr) &&
((Expr *) funcexpr)->opType == FUNC_EXPR)
{
Func *func;
List *argList;
FunctionCachePtr fcache;
ExprDoneCond argDone;
/*
* This path is similar to ExecMakeFunctionResult.
*/
direct_function_call = true;
funcrettype = ((Expr *) funcexpr)->typeOid;
func = (Func *) ((Expr *) funcexpr)->oper;
argList = ((Expr *) funcexpr)->args;
/* /*
* get the fcache from the Func node. If it is NULL, then initialize * get the fcache from the Func node. If it is NULL, then initialize
...@@ -889,11 +910,20 @@ ExecMakeTableFunctionResult(Expr *funcexpr, ...@@ -889,11 +910,20 @@ ExecMakeTableFunctionResult(Expr *funcexpr,
} }
} }
} }
}
else
{
/* Treat funcexpr as a generic expression */
direct_function_call = false;
funcrettype = exprType(funcexpr);
}
/* /*
* Prepare a resultinfo node for communication. We always do this * Prepare a resultinfo node for communication. We always do this
* even if not expecting a set result, so that we can pass * 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; fcinfo.resultinfo = (Node *) &rsinfo;
rsinfo.type = T_ReturnSetInfo; rsinfo.type = T_ReturnSetInfo;
...@@ -906,12 +936,13 @@ ExecMakeTableFunctionResult(Expr *funcexpr, ...@@ -906,12 +936,13 @@ ExecMakeTableFunctionResult(Expr *funcexpr,
rsinfo.setDesc = NULL; 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); 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 (;;) for (;;)
{ {
...@@ -920,15 +951,23 @@ ExecMakeTableFunctionResult(Expr *funcexpr, ...@@ -920,15 +951,23 @@ ExecMakeTableFunctionResult(Expr *funcexpr,
/* /*
* reset per-tuple memory context before each call of the * reset per-tuple memory context before each call of the
* function. This cleans up any local memory the function may leak * function or expression. This cleans up any local memory the
* when called. * function may leak when called.
*/ */
ResetExprContext(econtext); ResetExprContext(econtext);
/* Call the function one time */ /* Call the function or expression one time */
if (direct_function_call)
{
fcinfo.isnull = false; fcinfo.isnull = false;
rsinfo.isDone = ExprSingleResult; rsinfo.isDone = ExprSingleResult;
result = FunctionCallInvoke(&fcinfo); result = FunctionCallInvoke(&fcinfo);
}
else
{
result = ExecEvalExpr(funcexpr, econtext,
&fcinfo.isnull, &rsinfo.isDone);
}
/* Which protocol does function want to use? */ /* Which protocol does function want to use? */
if (rsinfo.returnMode == SFRM_ValuePerCall) if (rsinfo.returnMode == SFRM_ValuePerCall)
...@@ -949,8 +988,6 @@ ExecMakeTableFunctionResult(Expr *funcexpr, ...@@ -949,8 +988,6 @@ ExecMakeTableFunctionResult(Expr *funcexpr,
*/ */
if (first_time) if (first_time)
{ {
Oid funcrettype = funcexpr->typeOid;
oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
if (funcrettype == RECORDOID || if (funcrettype == RECORDOID ||
get_typtype(funcrettype) == 'c') get_typtype(funcrettype) == 'c')
...@@ -960,7 +997,9 @@ ExecMakeTableFunctionResult(Expr *funcexpr, ...@@ -960,7 +997,9 @@ ExecMakeTableFunctionResult(Expr *funcexpr,
* TupleTableSlot; use its descriptor * TupleTableSlot; use its descriptor
*/ */
slot = (TupleTableSlot *) DatumGetPointer(result); slot = (TupleTableSlot *) DatumGetPointer(result);
if (fcinfo.isnull || !slot || !IsA(slot, TupleTableSlot) || if (fcinfo.isnull ||
!slot ||
!IsA(slot, TupleTableSlot) ||
!slot->ttc_tupleDescriptor) !slot->ttc_tupleDescriptor)
elog(ERROR, "ExecMakeTableFunctionResult: Invalid result from function returning tuple"); elog(ERROR, "ExecMakeTableFunctionResult: Invalid result from function returning tuple");
tupdesc = CreateTupleDescCopy(slot->ttc_tupleDescriptor); tupdesc = CreateTupleDescCopy(slot->ttc_tupleDescriptor);
...@@ -993,7 +1032,9 @@ ExecMakeTableFunctionResult(Expr *funcexpr, ...@@ -993,7 +1032,9 @@ ExecMakeTableFunctionResult(Expr *funcexpr,
if (returnsTuple) if (returnsTuple)
{ {
slot = (TupleTableSlot *) DatumGetPointer(result); slot = (TupleTableSlot *) DatumGetPointer(result);
if (fcinfo.isnull || !slot || !IsA(slot, TupleTableSlot) || if (fcinfo.isnull ||
!slot ||
!IsA(slot, TupleTableSlot) ||
TupIsNull(slot)) TupIsNull(slot))
elog(ERROR, "ExecMakeTableFunctionResult: Invalid result from function returning tuple"); elog(ERROR, "ExecMakeTableFunctionResult: Invalid result from function returning tuple");
tuple = slot->val; tuple = slot->val;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -78,7 +78,7 @@ FunctionNext(FunctionScan *node)
TupleDesc funcTupdesc; TupleDesc funcTupdesc;
scanstate->tuplestorestate = tuplestorestate = scanstate->tuplestorestate = tuplestorestate =
ExecMakeTableFunctionResult((Expr *) scanstate->funcexpr, ExecMakeTableFunctionResult(scanstate->funcexpr,
econtext, econtext,
scanstate->tupdesc, scanstate->tupdesc,
&funcTupdesc); &funcTupdesc);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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, ...@@ -81,7 +81,7 @@ extern Datum ExecMakeFunctionResult(FunctionCachePtr fcache,
ExprContext *econtext, ExprContext *econtext,
bool *isNull, bool *isNull,
ExprDoneCond *isDone); ExprDoneCond *isDone);
extern Tuplestorestate *ExecMakeTableFunctionResult(Expr *funcexpr, extern Tuplestorestate *ExecMakeTableFunctionResult(Node *funcexpr,
ExprContext *econtext, ExprContext *econtext,
TupleDesc expectedDesc, TupleDesc expectedDesc,
TupleDesc *returnDesc); TupleDesc *returnDesc);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment