Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitae3129f

Browse files
committed
Quick-and-dirty fix for recursive plpgsql functions, per bug report from
Frank Miles 7-Sep-01. This is really just sticking a finger in the dike.Frank's case works now, but we still couldn't support a recursive functionreturning a set. Really need to restructure querytrees and executionstate so that the querytree is *read only*. We've run into this over andover and over again ... it has to happen sometime soon.
1 parentac0c234 commitae3129f

File tree

5 files changed

+123
-92
lines changed

5 files changed

+123
-92
lines changed

‎src/backend/executor/execQual.c

Lines changed: 61 additions & 44 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.87 2001/06/19 22:39:11 tgl Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.88 2001/09/21 00:11:30 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -54,9 +54,8 @@ static Datum ExecEvalOper(Expr *opClause, ExprContext *econtext,
5454
bool*isNull,ExprDoneCond*isDone);
5555
staticDatumExecEvalFunc(Expr*funcClause,ExprContext*econtext,
5656
bool*isNull,ExprDoneCond*isDone);
57-
staticExprDoneCondExecEvalFuncArgs(FunctionCachePtrfcache,
58-
List*argList,
59-
ExprContext*econtext);
57+
staticExprDoneCondExecEvalFuncArgs(FunctionCallInfofcinfo,
58+
List*argList,ExprContext*econtext);
6059
staticDatumExecEvalNot(Expr*notclause,ExprContext*econtext,bool*isNull);
6160
staticDatumExecEvalAnd(Expr*andExpr,ExprContext*econtext,bool*isNull);
6261
staticDatumExecEvalOr(Expr*orExpr,ExprContext*econtext,bool*isNull);
@@ -600,7 +599,7 @@ GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull)
600599
* Evaluate arguments for a function.
601600
*/
602601
staticExprDoneCond
603-
ExecEvalFuncArgs(FunctionCachePtrfcache,
602+
ExecEvalFuncArgs(FunctionCallInfofcinfo,
604603
List*argList,
605604
ExprContext*econtext)
606605
{
@@ -615,14 +614,13 @@ ExecEvalFuncArgs(FunctionCachePtr fcache,
615614
{
616615
ExprDoneCondthisArgIsDone;
617616

618-
fcache->fcinfo.arg[i]=ExecEvalExpr((Node*)lfirst(arg),
619-
econtext,
620-
&fcache->fcinfo.argnull[i],
621-
&thisArgIsDone);
617+
fcinfo->arg[i]=ExecEvalExpr((Node*)lfirst(arg),
618+
econtext,
619+
&fcinfo->argnull[i],
620+
&thisArgIsDone);
622621

623622
if (thisArgIsDone!=ExprSingleResult)
624623
{
625-
626624
/*
627625
* We allow only one argument to have a set value; we'd need
628626
* much more complexity to keep track of multiple set
@@ -631,12 +629,13 @@ ExecEvalFuncArgs(FunctionCachePtr fcache,
631629
*/
632630
if (argIsDone!=ExprSingleResult)
633631
elog(ERROR,"Functions and operators can take only one set argument");
634-
fcache->hasSetArg= true;
635632
argIsDone=thisArgIsDone;
636633
}
637634
i++;
638635
}
639636

637+
fcinfo->nargs=i;
638+
640639
returnargIsDone;
641640
}
642641

@@ -656,19 +655,25 @@ ExecMakeFunctionResult(FunctionCachePtr fcache,
656655
ExprDoneCond*isDone)
657656
{
658657
Datumresult;
658+
FunctionCallInfoDatafcinfo;
659+
ReturnSetInforsinfo;/* for functions returning sets */
659660
ExprDoneCondargDone;
661+
boolhasSetArg;
660662
inti;
661663

662664
/*
663665
* arguments is a list of expressions to evaluate before passing to
664666
* the function manager. We skip the evaluation if it was already
665667
* done in the previous call (ie, we are continuing the evaluation of
666668
* a set-valued function).Otherwise, collect the current argument
667-
* values intofcache->fcinfo.
669+
* values into fcinfo.
668670
*/
669-
if (fcache->fcinfo.nargs>0&&!fcache->argsValid)
671+
if (!fcache->setArgsValid)
670672
{
671-
argDone=ExecEvalFuncArgs(fcache,arguments,econtext);
673+
/* Need to prep callinfo structure */
674+
MemSet(&fcinfo,0,sizeof(fcinfo));
675+
fcinfo.flinfo=&(fcache->func);
676+
argDone=ExecEvalFuncArgs(&fcinfo,arguments,econtext);
672677
if (argDone==ExprEndResult)
673678
{
674679
/* input is an empty set, so return an empty set. */
@@ -679,15 +684,33 @@ ExecMakeFunctionResult(FunctionCachePtr fcache,
679684
elog(ERROR,"Set-valued function called in context that cannot accept a set");
680685
return (Datum)0;
681686
}
687+
hasSetArg= (argDone!=ExprSingleResult);
688+
}
689+
else
690+
{
691+
/* Copy callinfo from previous evaluation */
692+
memcpy(&fcinfo,&fcache->setArgs,sizeof(fcinfo));
693+
hasSetArg=fcache->setHasSetArg;
694+
/* Reset flag (we may set it again below) */
695+
fcache->setArgsValid= false;
696+
}
697+
698+
/*
699+
* If function returns set, prepare a resultinfo node for
700+
* communication
701+
*/
702+
if (fcache->func.fn_retset)
703+
{
704+
fcinfo.resultinfo= (Node*)&rsinfo;
705+
rsinfo.type=T_ReturnSetInfo;
682706
}
683707

684708
/*
685709
* now return the value gotten by calling the function manager,
686710
* passing the function the evaluated parameter values.
687711
*/
688-
if (fcache->func.fn_retset||fcache->hasSetArg)
712+
if (fcache->func.fn_retset||hasSetArg)
689713
{
690-
691714
/*
692715
* We need to return a set result.Complain if caller not ready
693716
* to accept one.
@@ -705,7 +728,6 @@ ExecMakeFunctionResult(FunctionCachePtr fcache,
705728
*/
706729
for (;;)
707730
{
708-
709731
/*
710732
* If function is strict, and there are any NULL arguments,
711733
* skip calling the function (at least for this set of args).
@@ -714,9 +736,9 @@ ExecMakeFunctionResult(FunctionCachePtr fcache,
714736

715737
if (fcache->func.fn_strict)
716738
{
717-
for (i=0;i<fcache->fcinfo.nargs;i++)
739+
for (i=0;i<fcinfo.nargs;i++)
718740
{
719-
if (fcache->fcinfo.argnull[i])
741+
if (fcinfo.argnull[i])
720742
{
721743
callit= false;
722744
break;
@@ -726,11 +748,11 @@ ExecMakeFunctionResult(FunctionCachePtr fcache,
726748

727749
if (callit)
728750
{
729-
fcache->fcinfo.isnull= false;
730-
fcache->rsinfo.isDone=ExprSingleResult;
731-
result=FunctionCallInvoke(&fcache->fcinfo);
732-
*isNull=fcache->fcinfo.isnull;
733-
*isDone=fcache->rsinfo.isDone;
751+
fcinfo.isnull= false;
752+
rsinfo.isDone=ExprSingleResult;
753+
result=FunctionCallInvoke(&fcinfo);
754+
*isNull=fcinfo.isnull;
755+
*isDone=rsinfo.isDone;
734756
}
735757
else
736758
{
@@ -741,14 +763,17 @@ ExecMakeFunctionResult(FunctionCachePtr fcache,
741763

742764
if (*isDone!=ExprEndResult)
743765
{
744-
745766
/*
746767
* Got a result from current argument.If function itself
747-
* returns set,flag that we wanttoreuse current
748-
*argument values on next call.
768+
* returns set,save the current argument valuestore-use
769+
*on the next call.
749770
*/
750771
if (fcache->func.fn_retset)
751-
fcache->argsValid= true;
772+
{
773+
memcpy(&fcache->setArgs,&fcinfo,sizeof(fcinfo));
774+
fcache->setHasSetArg=hasSetArg;
775+
fcache->setArgsValid= true;
776+
}
752777

753778
/*
754779
* Make sure we say we are returning a set, even if the
@@ -759,22 +784,15 @@ ExecMakeFunctionResult(FunctionCachePtr fcache,
759784
}
760785

761786
/* Else, done with this argument */
762-
fcache->argsValid= false;
763-
764-
if (!fcache->hasSetArg)
787+
if (!hasSetArg)
765788
break;/* input not a set, so done */
766789

767790
/* Re-eval args to get the next element of the input set */
768-
argDone=ExecEvalFuncArgs(fcache,arguments,econtext);
791+
argDone=ExecEvalFuncArgs(&fcinfo,arguments,econtext);
769792

770793
if (argDone!=ExprMultipleResult)
771794
{
772-
773-
/*
774-
* End of arguments, so reset the hasSetArg flag and say
775-
* "Done"
776-
*/
777-
fcache->hasSetArg= false;
795+
/* End of argument set, so we're done. */
778796
*isNull= true;
779797
*isDone=ExprEndResult;
780798
result= (Datum)0;
@@ -789,7 +807,6 @@ ExecMakeFunctionResult(FunctionCachePtr fcache,
789807
}
790808
else
791809
{
792-
793810
/*
794811
* Non-set case: much easier.
795812
*
@@ -798,18 +815,18 @@ ExecMakeFunctionResult(FunctionCachePtr fcache,
798815
*/
799816
if (fcache->func.fn_strict)
800817
{
801-
for (i=0;i<fcache->fcinfo.nargs;i++)
818+
for (i=0;i<fcinfo.nargs;i++)
802819
{
803-
if (fcache->fcinfo.argnull[i])
820+
if (fcinfo.argnull[i])
804821
{
805822
*isNull= true;
806823
return (Datum)0;
807824
}
808825
}
809826
}
810-
fcache->fcinfo.isnull= false;
811-
result=FunctionCallInvoke(&fcache->fcinfo);
812-
*isNull=fcache->fcinfo.isnull;
827+
fcinfo.isnull= false;
828+
result=FunctionCallInvoke(&fcinfo);
829+
*isNull=fcinfo.isnull;
813830
}
814831

815832
returnresult;

‎src/backend/utils/cache/fcache.c

Lines changed: 8 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/utils/cache/Attic/fcache.c,v 1.39 2001/03/22 03:59:57 momjian Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/utils/cache/Attic/fcache.c,v 1.40 2001/09/21 00:11:31 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -17,18 +17,19 @@
1717
#include"utils/fcache.h"
1818

1919

20-
/*-----------------------------------------------------------------
21-
*
20+
/*
2221
* Build a 'FunctionCache' struct given the PG_PROC oid.
23-
*
24-
*-----------------------------------------------------------------
2522
*/
2623
FunctionCachePtr
2724
init_fcache(Oidfoid,intnargs,MemoryContextfcacheCxt)
2825
{
2926
MemoryContextoldcontext;
3027
FunctionCachePtrretval;
3128

29+
/* Safety check (should never fail, as parser should check sooner) */
30+
if (nargs>FUNC_MAX_ARGS)
31+
elog(ERROR,"init_fcache: too many arguments");
32+
3233
/* Switch to a context long-lived enough for the fcache entry */
3334
oldcontext=MemoryContextSwitchTo(fcacheCxt);
3435

@@ -38,25 +39,8 @@ init_fcache(Oid foid, int nargs, MemoryContext fcacheCxt)
3839
/* Set up the primary fmgr lookup information */
3940
fmgr_info(foid,&(retval->func));
4041

41-
/* Initialize unvarying fields of per-call info block */
42-
retval->fcinfo.flinfo=&(retval->func);
43-
retval->fcinfo.nargs=nargs;
44-
45-
if (nargs>FUNC_MAX_ARGS)
46-
elog(ERROR,"init_fcache: too many arguments");
47-
48-
/*
49-
* If function returns set, prepare a resultinfo node for
50-
* communication
51-
*/
52-
if (retval->func.fn_retset)
53-
{
54-
retval->fcinfo.resultinfo= (Node*)&(retval->rsinfo);
55-
retval->rsinfo.type=T_ReturnSetInfo;
56-
}
57-
58-
retval->argsValid= false;
59-
retval->hasSetArg= false;
42+
/* Initialize additional info */
43+
retval->setArgsValid= false;
6044

6145
MemoryContextSwitchTo(oldcontext);
6246

‎src/include/utils/fcache.h

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
1212
* Portions Copyright (c) 1994, Regents of the University of California
1313
*
14-
* $Id: fcache.h,v 1.16 2001/03/22 04:01:12 momjian Exp $
14+
* $Id: fcache.h,v 1.17 2001/09/21 00:11:31 tgl Exp $
1515
*
1616
*-------------------------------------------------------------------------
1717
*/
@@ -25,46 +25,42 @@
2525
* A FunctionCache record is built for all functions regardless of language.
2626
*
2727
* We store the fmgr lookup info to avoid recomputing it on each call.
28-
* We also store a prebuilt FunctionCallInfo struct. When evaluating a
29-
* function-returning-set, fcinfo holds the argument values across calls
30-
* so that we need not re-evaluate the arguments for each call. Even for
31-
* non-set functions, fcinfo saves a few cycles per call by allowing us to
32-
* avoid redundant setup of its fields.
28+
*
29+
* We also need to store argument values across calls when evaluating a
30+
* function-returning-set. This is pretty ugly (and not re-entrant);
31+
* current-evaluation info should be somewhere in the econtext, not in
32+
* the querytree. As it stands, a function-returning-set can't safely be
33+
* recursive, at least not if it's in plpgsql which will try to re-use
34+
* the querytree at multiple execution nesting levels. FIXME someday.
3335
*/
3436

3537
typedefstructFunctionCache
3638
{
37-
3839
/*
3940
* Function manager's lookup info for the target function.
4041
*/
4142
FmgrInfofunc;
4243

4344
/*
44-
* Per-call info for calling the target function. Unvarying fields
45-
* are set up by init_fcache(). Argument values are filled in as
46-
* needed.
47-
*/
48-
FunctionCallInfoDatafcinfo;
49-
50-
/*
51-
* "Resultinfo" node --- used only if target function returns a set.
52-
*/
53-
ReturnSetInforsinfo;
54-
55-
/*
56-
* argsValid is true when we are evaluating a set-valued function and
57-
* we are in the middle of a call series; we want to pass the same
45+
* setArgsValid is true when we are evaluating a set-valued function
46+
* and we are in the middle of a call series; we want to pass the same
5847
* argument values to the function again (and again, until it returns
5948
* ExprEndResult).
6049
*/
61-
boolargsValid;/* TRUE if fcinfo contains valid arguments */
50+
boolsetArgsValid;
6251

6352
/*
64-
*hasSetArg is true if we found a set-valued argument to the
53+
*Flag to remember whether we found a set-valued argument to the
6554
* function. This causes the function result to be a set as well.
55+
* Valid only when setArgsValid is true.
56+
*/
57+
boolsetHasSetArg;/* some argument returns a set */
58+
59+
/*
60+
* Current argument data for a set-valued function; contains valid
61+
* data only if setArgsValid is true.
6662
*/
67-
boolhasSetArg;/* some argument returns a set */
63+
FunctionCallInfoDatasetArgs;
6864
}FunctionCache;
6965

7066

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp