Thanks to visit codestin.com
Credit goes to github.com

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)