Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 02f8c9a

Browse files
committed
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.
1 parent 9ee7409 commit 02f8c9a

File tree

3 files changed

+103
-62
lines changed

3 files changed

+103
-62
lines changed

src/backend/executor/execQual.c

Lines changed: 99 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.111 2002/11/30 21:25:04 tgl Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.112 2002/12/01 20:27:32 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -40,6 +40,7 @@
4040
#include "executor/functions.h"
4141
#include "executor/nodeSubplan.h"
4242
#include "miscadmin.h"
43+
#include "parser/parse_expr.h"
4344
#include "utils/array.h"
4445
#include "utils/builtins.h"
4546
#include "utils/fcache.h"
@@ -820,80 +821,109 @@ ExecMakeFunctionResult(FunctionCachePtr fcache,
820821
* object. (If function returns an empty set, we just return NULL instead.)
821822
*/
822823
Tuplestorestate *
823-
ExecMakeTableFunctionResult(Expr *funcexpr,
824+
ExecMakeTableFunctionResult(Node *funcexpr,
824825
ExprContext *econtext,
825826
TupleDesc expectedDesc,
826827
TupleDesc *returnDesc)
827828
{
828829
Tuplestorestate *tupstore = NULL;
829830
TupleDesc tupdesc = NULL;
830-
Func *func;
831-
List *argList;
832-
FunctionCachePtr fcache;
831+
Oid funcrettype;
833832
FunctionCallInfoData fcinfo;
834833
ReturnSetInfo rsinfo;
835-
ExprDoneCond argDone;
836834
MemoryContext callerContext;
837835
MemoryContext oldcontext;
838836
TupleTableSlot *slot;
837+
bool direct_function_call;
839838
bool first_time = true;
840839
bool returnsTuple = false;
841840

842-
/* Extract data from function-call expression node */
843-
if (!funcexpr || !IsA(funcexpr, Expr) ||funcexpr->opType != FUNC_EXPR)
844-
elog(ERROR, "ExecMakeTableFunctionResult: expression is not a function call");
845-
func = (Func *) funcexpr->oper;
846-
argList = funcexpr->args;
847-
848841
/*
849-
* get the fcache from the Func node. If it is NULL, then initialize
850-
* it
842+
* Normally the passed expression tree will be a FUNC_EXPR, since the
843+
* grammar only allows a function call at the top level of a table
844+
* function reference. However, if the function doesn't return set then
845+
* the planner might have replaced the function call via constant-folding
846+
* or inlining. So if we see any other kind of expression node, execute
847+
* it via the general ExecEvalExpr() code; the only difference is that
848+
* we don't get a chance to pass a special ReturnSetInfo to any functions
849+
* buried in the expression.
851850
*/
852-
fcache = func->func_fcache;
853-
if (fcache == NULL)
851+
if (funcexpr &&
852+
IsA(funcexpr, Expr) &&
853+
((Expr *) funcexpr)->opType == FUNC_EXPR)
854854
{
855-
fcache = init_fcache(func->funcid, length(argList),
856-
econtext->ecxt_per_query_memory);
857-
func->func_fcache = fcache;
858-
}
855+
Func *func;
856+
List *argList;
857+
FunctionCachePtr fcache;
858+
ExprDoneCond argDone;
859859

860-
/*
861-
* Evaluate the function's argument list.
862-
*
863-
* Note: ideally, we'd do this in the per-tuple context, but then the
864-
* argument values would disappear when we reset the context in the
865-
* inner loop. So do it in caller context. Perhaps we should make a
866-
* separate context just to hold the evaluated arguments?
867-
*/
868-
MemSet(&fcinfo, 0, sizeof(fcinfo));
869-
fcinfo.flinfo = &(fcache->func);
870-
argDone = ExecEvalFuncArgs(&fcinfo, argList, econtext);
871-
/* We don't allow sets in the arguments of the table function */
872-
if (argDone != ExprSingleResult)
873-
elog(ERROR, "Set-valued function called in context that cannot accept a set");
860+
/*
861+
* This path is similar to ExecMakeFunctionResult.
862+
*/
863+
direct_function_call = true;
874864

875-
/*
876-
* If function is strict, and there are any NULL arguments, skip
877-
* calling the function and return NULL (actually an empty set).
878-
*/
879-
if (fcache->func.fn_strict)
880-
{
881-
int i;
865+
funcrettype = ((Expr *) funcexpr)->typeOid;
866+
func = (Func *) ((Expr *) funcexpr)->oper;
867+
argList = ((Expr *) funcexpr)->args;
882868

883-
for (i = 0; i < fcinfo.nargs; i++)
869+
/*
870+
* get the fcache from the Func node. If it is NULL, then initialize
871+
* it
872+
*/
873+
fcache = func->func_fcache;
874+
if (fcache == NULL)
884875
{
885-
if (fcinfo.argnull[i])
876+
fcache = init_fcache(func->funcid, length(argList),
877+
econtext->ecxt_per_query_memory);
878+
func->func_fcache = fcache;
879+
}
880+
881+
/*
882+
* Evaluate the function's argument list.
883+
*
884+
* Note: ideally, we'd do this in the per-tuple context, but then the
885+
* argument values would disappear when we reset the context in the
886+
* inner loop. So do it in caller context. Perhaps we should make a
887+
* separate context just to hold the evaluated arguments?
888+
*/
889+
MemSet(&fcinfo, 0, sizeof(fcinfo));
890+
fcinfo.flinfo = &(fcache->func);
891+
argDone = ExecEvalFuncArgs(&fcinfo, argList, econtext);
892+
/* We don't allow sets in the arguments of the table function */
893+
if (argDone != ExprSingleResult)
894+
elog(ERROR, "Set-valued function called in context that cannot accept a set");
895+
896+
/*
897+
* If function is strict, and there are any NULL arguments, skip
898+
* calling the function and return NULL (actually an empty set).
899+
*/
900+
if (fcache->func.fn_strict)
901+
{
902+
int i;
903+
904+
for (i = 0; i < fcinfo.nargs; i++)
886905
{
887-
*returnDesc = NULL;
888-
return NULL;
906+
if (fcinfo.argnull[i])
907+
{
908+
*returnDesc = NULL;
909+
return NULL;
910+
}
889911
}
890912
}
891913
}
914+
else
915+
{
916+
/* Treat funcexpr as a generic expression */
917+
direct_function_call = false;
918+
funcrettype = exprType(funcexpr);
919+
}
892920

893921
/*
894922
* Prepare a resultinfo node for communication. We always do this
895923
* even if not expecting a set result, so that we can pass
896-
* expectedDesc.
924+
* expectedDesc. In the generic-expression case, the expression
925+
* doesn't actually get to see the resultinfo, but set it up anyway
926+
* because we use some of the fields as our own state variables.
897927
*/
898928
fcinfo.resultinfo = (Node *) &rsinfo;
899929
rsinfo.type = T_ReturnSetInfo;
@@ -906,12 +936,13 @@ ExecMakeTableFunctionResult(Expr *funcexpr,
906936
rsinfo.setDesc = NULL;
907937

908938
/*
909-
* Switch to short-lived context for calling the function.
939+
* Switch to short-lived context for calling the function or expression.
910940
*/
911941
callerContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
912942

913943
/*
914-
* Loop to handle the ValuePerCall protocol.
944+
* Loop to handle the ValuePerCall protocol (which is also the same
945+
* behavior needed in the generic ExecEvalExpr path).
915946
*/
916947
for (;;)
917948
{
@@ -920,15 +951,23 @@ ExecMakeTableFunctionResult(Expr *funcexpr,
920951

921952
/*
922953
* reset per-tuple memory context before each call of the
923-
* function. This cleans up any local memory the function may leak
924-
* when called.
954+
* function or expression. This cleans up any local memory the
955+
* function may leak when called.
925956
*/
926957
ResetExprContext(econtext);
927958

928-
/* Call the function one time */
929-
fcinfo.isnull = false;
930-
rsinfo.isDone = ExprSingleResult;
931-
result = FunctionCallInvoke(&fcinfo);
959+
/* Call the function or expression one time */
960+
if (direct_function_call)
961+
{
962+
fcinfo.isnull = false;
963+
rsinfo.isDone = ExprSingleResult;
964+
result = FunctionCallInvoke(&fcinfo);
965+
}
966+
else
967+
{
968+
result = ExecEvalExpr(funcexpr, econtext,
969+
&fcinfo.isnull, &rsinfo.isDone);
970+
}
932971

933972
/* Which protocol does function want to use? */
934973
if (rsinfo.returnMode == SFRM_ValuePerCall)
@@ -949,8 +988,6 @@ ExecMakeTableFunctionResult(Expr *funcexpr,
949988
*/
950989
if (first_time)
951990
{
952-
Oid funcrettype = funcexpr->typeOid;
953-
954991
oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
955992
if (funcrettype == RECORDOID ||
956993
get_typtype(funcrettype) == 'c')
@@ -960,7 +997,9 @@ ExecMakeTableFunctionResult(Expr *funcexpr,
960997
* TupleTableSlot; use its descriptor
961998
*/
962999
slot = (TupleTableSlot *) DatumGetPointer(result);
963-
if (fcinfo.isnull || !slot || !IsA(slot, TupleTableSlot) ||
1000+
if (fcinfo.isnull ||
1001+
!slot ||
1002+
!IsA(slot, TupleTableSlot) ||
9641003
!slot->ttc_tupleDescriptor)
9651004
elog(ERROR, "ExecMakeTableFunctionResult: Invalid result from function returning tuple");
9661005
tupdesc = CreateTupleDescCopy(slot->ttc_tupleDescriptor);
@@ -993,7 +1032,9 @@ ExecMakeTableFunctionResult(Expr *funcexpr,
9931032
if (returnsTuple)
9941033
{
9951034
slot = (TupleTableSlot *) DatumGetPointer(result);
996-
if (fcinfo.isnull || !slot || !IsA(slot, TupleTableSlot) ||
1035+
if (fcinfo.isnull ||
1036+
!slot ||
1037+
!IsA(slot, TupleTableSlot) ||
9971038
TupIsNull(slot))
9981039
elog(ERROR, "ExecMakeTableFunctionResult: Invalid result from function returning tuple");
9991040
tuple = slot->val;

src/backend/executor/nodeFunctionscan.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/executor/nodeFunctionscan.c,v 1.12 2002/09/04 20:31:18 momjian Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/executor/nodeFunctionscan.c,v 1.13 2002/12/01 20:27:32 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -78,7 +78,7 @@ FunctionNext(FunctionScan *node)
7878
TupleDesc funcTupdesc;
7979

8080
scanstate->tuplestorestate = tuplestorestate =
81-
ExecMakeTableFunctionResult((Expr *) scanstate->funcexpr,
81+
ExecMakeTableFunctionResult(scanstate->funcexpr,
8282
econtext,
8383
scanstate->tupdesc,
8484
&funcTupdesc);

src/include/executor/executor.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
10-
* $Id: executor.h,v 1.79 2002/11/30 05:21:03 tgl Exp $
10+
* $Id: executor.h,v 1.80 2002/12/01 20:27:32 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -81,7 +81,7 @@ extern Datum ExecMakeFunctionResult(FunctionCachePtr fcache,
8181
ExprContext *econtext,
8282
bool *isNull,
8383
ExprDoneCond *isDone);
84-
extern Tuplestorestate *ExecMakeTableFunctionResult(Expr *funcexpr,
84+
extern Tuplestorestate *ExecMakeTableFunctionResult(Node *funcexpr,
8585
ExprContext *econtext,
8686
TupleDesc expectedDesc,
8787
TupleDesc *returnDesc);

0 commit comments

Comments
 (0)